| name | dependency-cve-audit |
| description | Audit project dependencies for known CVEs — fulfills 紅線 18
(Critical/High CVE must be 0 to merge). Class-level skill that covers
BOTH npm + bun projects, lockfile quirks, and the "package is
unmaintained, no fix available" trap (xlsx, node-ipc, etc.).
Trigger conditions:
- 紅線 18 enforcement (任何 ship / release 之前)
- User 問「check security」、「CVE」、「npm audit」、「vulnerability」
- 任何 `package.json` / `bun.lock` / `package-lock.json` / `yarn.lock` 改動後
- 任何 new dep 引入(`bun add X`, `npm install X`)
- Project 入面有 `xlsx`, `node-ipc`, `event-stream`, `getcookies`,
`coa`, `rc`, `flatmap-stream` 等 known-unmaintained 嘅 package
|
| version | 1 |
| category | devops |
Dependency CVE Audit — 紅線 18 防護
為什麼需要這個 skill — David 嘅 pm-system 紅線 18 要求
Critical/High CVE = 0 先可以 merge。但 bun project 嘅 audit 工具鏈
唔直接 work,常見 lockfile 失配,仲有 "package 已 unmaintained,
永遠唔會出 fix" 嘅 dead-end 要識得 migrate。
🎯 核心流程
Audit 觸發(merge 前 / 引入新 dep 後 / 季檢)
↓
Step 1: Identify 全部 lockfile(npm + bun + yarn + pnpm)
↓
Step 2: 揀 scan tool(osv-scanner / npm audit / snyk)
↓
Step 3: Run audit
↓
Step 4: Categorize vulns
├─ Has fix:升級 / patch
├─ No fix (unmaintained):migrate path 決定 + plan
└─ Medium/Low:track 但唔阻擋
↓
Step 5: 紅線 18 過 → ship
紅線 18 fail → fix 完再 ship
紅線:Critical/High CVE ≠ 0 → 唔可以 merge(紅線 18)。
⚠️ Pitfall — Stale audit result trap: re-verify after rebase / migration (2026-06-08 pm-system)
場景:我喺 Sprint 1 retro 寫完之後,David 問「跑埋 npm audit 守住紅線 18」。我即跑 osv-scanner --lockfile=backend/bun.lock,結果出 xlsx 0.18.5 兩個 High CVE。我即刻諗「OK,plan A 係 migrate xlsx → exceljs」,並向 David 提議 4 個 option。
但其實 migration 已經喺上一個 session 做咗 — bun.lock 已經重 generate 過,package.json 已經換做 exceljs@^4.4.0,source code(backend/src/routes/documents.ts、backend/src/routes/attachments.ts、frontend/src/pages/WorkLogsPage.tsx)已經 import 緊 ExcelJS 唔係 XLSX。我 mental model 仲以為 xlsx 仍然喺度,但其實 bun.lock 早已乾淨。
我差啲重新做 migration — 浪費 30+ 分鐘 + 撞 David 嘅未 commit changes。
點解會誤判:
- 我跑 audit 嘅 時機:Sprint 1 retro 寫完之後,David 主動提議 CVE check
- 我嘅 mental model:Sprint 1 commit chain 完全冇 touch xlsx,所以我假設
bun.lock 同 Sprint 0 一樣有 xlsx
- Reality:
xlsx → exceljs migration 係 前一個 session 做嘅(commit 喺更早嘅 chain,documents.ts:161 有 TD-012 comment 引用),但 lockfile 已經 regenerate
- Final verification:re-run osv-scanner 顯示 "No issues found" 喺 backend (307 packages) 同 frontend (372 packages)
Symptoms(出現以下就要 re-verify):
- 跑 audit 撞 high CVE,但 code 入面
rg "xlsx" backend/src/routes/ 結果 0 個 match
- Lockfile 嘅 timestamp 遠比
package.json 新
git log -- <lockfile> 顯示有 migration commit 但 package.json 內容似未改
- 你嘅 mental model 認為 vulnerability 仍然喺度,但完全冇 code path 用緊嗰個 package
Prevention 紀律 — Trust but verify 3 步:
docker run --rm -v $PWD:/src -w /src ghcr.io/google/osv-scanner:latest \
--lockfile=backend/bun.lock 2>&1 | tail -20
rg "from 'xlsx'" --type ts --type tsx
rg "require.*xlsx" --type ts
rg "XLSX\." --type ts --type tsx
grep "xlsx" package.json
grep "xlsx" bun.lock
Re-generate lockfile fix:
cd <project-with-stale-lockfile>
rm bun.lock bun.lockb 2>/dev/null
bun install
docker run --rm -v $PWD:/src -w /src ghcr.io/google/osv-scanner:latest --lockfile=bun.lock
Detection signal checklist(跑 audit 之後必查):
Lesson:Stale audit result 同 stale lockfile 都係常見 trap。Audit 撞 high CVE → 第一步唔係 plan migration,係 grep code 確認個 package 真係用緊。如果 source code 已經 migrate 但 lockfile 仲有,呢個係 lockfile stale,fix 係 bun install 唔係 code rewrite。
🛠️ Step 1: 識別 lockfile
Bun 1.3+ 仍然保留 bun.lock 為 default,但 project 之間唔一致。
掃邊個 file 必須先睇清楚:
| Lockfile | Package manager | Scan tool 識唔識讀 |
|---|
package-lock.json | npm | ✅ npm audit / osv-scanner / snyk |
bun.lock(textformat) | bun 1.2+ | ⚠️ 只有 osv-scanner + bun 1.3.4+ 識 |
bun.lockb(binary) | bun 1.0-1.1 | ❌ 唔識 — 必須 upgrade bun 或 bun install --save-text-lockfile |
yarn.lock | yarn classic | ✅ npm audit / osv-scanner |
yarn.lock(Yarn Berry) | yarn 2+ | ⚠️ 大部分 tool 識,部分需 yarn 4.x+ |
pnpm-lock.yaml | pnpm | ✅ npm audit / osv-scanner |
| 冇 lockfile | — | ❌ 視為 audit 唔通過 — 紅線 18 fail |
踩坑親驗(2026-06-08 pm-system):
npm audit 喺 bun.lock 嘅 directory throw ENOLOCK(require package-lock.json)
npm i --package-lock-only 喺有 bun.lock 嘅 directory 失敗(bun 優先)
- Backend audit 完全冇 way out 用 npm 工具
- → 解法 = 用
osv-scanner 跨 ecosystem 識讀 bun.lock
🔍 Step 2: 揀 Scan Tool(4 個 option)
A) osv-scanner ⭐ 推薦(2026-06-08 pm-system 用呢個)
Pros:
- 跨 ecosystem + 跨 lockfile 識讀(npm/yarn/pnpm/bun.lock/Cargo/PyPI/...)
- 用 OSV.dev database(Google 維護,free,no rate limit)
- 識 Protobuf + JSON output
- Docker image 有,zero install
Cons:
- 冇 auto-fix(
npm audit fix 有)
- 冇 severity score color(只有 CVSS number)
- e2e bare
package.json(冇 lockfile)scan 唔到(2026-06-08 pm-system e2e/ 親驗)
Usage:
docker run --rm -v $PWD:/src -w /src ghcr.io/google/osv-scanner:latest \
--lockfile=backend/bun.lock
docker run --rm -v $PWD:/src -w /src ghcr.io/google/osv-scanner:latest \
--lockfile=frontend/package-lock.json
docker run --rm -v $PWD:/src -w /src ghcr.io/google/osv-scanner:latest \
--recursive /src
docker run --rm -v $PWD:/src -w /src ghcr.io/google/osv-scanner:latest \
--lockfile=backend/bun.lock --format json > /tmp/audit.json
Install alternative(Mac):
brew install osv-scanner
curl -L https://github.com/google/osv-scanner/releases/latest/download/osv-scanner_darwin_arm64.zip \
-o /tmp/osv.zip && unzip /tmp/osv.zip -d /tmp/osv-bin/
B) npm audit(標準 npm project 限定)
Pros:Auto-fix(npm audit fix)、severity 分色、HTML report
Cons:只識 package-lock.json(bun/yarn/pnpm 唔 work)
Usage:
cd <npm-project>
npm audit --omit=dev
npm audit fix
npm audit --json > /tmp/audit.json
C) snyk test(有商業 account 嘅 team)
Pros:Best-in-class vuln DB + remediation advice + license check
Cons:需要 Snyk account / API key,free tier 有 quota
Usage:
npm install -g snyk
snyk auth
snyk test
snyk test --json > /tmp/audit.json
D) bun audit(Bun 1.3.4+ only)
Pros:Native,fast
Cons:舊版 Bun 冇呢個 command(2026-06-08 pm-system bun audit 撞 error: Script not found "audit")
Usage:
bun --version
bun audit
⚠️ Step 3 嘅常見 Quirk(2026-06-08 pm-system 親驗)
Q1: npm audit 喺 bun project throw ENOLOCK
$ cd backend
$ npm audit
npm error code ENOLOCK
npm error audit This command requires an existing lockfile.
Fix:用 osv-scanner(識 bun.lock)或用 bun audit(需 Bun 1.3.4+)。
Q2: npm i --package-lock-only 喺 bun directory 失敗
$ cd backend && npm i --package-lock-only
Fix:唔好 try to 整 package-lock.json 喺 bun project 入面 — 直接用 osv-scanner。
Q3: osv-scanner bare package.json(冇 lockfile)scan 唔到
could not determine extractor suitable to this file: "/src/e2e/package.json"
Fix:
- 用
npm install 整 lockfile,e2e 通常用 npm install(npm 預設)
- 或 recursive mode scan 整個 directory
- 或 acknowledge — 1 個 dep 嘅 dev-only directory(eg. e2e 用
@playwright/test)視為低風險
Q4: Hermes 攔 long-lived docker run 互動
Fix:用 --rm(已經有)+ 唔好加 interactive。背景跑:
docker run --rm -v $PWD:/src -w /src ghcr.io/google/osv-scanner:latest \
--lockfile=backend/bun.lock 2>&1 | tail -30
Q5: Multi-package monorepo 唔可以只 scan 1 個
Monorepo rule:scan 全部 subpackage 嘅 lockfile。
for d in backend frontend e2e packages/*/; do
if [ -f "$d/bun.lock" ] || [ -f "$d/package-lock.json" ]; then
echo "=== $d ==="
docker run --rm -v $PWD:/src -w /src ghcr.io/google/osv-scanner:latest \
--lockfile="${d}bun.lock" 2>&1 | tail -10
fi
done
🚨 Step 4: 「No fix available」嘅 dead-end trap
最高頻 dead-end packages(2026-06-08 pm-system xlsx 親歷):
| Package | 狀況 | Migration target |
|---|
xlsx(SheetJS) | 2023 起 maintenance mode,拒絕 security patch | exceljs(recommended)/ read-excel-file + write-excel-file |
node-ipc | Author 故意 sabotage(protestware) | 完全 remove,用 child_process IPC |
event-stream | 2018 已被 supply chain attack | 完全 remove,改用其他 stream lib |
getcookies | 已知 CVE,maintainer MIA | 完全 remove,implement 自行 |
coa(RC parsing) | Prototype pollution CVE | js-yaml / 自寫 parser |
rc(npm config) | Prototype pollution CVE | cosmiconfig(無此 CVE) |
flatmap-stream | 跟 event-stream 同一攻擊 | 完全 remove |
get-pkg-repo | Prototype pollution | 完全 remove |
mongoose < 6.x | 多個 known CVE,fix 要 v6+ | 升 major version |
3 條 fix path(no fix 嘅情況):
Path 1: Migrate to maintained alternative(推薦)
xlsx → exceljs migration example(2026-06-08 pm-system 計劃):
cd backend
bun remove xlsx
bun add exceljs
cd ../frontend
bun remove xlsx
bun add exceljs
Code rewrite spot(3 個 file):
import * as XLSX from 'xlsx'
const wb = XLSX.utils.book_new()
const ws = XLSX.utils.aoa_to_sheet(rows)
XLSX.utils.book_append_sheet(wb, ws, '工作時數')
XLSX.writeFile(wb, 'export.xlsx')
import ExcelJS from 'exceljs'
const wb = new ExcelJS.Workbook()
const ws = wb.addWorksheet('工作時數')
ws.addRows(rows)
const buffer = await wb.xlsx.writeBuffer()
Risk:API 完全唔同,必須 unit test + E2E smoke 守住:
cd backend && bun test
cd ../e2e && npx playwright test
Path 2: Pin / sandbox 不可行(unmaintained 永遠 expose 攻擊面)
Anti-pattern:
- 寫到
<input> filter(只可以 input 1 個 row,never production-ready)
- CSP / 限制 input file size(降低 risk 唔係 fix)
- Vendor patch(主動 fork + 自己 maintain — 99% 嘅 team 唔應該咁做)
唯一可接受:如果個 CVE 係純 theoretical(untriggered by usage),可降風險 + 寫 ADR 標明 known-accepted-risk + 加 monitor。Critical/High 仍然要修。
Path 3: Remove 該 feature(最後手段)
- 將 .xlsx export 改 .csv(用戶仍可開 Excel 開 CSV)
- 將 .xlsx import 改人手 input
- Acceptable if 用戶 impact 細 + 有 workaround
📝 Step 5: 寫入 docs
Audit 結果寫入 docs/TECH-DEBT.md(如果發現 fix-available):
### TD-XXX: <package> 升級到 <version>(2026-MM-DD)
- **發現**:osv-scanner scan,GHSA-XXXX-XXXX-XXXX
- **Severity**:High(CVSS 7.8)
- **Package**:`<package>@<version>`
- **Fix**:升級到 `<package>@<fixed-version>`
- **Breaking changes**:列出
- **Verify**:`bun audit` 0 vulns + `bun test` 全綠 + E2E smoke 過
- **Commit**:`fix(deps): upgrade <package> to <version> (TD-XXX)`
- **Refs**:GHSA-XXXX-XXXX-XXXX
Audit 結果寫入 docs/TECH-DEBT.md(如果發現 no-fix / unmaintained):
### TD-XXX: <package> 無 fix,需 migrate(2026-MM-DD)
- **發現**:osv-scanner scan,GHSA-XXXX-XXXX-XXXX
- **Severity**:High(CVSS 7.8)
- **Package**:`<package>@<version>`(已 unmaintained)
- **Migration target**:<alternative-package>
- **Plan**:
1. <step 1>
2. <step 2>
3. <step 3>:re-run audit verify 0
- **Effort**:X hours
- **Refs**:GHSA-XXXX-XXXX-XXXX
Audit 結果寫入 docs/REGRESSION-GUARD.md(如已 fix):
### RG-XXX: <package> 嘅 GHSA-XXXX-XXXX-XXXX
- **Root cause**:<package> <version> 有 <CVE-type>
- **Fix**:升級 / migrate / remove
- **Prevention**:CI 加 `osv-scanner` step,merge 前必跑
CI integration(推薦):
name: CVE Gate
on: [pull_request]
jobs:
cve-audit:
runs-on: ubuntu
steps:
- uses: actions/checkout@v4
- name: Run osv-scanner
run: |
docker run --rm -v $PWD:/src -w /src \
ghcr.io/google/osv-scanner:latest --recursive /src
- name: Fail on Critical/High
run: |
# Parse JSON output,fail if any Critical or High severity
docker run --rm -v $PWD:/src -w /src \
ghcr.io/google/osv-scanner:latest --recursive /src --format json \
| jq '.results[].vulns[] | select(.severity == "HIGH" or .severity == "CRITICAL")' \
| head -1
📚 Take-aways(checklist)
任何「dependency CVE audit」session 必跑:
🚨 紅線(直接關聯 SOUL.md 紅線 18)
紅線 18:任何 Critical/High CVE(由 npm audit / snyk / osv-scanner
掃到)必須 0 才可 merge。
Enforcement 點:
- Pre-merge:PR 過 CI
osv-scanner --recursive(fail on Critical/High)
- Pre-release:release 之前 re-scan 確認 0
- Quarterly:季檢 scan,scan 結果 commit 喺
docs/retros/YYYY-QX-cve-audit.md
- New dep 引入:任何
bun add X / npm install X 之後立即 scan(新 dep 可能帶高危 transitive)
🔗 同其他 skill 嘅關係
regression-guard — Fix 完 CVE 必須有 RG-XXX entry(同 bug fix 一樣)
tech-debt-register — No-fix / 計劃中嘅 CVE fix 寫入 TECH-DEBT
cdk-deployment-rules — Production deploy 前必跑 scan(同 smoke test 一齊)
prisma-add-column-existing-db / prisma-circular-relation-debug — DB layer dep update 嘅 migration 紀律
umac-ai-deploy-prod — Production deploy SOP 內已含 npm audit 步驟
📊 Known-unmaintained 嘅 Watch List(2026-06-08 維護)
加入 watch list 條件:有 known CVE + 冇 fix 6 個月 + active maintained alternative 存在。
| Package | 替代品 | 加入日期 |
|---|
xlsx | exceljs / read-excel-file | 2026-06-08 |
node-ipc | child_process IPC / worker_threads | 2026-06-08 |
event-stream | 任何 modern stream lib | 2026-06-08 |
getcookies | 自寫 cookie parser | 2026-06-08 |
coa | js-yaml | 2026-06-08 |
rc | cosmiconfig | 2026-06-08 |
flatmap-stream | remove / lodash/fp | 2026-06-08 |
Update 呢個 list 嘅 trigger:
npm audit 出 unmaintained warning
snyk test 標 no fix available
- OSV.dev advisory comment 提及 "maintainer unresponsive"
🆕 Pitfall — overrides field for transitive dep fixes (2026-06-08 pm-system)
場景:Audit 報 @hono/node-server@1.19.11 (medium CVE, GHSA-92pp-h63x-v22m)
係 transitive dep — 唔係 package.json 直接依賴嘅 package。
Naive 升級:用 bun add @hono/node-server@1.19.13 喺自己 package.json
(污染咗 root 嘅 dependencies 表,1.19.13 唔係 root 用嘅 version)
正確做法 — 用 package.json 嘅 overrides field:
{
"dependencies": {
"elysia": "^1.4.28",
"xlsx": "^0.18.5"
},
"overrides": {
"@hono/node-server": "1.19.13",
"uuid": "11.1.1"
}
}
點解 overrides 啱:
- npm 5+ 支援, bun 1.2+ 完全相容
- 唔污染
dependencies 嘅 package.json(user 唔會見到 transitive dep 喺 root)
- 同一個 dep 喺多個 transitive 都被 force 升到 fix version
- 配合
bun install 自動重 generate bun.lock,audit verify 即時見效
Recipe(audit 報 transitive CVE → 改 overrides 1-line fix):
rg "@hono/node-server" backend/src/
cd backend && bun install
docker run --rm -v $PWD:/src -w /src ghcr.io/google/osv-scanner:latest \
--lockfile=backend/bun.lock | grep "hono" || echo "✅ fixed"
使用時機 checklist:
同直接升級嘅對比:
| Approach | 適合 | Risk |
|---|
bun add X@fix | X 係自己直接 import 嘅 | 升 major version 撞 breaking change 風險 |
package.json overrides | X 係 transitive | ✅ 推薦 — 唔污染 root dep,集中管理 |
bun pm trust X | Bun 1.2+ only, 互動 confirm | 慢,CI 唔 friendly |
| Vendor patch / fork | Maintainer MIA | 99% team 唔應該咁做 |
注意:overrides 同 resolutions(yarn 1.x classic)唔同。
npm + bun 用 overrides,yarn 2+ Berry 唔再 support resolutions。
如果 project 將來由 npm 轉 yarn Berry,要重寫 package.json。
📚 Related references
references/xlsx-unmaintained-migration-2026-06-08.md — SheetJS xlsx
migration 完整 reproduce:bun project 用 osv-scanner 跑 audit →
撞 xlsx 0.18.5 兩個 high CVE (GHSA-4r6h-8v6p-xvw6 Prototype Pollution
CVSS 7.8 + GHSA-5pgg-2g8v-p4x9 ReDoS CVSS 7.5) → 確認 SheetJS 2023
起 maintenance mode no fix → migrate to exceljs API rewrite
(XLSX.utils → ExcelJS.Workbook) → 3 file 改完 → 0 critical/high verify。
適合所有「xlsx / sheetjs 喺 production backend / frontend 用緊」嘅
session 跟呢個 reproduce。
references/osv-scanner-docker-recipe-and-stale-state-2026-06-08.md —
osv-scanner Docker image 完整 recipe(Mac arm64 唯一可靠 path:
npx / pip3 / brew / curl 全部 fail,只有 docker run ghcr.io/google/osv-scanner:latest
work) + stale audit state detection(撞 high CVE → 唔好即刻 plan migration,
先 rg "<package>" --type ts --type tsx 確認 code 真係 import 緊)。
包含完整 11-step audit trail(2026-06-08 pm-system 紅線 18 親驗)、
monorepo multi-lockfile 迭代 recipe、JSON output for CI、pre-merge
GitHub Actions workflow。任何「CVE audit on Mac + bun project」
session 直接跟呢個 recipe,唔好再重新研究 toolchain。