Yocto
SBOM 掃描工具為何會漏掉 Yocto 建置中的內容
掃描工具讀取的是檔案系統、二進位檔或資訊清單等輸出,再推論其中放了什麼。Yocto 映像檔隱藏了其中大部分:靜態函式庫與 Rust crate 被編譯進二進位檔,recipe 的修補程式會在不改變版本號的情況下修改程式碼,核心設定決定了哪些程式碼根本存在,而建置主機上的工具從不會進入映像檔。替代做法是記錄建置本身,並保留 Yocto 自身的修補程式與 CVE 中繼資料。
更新日期
一分鐘重點
- 建置主機:建置機器上的工具會影響映像檔,卻從不出現在其中。映像檔的掃描工具看不到它們。
- 下載:BitBake 會下載原始碼、Rust crate、Go 模組與工具。掃描工具只看得到最後留在映像檔中的部分。
- Recipe 與修補程式:recipe 保留上游版本,並在其上套用資安修補程式。比對版本的掃描工具會回報早已修正的 CVE。
- 已編譯的程式碼:靜態函式庫、crate 與核心設定最後都進了二進位檔。在放入了數十個元件的地方,掃描工具只看到一個檔案。
- 映像檔:正式環境映像檔常會移除套件資料庫。這時掃描工具沒有清單可讀,回報的內容很少,甚至什麼都沒有。
Yocto 建置與掃描工具的預期有何不同
Yocto 建置不是套件管理工具的安裝。最終進入裝置的內容,大部分是在建置時於 BitBake 內部、分五個層次決定的。 掃描工具事後只看其中一層,其餘只能猜測。
1. 建置主機
BitBake 會使用建置機器上的工具執行。HOSTTOOLS 變數列出建置任務可以呼叫的建置主機工具,
建置系統會將它們以符號連結放到 ${TMPDIR}/hosttools。BitBake 也會透過 -native 與 -cross recipe
建置自己的工具。主機工具會影響映像檔,但依設計並不屬於映像檔的一部分。
2. 下載
Recipe 在 SRC_URI 中指定其來源,由 fetcher 下載到 DL_DIR。它會先在 DL_DIR 中尋找,
接著是 PREMIRRORS,然後是上游,最後是 MIRRORS。Rust crate 透過 crate:// fetcher 取得,
並列在 -crates.inc 檔案中,該檔案由 cargo-update-recipe-crates 從 Cargo.lock 產生。Go 模組透過 go-mod class 取得,或以 go-vendor 內嵌。
BB_NO_NETWORK 與 BB_ALLOWED_NETWORKS 控管 fetcher 可以連線到哪裡。
3. Recipe 與修補程式
每個 recipe 都在 PV 中固定一個上游版本,並在其上套用修補程式。標頭含有 CVE:
一行的修補程式,會將該 CVE 標記為已修補。版本號仍是上游的版本號,因此 recipe 可能已經修正,版本號卻顯示並非如此。
4. 已編譯的程式碼
靜態函式庫(-staticdev 套件)、Rust crate 與 Go 模組,最後都進入使用它們的二進位檔。
核心及其模組是依產品專屬的設定建置的,因此兩個使用相同核心版本的平台,包含的程式碼並不相同。
5. 映像檔
也就是根檔案系統。package-management 映像檔功能會安裝套件管理工具,並保留套件管理工具的資料庫。
若未啟用,套件管理工具的資料檔案會從根檔案系統中刪除,這在正式環境映像檔上很常見。
共用狀態(sstate)快取
BitBake 可以從共用狀態快取還原任務的輸出,而不實際執行任務。Yocto 的概覽手冊說得很直白:如果有預先建置好的物件, 就不需要編譯器,「因此,編譯器甚至不會被下載」。從 sstate 還原的任務會跳過下載、編譯與安裝, 這對任何依賴觀察這些步驟的工具都有影響。詳見下方關於快取的章節。
映像檔與檔案系統掃描工具為何會漏掉大部分內容
-
沒有套件資料庫。未啟用
package-management建置的正式環境映像檔,沒有清單可供掃描工具讀取, 因此掃描工具只能退而依賴檔案啟發式判斷,或幾乎什麼都不回報。結果看起來很完整,實際上幾乎是空的。 -
靜態與內嵌的程式碼。編譯進
/usr/bin/foo的靜態函式庫或 crate 不會留下任何套件紀錄。librsvg就是一例:目前的 oe-core recipe 列出超過 350 個 Rust crate,而在裝置上它只是一個共用函式庫。 -
建置主機與下載的工具。它們從不在映像檔中,因此也從不出現在映像檔掃描結果裡,然而遭入侵的主機工具或
GitHub Action 卻能危害映像檔。xz utils 後門(CVE-2024-3094)是在函式庫建置過程中植入的,來源是發布用的 tarball,
而非 Git 儲存庫。2025 年 3 月,CISA 通報 GitHub Action
tj-actions/changed-files的所有版本遭入侵長達三天。 -
已套用的修補程式。映像檔顯示的是某個上游版本的
openssl。recipe 已套用影響該版本之 CVE 的修正, 並以CVE:標記該修補程式。版本範圍比對仍會將其標示出來。這是發行版回溯修補(backport)問題在 Yocto 中的形式, 也是如何降低漏洞雜訊一文所討論之雜訊的主要來源之一。
總結起來,就是缺漏的元件與錯誤的發現混雜在一起,而結果中沒有任何線索能告訴您哪個是哪個。 同樣的論點也適用於Docker 映像檔 SBOM 中的容器,以及 開源 SBOM 產生工具中的程式語言專案。
韌體的二進位掃描為何只能猜測
二進位分析會將解開的韌體中的字串、符號與程式碼特徵,與已知元件比對,再推論版本。當二進位檔帶有明確線索時,這行得通。 但在 Yocto 映像檔中,它往往只能猜測:
- 沒有版本字串的已剝除(stripped)二進位檔;
- 靜態連結的程式碼,函式庫之間的界線已經消失;
- 已編譯的 Rust,crate 之間的界線消失在單一執行檔中;
- 回溯修補的修補程式,在版本號中看不出來;
- 由設定決定編譯進去或排除在外的核心程式碼。
對於不是由您建置的韌體,二進位分析是正確的工具:供應商的 blob、外購的模組、您正在評估的裝置。 如果您沒有建置過程,分析二進位檔是唯一的選擇。判斷原則很簡單:如果是您建置的,就記錄它; 如果只是收到的,就分析它,並向供應商索取來自其建置的 SBOM。
Yocto 自身的 SBOM 與 CVE 工具做得好的地方
Yocto Project 的 SBOM 工作是紮實的工程成果,而且它知道外部工具無從得知的事。其文件指出,SBOM 是 「您所使用之所有元件、其授權、相依套件、來源、對其所做的變更,以及已修正之已知漏洞的描述」。
各版本的 create-spdx
- 4.3(Nanbield):預設啟用 SPDX 產生。
- 5.1(Styhead):預設改用 SPDX 3.0 取代 2.2,並新增
vexclass,為外部 CVE 工具提供所需資訊。 - 5.3(Whinlatter):
SPDX_INCLUDE_COMPILED_SOURCES,只納入實際用於編譯的原始碼。 - 6.0(Wrynose,長期支援):移除 SPDX 2.2;新增
SPDX_INCLUDE_KERNEL_CONFIG與SPDX_INCLUDE_PACKAGECONFIG;以 Cargo、Go、PyPI、npm 與 CPAN class 建置的 recipe 會取得生態系的 package URL。
SBOM 涵蓋 BitBake 所建置的內容,包括它為主機建置的原生工具。對於所有具備 recipe 的內容,它都相當完整。
SBOM 中以 VEX 表示的 CVE 狀態
在 SPDX 3 中,建置系統會將每個 recipe 的 CVE 狀態寫成 VEX 關係:已修補的 CVE 成為「fixed」並連結到修補檔案;
未修補的成為「affected」;被忽略的成為「not affected」,並以原因作為影響說明。SPDX_INCLUDE_VEX
設定納入的範圍:none、current(預設值,即上游原始碼中尚未修正的漏洞)或 all。
CVE_STATUS 與修補程式標記
CVE_STATUS 以原因與選填的說明,記錄針對每個 CVE 的判定。這些原因分別對應到已修補
(backported-patch、cpe-stable-backport、fixed-version 等)、
未修補(vulnerable-investigating)與忽略(cpe-incorrect、disputed、
not-applicable-config、not-applicable-platform、upstream-wontfix)。
CVE_STATUS_GROUPS 可將同一判定套用到一組 CVE,CVE_PRODUCT 則設定比對時使用的產品名稱。
# In a recipe or .bbappend
CVE_STATUS[CVE-2020-15523] = "not-applicable-platform: Issue only applies on Windows"
# In the header of a patch file
CVE: CVE-2022-3341
Upstream-Status: Backport [link to the upstream commit]
從 cve-check 到 sbom-cve-check
cve-check 會以 recipe 名稱(CVE_PRODUCT)與版本比對漏洞資料。5.2 版的發行說明警告,
自 2024 年初起,NVD 維護者已停止為許多 CVE 標註受影響的 CPE,因此「目前的 CVE 報告看起來可能不錯,
但實際上有些漏洞根本沒有被回報」。5.2 也將預設資料來源從 NVD 改為 FKIE feed。Yocto 6.0 移除了 cve-check,
並以 sbom-cve-check class 取代,該 class 會針對 SPDX 輸出執行 Bootlin 的
sbom-cve-check。
在 5.0 到 5.3 版中仍有 cve-check;請將其視為舊有功能。
Yocto 的 SBOM 止於何處
Yocto 看不到的,是 BitBake 中繼資料以外的一切。我們在具備 32 vCPU 的原生 ARM64 執行器上建置了兩次
core-image-sato,一次啟用 Yocto 的 SBOM 產生,一次不啟用,並比較 Yocto 以 1,632 個檔案寫出的 SPDX 3.0.1 SBOM
與 CRACI 記錄的 CycloneDX SBOM:
-
元件。CRACI 記錄的 SBOM 包含 Yocto SBOM 中的所有元件,另外還多了 88 個 Yocto 漏掉的元件:
建置機器上的 23 個主機套件(例如 Ubuntu 的
cpio)、10 個 GitHub 來源(包括 GitHub Actionactions/upload-artifact),以及建置期間下載的 55 個其他來源與工具。 -
Rust crate。兩者都列出相同的 653 個 Rust crate 版本。在我們建置的版本中,Yocto 的 SBOM 只將它們列為帶有下載位置的
.crate檔案,沒有pkg:cargopackage URL,因此無法與資安公告比對。 自 Yocto 6.0 起,以 Cargo class 建置的 recipe 本身會帶有 package URL,例如pkg:cargo/librsvg; 但在目前的程式碼中,個別的 crate 下載仍然沒有。請確認您使用的版本實際寫出了什麼。 -
成本。啟用 Yocto SBOM 產生的建置耗時 155 分 40 秒,並寫出 1,632 個 SPDX 檔案。
不啟用時,建置耗時 88 分 48 秒,少了 43%。當時已啟用
SPDX_INCLUDE_SOURCES、SPDX_INCLUDE_COMPILED_SOURCES、SPDX_INCLUDE_KERNEL_CONFIG與SPDX_INCLUDE_PACKAGECONFIG,因此預設設定的成本會較低。Yocto 自己的文件也警告,納入已編譯的原始碼時,core-image-minimal未壓縮的 SBOM「可能從數百 MB 成長到數 GB」。
cmake、llvm、ninja 與 qemu 等原生 recipe 都在 Yocto 的 SBOM 中。
方法、兩份 SBOM 與完整報告,請見在 CRACI 上執行 Yocto 建置。
Linux 核心 CVE:依設定而異的問題
Linux 核心專案於 2024 年 2 月 13 日宣布成為 CVE Numbering Authority,並且相當積極地為修正指派 CVE。 其 CVE 文件指出,任何特定 CVE 是否適用「由 Linux 的使用者自行判斷」,因為「我們不知道您的使用情境, 也不知道您使用了核心的哪些部分」。
因此,核心版本比對會產生數百個發現,其中大多數涉及您的設定從未編譯的程式碼。 四個硬體平台搭配四種核心設定,即使核心版本相同,也會得到四種不同的答案。處理方式有四種:
- 僅比對版本。回報該版本的所有 CVE。雜訊最多。
-
人工整理的排除清單。從核心的 CVE 資料產生的
CVE_STATUS項目。2026 年 5 月, oe-core 移除了為linux-yocto產生的清單,因為sbom-cve-check會根據核心提供的 CVE 清單自行處理, CVE 報告不變,而 SPDX 小了將近 20 MB。 -
依實際編譯的內容過濾。為核心 recipe 啟用
SPDX_INCLUDE_COMPILED_SOURCES後,improve_kernel_cve_report.py會檢查 CVE 影響的檔案是否被用於建置核心。Yocto 的資安手冊表示,這能「將 CVE 誤報減少 70%-80%」。 - 記錄設定。
SPDX_INCLUDE_KERNEL_CONFIG(6.0)會將核心設定寫入 SBOM,以便日後檢查答案。
實例。網路驅動程式中的一個核心 CVE。平台 A 建置了該驅動程式;平台 B 沒有。
| 平台 | 是否編譯驅動程式? | 版本比對 | 已編譯原始碼過濾 |
|---|---|---|---|
| A | 是 | 回報 | 回報:受影響的檔案已被建置 |
| B | 否 | 回報 | 不回報:受影響的檔案未被建置 |
若您現在就需要依平台區分的核心 CVE 報告,請針對每種機器設定各執行一次 Yocto 的已編譯原始碼工具。 決定答案的是設定,因此報告必須來自每種設定的建置。
快取、鏡像與內嵌:任何記錄工具都必須小心之處
-
完全從 sstate 或已填滿的
DL_DIR還原的建置,幾乎不會下載任何東西。觀察下載內容的記錄工具, 必須讓其佐證資料隨這些快取一起保存,否則紀錄就會縮水。 - 指向預先內嵌之 tarball 的 recipe,只會顯示一次下載,而不是其中的 crate 或模組。
- 無論您使用哪種工具,都請從已知的快取狀態建置正式版本映像檔、保留由您自行控管的鏡像,並比較各次建置之間的元件數量。
在 CRACI 上,從 CI 快取還原的套件會保留在 SBOM 中,而每個作業的完整性狀態會標示出記錄無法看到的部分。
該執行什麼:決策表
| 情境 | 使用工具 | 您得到什麼 | 您仍然缺少什麼 |
|---|---|---|---|
| 您在 GitHub Actions 中建置映像檔 | 記錄建置(CRACI);若您依賴其修補程式與 CVE 狀態,請保留 create-spdx |
主機套件、下載的來源與 Actions、每個 crate 的 pkg:cargo URL、持續監控 |
依設定區分的核心過濾:請使用 Yocto 的工具 |
| 您在其他 CI 系統或建置伺服器上以 Yocto 建置 | create-spdx 搭配 sbom-cve-check(舊版則用 cve-check),以已編譯原始碼進行核心過濾 |
Recipe、修補程式、以 VEX 表示的 CVE 狀態 | 主機套件與下載工具的資產清冊,需另外維護 |
| 您收到的是非您建置的韌體 | 二進位分析,以及向供應商索取建置 SBOM | 從二進位檔推論出的元件 | 版本、修補程式與建置輸入的確定性 |
| 您需要依平台區分的核心 CVE | Yocto 的已編譯原始碼工具,依機器設定分別執行 | 每種設定所建置之程式碼的 CVE | 核心以外的元件,需來自您的 SBOM |
| 您只有映像檔掃描工具 | 若保留了套件資料庫,可用於已安裝的套件 | 部分執行期套件清單 | 所有被編譯進去、經修補或留在建置主機上的內容;乾淨的結果不等於佐證資料 |
各方法看得到什麼
| 元件類別 | 映像檔掃描工具 | 二進位分析 | Yocto create-spdx |
建置記錄(CRACI) |
|---|---|---|---|---|
| 已安裝的套件 | 僅在保留套件資料庫時 | 從檔案推論 | 是 | 是,來自建置下載的來源 |
| 靜態函式庫 | 否 | 從特徵推論 | 是,以 recipe 形式 | 是,來自建置下載的來源 |
| Rust crate | 否 | 推論;crate 界線會消失 | 以 .crate 下載形式,沒有個別 crate 的 package URL |
是,每個都附有 pkg:cargo package URL |
| Go 模組 | 否 | 從二進位檔推論 | 以 recipe 的模組下載形式 | 是,若於建置期間下載 |
| 已套用的修補程式與 CVE 狀態 | 否 | 否 | 是,以 VEX 形式 | 否 |
| 核心設定 | 否 | 否 | 是,使用 SPDX_INCLUDE_KERNEL_CONFIG(6.0) |
否 |
| 建置機器上的主機工具 | 否 | 否 | 否(原生 recipe 則是) | 是 |
| 在 recipe 之外下載的建置工具,以及 GitHub Actions | 否 | 否 | 否 | 是 |
CRACI 的定位
對於在 GitHub Actions 中執行的 Yocto 建置,CRACI 就是執行器,最高可達 32 vCPU 與 96 GB RAM。 它會記錄建置拉進的內容,包括主機套件、GitHub 來源與 Actions,以及其他下載的來源,並為每個 Rust crate 附上 package URL: 在已公布的比較中,它涵蓋 Yocto SBOM 中的所有元件,另外還多了 88 個 Yocto 漏掉的元件。 它會將元件與資安公告比對,在新公告發布時持續重新評估 SBOM,並顯示哪些建置包含有漏洞的相依套件。
BitBake 本身已能以 BB_ALLOWED_NETWORKS 限制其 fetcher。CRACI 的
對外連線政策則在執行器上加了第二道控管:在作業開始前檢查,且失效即封鎖,
因此繞過 fetcher 的腳本或工具,仍然無法連線到您未允許的主機。
在供應鏈涵蓋範圍上,CRACI 的 SBOM 可以取代 create-spdx。但 Yocto 的 SPDX 仍帶有已套用的修補程式與標記為已修正的 CVE,
而這些 CRACI 並不記錄,因此若您依賴這些資訊,請保持 create-spdx 啟用。CRACI 文件中有一個建置
core-image-sato 的範例。為何記錄普遍優於掃描,請見建置時 SBOM;
Yocto 工具的比較則請見嵌入式資安工具比較與
CRACI 與 Timesys Vigiles 比較。
何時 CRACI 不是答案
- 您依賴修補程式層級的 CVE 狀態,例如
CVE_STATUS判定,以及以 VEX 表示的已修補或忽略的 CVE。這些資訊存在於 Yocto 的 SPDX 中:請保留它。 - 您現在就需要依設定區分的核心 CVE 報告。請使用 Yocto 的已編譯原始碼工具。
- 韌體不是由您建置的。請使用二進位分析。
- 您的 Yocto 建置在 GitHub Actions 之外執行,或必須在您自己的基礎架構上執行。CRACI 目前提供在歐洲基礎架構上的代管執行器; 其他 CI 系統與客戶自行託管的執行器已列入產品藍圖。
- 您特別需要 SPDX 格式的 Yocto 作業 SBOM。在已公布的比較中,CRACI 的 SBOM 為 CycloneDX;Yocto 自己的 SBOM 則是 SPDX。
正在為歐盟市場打造聯網裝置嗎?請閱讀 CRA 的要求、 VEX 的運作方式,以及 CRACI 如何適用於電信與網路設備、 工業物聯網與 消費性電子。
Yocto CVE 檢查與 SBOM:常見問題
為什麼我的 SCA 工具無法掃描我們以 Yocto 為基礎的產品?
因為大多數 SCA 工具讀取的是資訊清單、lockfile 或映像檔的套件資料庫,而 Yocto 建置是在 BitBake 內部決定其內容。正式環境映像檔常會移除套件資料庫,靜態函式庫與 Rust crate 被編譯進二進位檔,recipe 的修補程式會保留上游版本號,而建置主機上的工具從不會進入映像檔。
我可以用 Trivy、Grype 或二進位掃描工具掃描 Yocto 映像檔或韌體嗎?
可以,但您只會得到部分答案。映像檔掃描工具在保留套件資料庫時能讀取已安裝的套件,卻會漏掉編譯進去的內容。二進位掃描工具從字串與特徵推論元件,面對已剝除的二進位檔、靜態程式碼與已編譯的 Rust 時只能猜測。兩者都看不到建置主機,也看不到 recipe 套用的修補程式。
Yocto 的 SBOM 包含 Rust crate 與 Go 模組嗎?
它會將它們列為使用它們之 recipe 的來源。在我們建置的版本中,每個 Rust crate 都以 .crate 下載的形式出現,帶有下載位置,但沒有 pkg:cargo package URL。自 Yocto 6.0 起,以 Cargo 與 Go class 建置的 recipe 會為 recipe 本身取得生態系的 package URL,而不是為每個 crate 或模組下載取得。
為什麼我的掃描工具會回報我們的 Yocto recipe 已經修補過的 CVE?
因為它比對的是版本號。Yocto recipe 會保留上游版本,並以修補程式的形式套用資安修正,這些修補程式以 CVE: 一行標記,或記錄在 CVE_STATUS 中。版本範圍比對看不到修補程式。Yocto 自己的 SPDX 會將這些 CVE 記錄為已修正,並連結到修補檔案。
Yocto 中取代 cve-check 的是什麼?
是 Yocto 6.0 中的 sbom-cve-check class。它會針對建置的 SPDX 輸出執行 Bootlin 的 sbom-cve-check。5.0 到 5.3 版仍有 cve-check,它比對的是 recipe 名稱與版本;5.2 版的發行說明警告,由於許多 CVE 已不再帶有 CPE 資料,其報告可能會漏掉漏洞。
如何取得針對我自己核心設定的 Linux 核心 CVE?
為核心 recipe 啟用 SPDX_INCLUDE_COMPILED_SOURCES,並執行 improve_kernel_cve_report.py,它會檢查每個 CVE 影響的檔案是否被編譯。Yocto 的資安手冊表示,這能將 CVE 誤報減少 70% 到 80%。請依機器設定分別執行,因為每種設定建置的程式碼都不同。
共用狀態快取會影響 SBOM 嗎?
有可能。從 sstate 還原的任務會跳過下載與編譯,因此依賴觀察這些步驟的工具會看到較少內容。Yocto 的 create-spdx 是根據 recipe 中繼資料運作。無論採用哪種方法,都請從已知的快取狀態建置正式版本映像檔,並比較各次建置之間的元件數量。
Yocto 內建的 SBOM 足以因應 CRA 嗎?
CRA 要求 SBOM 至少涵蓋最上層的相依套件,而 Yocto 的 SBOM 對 BitBake 建置的內容相當完整。它不會列出建置機器上的主機套件、GitHub Actions,或在 recipe 之外下載的工具。這樣是否足夠由您判斷;SBOM 是您流程的佐證資料,本身並不等於法規遵循。