Files
proffesor-for-testing__agen…/.github/workflows/consumer-audit.yml
Dragan Spiridonov f03c3150ac ci: shift-left consumer-side supply-chain audit onto PRs and main
npm-publish.yml runs a consumer-POV tarball audit as a publish blocker, but
only at release time — so an override-masked vuln (root-only `overrides` don't
propagate to consumers) merges to main undetected and only surfaces as a late,
blocked release. This adds consumer-audit.yml running the SAME clean-consumer
tarball audit on every dependency-affecting PR and push to main, catching the
regression before merge so main stays releasable.

Mirrors the proven publish-time job: npm ci -> build -> npm pack -> install the
tarball into a fresh consumer project -> `npm audit --audit-level=high` from the
consumer's perspective (where our overrides do NOT apply). Path-filtered to
dependency-affecting files (package.json, package-lock.json, build/pack scripts,
.npmignore, this workflow), least-privilege (contents: read), concurrency-guarded.

Precedent: v3.9.13 shipped 15 CRITICAL protobufjs ACE vulns to every installer
while in-repo `npm audit` read 0 (GHSA-xq3m-2v4x-88gg). Verified: actionlint
clean; logic confirmed passing on the current v3.10.6 tree (consumer install
resolves protobufjs@7.6.3, 0 high/critical).

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-06-12 12:27:43 +00:00

106 lines
4.0 KiB
YAML

# =============================================================================
# Consumer-side Audit — shift-left supply-chain gate
# =============================================================================
#
# `npm audit` run inside THIS repo audits from our perspective, where the
# top-level `overrides` block in package.json forces patched versions of
# otherwise-vulnerable transitives (e.g. protobufjs >=7.5.6). But npm
# `overrides` are root-only — they do NOT propagate to downstream consumers.
# So an in-repo audit can read clean while `npm install agentic-qe`
# simultaneously exposes real high/critical CVEs to every user.
#
# npm-publish.yml already runs this exact check as a publish blocker, but only
# at release time. By then a masked vuln has already merged to `main` and the
# discovery is a late, disruptive blocked release. This workflow shifts the
# SAME consumer-POV audit LEFT — onto every dependency-affecting PR and every
# push to main — so the regression is caught before merge and main stays
# releasable.
#
# Precedent: at v3.9.13, 15 CRITICAL protobufjs ACE vulns
# (@xenova/transformers -> onnxruntime-web -> onnx-proto -> protobufjs,
# GHSA-xq3m-2v4x-88gg) shipped to every installer while in-repo `npm audit`
# reported 0 — invisible until a manual consumer-tarball install surfaced them.
# =============================================================================
name: Consumer Audit
on:
pull_request:
paths:
- 'package.json'
- 'package-lock.json'
- 'scripts/build-cli.mjs'
- 'scripts/build-mcp.mjs'
- '.npmignore'
- '.github/workflows/consumer-audit.yml'
push:
branches: [main]
paths:
- 'package.json'
- 'package-lock.json'
- 'scripts/build-cli.mjs'
- 'scripts/build-mcp.mjs'
- '.npmignore'
- '.github/workflows/consumer-audit.yml'
workflow_dispatch:
# Least-privilege: only reads the repo, packs a tarball, and audits.
permissions:
contents: read
# A newer commit on the same ref supersedes an in-flight audit.
concurrency:
group: consumer-audit-${{ github.ref }}
cancel-in-progress: true
env:
NODE_VERSION: '24.13.0'
jobs:
consumer-audit:
name: Consumer-side audit (tarball install)
runs-on: ubuntu-latest
timeout-minutes: 15
steps:
- name: Checkout code
uses: actions/checkout@v4
- name: Setup Node.js
uses: actions/setup-node@v4
with:
node-version: ${{ env.NODE_VERSION }}
cache: 'npm'
- name: Install dependencies
run: npm ci
- name: Build (so the packed tarball matches what publish ships)
run: npm run build
- name: Pack tarball
id: pack
run: |
TARBALL=$(npm pack --json | jq -r '.[0].filename')
echo "tarball=${TARBALL}" >> "$GITHUB_OUTPUT"
echo "Packed: ${TARBALL}"
- name: Audit against a clean consumer install
run: |
set -euo pipefail
CONSUMER_DIR=$(mktemp -d)
pushd "${CONSUMER_DIR}" >/dev/null
echo '{"name":"audit-consumer","version":"0.0.0","private":true}' > package.json
# Install our tarball exactly as a downstream consumer would. Our
# root-only `overrides` do NOT apply here — this is the user's view.
npm install --no-audit --no-fund "${GITHUB_WORKSPACE}/${{ steps.pack.outputs.tarball }}"
# The moment of truth: fail on any high/critical CVE a consumer sees.
if ! npm audit --audit-level=high; then
echo "::error::Consumer-side npm audit found high/critical vulnerabilities reachable by end users. \
In-repo overrides do NOT protect consumers — fix the dependency (upgrade, replace, or drop the vulnerable path) \
before merging. See .github/workflows/consumer-audit.yml header for context."
popd >/dev/null
exit 1
fi
popd >/dev/null
echo "Consumer-side audit clean — no high/critical CVEs reachable by end users."