diff --git a/.github/workflows/ci.yaml b/.github/workflows/ci.yaml index 1d4c3638cd..973f4b0917 100644 --- a/.github/workflows/ci.yaml +++ b/.github/workflows/ci.yaml @@ -22,8 +22,6 @@ on: permissions: contents: read pull-requests: write # needed by lint (PR comment) + supply-chain review_status - actions: read # needed by osv-scanner (SARIF upload) - security-events: write # needed by osv-scanner (SARIF upload) concurrency: group: ci-${{ github.ref }} @@ -215,10 +213,6 @@ jobs: mcp_catalog: ${{ needs.detect.outputs.mcp_catalog == 'true' }} supply_chain: ${{ needs.supply-chain.outputs.critical_findings == 'true' }} - osv-scanner: - name: OSV scan - uses: ./.github/workflows/osv-scanner.yml - # ───────────────────────────────────────────────────────────────────── # Gate: runs after everything. ``if: always()`` ensures it reports a # status even when some deps were skipped. Only actual ``failure`` @@ -250,11 +244,10 @@ jobs: - profile-artifact-check - supply-chain - review-labels - # osv-scanner is deliberately NOT a dependency: osv-scanner.yml is - # detection-only (fail-on-vuln: false, findings go to the Security tab) - # and its SARIF upload trips GitHub's per-installation API rate limit - # during merge trains, which turned an advisory scan into a merge - # blocker for whole batches of unrelated PRs. + # OSV runs weekly against main (osv-scanner.yml schedule), not per PR: + # every PR was reporting the same repo-wide baseline of pinned-dep CVEs + # in its review comment, and the SARIF upload tripped GitHub's + # per-installation API rate limit during merge trains. # The image build runs in its own workflow (docker.yml) and reports # its own check. It was never required here, because it is too slow # to block a merge. A separate run also stops it from holding this diff --git a/.github/workflows/osv-scanner.yml b/.github/workflows/osv-scanner.yml index 2e61fcc92c..c7634bb833 100644 --- a/.github/workflows/osv-scanner.yml +++ b/.github/workflows/osv-scanner.yml @@ -1,8 +1,10 @@ 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. +# database. Runs on a weekly schedule against main (and on manual dispatch); +# it is deliberately NOT part of per-PR CI — the findings are the repo-wide +# baseline of pinned-dep CVEs, identical for every PR, and belong in the +# Security tab, not in each PR's review comment. # # 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 @@ -18,13 +20,8 @@ name: OSV-Scanner # 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. @@ -50,97 +47,6 @@ jobs: --lockfile=website/package-lock.json --lockfile=plugins/platforms/photon/sidecar/package-lock.json --lockfile=scripts/whatsapp-bridge/package-lock.json - # The upstream reusable workflow uploads this exact file under its - # fixed artifact name, which the wrapper downloads below. results-file-name: osv-results.sarif fail-on-vuln: false - emit-status: - name: Emit review status - runs-on: ubuntu-latest - # Downloads one small SARIF artifact and runs two inline python snippets — - # minutes of work. Bound it so a wedged artifact download can't hold a - # runner for GitHub's 6-hour default (the only unbounded job left in - # .github/workflows; every other workflow already sets timeout-minutes). - timeout-minutes: 10 - 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 Scanner SARIF file - 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" - echo "review_status=${STATUS}" > review-status.json - - - name: Upload review status artifact - if: always() && steps.emit.outcome != 'skipped' - continue-on-error: true - uses: actions/upload-artifact@043fb46d1a93c77aae656e7c1c64a875d1fc6a0a # v7 - with: - name: review-status-osv-scanner - path: review-status.json - retention-days: 1 - overwrite: true - if-no-files-found: ignore