ci: run the OSV lockfile scan weekly against main, not on every PR
The scan is detection-only and its findings are the repo-wide baseline of CVEs in pinned dependencies — identical for every PR, unrelated to any PR's diff. Reporting that baseline in each PR's review comment read as "this PR has 76 vulnerabilities" to contributors, and the SARIF upload tripped GitHub's per-installation API rate limit during merge trains. The scheduled weekly run (plus workflow_dispatch) keeps feeding the Security tab; the per-PR workflow_call, the review_status wrapper job, and the orchestrator's now-unneeded SARIF permissions are removed.
This commit is contained in:
15
.github/workflows/ci.yaml
vendored
15
.github/workflows/ci.yaml
vendored
@@ -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
|
||||
|
||||
102
.github/workflows/osv-scanner.yml
vendored
102
.github/workflows/osv-scanner.yml
vendored
@@ -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
|
||||
|
||||
Reference in New Issue
Block a user