Skip to content

Node.js image 弱掃誤判?先分 app 套件與 npm 內建套件

2026年3月20日 1 分鐘
TL;DR Node.js image 的弱掃結果不能只看套件名稱,先分清楚是專案依賴,還是 base image 內建 npm 自己帶的相依套件,才不會修錯地方。

TL;DR

package.json 升完,不代表 Docker image 就乾淨了。Node.js image 的弱掃結果,通常至少要先分成兩類來看:專案自己的套件,以及 base image 內建 npm 帶進來的套件。這次 image 被掃到的 globminimatchtardiff,最後不是 app 的 node_modules 沒升乾淨,而是 node:22-alpine 內建的 npm@10.9.4 自己帶了舊版相依套件。解法不是只改 package.json,而是把 image 裡的 npm 升到 11.11.1

先講結論

遇到 Node.js image 弱掃,不要先急著懷疑 lockfile 沒更新。先做這個判斷:

  1. 先查 app 依賴樹
  2. 再查 container 內 npm 的全域依賴樹
  3. 最後再決定要改 package.json / lockfile,還是改 Dockerfile

這樣可以很快分清楚:你修的是應用程式套件問題,還是 base image 工具鏈問題。

情境

這次在整理 Node.js 專案的弱掃時,先把 package.jsonyarn.lock 裡能升的套件都升掉了,包含 runtime dependency 和 dev dependency。

專案側看起來已經很乾淨:

  • package.json 已升到較新的依賴版本
  • yarn.lock 也重新生成
  • 本地依賴樹裡沒有看到掃描報告列出的那些舊版 diffglobtar

但重新打包 Docker image 去掃,結果還是一直看到這些項目:

  • diff@5.2.0
  • glob@10.4.5
  • minimatch@9.0.5
  • tar@6.2.1
  • tar@7.4.3

問題

看起來很像「套件根本沒升成功」,但實際上專案依賴樹跟 image 掃描結果對不起來。

如果只盯著 package.jsonyarn.lock,很容易卡在這個問題:

npm ls diff glob minimatch tar --all --depth=6

在專案裡看到的結果很少,甚至跟弱掃報告列出的版本完全不同;可是 image 掃描結果又很明確,表示那些有弱點的版本真的存在於容器內。

嘗試過程

這種情況,我後來會固定分兩層來查。

第一層是 app 套件

  • 檢查 package.json
  • 檢查 yarn.lock
  • 檢查 node_modules 的實際依賴樹

第二層是 npm 自己的套件樹

  • 檢查 base image 的 Node / npm 版本
  • 直接在容器裡看全域 npm dependency tree

像這次用的 base image 是:

FROM node:22-alpine

進去看之後就很清楚了:

docker run --rm node:22-alpine sh -lc "node -v && npm -v && npm ls -g diff glob minimatch tar --all --depth=6 || true"

結果直接對上弱掃報告:

v22.22.1
10.9.4

/usr/local/lib
└─┬ npm@10.9.4
  ├── glob@10.4.5
  ├── minimatch@9.0.5
  ├── tar@6.2.1
  ├── tar@7.4.3
  └── diff@5.2.0

也就是說,掃描命中的不是專案安裝的 app dependency,而是 base image 內建的 npm 套件樹

解法

既然 root cause 在 npm,就不能只升專案 dependency,還要在 image 裡把 npm 一起升掉。

這次最後加的是:

FROM node:22-alpine

RUN apk update && apk upgrade --available && sync
RUN apk add --update ca-certificates openssl && update-ca-certificates

# Upgrade npm to fix bundled tar CVEs (tar@6.x bundled in npm@10, fixed in npm@11.11.1+)
RUN npm install -g npm@11.11.1

升完之後再驗證:

docker run --rm <your-image> sh -lc "npm -v && npm ls -g tar --depth=2 || true"

會看到類似:

11.11.1
/usr/local/lib
└─┬ npm@11.11.1
  └── tar@7.5.11

這時候 image 裡的舊版 tar@6.x 就不在了,弱掃結果才會真的消掉。

為什麼會這樣

因為 Docker image 的弱掃不是只掃你的應用程式目錄,也會掃整個檔案系統裡已安裝的套件。

對 Node.js image 來說,常見來源至少有兩塊:

  1. 專案自己的依賴 來自 package.json / lockfile / node_modules

  2. base image 內建工具鏈 像是 npm 本身,以及 npm 內部再依賴的 globminimatchtardiff

所以同樣看到 tar,不代表一定是你專案某個 library 帶進來的。它也可能只是 npm 自己用來處理 package archive 的內部依賴。

如果沒有先把這兩類分開,很容易出現兩種誤判:

  • 明明 app 套件已經升完,卻一直懷疑 lockfile 沒更新
  • 明明問題在 base image,卻一直在 package.json 裡盲目加 overrides / resolutions

快速判斷流程

下次遇到類似情況,我會直接照這個順序查:

  1. 先看 package.json 和 lockfile,確認專案是否真的還 pin 在舊版
  2. 跑 app 依賴樹,確認問題是不是來自 node_modules
  3. 直接進 base image 或目標 image 跑 npm ls -g ...
  4. 如果命中的是 npm 內建依賴,就改 Dockerfile
  5. 如果命中的是 app dependency,才回頭改套件版本或 lockfile

學到的事

看 Node.js Docker image 弱掃時,先問自己一句:這個弱點是 app 套件,還是 npm 自己的套件? 先分清來源,再決定要改 package.json,還是改 Dockerfile。這樣不只比較快,也比較不容易修錯地方。

參考資料