內部做法

[Track J/資安實踐] 我們每週一早上 9:20 花 40 分鐘做的事:Renovate、composer audit 與 Trivy 之後,剩下那個要人腦判斷的問題

2026.09.03 · 26 次瀏覽
[Track J/資安實踐] 我們每週一早上 9:20 花 40 分鐘做的事:Renovate、composer audit 與 Trivy 之後,剩下那個要人腦判斷的問題

機器每天自動掃、每週一集中處理,只有三種結論:今天修、這個 Sprint 修、記錄後不修。

分享:

那次我們花了 6 小時,只為了搞懂一個「高風險」到底關不關我們的事

一個週三下午,某個 PHP 套件被標為高風險漏洞。客戶窗口在 LINE 上問「我們有沒有事」,我們說「查一下」——然後查了六小時。因為我們當時只知道 composer.lock 裡有那個套件,不知道它被哪段程式碼呼叫、有沒有走到有問題的函式,也不知道另外七個客戶專案裡有幾個裝了同一版。那次之後我們把它排成固定行程:每週一早上 9:20,40 分鐘。不是為了更安全,是為了讓「我們有沒有事」有一個 20 分鐘內能回答的版本。

我們的做法

一句話總結:機器每天自動掃、每週一集中處理,並且只有三種結論——今天修、這個 Sprint 修、記錄後不修。

流程圖是這樣跑的:

Renovate 每日排程 → 產生升級 PR → CI 跑(Pest 測試 + PHPStan + Trivy 掃描)→ 綠燈者自動合併(僅 patch 版)→ 非綠燈與 minor/major 進入「週一清單」 → 週一 9:20 分級會議(15 分鐘)→ 分為 P0/P1/P2 → P0 當日處理、P1 進本 Sprint、P2 寫進 SECURITY-DECISIONS.md 存查

關鍵不在工具,在最後那一步:「不修」也是一個要留下書面理由的決定。這是我們跟大多數團隊做法最大的差別。

為什麼這樣做

我們一開始做「有告警就修」,兩個月就放棄了。原因很現實:同時維護十幾個 Laravel + Flutter 專案的工作室,如果每則依賴告警都當事故處理,工程師一週被打斷十幾次,而大多數結論是「這套件我們只在測試環境用」或「那個函式我們根本沒呼叫」。

所以做了三個取捨:

  • 取捨 1:用固定時段換上下文完整。隨機被打斷的處理品質遠低於每週一次帶著全部專案清單一起看。代價是最壞有 6 天延遲窗口——這就是 P0 需要繞過機制的原因。
  • 取捨 2:只自動合併 patch 版。minor 與 major 一律人工看。自動合併 minor 在 Laravel 生態常常沒事,但我們有金流與排班邏輯,一次意外行為變更的成本遠高於省下的 10 分鐘。
  • 取捨 3:接受「不修」並記錄。沒有這條,週一清單會在三個月內長到沒人想看。

P0 繞過機制很簡單:漏洞同時符合「有公開可用的攻擊程式碼」且「我們的程式碼路徑確實會走到」,任何人可直接開 hotfix。過去 12 個月用過 2 次。

具體執行

Step 1:每個專案根目錄放一份 renovate.json核心設定是把安全性更新與一般更新分開處理:

{
  "extends": ["config:recommended"],
  "schedule": ["before 6am on monday"],
  "packageRules": [
    { "matchUpdateTypes": ["patch"], "automerge": true },
    { "matchUpdateTypes": ["minor", "major"], "automerge": false }
  ],
  "vulnerabilityAlerts": { "labels": ["security"], "schedule": ["at any time"] }
}

Step 2:CI 在每個 PR 上跑三道關卡。用 GitHub Actions,順序是先便宜後昂貴:

- run: composer audit --locked --format=plain
- run: vendor/bin/phpstan analyse --memory-limit=1G
- run: vendor/bin/pest --parallel
- uses: aquasecurity/trivy-action@master
  with: { scan-type: 'fs', severity: 'HIGH,CRITICAL', exit-code: '1' }

composer audit 讀的是 Packagist 安全公告資料庫;Flutter 端用 dart pub outdated 搭配 GitHub Advisory Database 人工比對——Dart 生態的自動化工具還不夠成熟,這部分還是人在看。

Step 3:週一 9:20,開一份共用清單。四欄:套件/版本差距/我們有沒有呼叫到/分級。第三欄是全部價值所在,也是唯一需要人腦的部分——通常用 rg 搜函式名稱兩分鐘內就能判斷。

Step 4:P2 寫進 SECURITY-DECISIONS.md格式極簡:日期、套件與版本、決定、理由、複查日期。複查日期是強制欄位,通常設 90 天後。

