OSV-Scanner #3
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: 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 five sources of truth and skip vendored / test / worktree dirs. | |
| scan-args: |- | |
| --lockfile=uv.lock | |
| --lockfile=package-lock.json | |
| --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 | |
| 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" |