Stacked fixes to clear the remaining red CI checks on \`main\` that pre-dated the #2412 lockfile-cascade fix. Each one verified locally before commit. 1. **Build V3 (ubuntu/macos/windows)** — \`graph-edge-writer.ts\` did a static \`await import('better-sqlite3')\` which forces tsc to resolve the package's types at build time. \`better-sqlite3\` is in \`optionalDependencies\` (so CI doesn't install it) — type resolution failed with TS2307 on every platform. Routed the module name through a variable so the static analyzer stops trying to resolve it; runtime presence is still gated by the existing try/catch. 2. **Static regression guards (#2267)** — \`scripts/smoke-workflows-yaml.mjs\` crashed with \`SyntaxError: The requested module 'js-yaml' does not provide an export named 'default'\` after js-yaml dropped the synthetic default from its package exports. Switched to \`import * as yaml from 'js-yaml'\`. 3. **Tool description discoverability (ADR-112)** — two MCP tools regressed the no-guidance baseline (count went from 0 → 2): - \`metaharness_bench\` had no Use-when / Prefer / Pair-with hint - \`testgen_tdd_repair\` had \"Use when …\" in a *later* string fragment of a concatenated description, which the audit regex (single-quoted-literal-only) misses Added explicit \"Use when …\" guidance to the first fragment of each. Audit now reports 340/340 tools with guidance. 4. **witness verify precondition smoke (#1880)** — Root \`package-lock.json\` drifted: \`agentdb@3.0.0-alpha.16\` in lock vs \`^3.0.0-alpha.17\` in package.json (same root cause as #2412 but for the npm lockfile, not the pnpm one fixed last release). Every job that runs \`npm ci\` was failing at install. Regenerated via \`npm install --package-lock-only\`. Verified: \`npm ci --dry-run --legacy-peer-deps\` now exits 0. 5. **Supply-chain audit (#2046)** — \`plugins/ruflo-graph-intelligence\` had 1 HIGH/CRITICAL unaccepted direct-dep finding: - \`ws@8.20.1\` pulled by \`sublinear-time-solver\` (GHSA-96hv-2xvq-fx4p, memory exhaustion DoS) — bumped root override \`ws >=8.18.4\` → \`>=8.21.0\` and added the same override to the plugin's own package.json so it propagates through the plugin's own npm install - \`@claude-flow/cli\` peerDep surfaces every HIGH transitive in cli's own tree inside the plugin's audit report — added a tracked, expiring accepted-finding (cli ships its own audit pipeline on every publish; the plugin doesn't redistribute cli's tree). Audit now reports \`OK: no hard-fail findings\`. Verified locally: - \`npm run build\` in v3/@claude-flow/cli: clean - \`node scripts/smoke-workflows-yaml.mjs\`: exit 0, 53 workflows parsed - \`node scripts/audit-tool-descriptions.mjs\`: 340/340 with guidance, OK - \`npm ci --dry-run --legacy-peer-deps\` at repo root: exit 0 - \`node scripts/audit-supply-chain.mjs\`: OK, no hard-fail findings Co-Authored-By: RuFlo <ruv@ruv.net>
Supply-chain hardening — .github/supply-chain/
This directory is the maintained surface for ruflo's supply-chain defence.
CI runs scripts/audit-supply-chain.mjs on every PR and push that touches a package.json, a lockfile, this directory, or the audit script itself. Five layers of defence:
| # | Layer | Source of truth | Hard-fail? |
|---|---|---|---|
| 1 | CVE audit (HIGH/CRITICAL in DIRECT deps) | npm audit |
✅ unless accepted |
| 2 | Lockfile integrity (SHA-512 on every downloaded entry) | package-lock.json |
✅ unless accepted |
| 3 | Top-level allowlist | allowed-deps.json |
✅ |
| 4 | Typosquat reject | allowed-deps.json → policies.rejectInRegistry |
✅ |
| 5 | Publisher trust snapshot | live npm view |
logged-only |
Plus, on PRs only, GitHub's actions/dependency-review-action@v4 flags newly-added vulnerable deps + denies the deny-list of compromised package versions.
Files
allowed-deps.json
Whitelist of direct deps per package. Adding a new direct dep to a covered package requires an explicit allowlist edit. Also lists policies.rejectInRegistry — names we always block regardless of whether they're real (typosquat defence) — and publisherTrust.criticalUpstreamPackages — upstream deps whose maintainer identity we snapshot in CI logs so unexpected handover is visible.
accepted-findings.json
Triaged findings that the audit currently tolerates. Every entry must cite a tracking issue or ADR, and should have an expiresAt date so the audit re-fails after the triage window expires.
Editing this file requires CODEOWNER review per .github/CODEOWNERS.
Running locally
# Full audit
node scripts/audit-supply-chain.mjs
# JSON output (for tooling)
node scripts/audit-supply-chain.mjs --json
# Just one layer
node scripts/audit-supply-chain.mjs --scope cve
node scripts/audit-supply-chain.mjs --scope lockfile
node scripts/audit-supply-chain.mjs --scope allowlist
node scripts/audit-supply-chain.mjs --scope typosquat
node scripts/audit-supply-chain.mjs --scope publisher-trust
Adding a new package to the audit
- Add the package path to
PACKAGES_TO_AUDITinscripts/audit-supply-chain.mjs. - Add a
packages.<pkgName>block toallowed-deps.jsonlisting the expecteddependencies/devDependencies/peerDependencies. - Add a dependabot stanza to
.github/dependabot.yml. - Run
node scripts/audit-supply-chain.mjslocally — fix any new findings before opening the PR.
Triaging a new finding
If audit-supply-chain.mjs fails on your PR:
- CVE in direct dep — first try to upgrade the offending dep. If the upstream maintainer hasn't released a fix yet, add an entry to
accepted-findings.json.cve[]with:package,depName,severity,via— copy from the audit's failure outputtracking— short label (e.g. "ADR-121 Phase 4")trackingIssue— link to the GitHub issue / ADR fileexpiresAt— 90 days from today (audit will re-fail then so re-triage is forced)rationale— why we can ship with this finding (e.g. "vulnerable code path not invoked from our usage")
- Lockfile missing integrity — check if it's a pnpm-store-shadow or workspace-link entry (those legitimately have no integrity). If so, add a
lockfile[]entry with the path pattern. - Allowlist violation — either the new dep is legitimate (add it to
allowed-deps.json) or unintended (remove from package.json). - Typosquat hit — never accept. Either fix the typo or remove the dep entirely.
Why this matters
Three real supply-chain incidents on npm in the last 18 months involved:
- a maintainer takeover of a popular package (
colors@1.4.1,ua-parser-js) - a typosquat that pulled in
event-stream → flatmap-streamand exfiltrated wallets - a postinstall script in a transitively-pulled dep that ran during
npm install
This audit doesn't make ruflo immune. It does make these patterns visible in CI before they reach production, with structured failures the maintainer can triage rather than silent installs.
Related: ADR-123 (introduced the new plugin under audit), ADR-122 (introduced the browser substrate under audit).