diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index d70d30c0a..c8c9e60a9 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -152,21 +152,30 @@ jobs: - name: Run npm audit working-directory: frontend run: | - # Only audit production dependencies — dev dependency vulnerabilities - # don't affect end users. Detailed audits are handled by security.yml. + # Only audit production dependencies and filter out npm-internal packages + # (npm audit reports vulnerabilities in its own bundled deps like tar/minimatch). npm audit --omit=dev --json > /tmp/audit.json 2>/dev/null || true + npm ls --omit=dev --all --json 2>/dev/null > /tmp/deps.json || true python3 -c " - import json, sys + import json, sys, subprocess data = json.load(open('/tmp/audit.json')) + deps = json.load(open('/tmp/deps.json')) + prod = set() + def walk(obj): + for name, info in (obj.get('dependencies') or {}).items(): + prod.add(name) + walk(info) + walk(deps) vulns = data.get('vulnerabilities', {}) fixable = {n: v for n, v in vulns.items() - if v.get('severity') in ('high', 'critical') and v.get('fixAvailable')} + if n in prod and v.get('severity') in ('high', 'critical') and v.get('fixAvailable')} + skipped = len(vulns) - len({n: v for n, v in vulns.items() if n in prod}) if fixable: for name, v in fixable.items(): print(f'FIXABLE {v[\"severity\"].upper()}: {name}') sys.exit(1) - total = sum(1 for v in vulns.values() if v.get('severity') in ('high', 'critical')) - print(f'npm audit: {total} high/critical (0 fixable), {len(vulns)} total known') + total = sum(1 for n, v in vulns.items() if n in prod and v.get('severity') in ('high', 'critical')) + print(f'npm audit: {total} high/critical (0 fixable), {len(vulns)} total ({skipped} npm-internal filtered)') " frontend-typecheck: