From 72f966eb2e4f2f0554e694e33f94a318f6e1ebba Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=D0=98=D0=BB=D1=8C=D1=8F=20=D0=9B=D1=8F=D1=89=D1=83=D0=BA?= <40496434+prog-time@users.noreply.github.com> Date: Wed, 22 Apr 2026 22:18:02 +0300 Subject: [PATCH 1/3] issues-15|add pip-audit CI snippet --- CI/security/pip-audit.yml | 44 +++++++++++++++++++++++++++++ scripts/CI/security/pip-audit.yml | 14 +++++++++ scripts/shell/security/pip-audit.sh | 31 ++++++++++++++++++++ 3 files changed, 89 insertions(+) create mode 100644 CI/security/pip-audit.yml create mode 100644 scripts/CI/security/pip-audit.yml create mode 100644 scripts/shell/security/pip-audit.sh diff --git a/CI/security/pip-audit.yml b/CI/security/pip-audit.yml new file mode 100644 index 0000000..bc7b065 --- /dev/null +++ b/CI/security/pip-audit.yml @@ -0,0 +1,44 @@ +pip-audit: + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@v4 + + - uses: actions/setup-python@v5 + with: + python-version: "3.12" + + - name: Install pip-audit + run: pip install pip-audit==2.7.3 + + - name: Run pip-audit + run: | + set -euo pipefail + + if ! command -v pip-audit &> /dev/null; then + echo "::error::pip-audit not found. Install it before running this script." + exit 1 + fi + + REQ_FILE="" + if [[ -f "requirements.txt" ]]; then + REQ_FILE="requirements.txt" + elif [[ -f "pyproject.toml" ]]; then + REQ_FILE="pyproject.toml" + elif [[ -f "Pipfile.lock" ]]; then + REQ_FILE="Pipfile.lock" + fi + + if [[ -z "$REQ_FILE" ]]; then + echo "::error::No Python requirements file found (requirements.txt, pyproject.toml, or Pipfile.lock)." + exit 1 + fi + + echo "ℹ️ Running pip-audit on $REQ_FILE..." + + if [[ "$REQ_FILE" == "requirements.txt" ]]; then + pip-audit --strict -r requirements.txt + else + pip-audit --strict + fi + + echo "✅ pip-audit passed" diff --git a/scripts/CI/security/pip-audit.yml b/scripts/CI/security/pip-audit.yml new file mode 100644 index 0000000..f3b731b --- /dev/null +++ b/scripts/CI/security/pip-audit.yml @@ -0,0 +1,14 @@ +pip-audit: + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@v4 + + - uses: actions/setup-python@v5 + with: + python-version: "3.12" + + - name: Install pip-audit + run: pip install pip-audit==2.7.3 + + - name: Run pip-audit + run: bash scripts/shell/security/pip-audit.sh diff --git a/scripts/shell/security/pip-audit.sh b/scripts/shell/security/pip-audit.sh new file mode 100644 index 0000000..6d2c281 --- /dev/null +++ b/scripts/shell/security/pip-audit.sh @@ -0,0 +1,31 @@ +#!/usr/bin/env bash +set -euo pipefail + +if ! command -v pip-audit &> /dev/null; then + echo "::error::pip-audit not found. Install it before running this script." + exit 1 +fi + +REQ_FILE="" +if [[ -f "requirements.txt" ]]; then + REQ_FILE="requirements.txt" +elif [[ -f "pyproject.toml" ]]; then + REQ_FILE="pyproject.toml" +elif [[ -f "Pipfile.lock" ]]; then + REQ_FILE="Pipfile.lock" +fi + +if [[ -z "$REQ_FILE" ]]; then + echo "::error::No Python requirements file found (requirements.txt, pyproject.toml, or Pipfile.lock)." + exit 1 +fi + +echo "ℹ️ Running pip-audit on $REQ_FILE..." + +if [[ "$REQ_FILE" == "requirements.txt" ]]; then + pip-audit --strict -r requirements.txt +else + pip-audit --strict +fi + +echo "✅ pip-audit passed" From 4a6701d6688fb4d86b0e5a6e4088744d7ea9617e Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=D0=98=D0=BB=D1=8C=D1=8F=20=D0=9B=D1=8F=D1=89=D1=83=D0=BA?= <40496434+prog-time@users.noreply.github.com> Date: Wed, 22 Apr 2026 22:18:02 +0300 Subject: [PATCH 2/3] issues-15|add BATS tests for pip-audit --- tests/security/pip-audit.bats | 57 +++++++++++++++++++++++++++++++++++ 1 file changed, 57 insertions(+) create mode 100644 tests/security/pip-audit.bats diff --git a/tests/security/pip-audit.bats b/tests/security/pip-audit.bats new file mode 100644 index 0000000..99b12e1 --- /dev/null +++ b/tests/security/pip-audit.bats @@ -0,0 +1,57 @@ +#!/usr/bin/env bats + +load "../helpers/common" + +SCRIPT="$BATS_TEST_DIRNAME/../../scripts/shell/security/pip-audit.sh" + +setup() { + setup_test_dir + mkdir -p "$TEST_DIR/bin" + export PATH="$TEST_DIR/bin:$PATH" +} + +teardown() { + teardown_test_dir +} + +make_pip_audit_stub() { + local exit_code="$1" + cat > "$TEST_DIR/bin/pip-audit" < "$TEST_DIR/requirements.txt" + cd "$TEST_DIR" + run bash "$SCRIPT" + [ "$status" -eq 0 ] + [[ "$output" == *"✅ pip-audit passed"* ]] +} + +@test "vulnerable pin in requirements.txt: exits 1 with failure message" { + make_pip_audit_stub 1 + echo "django==2.2.0" > "$TEST_DIR/requirements.txt" + cd "$TEST_DIR" + run bash "$SCRIPT" + [ "$status" -eq 1 ] + [[ "$output" != *"✅ pip-audit passed"* ]] +} From 22a5fc4254cde2ab5cd9f0410ad5519d9a90f62f Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=D0=98=D0=BB=D1=8C=D1=8F=20=D0=9B=D1=8F=D1=89=D1=83=D0=BA?= <40496434+prog-time@users.noreply.github.com> Date: Wed, 22 Apr 2026 22:18:02 +0300 Subject: [PATCH 3/3] issues-15|document pip-audit in README --- README.md | 9 +++++++-- 1 file changed, 7 insertions(+), 2 deletions(-) diff --git a/README.md b/README.md index b168c41..7733a7d 100644 --- a/README.md +++ b/README.md @@ -10,8 +10,9 @@ that projects compose into their own workflows. | Tool | Category | File | |------|----------|------| | gitleaks | security | [CI/security/gitleaks.yml](https://github.com/prog-time/workflows/blob/main/CI/security/gitleaks.yml) | -| trivy | security | [CI/security/trivy.yml](https://github.com/prog-time/workflows/blob/main/CI/security/trivy.yml) | +| pip-audit | security | [CI/security/pip-audit.yml](https://github.com/prog-time/workflows/blob/main/CI/security/pip-audit.yml) | | semgrep | security | [CI/security/semgrep.yml](https://github.com/prog-time/workflows/blob/main/CI/security/semgrep.yml) | +| trivy | security | [CI/security/trivy.yml](https://github.com/prog-time/workflows/blob/main/CI/security/trivy.yml) | | Clippy | linters | [CI/linters/clippy.yml](https://github.com/prog-time/workflows/blob/main/CI/linters/clippy.yml) | | ESLint | linters | [CI/linters/eslint.yml](https://github.com/prog-time/workflows/blob/main/CI/linters/eslint.yml) | | golangci-lint | linters | [CI/linters/golangci-lint.yml](https://github.com/prog-time/workflows/blob/main/CI/linters/golangci-lint.yml) | @@ -92,6 +93,7 @@ Workflows/ │ │ │ └── yamllint.yml │ │ ├── security/ │ │ │ ├── gitleaks.yml +│ │ │ ├── pip-audit.yml │ │ │ ├── semgrep.yml │ │ │ └── trivy.yml │ │ ├── static_analysis/ @@ -126,6 +128,7 @@ Workflows/ │ │ └── yamllint.sh │ ├── security/ │ │ ├── gitleaks.sh +│ │ ├── pip-audit.sh │ │ ├── semgrep.sh │ │ └── trivy.sh │ └── tests/ @@ -152,6 +155,7 @@ Workflows/ │ │ └── yamllint.bats │ ├── security/ │ │ ├── gitleaks.bats +│ │ ├── pip-audit.bats │ │ ├── semgrep.bats │ │ └── trivy.bats │ ├── tests/ @@ -229,8 +233,9 @@ shellcheck: | Snippet | Tool | What it checks | |---------|------|----------------| | `CI/security/gitleaks.yml` | [gitleaks](https://github.com/gitleaks/gitleaks) | Hardcoded secrets, tokens, and API keys | -| `CI/security/trivy.yml` | [trivy](https://github.com/aquasecurity/trivy) | CVEs in OS packages, container images, and dependency manifests | +| `CI/security/pip-audit.yml` | [pip-audit](https://github.com/pypa/pip-audit) | CVEs in Python dependencies via the PyPI Advisory Database | | `CI/security/semgrep.yml` | [semgrep](https://semgrep.dev) | OWASP Top 10 patterns and insecure coding patterns across Python, JS/TS, Go, Java, Ruby, and more | +| `CI/security/trivy.yml` | [trivy](https://github.com/aquasecurity/trivy) | CVEs in OS packages, container images, and dependency manifests | ### Linters