這個做法的代價

  • 代價 1:週一固定被吃掉 40 分鐘。兩人參與,一年約 70 人時。這是真實成本,不是「順手做」。
  • 代價 2:最壞有 6 天延遲。週二爆出的中風險問題要等下週一才進清單。P0 繞過機制補了上緣,中風險確實是接受風險。
  • 代價 3:Renovate 的 PR 噪音很大。十幾個專案每週產生 20 到 40 個 PR,即使自動合併 patch,剩下的仍要人看。我們調過三次排程才降到可接受。
  • 代價 4:SECURITY-DECISIONS.md 需要維護。沒人維護的複查日期等於沒寫,我們把它排進每季技術債檢視。

不適合什麼情境

  • 單一產品、單一團隊的公司。如果你只有一個 codebase,即時處理告警的成本沒那麼高,週會反而是多餘的儀式。
  • 受高度監管的產業(金融、醫療器材)。這些領域通常有明訂的修補時限(例如高風險 72 小時內),週會節奏會直接違規,必須改成事件驅動。
  • 還沒有自動化測試的專案。整套流程的前提是「CI 綠燈可以信任」。沒有測試就自動合併 patch,等於把賭注押在套件作者的 semver 紀律上。

客戶為什麼該關心

三件事會直接影響你:

  • 回覆速度。新聞爆出框架漏洞時你問「有沒有事」,我們的目標是 20 分鐘內給答案,因為清單、版本與呼叫關係已經在那裡。這沒讓系統更安全,但你不用在不確定中等一天。
  • 維護費不會突然暴增。依賴堆積兩年不升,某次不得不升時會變成 40 到 80 工時的專案。每週處理的邊際成本遠低於一次性補繳。
  • 你會拿到一份「我們決定不修什麼」的紀錄。資安稽核、投標、換供應商時都用得上。多數團隊只留下修了什麼,而稽核會問的是為什麼不修。

如果你也想這樣做

  • 建議 1:先練「呼叫關係判斷」,不要先買工具。挑一則依賴告警,用 rg 搜有問題的函式名稱,看你多久能回答「我們有沒有走到」。這個能力比任何掃描器重要。
  • 建議 2:分級只設三級。我們試過 P0 到 P4,P3 和 P4 從來沒差別,只增加討論時間。三級的邊界要用行為定義——今天修/本 Sprint 修/不修但記錄——不是用嚴重度分數。
  • 建議 3:把「不修」制度化。流程裡沒有合法的「不修」出口,工程師就會用「先放著」代替,而「先放著」不留紀錄。
  • 建議 4:自動合併從 patch 開始,觀察三個月再擴大。我們到現在沒把 minor 納入,不是保守,是算過一次意外行為變更的除錯成本。
  • 建議 5:CI 關卡先便宜後昂貴。composer audit 跑 3 秒,測試跑 4 分鐘,順序排錯每天多等好幾分鐘。

我們在哪些客戶身上用過

一個電商平台客戶(去識別化)導入前,依賴已 19 個月沒整體升級。第一次盤點清單上有 63 項,我們花兩個 Sprint 清到 11 項,之後靠每週 40 分鐘維持在 5 到 15 項之間。真正的成效在第二年:某個廣泛使用的 PHP 套件爆出高風險那天,我們 18 分鐘後告訴他們「你們有裝,但沒呼叫到那個函式,本週一併升級即可」——而不是要他們緊急停機。

另一個排班系統客戶則證明了限制:他們的 Flutter App 依賴一個已停止維護的第三方套件,週一會議連續四次只能標 P2,最後用兩個 Sprint 換掉。流程能讓問題持續可見,但不會替你做決定。

常見問題 FAQ

為什麼不用 Dependabot 就好?

我們兩個都用。GitHub Advisory Database 的告警品質很好,所以 Dependabot 的安全告警我們保留;但排程控制、群組升級與自動合併規則 Renovate 的設定彈性高很多,所以升級 PR 走 Renovate。

40 分鐘怎麼夠看十幾個專案?

因為 80% 的項目在會議前已經被 CI 自動處理掉了。會議上真正討論的通常是 5 到 12 項,其中大半在 30 秒內就能判斷「沒呼叫到,P2」。花時間的永遠是那 1 到 2 個需要看程式碼的。

客戶要付這 40 分鐘的錢嗎?

在我們的月費維護約裡,這是包含在內的,不另外計費。單次專案(做完即結案)不含此服務——這也是我們建議有長期系統的客戶採月費制的理由之一。

如果我們沒有測試,可以先做哪一部分?

先做週一清單與分級,不要做自動合併。清單本身就有價值:它讓「我們有沒有事」變成可以查的問題。自動合併等測試覆蓋率到位再開。

下一步

如果你的專案已經超過一年沒有整體檢視依賴,我們可以做一次依賴健檢,交付一份含呼叫關係判斷的清單與分級建議。如果你是工程師、對這種「把流程寫下來然後照著跑」的做事方式有興趣,我們也長期歡迎聊聊。

分享: