檢查日期 2026-09-26|對應卡片 CM-2167(母卡 CM-2152)|檢查範圍 7 個檔案 996 行|工具 run ID
wf_21bd999c-982(報告目錄CLAUDE-SECURITY-20260926-024832)|驗證章verified
這七支組裝表沒有漏接任何安全檢查零件,淨新增 0 條。 工具報了 4 條,三人面板都是 3:0 通過,但全是總表已經登記過的舊帳(第 188、192、65 項,以及 CM-1631 金鑰外洩)。四條沒有一條是組裝表本身寫錯:研究員順著組裝表追進它組出來的服務,在服務裡撿到的。
卡片要我回答的三件事:
| 卡片問的 | 答案 |
|---|---|
總組裝表(containers.py)有沒有漏登記哪個容器 |
沒有漏。全專案 36 個子組裝表逐一比對,36 個都有登記;每個子組裝表向外要的零件(共 107 個插槽)只缺 1 個,是一個宣告了卻從來沒人用的空插槽,無害 |
| 登記清單(哪些程式要自動拿零件)有沒有漏掛,漏掛的網址會怎樣 | 沒有漏掛。全部 62 支用到「自動拿零件」的程式,60 支在清單上,另外 2 支是自己在說明文字裡寫了範例、實際不需要掛。就算真的漏掛,實測結果是一呼叫就報錯(500),不會靜默改用別的東西 |
| 建構子有「預設是空」的守門零件,組裝表沒接時是拒絕還是放行 | 七支範圍內只有摘要報告的兩支服務屬於「沒接就放行」的寫法,但組裝表有接,我把組裝表實際建起來,確認拿到的是真零件。比對腳本在這七支標出的 6 處「可選未接」,逐一核過全都不是守門零件 |
系統啟動時,主專案要把幾百個「零件」(資料服務、權限檢查、授權檢查……)組起來,交給每個網址入口使用。負責「誰拿到哪個零件」的就是這批組裝表(DI 容器:Dependency Injection,依賴注入,意思是程式不自己建零件,由組裝表統一分配)。
這類問題的危險在於安靜:組裝表少接一個安全檢查零件,服務不會報錯,只會把那道檢查跳過去。之前在合規稽核套件、SSP 套件都發生過。
| 檔案 | 行數 | 組裝的是什麼 |
|---|---|---|
di_containers/containers.py |
397 | 總組裝表。所有子組裝表在這裡登記,並互相接線 |
di_containers/cloud_integration/cloud_integration_containers.py |
310 | 雲端硬碟整合(U4~U6 那三棒的服務) |
di_containers/license/license_containers.py |
84 | 商務授權檢查(U1 的 license.py 從這裡拿零件) |
di_containers/support/support_containers.py |
82 | 系統診斷包(U10/U11) |
di_containers/project/project_summary_report_containers.py |
48 | 專案摘要報告(U9) |
di_containers/setup/setup_containers.py |
41 | 首次安裝精靈(U3) |
di_containers/identity/identity_containers.py |
34 | 審計欄位補暱稱用的名冊 |
沒有。 我寫了一支小腳本,把每個子組裝表宣告「我需要外面給我哪些零件」的插槽,和總組裝表實際塞進去的東西逐一配對(包含後面那些「回頭補接」的寫法,因為有些容器互相需要,只能先登記一個再回頭補)。
di_containers/ 底下 36 個子組裝表,36 個都在總組裝表登記,沒有孤兒。di_containers/associations/associations_containers.py:45 的 job_execution_container。全專案搜尋,沒有任何地方用到這個插槽,是一個宣告了卻從沒用過的殘骸,無害(順手刪掉即可,不必開卡)。config/di_modules.py 那份清單)總組裝表另外有一份「哪些程式要自動拿零件」的清單。漏掛的程式不會在啟動時報錯,會拿到一個佔位物件。
api/ app/ common/ core/ config/ 底下真的用到「自動拿零件」的程式 62 支。只有 2 支不在清單上:
common/authz/license.py:只在檔頭說明裡寫了一段範例,自己不需要自動拿零件(它是在呼叫當下從容器直接取,:191、:197)。config/socketio_namespaces.py:同樣只是說明文字提到。AttributeError(使用者看到 500)。不會靜默改用預設值、也不會跳過檢查。license_containers.py)對得上。 組裝表給 LicenseGuard 的三個零件,就是它建構子要的三個(common/authz/license.py:153-154),沒有多、沒有少、沒有預設值可以偷懶。
卡片擔心的「兩條接線路徑只給一邊」也核過:租戶目錄(用來判斷「子公司繼承母公司的授權」)在組裝表這邊有給(license_containers.py:59),另一條網址用的接線在 core/plugins/license.py:334 也有給,兩邊都接上了。套件那端如果沒拿到租戶目錄,會記一行警告並退回「只看自己的授權」(jedi-license-runtime license_tenant_resolver.py:44),所以這個零件漏接屬於安靜型缺線,但目前兩邊都有接。
另外兩個「預設是空」的參數也看過:套件裡授權服務的「停權紀錄」零件預設是空、空的時候當作「沒人被停權」(套件原始碼自己註明是刻意不擋),但組裝表有接(license_containers.py:74),不觸發。
session 參數。那是留給測試塞假連線用的,正式執行時從目前的交易自動取,不是缺線。U5/U6 查到的「初始化資料夾不檢查專案歸屬」(第 192 項)是服務本身就沒寫檢查,不是組裝表沒接;同一張表裡需要檢查的 drive_project_verify_service,專案成員零件有接(cloud_integration_containers.py:308)。加密金鑰(:109)沒設的話,建構時直接報錯(fernet_crypto.py:8-11),不會退回沒加密。require_platform_admin(),diag_bundle_export_service.py:74、recent_error_app_service.py:46),組裝表沒辦法讓它們放行。U10 已確認這組容器只接了兩條網址,與我看到的一致。SetupWizardService 的七個零件全部是必填,沒有預設值,組裝表七個都有給,少一個啟動時就會報錯。比對腳本(di-wiring-audit.md)標紅的 3 處,都不在這七支裡(在 auth_containers.py 與 oscal_containers.py),屬於 U13a/首腦串接縫的範圍。我還是順手開了套件原始碼看一眼,給首腦參考:
| 腳本標紅 | 我看到的 | 判斷 |
|---|---|---|
TenantProvisioningService.default_role_name(auth_containers.py:269) |
預設值是字串 "System Manager"(jedi-iam tenant_provisioning_service.py:47),是「新公司預設建哪個角色名稱」 |
不成立。參數名含 role 被腳本誤判,其實是設定值,不是守門零件 |
MetadataCloneService.role_repo(oscal_containers.py:177) |
沒給時套件自己建一個(metadata_clone_service.py:54,role_repo or RoleRepoImpl()),用途是複製 OSCAL 文件裡的「角色」欄位資料 |
不成立。這是資料存取物件,不是權限檢查;沒接也不會變空 |
OscalIoService.role_repo(oscal_containers.py:206) |
同上(oscal_io_service.py:199) |
不成立,理由同上 |
比對表「可選未接:預設 None」那節裡屬於這七支的 6 處,逐一核過:
| 位置 | 缺的參數 | 是什麼 | 判斷 |
|---|---|---|---|
cloud_integration_containers.py:97 |
TenantDriveIntegrationRepoImpl.session |
測試用的假連線入口,正式執行自動取目前交易 | 不是缺線 |
cloud_integration_containers.py:131 |
DriveFolderMappingRepoImpl.session |
同上 | 不是缺線 |
cloud_integration_containers.py:132 |
DriveSyncJobRepoImpl.session |
同上 | 不是缺線 |
identity_containers.py:31 |
IdentityContext.tenant_resolver |
把「公司編號」補成公司名稱,只影響顯示 | 不是守門;沒接時回空、畫面顯示編號(套件刻意設計) |
identity_containers.py:31 |
IdentityContext.org_unit_resolver |
把「部門編號」補成部門名稱,只影響顯示 | 同上 |
support_containers.py:54 |
DiagLogReader.basename |
日誌檔名,預設 app.log |
設定值,不是缺線 |
我另外用程式實際載入這六支子組裝表、逐一比對建構子(不靠靜態腳本),結果與腳本完全一致,沒有腳本漏掉的項目。
project_summary_report_service.py:40 的寫法是:專案成員檢查零件如果是空的,直接放行。同樣的寫法在 project_summary_report_history_service.py:57 也有。
project_summary_report_containers.py:38、:46),我把容器實際建起來,兩支服務拿到的都是真零件。反過來把那個插槽拿掉,容器直接報錯,不會給空值。所以今天不會觸發。is None 改成「沒接就拒絕」,一行的事。ProjectSummaryReportService( 零命中,只有測試)。四條都是 3:0 通過,全部是舊帳,不另計。
| 工具編號 | 嚴重度 | 說的是什麼(白話) | 位置 | 總表對應 |
|---|---|---|---|---|
| F1 | 高 | Google 硬碟授權完成的回呼頁,不用登入就能打,網址裡的錯誤訊息被原樣塞進頁面程式碼,可以插入攻擊程式偷走登入權杖 | api/cloud_integration/routes/google_drive_integration_route.py:120 |
第 188 項(U4 已記,建議先轉 FR-114) |
| F2 | 中 | Google 硬碟應用程式的密鑰,原文出現在 12 支已進版控的對話紀錄檔,而且跟目前 .env 用的是同一把 |
docs/conversation-history/2026-04-21-google-drive-integration/...part-05-of-14.md:8157 等 12 檔 |
CM-1631(檔數 12 與 FR-113 O2 重數相同) |
| F3 | 中 | 雲端硬碟「初始化資料夾」不檢查專案是不是你的,可以把別家公司的專案結構建進自己的硬碟,再反過來塞假證據 | app/cloud_integration/service/drive_sync_admin_service.py:125 |
第 192 項(U5/U6 已記,總表評高、決策者 09-25 已裁要修) |
| F4 | 中 | 摘要報告的單筆、清單、歷史四支讀取不檢查是不是專案成員,同公司任何人都讀得到 | app/project_summary_report/service/project_summary_report_service.py:79 |
第 65 項(主線 :78-86 仍未補,修正線 CM-2037 在處理) |
現況(2026-10-01):本棒各條後來的處理結果如下(過程紀錄保留,不改)。
- 本棒淨新增 0 條,工具報的 4 條全為舊帳(總表第 188/192/65 項與 Google 密鑰):第 188 項 ✅ 已修(CM-2200)、第 192 項 ✅ 已修(CM-2201)、第 65 項 ✅ 已修(CM-2179)、Google 密鑰 ✅ 已修(CM-2051,原卡 CM-1631 作廢)。
面板否決 1 條(0:3):雲端硬碟權杖的加密金鑰原文出現在 10 支對話紀錄檔。外洩是真的,但面板認為光有這把金鑰拿不到任何東西,還要有資料庫存取權。這把金鑰已在 CM-1631 裡(10 檔),而且總表已記下「跟 CM-1608 資料庫密碼串起來就能讀客戶硬碟」這條串接風險。所以面板否決不影響現有登記。
嚴重度差異:F3 工具評中、總表第 192 項評高。總表是 U6 在 DEV 實際跑通跨公司寫證據後定的,維持總表的高。
第一層:工具正式報告(有三人面板背書)
verified:5 個候選、15 票全投完、零漏投,4 條 3:0 通過、1 條 0:3 否決。研究員 2 派 2 回,failed 0。CLAUDE-SECURITY-REVISION-222a13f80868-dirty.json。-dirty 是因為工作區有平行線未 commit 的改動,這批改動都不在這七支檔裡。low),只有一位主研究員加一道密鑰掃描。工具擅長抓「寫錯的邏輯」,不擅長抓「應該有卻沒有的零件」。這一棒工具的四條產出全落在範圍外,對組裝表本身沒有提供任何判斷,所以本棒的主要結論靠第二層。第二層:runner 自己開檔與實測(未經三人面板)
flow_control_containers.py:33 找不到 jedi_compliance_audit.app.service.project_current_ssp_service,因為目前虛擬環境指向修正線工作樹,套件版本與主線對不上。所以「授權檢查/診斷包/雲端硬碟在總組裝表裡拿到真零件」這一半是讀碼加子容器單獨實測推出的,不是整張總表端到端實跑。這件事跟 U5、U6 回報的「本機 BE 起不來」是同一類環境問題,不影響本棒結論,但請首腦知道。config/di_modules.py、core/app_factory.py:275-300(組裝與登記的時機)、common/authz/license.py、core/plugins/license.py、全部 36 支子組裝表的插槽宣告、摘要報告/診斷包/安裝精靈/雲端硬碟相關服務的建構子,以及 jedi-license-runtime、jedi-common(IdentityContext)、jedi-iam(TenantProvisioningService)、jedi-oscal-v2(兩支 role_repo)的原始碼。
| 項目 | 內容 |
|---|---|
| 範圍 | 7 檔/996 行(`git ls-files |
| 工具 | claude-security 0.11.0,effort low、focus attack-surface |
| run ID | wf_21bd999c-982(報告目錄 CLAUDE-SECURITY-20260926-024832) |
| 掃描版本 | 222a13f8086847570815e247891453cc19dde1f2(dirty,平行線改動不在範圍內) |
| 耗時 | 約 36 分鐘(02:48~03:25 UTC) |
| agent | 17 派 17 回,failed 0 |
| 候選/票數 | 5 候選、15 票全投,4 條 3:0 通過、1 條 0:3 否決 |
| 驗證章 | verified |
| 淨新增 | 0(工具 4 條全為舊帳:第 188、192、65 項、CM-1631) |
| 順帶觀察(不開卡) | associations_containers.py:45 有一個從沒用過的空插槽,可順手刪;摘要報告「零件沒接就放行」建議修第 65 項時一起改成拒絕 |