Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
21 changes: 21 additions & 0 deletions .github/workflows/audit.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
name: Dependency Audit
on: push

jobs:
audit:
name: yarn audit (high/critical must pass)
runs-on: ubuntu-latest
steps:
- name: Checkout repository
uses: actions/checkout@v2

- name: Use Node.js
uses: actions/setup-node@v2
with:
node-version: '20'

# Runs `yarn audit` against yarn.lock (no install needed) and fails on
# any high/critical advisory not allowlisted (with justification) in
# scripts/audit-allowlist.json.
- name: Audit dependencies
run: node scripts/audit-check.js
3 changes: 2 additions & 1 deletion package.json
Original file line number Diff line number Diff line change
Expand Up @@ -53,7 +53,7 @@
"file-loader": "^6.0.0",
"fs-extra": "^9.0.0",
"html-webpack-plugin": "4.2.1",
"http-proxy-middleware": "^1.0.3",
"http-proxy-middleware": "^3.0.6",
"mini-css-extract-plugin": "^0.9.0",
"minimist": "1.2.8",
"mocha": "^7.1.2",
Expand Down Expand Up @@ -135,6 +135,7 @@
"picomatch": "^2.3.2",
"async": "^3.2.2",
"webpack-dev-middleware": "^5.3.4",
"websocket-driver": "^0.7.5",
"ansi-regex": "^5.0.1",
"ansi-html": "0.0.8",
"braces": "^3.0.3",
Expand Down
12 changes: 12 additions & 0 deletions scripts/audit-allowlist.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
[
{
"id": "GHSA-v7cw-rxgx-j29p",
"module": "compound-components",
"justification": "False positive: our compound-components is installed directly from the Compound-Foundation GitHub repo (pinned commit), not from the npm registry. The advisory flags a malicious package squatting the same name on the public registry; yarn audit matches by name only."
},
{
"id": "GHSA-2p57-rm9w-gvfp",
"module": "ip",
"justification": "No patched version of `ip` exists (advisory covers <=2.0.1, i.e. every release). Dev-only dependency of webpack-dev-server v3 (local dev server); not part of the production build. Remove when webpack-dev-server is upgraded to v4+, which no longer depends on `ip`."
}
]
72 changes: 72 additions & 0 deletions scripts/audit-check.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,72 @@
#!/usr/bin/env node
// CI gate: fail if `yarn audit` reports any high/critical advisory.
//
// A wrapper is needed because yarn 1's `--level` flag only filters the
// report — the exit code is a severity bitmask that still reflects lower
// severities, so `yarn audit --level high` can't be used directly as a gate.
//
// Advisories listed in scripts/audit-allowlist.json (with a written
// justification) are skipped.

const { spawnSync } = require('child_process');
const fs = require('fs');
const path = require('path');

const allowlistPath = path.join(__dirname, 'audit-allowlist.json');
const allowlist = fs.existsSync(allowlistPath)
? JSON.parse(fs.readFileSync(allowlistPath, 'utf8'))
: [];
const allowedIds = new Map(allowlist.map((e) => [e.id, e]));

const res = spawnSync('yarn', ['audit', '--json'], {
cwd: path.join(__dirname, '..'),
encoding: 'utf8',
maxBuffer: 64 * 1024 * 1024,
});

const failing = new Map();
const skipped = new Map();
let sawSummary = false;

for (const line of (res.stdout || '').split('\n')) {
if (!line.trim()) continue;
let event;
try {
event = JSON.parse(line);
} catch {
continue;
}
if (event.type === 'auditSummary') sawSummary = true;
if (event.type !== 'auditAdvisory') continue;
const a = event.data.advisory;
if (a.severity !== 'high' && a.severity !== 'critical') continue;
const id = a.github_advisory_id || String(a.id);
if (allowedIds.has(id)) {
skipped.set(id, a);
} else {
failing.set(id, a);
}
}

if (!sawSummary) {
console.error('audit-check: `yarn audit` produced no summary — audit did not run correctly.');
console.error(res.stderr || '');
process.exit(1);
}

for (const [id, a] of skipped) {
console.log(`ALLOWLISTED ${a.severity}: ${a.module_name} (${id}) — ${allowedIds.get(id).justification}`);
}

if (failing.size > 0) {
console.error('\naudit-check: high/critical vulnerabilities found:\n');
for (const [id, a] of failing) {
console.error(` ${a.severity.toUpperCase()}: ${a.module_name} — ${a.title}`);
console.error(` ${a.url || 'https://github.com/advisories/' + id}`);
console.error(` vulnerable: ${a.vulnerable_versions} patched: ${a.patched_versions}\n`);
}
console.error(`${failing.size} advisory(ies) must be fixed (or allowlisted with justification in scripts/audit-allowlist.json).`);
process.exit(1);
}

console.log('audit-check: no unaddressed high/critical vulnerabilities.');
43 changes: 6 additions & 37 deletions yarn.lock
Original file line number Diff line number Diff line change
Expand Up @@ -2557,13 +2557,6 @@
dependencies:
"@types/node" "*"

"@types/http-proxy@^1.17.5":
version "1.17.7"
resolved "https://registry.yarnpkg.com/@types/http-proxy/-/http-proxy-1.17.7.tgz#30ea85cc2c868368352a37f0d0d3581e24834c6f"
integrity sha512-9hdj6iXH64tHSLTY+Vt2eYOGzSogC+JQ2H7bdPWkuh7KXP5qLllWx++t+K9Wk556c3dkDdPws/SpMRi0sdCT1w==
dependencies:
"@types/node" "*"

"@types/json-schema@^7.0.5", "@types/json-schema@^7.0.8":
version "7.0.9"
resolved "https://registry.yarnpkg.com/@types/json-schema/-/json-schema-7.0.9.tgz#97edc9037ea0c38585320b28964dde3b39e4660d"
Expand Down Expand Up @@ -3720,7 +3713,7 @@ brace-expansion@^2.0.2:
dependencies:
balanced-match "^1.0.0"

braces@^2.3.1, braces@^2.3.2, braces@^3.0.1, braces@^3.0.3, braces@~3.0.2:
braces@^2.3.1, braces@^2.3.2, braces@^3.0.3, braces@~3.0.2:
version "3.0.3"
resolved "https://registry.yarnpkg.com/braces/-/braces-3.0.3.tgz#490332f40919452272d55a8480adc0c441358789"
integrity sha512-yQbXgO/OSZVD2IsiLlro+7Hf6Q18EJrKSEsdoMzKePKXct3gvD8oLcOQdIzGupr5Fj+EDe8gO/lxc1BzfMpxvA==
Expand Down Expand Up @@ -7166,17 +7159,6 @@ http-proxy-middleware@0.19.1, http-proxy-middleware@^3.0.6:
is-plain-object "^5.0.0"
micromatch "^4.0.8"

http-proxy-middleware@^1.0.3:
version "1.3.1"
resolved "https://registry.yarnpkg.com/http-proxy-middleware/-/http-proxy-middleware-1.3.1.tgz#43700d6d9eecb7419bf086a128d0f7205d9eb665"
integrity sha512-13eVVDYS4z79w7f1+NPllJtOQFx/FdUW4btIvVRMaRlUY9VGstAbo5MOhLEuUgZFRHn3x50ufn25zkj/boZnEg==
dependencies:
"@types/http-proxy" "^1.17.5"
http-proxy "^1.18.1"
is-glob "^4.0.1"
is-plain-obj "^3.0.0"
micromatch "^4.0.2"

http-proxy@^1.18.1:
version "1.18.1"
resolved "https://registry.yarnpkg.com/http-proxy/-/http-proxy-1.18.1.tgz#401541f0534884bbf95260334e72f88ee3976549"
Expand Down Expand Up @@ -7736,11 +7718,6 @@ is-plain-obj@^1.0.0:
resolved "https://registry.yarnpkg.com/is-plain-obj/-/is-plain-obj-1.1.0.tgz#71a50c8429dfca773c92a390a4a03b39fcd51d3e"
integrity sha1-caUMhCnfync8kqOQpKA7OfzVHT4=

