本卡屬 module_frame 資安掃描母案(見母卡),第 B6 棒。只掃不修。範圍 7 檔/1656 行。盤點檔:docs/features/FR-113-2609-oscal-host-wiring-security-scan/module-frame-scan-inventory.md
這一棒在做什麼(白話)
掃「把資料寫成 Excel 檔案」這套底層機制的 7 支檔、1,656 行。B4 匯出整份系統安全計畫、B5 匯出範本控制項清單,兩邊匯出的資料不一樣,但產生 Excel 檔案時共用同一套機制——就是這一套。只找問題、不修問題。
為什麼切這一塊
- 已經知道這裡有一條中風險:FR-113 的 O5 那一棒找到 generator.py 第 578 行存進去的文字會被 Excel 當公式執行(公式注入)。那條發現時在別棒的範圍外,B6 才是它真正的家,值得順便看這個洞有沒有別的姊妹洞。
- 共用檔的特有風險只有單獨看才看得出來:同一支函式,B4 那條線呼叫前驗過、B5 那條線沒驗,這種「兩邊各以為對方做了」的不一致,分開在兩棒裡看永遠看不到。
- 波及面大:這套機制除了 B4/B5,還被 auth 模組的使用者匯入範本(app/auth/service/user_import_template_app_service.py)跟 oscal 模組的 Excel 解析器(app/oscal/service/excel_parser/sheet_handlers.py)共用。這裡發現的問題、後續的修法,都要考慮這三方的波及。
🔴 本棒必須與 B4 由同一個 runner 連續跑
不可分開派給不同人,也不可隔開時間。
理由:B6 本身不含任何「這筆資料是不是你的」的判斷邏輯,它只負責把資料寫成 Excel 檔案格式;歸屬判斷應該在呼叫它的 B4/B5 那一層做。兩棒有呼叫關係但沒有任何物理重複的檔案——分開派會出現責任真空:已知的公式注入洞在 generator.py:578,同支檔案其他寫入儲存格的地方要不要一起算進同一批修法,只有接續看過 B4 呼叫端的人才判斷得準。這正是跨 arc 總表第 118/119 項那條高風險的成因。
重點看什麼
- 🔴 已知洞在 generator.py:578(公式注入)。檢查同支檔案裡其他寫入儲存格的地方有沒有同樣的問題。 O5 的報告已經點名 generator.py:319(直式工作表)跟 lookup_builder.py 也要一起看。
- 白話解釋一下公式注入:使用者在系統裡填的文字,如果是 = + - @ 開頭,寫進 Excel 儲存格之後,別人打開這個檔案時 Excel 會把它當成公式執行——可以用來讀取對方電腦上的檔案或連到外部網址。修法是寫進去之前在前面加一個單引號之類的中和字元。
- 🔴 確認這個套件本身不含任何跟資料歸屬有關的判斷邏輯——這是正常狀態(它只負責格式化)。但如果發現這裡面藏了跟資料歸屬有關的邏輯,要另外標記:代表判斷邏輯散落在不該散落的地方,之後補洞時很容易漏掉這一處。
- 接著 B4 追的那條線:B4 的服務呼叫 generator.py(第 77 行 import)與 sheet_definitions.py(第 81 行 import),把「使用者填的文字」從 B4 的呼叫端一路追到這裡寫進儲存格,看全程有沒有被安全處理。
- sheet_definitions.py(617 行)跟 header_i18n.py(176 行):定義工作表有哪些欄位、欄位標題怎麼翻譯。查有沒有把使用者可控的內容當成欄位名稱/公式片段直接組進去。
範圍與邊界
本棒 7 檔/1,656 行,是完整的一個共用套件目錄 app/module_frame/excel_template/。
不在本棒:呼叫端(B4 的 SSP 匯出、B5 的範本匯入匯出)各自成棒。auth 模組與 oscal 模組那兩個外部消費者不在這批範圍內,但發現問題時要在報告裡點出波及到它們。
盤點檔在 docs/features/FR-113-2609-oscal-host-wiring-security-scan/module-frame-scan-inventory.md,裡面有完整的範圍界定、八棒切法與推導。有疑問先翻盤點檔,但範圍以本卡為準。