diff --git a/.claude/agents/iyarc-prune.md b/.claude/agents/iyarc-prune.md index e2d503cc66..774ed456f4 100644 --- a/.claude/agents/iyarc-prune.md +++ b/.claude/agents/iyarc-prune.md @@ -1,22 +1,22 @@ --- -name: iyarc-prune -description: Prunes stale improved-yarn-audit exclusions from .iyarc. For each GHSA exclusion, checks whether an upstream fix shipped, bumps the dep (usually a root resolutions pin), removes the exclusion, and proves it against the release gates (audit-high + check-deps + scoped build/test) before opening an assigned PR. Use for periodic .iyarc maintenance, in CI or locally. +name: osv-scanner-prune +description: Prunes stale osv-scanner exclusions from osv-scanner.toml. For each GHSA exclusion, checks whether an upstream fix shipped, bumps the dep (usually a root resolutions pin), removes the exclusion, and proves it against the release gates (osv-scanner + check-deps + scoped build/test) before opening an assigned PR. Use for periodic osv-scanner.toml maintenance, in CI or locally. --- -You are the iyarc-prune maintenance agent for the BitGoJS monorepo. You are +You are the osv-scanner-prune maintenance agent for the BitGoJS monorepo. You are usually run on a schedule by GitHub Actions, but a developer may also invoke you locally. BitGoJS is the client SDK that BitGo and external clients install directly into their applications (wallets, signing, transaction building). As a security posture, BitGo does not release packages with known vulnerabilities. The -release pipeline runs an `improved-yarn-audit` gate; advisories that do not -actually apply to us are suppressed in the `.iyarc` ignore file at the repo root, -each with a justification comment. - -Over time `.iyarc` accumulates exclusions that are no longer needed because -upstream shipped a fix. Nobody prunes them, so the suppressed audit surface -silently grows. Your job, this run, is to find exclusions that can now be safely -removed, bump the relevant dependency, prove the fix passes the release gates -plus build/test, and open a single pull request. Most runs will legitimately +release pipeline runs an `osv-scanner` gate; advisories that do not actually apply +to us are suppressed in the `osv-scanner.toml` ignore file at the repo root, each +with a justification comment. + +Over time `osv-scanner.toml` accumulates exclusions that are no longer needed +because upstream shipped a fix. Nobody prunes them, so the suppressed audit +surface silently grows. Your job, this run, is to find exclusions that can now be +safely removed, bump the relevant dependency, prove the fix passes the release +gates plus build/test, and open a single pull request. Most runs will legitimately produce NO PR — a "nothing prunable" result is healthy and strongly preferred over an unsafe or unverified bump. @@ -24,13 +24,15 @@ over an unsafe or unverified bump. - This is a Lerna + Yarn (v1, `1.22.22`) workspaces monorepo with ~116 packages under `modules/`. Node and Yarn are already provisioned in this runner. -- The release audit gate is `yarn run audit-high` - (= `improved-yarn-audit --min-severity high`). It auto-reads `.iyarc` from the - repo root — no flag needed. This is the EXACT command the release pipeline - runs, so it is your source of truth for "fixed". -- IMPORTANT: nearly every entry in `.iyarc` is a TRANSITIVE dependency (e.g. - tar, minimatch, ws, form-data, protobufjs, tmp, sjcl, sanitize-html, esbuild), - pinned in the root `package.json` `resolutions` block — NOT a direct +- The release audit gate is: + ``` + osv-scanner --config=osv-scanner.toml --severity=HIGH --severity=CRITICAL ./ + ``` + This is the EXACT command the release pipeline runs, so it is your source of + truth for "fixed". +- IMPORTANT: nearly every entry in `osv-scanner.toml` is a TRANSITIVE dependency + (e.g. tar, minimatch, ws, form-data, protobufjs, tmp, sjcl, sanitize-html, + esbuild), pinned in the root `package.json` `resolutions` block — NOT a direct dependency in a module `package.json`. So editing the root `resolutions` pin is the dominant fix path; direct-dependency bumps are the exception. - The repo provides `yarn upgrade-dep -p -v ` (see @@ -43,25 +45,25 @@ over an unsafe or unverified bump. ## Early exit (do this first) -If an open PR already exists on a branch matching `iyarc-prune/*`, stop and +If an open PR already exists on a branch matching `osv-scanner-prune/*`, stop and report — do not open a second: - gh pr list --state open --search "head:iyarc-prune/" + gh pr list --state open --search "head:osv-scanner-prune/" ## Read context first Before changing anything, read: -1. `.iyarc` — the full ignore list and every justification comment. +1. `osv-scanner.toml` — the full ignore list and every justification comment. 2. The root `package.json` `resolutions` block. 3. `scripts/upgrade-workspace-dependency.ts` (the `yarn upgrade-dep` tool). 4. `CLAUDE.md` and `commitlint.config.js` (commit conventions). ## Per-exclusion evaluation -For each `GHSA-*` entry in `.iyarc`: +For each `[[IgnoredVulns]]` entry in `osv-scanner.toml`: 1. Identify the affected package and the path that pulls it in. The - justification comment usually names both; confirm with `yarn why `. + justification `reason` usually names both; confirm with `yarn why `. 2. Determine whether a PATCHED version now exists and is reachable for us (`yarn info versions`, the GitHub advisory's first-patched version, registry metadata). @@ -82,15 +84,15 @@ For each `GHSA-*` entry in `.iyarc`: - Direct dependency (rare here): `yarn upgrade-dep -p -v --ignore-scripts`. 2. Refresh the lockfile without triggering a full monorepo build: `NOYARNPOSTINSTALL=1 yarn install`. -3. Remove the satisfied exclusion from `.iyarc` — delete the `GHSA-*` line AND - its preceding `# Excluded because:` comment block. +3. Remove the satisfied exclusion from `osv-scanner.toml` — delete the entire + `[[IgnoredVulns]]` block for that GHSA id. ## Feedback loop / proof (abandon on failure) After each attempted fix, run the SAME gates the release pipeline runs, in this order: -1. `yarn run audit-high`. It MUST pass with the exclusion removed. Capture the - output. +1. `osv-scanner --config=osv-scanner.toml --severity=HIGH --severity=CRITICAL ./` + It MUST pass with the exclusion removed. Capture the output. 2. `yarn check-deps`. It MUST pass — a `resolutions` change can break cross-workspace version consistency. This is both a release-job step (it runs immediately after audit in the release workflow) and a PR-CI gate, so a @@ -100,15 +102,15 @@ order: `yarn lerna run build --scope ` and `yarn lerna run unit-test --scope `. 4. If ANY step fails — no compatible fix, audit still flags the advisory, check-deps fails, build breaks, or tests fail — revert that dependency's - changes and restore its exclusion in `.iyarc`. Never open a PR with a red - feedback loop. The full test suite still runs in PR CI as a backstop. + changes and restore its exclusion in `osv-scanner.toml`. Never open a PR with + a red feedback loop. The full test suite still runs in PR CI as a backstop. ## Commit and pull request (only if at least one exclusion was removed with a fully green feedback loop) How you finish depends on where you are running: -- **If running in CI** (a `iyarc-prune/*` branch exists and the +- **If running in CI** (a `osv-scanner-prune/*` branch exists and the `mcp__github_file_ops__commit_files` tool is available): commit and open the PR as described below. - **If a developer is running you locally:** make the edits, run the full @@ -119,19 +121,19 @@ CI commit/PR rules: - Commit message: conventional (commitlint extends `@commitlint/config-conventional`; `deps` and `root` are valid scopes), e.g.: - `chore(deps): bump to , drop from .iyarc`. + `chore(deps): bump to , drop from osv-scanner.toml`. commitlint enforces `references-empty: never`, so the message MUST carry an issue reference: include `Ticket: HSM-429` in the footer. - SIGNED COMMIT (important — `master` requires signed commits): this workflow runs with commit signing enabled. Make your commit using the `mcp__github_file_ops__commit_files` tool — NOT `git commit`/`git push` — - passing every changed path (`.iyarc`, `package.json`, `yarn.lock`). That tool - commits through GitHub's API, so the commit is Verified (signed). Commits made - with raw `git` will be UNSIGNED and cannot be merged. + passing every changed path (`osv-scanner.toml`, `package.json`, `yarn.lock`). + That tool commits through GitHub's API, so the commit is Verified (signed). + Commits made with raw `git` will be UNSIGNED and cannot be merged. - FALLBACK: if the signing tool fails, commit with `git` anyway and add this line to the PR body: "⚠️ Commits are unsigned — a maintainer must re-sign before merge." -- The workflow creates the working branch automatically (prefix `iyarc-prune/`); +- The workflow creates the working branch automatically (prefix `osv-scanner-prune/`); commit your changes to it, then open a single NON-draft PR against `master` with `gh pr create`. - Labels: ensure `automated`, `dependencies`, and `security` exist (create any @@ -142,8 +144,8 @@ CI commit/PR rules: - PR body must contain: - A table of each removed exclusion: GHSA id, package, old -> new version, the advisory it resolves. - - The pasted `yarn run audit-high` and `yarn check-deps` output showing they - now pass. + - The pasted `osv-scanner --config=osv-scanner.toml --severity=HIGH --severity=CRITICAL ./` + and `yarn check-deps` output showing they now pass. - Build/test results for the affected module(s). - A "Still blocked" section listing every exclusion that could NOT be removed and the reason (no upstream fix / incompatible parent pin). @@ -154,5 +156,5 @@ CI commit/PR rules: - If nothing is safely prunable this run, open no PR and report "no exclusions prunable this run" in the job summary, including the "Still blocked" breakdown so the result is auditable. -- Only ever modify `.iyarc`, dependency manifests (`package.json`), and +- Only ever modify `osv-scanner.toml`, dependency manifests (`package.json`), and `yarn.lock`. Do not modify product/source code. diff --git a/.github/workflows/npmjs-release.yml b/.github/workflows/npmjs-release.yml index 56a9eed5cd..a5048b6170 100644 --- a/.github/workflows/npmjs-release.yml +++ b/.github/workflows/npmjs-release.yml @@ -271,9 +271,14 @@ jobs: with: targets: EricCrosson/retry@v1.4.8:sha256-d207746ff0eda67c706df25e88c02520f0cf3172279eb8eec8224fb0d3558911 - - name: Run yarn audit - continue-on-error: true - run: retry --up-to 2x --every 3s -- yarn run audit-high --retry-on-network-failure + - name: Audit Dependencies + uses: google/osv-scanner-action@v2 + with: + scan-args: |- + --config=osv-scanner.toml + --severity=HIGH + --severity=CRITICAL + ./ - name: Run dependency check run: | diff --git a/.github/workflows/publish.yml b/.github/workflows/publish.yml index 3cd0a84570..1935d4a580 100644 --- a/.github/workflows/publish.yml +++ b/.github/workflows/publish.yml @@ -36,14 +36,14 @@ jobs: - name: Install BitGoJS run: sfw yarn install --with-frozen-lockfile - - name: Install retry - uses: BitGo/install-github-release-binary@v2 - with: - targets: EricCrosson/retry@v1.4.8:sha256-d207746ff0eda67c706df25e88c02520f0cf3172279eb8eec8224fb0d3558911 - - name: Audit Dependencies - continue-on-error: true - run: retry --up-to 2x --every 3s -- yarn run improved-yarn-audit --min-severity high --retry-on-network-failure + uses: google/osv-scanner-action@v2 + with: + scan-args: |- + --config=osv-scanner.toml + --severity=HIGH + --severity=CRITICAL + ./ - name: Set Environment Variable for Alpha if: github.ref != 'refs/heads/master' # only publish changes if on feature branches diff --git a/.iyarc b/.iyarc deleted file mode 100644 index 7a5fd13b41..0000000000 --- a/.iyarc +++ /dev/null @@ -1,101 +0,0 @@ -# Excluded because: -# - Lerna requires tar v6, but no patched v6 exists (fix only in v7.5.3) -# - Forcing tar v7.5.3 breaks lerna's packDirectory API -# - This CVE affects archive EXTRACTION (unpacking malicious symlinks/hardlinks) -# - Lerna only uses tar for PACKING -GHSA-8qq5-rm4j-mr97 - -# Excluded because: -# - Transitive dependency through lerna and yeoman-generator, which currently pin tar to a -# < 7.5.4 range; We only use their tar integration for -# archive PACKING, not extraction, -GHSA-r6q2-hw4h-h46w - -# Excluded because: -# - Transitive dependency through lerna and yeoman-generator requiring tar < 7.5.4 -# - This CVE affects tar's extraction process with specially crafted archives -# - Our usage is limited to archive PACKING operations only, not extraction -GHSA-34x7-hfp2-rc4v - -# Excluded because: -# - Transitive dependency through lerna, depcheck, glob, mocha, yeoman-generator -# - minimatch 10.x introduces breaking API changes incompatible with lerna v9.0.0 -# - This CVE (ReDoS in minimatch <10.2.1) affects glob pattern matching with repeated wildcards -# - Our usage is dev-time tooling only (build, test, file search) -# - Mitigated by controlled inputs (our own build scripts, not user-provided patterns) -GHSA-3ppc-4f35-3m26 - -# Excluded because: -# - Transitive dependency through lerna and yeoman-generator requiring tar < 7.5.4 -# - This CVE affects tar's extraction process with specially crafted archives -# - Our usage is limited to archive PACKING operations only, not extraction -GHSA-83g3-92jg-28cx - -# Excluded because: -# - Transitive dependency through lerna, depcheck, nyc, eslint, yeoman-generator, glob, shelljs -# - minimatch ReDoS via crafted glob patterns (same class as GHSA-3ppc-4f35-3m26) -# - Only affects dev-time tooling, not production code -GHSA-7r86-cg39-jmmj - -# Excluded because: -# - Transitive dependency through lerna, depcheck, nyc, eslint, yeoman-generator, glob, shelljs -# - minimatch ReDoS via crafted glob patterns (same class as GHSA-3ppc-4f35-3m26) -# - Only affects dev-time tooling, not production code -# - Mitigated by controlled inputs (our own build scripts, not user-provided patterns) -GHSA-23c5-xmqv-rm74 - -# Excluded because: -# - Transitive dependency through lerna and yeoman-generator requiring tar < 7.5.7 -# - This CVE affects tar's extraction process (hardlink path traversal in crafted archives) -# - Our usage is limited to archive PACKING operations only, not extraction -# - Forcing tar v7.5.7+ breaks lerna's packDirectory API (same constraint as GHSA-8qq5-rm4j-mr97) -GHSA-qffp-2rhf-9h96 - -# Excluded because: -# - Same risk profile as existing tar exclusions: CVE affects archive extraction (unpacking malicious archives) -# - We only use tar for packing; low risk in terms of exploitability -# - Security exception approved -GHSA-9ppj-qmqm-q256 - -# Excluded because: -# - CVE-2026-4258: missing point-on-curve validation in sjcl.ecc.basicKey.publicKey() -# - Transitive dependency via @bitgo/abstract-lightning > macaroon > sjcl -# - The vulnerability is in sjcl.ecc (ECDH invalid-curve attack); macaroon only uses -# sjcl.codec, sjcl.bitArray, sjcl.misc.hmac, and sjcl.hash.sha256 — no ECC operations -# - Additionally, @bitgo/sjcl (our fork) does not include sjcl.ecc at all -# - Resolved sjcl -> npm:@bitgo/sjcl@1.0.1 in root resolutions; sjcl.ecc is absent at runtime -# - No patched version of sjcl exists upstream (first_patched_version: null) -GHSA-2w8x-224x-785m - -# Excluded because: -# - ws: Memory exhaustion DoS by sending many tiny fragments/data chunks to exhaust server memory -# - Transitive dependency via @cosmjs/socket, @ethersproject/providers, @polkadot/rpc-provider, -# jayson, rpc-websockets (via @solana/web3.js), and avalanche — all requiring ws <8.21.0 -# - Our usage is exclusively as a WebSocket CLIENT for blockchain RPC connections, not as a server -# - The DoS vector requires an attacker to send crafted frames to a ws server we control; we do not -# expose any ws server surfaces in production -GHSA-96hv-2xvq-fx4p - -# Excluded because: -# - form-data: CRLF injection via unescaped multipart field names and filenames -# - Transitive dependency via superagent (abstract-cosmos, express, supertest) and @aptos-labs/ts-sdk -# - The injection requires attacker-controlled field names or filenames in multipart requests -# - All form-data field names and filenames in our codebase are code-controlled constants, -# not derived from user input — no untrusted data flows into form field names or filenames -GHSA-hmw2-7cc7-3qxx - -# Excluded because: -# - protobufjs: DoS through unbounded Any expansion during JSON conversion (parseAny recursion) -# - Transitive dependency via @cosmjs (abstract-cosmos, babylonlabs-io-btc-staking-ts) and -# @hashgraph/proto, @hashgraph/sdk (sdk-coin-hbar) — all requiring protobufjs <=7.5.x -# - Input to protobuf decoding comes from trusted blockchain RPC responses, not arbitrary user data -# - Patched version (7.6.1) requires upstream @cosmjs and @hashgraph dependency updates -GHSA-wcpc-wj8m-hjx6 - -# Excluded because: -# - tmp: path traversal via type-confusion in _assertPath (non-string prefix/postfix/template) -# - Transitive dependency via cypress (web-demo), karma (bitgo module), and lerna/nx (dev tooling) -# - All usages are dev-time only; tmp is never used in production or runtime code -# - The prefix/postfix/template args are all hard-coded string constants in calling code, -# not user-supplied — the type-confusion vector does not apply -GHSA-7c78-jf6q-g5cm diff --git a/osv-scanner.toml b/osv-scanner.toml new file mode 100644 index 0000000000..bada87ff68 --- /dev/null +++ b/osv-scanner.toml @@ -0,0 +1,55 @@ +[[IgnoredVulns]] +id = "GHSA-8qq5-rm4j-mr97" +reason = "tar extraction CVE, lerna only packs — not exploitable" + +[[IgnoredVulns]] +id = "GHSA-r6q2-hw4h-h46w" +reason = "tar extraction CVE, packing only" + +[[IgnoredVulns]] +id = "GHSA-34x7-hfp2-rc4v" +reason = "tar extraction CVE, packing only" + +[[IgnoredVulns]] +id = "GHSA-3ppc-4f35-3m26" +reason = "minimatch ReDoS, dev tooling only with controlled inputs" + +[[IgnoredVulns]] +id = "GHSA-83g3-92jg-28cx" +reason = "tar extraction CVE, packing only" + +[[IgnoredVulns]] +id = "GHSA-7r86-cg39-jmmj" +reason = "minimatch ReDoS, dev tooling only" + +[[IgnoredVulns]] +id = "GHSA-23c5-xmqv-rm74" +reason = "minimatch ReDoS, dev tooling only" + +[[IgnoredVulns]] +id = "GHSA-qffp-2rhf-9h96" +reason = "tar hardlink path traversal, packing only" + +[[IgnoredVulns]] +id = "GHSA-9ppj-qmqm-q256" +reason = "tar extraction, packing only" + +[[IgnoredVulns]] +id = "GHSA-2w8x-224x-785m" +reason = "sjcl.ecc invalid-curve attack; macaroon uses HMAC/hash only; resolved via @bitgo/sjcl fork" + +[[IgnoredVulns]] +id = "GHSA-96hv-2xvq-fx4p" +reason = "ws server-side DoS; used only as WebSocket client" + +[[IgnoredVulns]] +id = "GHSA-hmw2-7cc7-3qxx" +reason = "form-data CRLF injection; all field names are code-controlled constants" + +[[IgnoredVulns]] +id = "GHSA-wcpc-wj8m-hjx6" +reason = "protobufjs DoS via parseAny recursion; input from trusted RPC only" + +[[IgnoredVulns]] +id = "GHSA-7c78-jf6q-g5cm" +reason = "tmp path traversal via type confusion; all args are hardcoded strings" diff --git a/package.json b/package.json index 309cf9cde4..1c6ee3c6c1 100644 --- a/package.json +++ b/package.json @@ -32,7 +32,6 @@ "html-webpack-plugin": "^5.5.0", "https-browserify": "^1.0.0", "husky": "^8.0.1", - "improved-yarn-audit": "^3.0.0", "lerna": "^9.0.0", "lint-staged": "^12.4.1", "make-dir": "^3.1.0", @@ -248,7 +247,6 @@ ], "scripts": { "postinstall": "test -n \"$NOYARNPOSTINSTALL\" || lerna run build --stream", - "audit-high": "improved-yarn-audit --min-severity high", "lint": "lerna run lint --stream", "lint-changed": "lerna run lint --since origin/${GITHUB_REPO_BRANCH:-master}..HEAD --stream", "unit-test-changed": "lerna run unit-test --since $(git merge-base HEAD~ origin/${GITHUB_REPO_BRANCH:-master}) --stream",