資安指南
osv-scanner 實務:pnpm、忽略 CVE、離線模式與常見錯誤
導入之後才會遇到的問題:pnpm-lock.yaml 支援、用 osv-scanner.toml 忽略單一 CVE、三個離線旗標及其引發的錯誤、CI 中的結束碼 0/1/127/128,以及從 v1 到 v2 改名的旗標。
適用對象:已經安裝 osv-scanner 並遇到問題的人。本文涵蓋的是導入之後才會遇到的問題,而不是安裝。所有內容都依據官方文件。
指定鎖定檔
# a single lockfile
osv-scanner scan source -L pnpm-lock.yaml
# walk a directory
osv-scanner scan source -r ./my-project/當無法從檔名推斷格式時,在路徑前面加上要使用的解析器:
# parse an oddly named file as requirements.txt
osv-scanner scan source -L 'requirements.txt:/path/to/extra-requirements.txt'
# path containing a colon — lead with a bare ':' to keep filename inference
osv-scanner scan source -L ':/path/to/my:projects/package-lock.json'為什麼用鎖定檔而不是 manifest
manifest(package.json 等)記錄的是你要求的範圍,而不是實際安裝的版本。鎖定檔記錄的是解析後的確切版本,所以掃描結果與實際安裝的內容一致。依範圍判斷再期待結果正確,是弱得多的依據。
如何檢查自己的環境
結果看起來不對時,在加更多旗標之前,先進行以下五項確認。每一項都只檢查你自己的專案。
確認版本
osv-scanner --version。2025 年發布的 v2 改變了指令形式和多個旗標名稱,所以為 v1 寫的說明照做常常會失敗。官方文件現在以 v2 為準。確認它讀了什麼
--format json --all-packages(--all-packages 只能搭配 JSON 輸出)。確認結束碼
echo $?,在 PowerShell 中用 $LASTEXITCODE。各值的意義見下表。確認設定實際套用
osv-scanner.toml 只套用到它所在目錄中的檔案,不會傳遞到子目錄。在用 -r 掃描的 monorepo 中,在每個鎖定檔旁邊各放一個,或用 --config 把同一個檔案套用到全部。確認離線資料庫的位置和新舊
OSV_SCANNER_LOCAL_DB_CACHE_DIRECTORY 就從那裡讀取,否則依序是使用者快取目錄、系統暫存目錄。其中的檔案依 osv-scanner/<ecosystem>/all.zip 的形式存放。如果它們的修改日期很舊,你的判定結果也一樣舊。結束碼與 CI 的處理方式
| 結束碼 | 官方意義 | 在 CI 中 |
|---|---|---|
0 | 找到套件,沒有符合任何已知漏洞 | 通過 |
1 | 找到套件,有符合的漏洞 | 失敗(有檢出:修正,或設定到期日後忽略) |
127 | 一般錯誤 | 失敗(掃描本身出錯) |
128 | 沒有找到套件(沒有讀到任何可讀的內容) | 失敗(修正路徑或加入鎖定檔) |
文件把 1–126 保留給與結果相關的情況,129–255 保留給與結果無關的錯誤。把 CI 的處理分成三種:0 通過、1 是檢出、其他都是掃描故障,排查失敗原因會快得多。
忽略特定漏洞
在被掃描的目錄中放一個 osv-scanner.toml:
[[IgnoredVulns]]
id = "GHSA-xxxx-xxxx-xxxx"
ignoreUntil = 2026-12-31
reason = "Feature is not used and the code path is unreachable; re-evaluate at the December inventory"id(必填):漏洞識別碼ignoreUntil(選填):過了這個日期就會再次回報reason(選填):還是要寫,為了下一個人,而那個人通常就是你自己
--config=/path/to/config.toml 會覆蓋各目錄的設定檔,套用到所有地方。注意,忽略一個漏洞時,被視為其別名的漏洞也會一併被忽略。
每個忽略都設定到期日,而不是永久移除
沒有 ignoreUntil 的項目,就是會永久消失、沒有人再回頭看的項目。我們的規則是:每個忽略都帶有到期日和理由。日期一到它就會再次出現,延後的判斷就不會被默默遺忘。為了讓 CI 安靜而加上的無期限忽略,正是半年後變成事故的那種東西。
離線模式:三個旗標,三種用途
最容易混淆的就是這裡。
| 旗標 | 作用 |
|---|---|
--offline | 完全離線。 用事先下載的本機資料庫判定,不會更新它,也不會把專案或相依套件的資訊傳送到任何地方 |
--offline-vulnerabilities | 只有漏洞比對是離線的;其他工作(例如遞移相依的解析)仍可能使用網路 |
--download-offline-databases | 允許取得/更新本機資料庫,只有同時設定離線旗標時才會生效 |
--offline
完全離線;不更新資料庫,不傳送任何資料
--offline-vulnerabilities
只有比對在本機進行;其他工作可能使用網路
--download-offline-databases(單獨使用無效)
需要搭配上面其中之一 → 單獨使用會出現 "databases can only be downloaded when running in offline mode"
錯誤訊息與原因
databases can only be downloaded when running in offline mode→ 單獨傳入了--download-offline-databases;它需要搭配離線旗標- 還沒有本機資料庫就使用
--offline→ 會出現資料庫不存在的錯誤;第一次執行必須先取得資料庫 no package sources found→ 該路徑下沒有任何東西被辨識為鎖定檔或 manifest;用-L指定,或用-r走訪
不靠猜測的選擇方式
- 在隔離網路中執行 →
--offline(第一次執行時加上--download-offline-databases) - 只有漏洞資料必須留在本機 →
--offline-vulnerabilities - 固定資料庫的位置 → 環境變數
OSV_SCANNER_LOCAL_DB_CACHE_DIRECTORY - 除錯時 → 先不加任何旗標執行,再逐步縮小範圍
v1 文章中的旗標無法使用時
許多搜尋結果是為 v1 寫的。官方遷移指南列出的主要變更:
| v1 | v2 |
|---|---|
--experimental-offline | --offline |
--experimental-download-offline-databases | --download-offline-databases |
--experimental-call-analysis | --call-analysis |
--experimental-licenses 及相關旗標 | 合併為 --licenses |
--docker / -D(容器) | osv-scanner scan image <image> |
--json | --format=json |
--skip-git | --include-git-root(意義相反) |
osv-scanner <dir> 仍可作為 osv-scanner scan source <dir> 的簡寫。在腳本中寫出完整的 scan source,下一個讀的人更容易理解意圖。
常見錯誤與修正
常見錯誤
- CI 把「除了 1 以外的任何結束碼」都當作成功
- 加上
|| true讓失敗消失 - 只在儲存庫根目錄放一個
osv-scanner.toml,以為它涵蓋子目錄 - 只 commit 了
package.json,沒有鎖定檔 - 以為
--offline仍會解析pom.xml的遞移相依 - 寫入忽略設定卻沒有
ignoreUntil
修正方式
- 只在 0 時通過,讓 128(什麼都沒讀到)無法變成綠燈
- 改用帶到期日的忽略,逐一處理個別檢出
- 在每個目錄各放一個檔案,或用
--config把一個檔案套用到全部 - commit 鎖定檔(JavaScript 為
package-lock.json、pnpm-lock.yaml、yarn.lock或bun.lock) - 離線時遞移相依的解析會被停用;如果允許使用網路,就用
--offline-vulnerabilities - 一定加上日期和理由,到期時重新判斷
Maven 的 pom.xml 不像鎖定檔那樣列出所有解析後的版本。osv-scanner 預設使用 deps.dev 的解析器(或用 --data-source=native 改用 Maven Central)計算遞移相依關係圖。這需要網路,在 --offline 和 --no-resolve 下會被略過。在完全隔離網路的執行中,要預期結果只涵蓋 pom.xml 中直接寫明的相依套件。
實際案例:為什麼 Log4Shell 難以回答
2021 年 12 月 Log4Shell(CVE-2021-44228) 出現時,許多組織無法迅速說出自己是否受影響。他們從未自己加入 Log4j;它是透過框架和其他函式庫帶進來的。CISA 的指引要求組織全面列出已安裝的軟體,並與受影響軟體的清單比對。
套用到 osv-scanner,就是要確認兩件事:
- 遞移相依確實有被讀取:生態系有鎖定檔就用鎖定檔,Maven 則在啟用解析的情況下執行(不是離線,也不是
--no-resolve) - 清單中確實包含間接引入的函式庫:用
--format json --all-packages印出來看
漏洞公開當天能否在幾分鐘內回答「我們有沒有受影響?」,取決於事先是否把這份清單做對。
與其他工具的差異
| 工具 | 讀取的資料 | 擅長的地方 |
|---|---|---|
| osv-scanner | OSV.dev | 跨生態系、以鎖定檔為單位、可離線執行 |
npm audit / pnpm audit | npm 安全公告資料庫 | npm 專案立即可用,不需額外安裝 |
| Dependabot | GitHub Advisory | 建立更新 PR:不只指出問題,還提出修正 |
| Trivy | 多個來源(含 OSV) | 除了應用程式相依套件,也涵蓋容器映像檔和 OS 套件 |
只選一個才是錯誤。 資料庫不同,找到的東西也不同。我們以 pnpm audit 作為每日的自動檢查,並用 osv-scanner 進行跨鎖定檔的檢查。
本站的看法:掃描工具不會告訴你固定的版本已經過時
有一個問題只有在實際維運中才會出現。用 overrides 把遞移相依固定在某個確切版本時,那個套件就不再收到修補更新;而掃描工具只有在存在已知漏洞時才會發出提示。所以「你凍結的版本已經舊了」是沒有任何工具會回報的狀態。我們遇到這個問題三次之後,才加上自己的每日檢查,把每個固定版本與其主版本系列中最新的修補版比對。掃描工具顯示的是今天的已知漏洞;日後可能讓你暴露在風險中的狀態,需要另外的機制。
出處
- OSV-Scanner 文件 — Usage / Offline Mode / Configuration
- Supported Artifacts and Manifests
- OSV-Scanner 文件 — Output(輸出格式與結束碼)/ Migration Guide(v1 到 v2)
- CISA — Apache Log4j Vulnerability Guidance
接下來閱讀
- 入門:osv-scanner 的安裝與使用
- 排定優先順序:決定先修補哪個(CVSS、EPSS、KEV)
- 事故:Log4Shell(遞移相依讓人看不清實際用了什麼)
- 實務:漏洞應對的實務 / 防範 npm 供應鏈蠕蟲(英文)
FAQ
Qosv-scanner 支援 pnpm-lock.yaml 嗎?
支援。npm、yarn 和 pnpm 的鎖定檔都有支援。用 `osv-scanner scan source -L pnpm-lock.yaml` 直接指定一個檔案,或用 `-r` 走訪整個目錄樹。
Q如何忽略單一 CVE?
在被掃描的目錄中放一個 `osv-scanner.toml`,加入帶有 `id` 的 `[[IgnoredVulns]]` 項目。也可以設定 `ignoreUntil`(到期日)和 `reason`,讓例外記錄下時間和理由,而不是永遠消失。傳入 `--config=/path/to/config.toml` 會覆蓋各目錄的設定檔,套用到全部。
Q為什麼會出現「databases can only be downloaded when running in offline mode」?
因為 `--download-offline-databases` 只有在同時設定離線旗標時才會生效,不能單獨使用。想在執行時一併取得本機資料庫,就把它和離線旗標一起使用。
Q已經有 npm audit 和 Dependabot,還需要 osv-scanner 嗎?
它們讀取不同的資料庫,涵蓋的範圍也不同。npm audit 使用 npm 的安全公告資料庫,只支援 npm;Dependabot 主要在 GitHub 上提出更新建議;osv-scanner 讀取 OSV.dev,跨生態系(npm、PyPI、Go、Maven 等),以鎖定檔為單位運作,也能完全離線執行。一個抓到另一個漏掉的東西是正常的,所以把它們當作不同的工作,而不是只選一個。
QCI 應該如何處理 osv-scanner 的結束碼?
依官方文件:0 表示找到套件且沒有符合任何已知漏洞,1 表示找到漏洞,127 是一般錯誤,128 表示完全沒有找到套件。只把 0 當作成功。像「除了 1 以外都通過」這樣的規則,會讓 128(什麼都沒掃描)變成綠燈。
Q為什麼舊文章裡的 --experimental-offline 或 --docker 無法使用?
它們在 v2 中被改名或移動了。官方遷移指南把 --experimental-offline 對應到 --offline,--experimental-download-offline-databases 對應到 --download-offline-databases,--docker 對應到 `osv-scanner scan image` 指令,--json 對應到 `--format=json`。先執行 `osv-scanner --version` 確認你用的是哪個主版本。