From 8eedac66b76cd91039891b3717a3f64fed4a23fc Mon Sep 17 00:00:00 2001 From: Marco D'Aleo Date: Sat, 3 Oct 2026 09:51:31 +0100 Subject: [PATCH] Harden lint workflow and add GitHub mirror support --- .gitea/workflows/lint-and-security.yml | 65 ++++++++++++++++---- .github/CODEOWNERS | 1 + .github/workflows/lint-and-security.yml | 79 +++++++++++++++++++++++++ 3 files changed, 134 insertions(+), 11 deletions(-) create mode 100644 .github/CODEOWNERS create mode 100644 .github/workflows/lint-and-security.yml diff --git a/.gitea/workflows/lint-and-security.yml b/.gitea/workflows/lint-and-security.yml index 3c6bf54..b6f1f6c 100644 --- a/.gitea/workflows/lint-and-security.yml +++ b/.gitea/workflows/lint-and-security.yml @@ -2,35 +2,78 @@ name: Lint & Security on: pull_request: + workflow_dispatch: + +permissions: + contents: read + +concurrency: + group: ${{ github.workflow }}-${{ github.ref }} + cancel-in-progress: true jobs: precommit-and-security: + name: lint-and-security runs-on: ubuntu-latest + timeout-minutes: 15 steps: - name: Checkout code uses: actions/checkout@v4 + with: + persist-credentials: false - name: Set up Python uses: actions/setup-python@v5 with: python-version: "3.13" - - name: Install pre-commit - run: pip install pre-commit + - name: Install Poetry + run: pip install "poetry>=2,<3" + + - name: Install dev dependencies (lockfile-pinned pre-commit) + run: poetry install --with dev --no-root - name: Run pre-commit hooks - run: pre-commit run --all-files --color always - - - name: Install Poetry - run: | - pip install "poetry>=2,<3" - poetry self add poetry-plugin-export + run: poetry run pre-commit run --all-files --color always - name: Install pip-audit - run: pip install pip-audit + run: pip install "pip-audit==2.10.1" - name: Audit dependencies (Poetry lockfile) run: | - poetry export -f requirements.txt --without-hashes \ - | pip-audit -r /dev/stdin + set -euo pipefail + + poetry self add poetry-plugin-export + poetry export -f requirements.txt --without-hashes -o requirements-audit.txt + + # pip-audit exits non-zero for ANY vulnerability; capture the report, then gate below. + pip-audit --no-deps -r requirements-audit.txt -f json -o pip-audit.json || true + + echo "Fixable vulnerabilities:" + jq -r ' + .dependencies[]? + | select(has("vulns")) + | . as $d + | .vulns[] + | select((.fix_versions | length) > 0) + | "\($d.name)@\($d.version) -> \(.id) [fix: \(.fix_versions | join(", "))]" + ' pip-audit.json + + echo + echo "All reported vulnerabilities:" + jq -r ' + .dependencies[]? + | select(has("vulns")) + | . as $d + | .vulns[] + | "\($d.name)@\($d.version) -> \(.id) [fix: \(if (.fix_versions | length) > 0 then (.fix_versions | join(", ")) else "none" end)]" + ' pip-audit.json + + # Fail only on fixable findings (mirrors the Grype policy in security-scan.yml). + # The type guard ensures a crashed/empty report fails rather than silently passing. + jq -e ' + (.dependencies | type) == "array" + and + ( [ .dependencies[] | (.vulns // [])[] | select((.fix_versions | length) > 0) ] | length == 0 ) + ' pip-audit.json diff --git a/.github/CODEOWNERS b/.github/CODEOWNERS new file mode 100644 index 0000000..e7e65c3 --- /dev/null +++ b/.github/CODEOWNERS @@ -0,0 +1 @@ +* @mdaleo404 diff --git a/.github/workflows/lint-and-security.yml b/.github/workflows/lint-and-security.yml new file mode 100644 index 0000000..b6f1f6c --- /dev/null +++ b/.github/workflows/lint-and-security.yml @@ -0,0 +1,79 @@ +name: Lint & Security + +on: + pull_request: + workflow_dispatch: + +permissions: + contents: read + +concurrency: + group: ${{ github.workflow }}-${{ github.ref }} + cancel-in-progress: true + +jobs: + precommit-and-security: + name: lint-and-security + runs-on: ubuntu-latest + timeout-minutes: 15 + + steps: + - name: Checkout code + uses: actions/checkout@v4 + with: + persist-credentials: false + + - name: Set up Python + uses: actions/setup-python@v5 + with: + python-version: "3.13" + + - name: Install Poetry + run: pip install "poetry>=2,<3" + + - name: Install dev dependencies (lockfile-pinned pre-commit) + run: poetry install --with dev --no-root + + - name: Run pre-commit hooks + run: poetry run pre-commit run --all-files --color always + + - name: Install pip-audit + run: pip install "pip-audit==2.10.1" + + - name: Audit dependencies (Poetry lockfile) + run: | + set -euo pipefail + + poetry self add poetry-plugin-export + poetry export -f requirements.txt --without-hashes -o requirements-audit.txt + + # pip-audit exits non-zero for ANY vulnerability; capture the report, then gate below. + pip-audit --no-deps -r requirements-audit.txt -f json -o pip-audit.json || true + + echo "Fixable vulnerabilities:" + jq -r ' + .dependencies[]? + | select(has("vulns")) + | . as $d + | .vulns[] + | select((.fix_versions | length) > 0) + | "\($d.name)@\($d.version) -> \(.id) [fix: \(.fix_versions | join(", "))]" + ' pip-audit.json + + echo + echo "All reported vulnerabilities:" + jq -r ' + .dependencies[]? + | select(has("vulns")) + | . as $d + | .vulns[] + | "\($d.name)@\($d.version) -> \(.id) [fix: \(if (.fix_versions | length) > 0 then (.fix_versions | join(", ")) else "none" end)]" + ' pip-audit.json + + # Fail only on fixable findings (mirrors the Grype policy in security-scan.yml). + # The type guard ensures a crashed/empty report fails rather than silently passing. + jq -e ' + (.dependencies | type) == "array" + and + ( [ .dependencies[] | (.vulns // [])[] | select((.fix_versions | length) > 0) ] | length == 0 ) + ' pip-audit.json