name: OSV-Scanner # Scans lockfiles (uv.lock, package-lock.json) against the OSV vulnerability # database. Runs on every PR/push (via the ci.yml orchestrator's workflow_call) # and on a weekly schedule against main. # # This is detection-only — OSV-Scanner does NOT open PRs or modify pins. # It reports known CVEs in currently-pinned dependency versions so we can # decide when and how to patch on our own schedule. Our pinning strategy # (full SHA / exact version) is preserved; only the notification signal # is added. # # Complements the supply-chain-audit.yml workflow (which scans for malicious # code patterns in PR diffs) by covering the orthogonal "currently-pinned # dep became known-vulnerable" case. # # Uses Google's officially-recommended reusable workflow, pinned by SHA. # Findings land in the repo's Security tab (Code Scanning > OSV-Scanner). # fail-on-vuln is disabled so the job does not block merges on pre-existing # vulnerabilities in pinned deps that we may need to patch deliberately. # # The reusable workflow can't emit custom outputs, so a wrapper job # downloads the SARIF result and summarizes the vulnerability count into # a review_status for the unified PR comment. on: workflow_call: schedule: # Weekly scan against main — catches CVEs published after merge for # deps that haven't changed since. - cron: '0 9 * * 1' workflow_dispatch: permissions: # Required to upload SARIF file to CodeQL. See: https://github.com/github/codeql-action/issues/2117 actions: read contents: read security-events: write jobs: scan: name: Scan lockfiles uses: google/osv-scanner-action/.github/workflows/osv-scanner-reusable.yml@9a498708959aeaef5ef730655706c5a1df1edbc2 # v2.3.8 with: # Scan explicit lockfiles rather than recursing, so we only look at # the three sources of truth and skip vendored / test / worktree dirs. scan-args: |- --lockfile=uv.lock --lockfile=package-lock.json --lockfile=website/package-lock.json fail-on-vuln: false emit-status: name: Emit review status runs-on: ubuntu-latest needs: scan if: always() outputs: review_status: ${{ steps.emit.outputs.review_status }} steps: - name: Checkout code uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2 - name: Download SARIF result uses: actions/download-artifact@d3f86a106a0bac45b974a628896c90dbdf5c8093 # v4 with: name: osv-results path: /tmp/osv-results continue-on-error: true - name: Emit review_status id: emit run: | set -euo pipefail STATUS="[]" if [ -f /tmp/osv-results/osv-results.sarif ]; then # Count vulnerabilities from the SARIF file VULN_COUNT=$(python3 -c " import json, sys try: with open('/tmp/osv-results/osv-results.sarif') as f: data = json.load(f) count = 0 vulns = [] for run in data.get('runs', []): for result in run.get('results', []): count += 1 rule_id = result.get('ruleId', 'unknown') message = result.get('message', {}).get('text', '') loc = result.get('locations', [{}])[0].get('physicalLocation', {}).get('artifactLocation', {}).get('uri', '') vulns.append(f'- {rule_id} in {loc}: {message}') print(count) if vulns: print('\n'.join(vulns[:20]), file=sys.stderr) except Exception: print(0) ") VULN_DETAIL="" if [ "$VULN_COUNT" -gt 0 ] 2>/dev/null; then VULN_PLURAL=$([ "$VULN_COUNT" -eq 1 ] && echo "y" || echo "ies") VULN_DETAIL=$(python3 -c " import json, sys try: with open('/tmp/osv-results/osv-results.sarif') as f: data = json.load(f) vulns = [] for run in data.get('runs', []): for result in run.get('results', []): rule_id = result.get('ruleId', 'unknown') loc = result.get('locations', [{}])[0].get('physicalLocation', {}).get('artifactLocation', {}).get('uri', '') vulns.append(f'- {rule_id} in {loc}') print(json.dumps('\n'.join(vulns[:20]))) except Exception: print(json.dumps('')) ") STATUS="[{\"source\":\"osv scan\",\"results\":[{\"kind\":\"warning\",\"title\":\"OSV vulnerability scan\",\"summary\":\"${VULN_COUNT} known vulnerabilit${VULN_PLURAL} found in pinned dependencies.\",\"detail\":${VULN_DETAIL},\"how_to_fix\":\"Review the findings in the [Security tab](../../security/code-scanning). Update the affected dependencies if a patched version is available.\"}]}]" else STATUS="[]" fi fi echo "review_status=${STATUS}" >> "$GITHUB_OUTPUT"