這是一張有關標題為 Git、Yocto 與 CVE:透過 Git Patch 修補開源專案漏洞的完整實戰 的圖片

Git、Yocto 與 CVE:透過 Git Patch 修補開源專案漏洞的完整實戰

解析 git diff/apply 與 format-patch/am 差異與實務,示範產生與命名 patch、在 Yocto/BitBake Recipe 整合,結合 do_patch 與 cve-check 稽核 CVE,輔以 SCA 工具,制定驗證流程、保留作者與提交資訊,強化開源專案修補能力。

前言

在團隊開發中,我們通常會架設自己的 Git 伺服器,團隊成員根據權限進行程式碼的拉取(pull)與推送(push)。然而,當需要貢獻程式碼至一個沒有推送權限的專案,或是在嚴格的存取控制下進行協作時,直接推送便不可行。

這就是 Git Patch 工作流程發揮作用的地方。它允許我們將一個或多個提交(commit)打包成獨立、可傳遞的 .patch 檔案。接收者可以將這些檔案應用到他們的程式碼庫中,並完整保留原始的提交資訊,如作者、日期與訊息。

Git Patch 基礎機制

Git 提供了兩種主要的 Patch 生成與應用方式:git diff/git applygit format-patch/git am。它們看似相似,但在功能和應用場景有著本質的區別。

何時使用哪種方式(快速判斷)

  • 若只想分享檔案內容差異、且不需要保留原作者與提交歷史:用 git diff + git apply
  • 若要保留原作者、提交訊息與可直接匯入 commit 歷史:用 git format-patch + git am推薦於正式貢獻流程)。

工作流程 1:git diff / git apply (僅應用程式碼變更)

這種方法僅關心檔案內容的差異,生成的 .patch 檔不包含任何提交的元數據,如作者、日期或提交訊息。

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
# 於本機 A 產生差異,比較 HEAD 與前一個 commit 的差異,並輸出為 changes.patch
git diff HEAD~1 HEAD > changes.patch

# 於接收主機 B - 檢查 patch 是否能乾淨地應用
git apply --check changes.patch

# 確認無誤後,正式應用 patch 至工作目錄
git apply changes.patch

# 此時變更已在工作目錄中,需要手動暫存並建立一個新的 commit
git add .
# 主機 B 的使用者進行提交,原作者 A 會被洗掉
git commit -m "Apply upstream patch: description"

工作流程 2:git format-patch / git am(保留作者資訊)

這是更推薦的標準做法。format-patch 生成的 .patch 檔實質上是一個郵件格式的檔案,完整保留了原始提交的所有元數據。維護者使用 git am(apply from mailbox)應用它時,能夠完美還原原始的提交歷史。

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
# 於本機 A 為最新的 commit 生成一個 patch 檔案
git format-patch -1 HEAD
# 產生的檔案會是 0001-commit-message.patch

# 或產生最近 3 個 commit 的 patch(會產生 0001-... 0002-... 0003-...)
git format-patch -3 HEAD

# 或只產生單一 commit 的 patch
git format-patch -1 <commit-id>

# 於維護者本機 - 應用單個 patch
git am 0001-*.patch

# 或一次套用多個 patch
git am 000*.patch

# 若想在套用時加入 DCO signoff(視團隊政策)
git am --signoff 000*.patch

# 查看 log,該提交作者還是原先的 A 使用者而不會是主機 B 的使用者
git log -1

DCO(Developer Certificate of Origin)

DCO 是由 Linux Foundation 制定的來源聲明;加入 Signed-off-by 表示你聲明此貢獻由你(全部或部分)創作,且有權依該授權提交。

常見用法:在套用 patch 或 commit 時加入簽署以保留來源與責任。

範例:

  • git am --signoff 000*.patch
  • git commit -s -m "Your commit message"

簽署後會在 commit 中加入 Signed-off-by: Name <you@example.com>

Yocto / BitBake 實務應用

在嵌入式 Linux 開發中,Yocto Project 是構建客製化系統的標準。BitBake 負責建置與組裝大量開源套件;CVE 修補通常以 .patch 形式加入 recipe(以 SRC_URI 引用),並由 BitBake 在 do_patch 階段自動套用。

