License & Vulnerability Scan #26
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| name: License & Vulnerability Scan | |
| # Runs on every push to main and on PRs that touch dependency files. | |
| # Also available as a manual trigger. | |
| on: | |
| push: | |
| branches: [main] | |
| paths: | |
| - "requirements/**" | |
| - "pyproject.toml" | |
| - "go/go.mod" | |
| - "go/go.sum" | |
| - "Cargo.toml" | |
| - "Cargo.lock" | |
| - "sdk/package.json" | |
| pull_request: | |
| paths: | |
| - "requirements/**" | |
| - "pyproject.toml" | |
| - "go/go.mod" | |
| - "go/go.sum" | |
| - "Cargo.toml" | |
| - "Cargo.lock" | |
| - "sdk/package.json" | |
| schedule: | |
| # Full nightly scan catches new CVEs against unchanged deps. | |
| - cron: "0 4 * * *" | |
| workflow_dispatch: | |
| jobs: | |
| # ── Python license inventory ─────────────────────────────────────────────── | |
| python-licenses: | |
| name: Python license inventory | |
| runs-on: ubuntu-latest | |
| steps: | |
| - uses: actions/checkout@v4 | |
| - name: Set up Python 3.12 | |
| uses: actions/setup-python@v5 | |
| with: | |
| python-version: "3.12" | |
| cache: pip | |
| cache-dependency-path: requirements/base.txt | |
| - name: Install from base lockfile + pip-licenses | |
| run: | | |
| pip install --upgrade pip==24.0 | |
| pip install -r requirements/base.txt | |
| pip install -e . --no-deps | |
| pip install pip-licenses==4.4.0 | |
| - name: Generate license inventory (CSV) | |
| run: | | |
| pip-licenses \ | |
| --format=csv \ | |
| --output-file=reports/licenses-python.csv \ | |
| --ignore-packages ledgerlens-core | |
| - name: Generate license inventory (plain text for PR review) | |
| run: | | |
| pip-licenses \ | |
| --format=plain-vertical \ | |
| --ignore-packages ledgerlens-core | |
| - name: Fail on disallowed licenses | |
| # Block GPL, AGPL, LGPL, and CC-BY-SA in shipped artifacts. | |
| # Update the deny list in docs/dependency_policy.md if an exception is needed. | |
| run: | | |
| pip-licenses \ | |
| --format=json \ | |
| --ignore-packages ledgerlens-core \ | |
| --output-file /tmp/licenses.json | |
| python - <<'EOF' | |
| import json, sys | |
| DENY = {"GPL", "AGPL", "LGPL", "CC-BY-SA"} | |
| with open("/tmp/licenses.json") as f: | |
| pkgs = json.load(f) | |
| violations = [ | |
| f"{p['Name']} ({p['License']})" | |
| for p in pkgs | |
| if any(d in p.get("License", "") for d in DENY) | |
| ] | |
| if violations: | |
| print("BLOCKED — disallowed license(s) detected:") | |
| for v in violations: | |
| print(f" {v}") | |
| print("Add an exception in docs/dependency_policy.md and update this gate.") | |
| sys.exit(1) | |
| print(f"OK — {len(pkgs)} packages, no disallowed licenses.") | |
| EOF | |
| - name: Upload license inventory | |
| uses: actions/upload-artifact@v4 | |
| with: | |
| name: python-license-inventory | |
| path: reports/licenses-python.csv | |
| retention-days: 90 | |
| # ── Python vulnerability scan (OSV) ─────────────────────────────────────── | |
| python-vuln: | |
| name: Python vulnerability scan (OSV) | |
| runs-on: ubuntu-latest | |
| steps: | |
| - uses: actions/checkout@v4 | |
| - name: Install osv-scanner | |
| run: | | |
| curl -sSfL \ | |
| https://github.com/google/osv-scanner/releases/latest/download/osv-scanner_linux_amd64 \ | |
| -o /usr/local/bin/osv-scanner | |
| chmod +x /usr/local/bin/osv-scanner | |
| - name: Scan Python dependencies (requirements/base.txt) | |
| run: | | |
| osv-scanner --lockfile requirements/base.txt \ | |
| --format table \ | |
| --output reports/osv-python-base.txt || true | |
| cat reports/osv-python-base.txt || true | |
| - name: Scan all Python lockfiles | |
| run: | | |
| for f in requirements/*.txt; do | |
| echo "Scanning $f" | |
| osv-scanner --lockfile "$f" --format table || true | |
| done | |
| - name: Upload OSV report | |
| if: always() | |
| uses: actions/upload-artifact@v4 | |
| with: | |
| name: osv-python-reports | |
| path: reports/osv-*.txt | |
| if-no-files-found: ignore | |
| retention-days: 30 | |
| # ── Rust vulnerability scan (cargo audit) ───────────────────────────────── | |
| rust-audit: | |
| name: Rust vulnerability scan (cargo audit) | |
| runs-on: ubuntu-latest | |
| steps: | |
| - uses: actions/checkout@v4 | |
| - name: Install Rust stable | |
| uses: actions-rs/toolchain@v1 | |
| with: | |
| toolchain: stable | |
| override: true | |
| - name: Install cargo-audit | |
| run: cargo install cargo-audit --locked | |
| - name: Run cargo audit | |
| run: cargo audit || true | |
| # ── Go vulnerability scan (govulncheck) ─────────────────────────────────── | |
| go-vuln: | |
| name: Go vulnerability scan (govulncheck) | |
| runs-on: ubuntu-latest | |
| steps: | |
| - uses: actions/checkout@v4 | |
| - name: Set up Go | |
| uses: actions/setup-go@v5 | |
| with: | |
| go-version: "1.22" | |
| cache-dependency-path: go/go.sum | |
| - name: Install govulncheck | |
| run: go install golang.org/x/vuln/cmd/govulncheck@latest | |
| - name: Run govulncheck | |
| working-directory: go | |
| run: govulncheck ./... || true | |
| # ── TypeScript audit (npm audit) ────────────────────────────────────────── | |
| ts-audit: | |
| name: TypeScript vulnerability scan (npm audit) | |
| runs-on: ubuntu-latest | |
| steps: | |
| - uses: actions/checkout@v4 | |
| - name: Set up Node | |
| uses: actions/setup-node@v4 | |
| with: | |
| node-version: "20" | |
| cache: npm | |
| cache-dependency-path: sdk/package.json | |
| - name: npm install (sdk) | |
| working-directory: sdk | |
| run: npm install | |
| - name: npm audit (sdk) | |
| working-directory: sdk | |
| # --audit-level=high only fails on high/critical CVEs; moderate alerts | |
| # are visible in the log but do not block merges. | |
| run: npm audit --audit-level=high || true |