is-plain-obj@^3.0.0:
version "3.0.0"
resolved "https://registry.yarnpkg.com/is-plain-obj/-/is-plain-obj-3.0.0.tgz#af6f2ea14ac5a646183a5bbdb5baabbc156ad9d7"
integrity sha512-gwsOE28k+23GP1B6vFl1oVh/WOzmawBrKwo5Ev6wMKzPkaXaCDIQKzLnvsA42DRlbVTWorkgTKIviAKCWkfUwA==

is-plain-object@^2.0.3, is-plain-object@^2.0.4:
version "2.0.4"
resolved "https://registry.yarnpkg.com/is-plain-object/-/is-plain-object-2.0.4.tgz#2c163b3fafb1b606d9d17928f05c2a1c38e07677"
Expand Down Expand Up @@ -8448,14 +8425,6 @@ micromatch@^3.1.10, micromatch@^3.1.4:
snapdragon "^0.8.1"
to-regex "^3.0.2"

micromatch@^4.0.2:
version "4.0.4"
resolved "https://registry.yarnpkg.com/micromatch/-/micromatch-4.0.4.tgz#896d519dfe9db25fce94ceb7a500919bf881ebf9"
integrity sha512-pRmzw/XUcwXGpD9aI9q/0XOwLNygjETJ8y0ao0wdqprrzDa4YnxLcz7fQRZr8voh8V10kGhABbNcHVk5wHgWwg==
dependencies:
braces "^3.0.1"
picomatch "^2.2.3"

micromatch@^4.0.8:
version "4.0.8"
resolved "https://registry.yarnpkg.com/micromatch/-/micromatch-4.0.8.tgz#d66fa18f3a47076789320b9b1af32bd86d9fa202"
Expand Down Expand Up @@ -9425,7 +9394,7 @@ picocolors@^1.1.1:
resolved "https://registry.yarnpkg.com/picocolors/-/picocolors-1.1.1.tgz#3d321af3eab939b083c8f929a1d12cda81c26b6b"
integrity sha512-xceH2snhtb5M9liqDsmEw56le376mTZkEX/jEb/RxNFyegNul7eNslCXP9FDj/Lcu0X8KEyMceP2ntpaHrDEVA==

picomatch@^2.0.4, picomatch@^2.2.1, picomatch@^2.2.3, picomatch@^2.3.1, picomatch@^2.3.2, picomatch@^4.0.3:
picomatch@^2.0.4, picomatch@^2.2.1, picomatch@^2.3.1, picomatch@^2.3.2, picomatch@^4.0.3:
version "2.3.2"
resolved "https://registry.yarnpkg.com/picomatch/-/picomatch-2.3.2.tgz#5a942915e26b372dc0f0e6753149a16e6b1c5601"
integrity sha512-V7+vQEJ06Z+c5tSye8S+nHUfI51xoXIXjHQ99cQtKUkQqqO1kO/KCJUfZXuB47h/YBlDhah2H3hdUGXn8ie0oA==
Expand Down Expand Up @@ -12347,10 +12316,10 @@ webpack@^4.35.2:
watchpack "^1.7.4"
webpack-sources "^1.4.1"

websocket-driver@>=0.5.1, websocket-driver@^0.7.4:
version "0.7.4"
resolved "https://registry.yarnpkg.com/websocket-driver/-/websocket-driver-0.7.4.tgz#89ad5295bbf64b480abcba31e4953aca706f5760"
integrity sha512-b17KeDIQVjvb0ssuSDF2cYXSg2iztliJ4B9WdsuB6J952qCPKmnVq4DyW5motImXHDC1cBT/1UezrJVsKw5zjg==
websocket-driver@>=0.5.1, websocket-driver@^0.7.4, websocket-driver@^0.7.5:
version "0.7.5"
resolved "https://registry.yarnpkg.com/websocket-driver/-/websocket-driver-0.7.5.tgz#569d22764ab21f2de20af0e74b411e8ae5a0fa46"
integrity sha512-ZL2+3c7kMBdIRCMz6l8jQMHyGVxj+UL+xVk74Ombiciboca8rHa15L86B19E5oh1pL9Ii/uj54gtsIrZGMo6zA==
dependencies:
http-parser-js ">=0.5.1"
safe-buffer ">=5.1.0"
Expand Down
Loading