使用 ‘.patch’ 修正 CVE 錯誤,其原因是盡可能追求系統的穩定性,產品的 Linux 核心通常依賴特定套件的特定版本,避免因升級帶來非預期的風險。然而,這也意味著我們無法透過簡單的版本升級來獲取安全性更新。此時,精準的漏洞修補便成為關鍵。

這正是商業級軟體組成分析(software composition analysis,SCA)工具發揮價值的地方,例如 Vigiles 或 Black Duck。這些工具能深入掃描專案的原始碼,分析出所有使用的開源元件,並比對出其中存在的已知漏洞。

一旦 SCA 工具報告了特定的 CVE 編號,開發者的任務就是根據這些編號,去上游專案或開源社群中尋找已經被製作好的修補程式。接著,將這些找到的 .patch 檔整合進 Yocto 的 BitBake Recipe 中,實現「不動主體、只修漏洞」的精準維護。

在 Recipe 中應用 Patch

Yocto 的 Recipe (.bb 檔) 透過 SRC_URI 變數來管理原始碼和修補程式。開發者只需將 format-patch 生成的 .patch 檔放置於指定目錄,並在 SRC_URI 中引用即可。

假設我們要為 example-package 套件修補兩個 CVE,其 Recipe 可能如下所示:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
# meta-mylayer/recipes-example/example-package/example-package_1.0.bb

SUMMARY = "An example package"
LICENSE = "MIT"
LIC_FILES_CHKSUM = "file://LICENSE;md5=..."

# 指向原始碼壓縮檔
SRC_URI = "https://example.com/downloads/example-package-${PV}.tar.gz"

# 指向要應用的 patch 檔案
# 這些檔案應放在 meta-mylayer/recipes-example/example-package/files/ 目錄下
SRC_URI += " \
    file://CVE-2020-12284.patch;striplevel=1 \
    file://CVE-2021-3566.patch;striplevel=1 \
"

#  patch 檔應該套用到來源子目錄,可指定 patchdir
# SRC_URI += "file://fix-in-subdir.patch;patchdir=subdir;striplevel=1"

在 Yocto 的建置過程中 (do_patch 任務),BitBake 會自動解壓縮原始碼,並依序應用 SRC_URI 中定義的所有 patch。

建議以 CVE 編號命名 patch,例如:CVE-2021-3566.patch。這能讓自動化工具(如 Yocto 的 cve-check)根據檔名標記為 Patched。但該 patch 是否真正解決漏洞。這部分沒辦法實際得知。

結論

掌握 Git 的 patch 工作流程,能讓協作與維護更有條理且容易驗證。簡單來說:當只需套用檔案差異、且不保留原始提交資訊時,git diff/git apply 是快速的選擇;當需要保留作者、訊息與可回溯的提交歷史時,則應優先使用 git format-patch/git am

在 Yocto 與嵌入式產品維護情境中,採用以 CVE 編號命名的 patch(例如 CVE-2021-3566.patch)並透過 recipe 的 SRC_URI 引入,可讓 BitBake 在 do_patch 階段自動套用,並與 SCA 工具與 cve-check 等自動化稽核流程整合。但請記得:檔名標註僅表示已嘗試修補,仍需透過建置、測試與回歸驗證來確認漏洞真被修復。

製作 patch 時務必注意提交標題中的特殊字元與編碼問題。若在標題使用 gitmoji、emoji(或 emoji shortcode,如 :sparkles:),標題內的冒號或其他特殊字元可能會因字元編碼、字型呈現或不同 Git 版本的處理差異,而導致在套用 patch 時發生錯誤。建議提交標題以英文為優先;若需使用本地語言,應避免使用特殊符號或控制字元,並確認 patch 檔案採用 UTF-8 編碼且與使用的 Git 版本相容,最後以建置與測試驗證 patch 是否能正確套用並完成修復。

最後,我個人開發時更常用 git diff --cached | code - 來快速檢視 staged 內容;如果想把提交訊息寫得更好,可參考《利用 GPT 工具輕鬆撰寫高品質的 Git 提交內容》。

參考文獻

  1. Git - git-format-patch Documentation
  2. Git - git-am Documentation
  3. Writing a New Recipe — The Yocto Project
主題 Stack 由 Jimmy 設計