本卡屬 FR-166(母卡 CM-3109 延伸),決策者 2026-10-28 要求「主系統可以預覽 HTML,主控台也要」。Repo:evidence-agent。首腦第 6 棒直接實作(決策者指示「開完直接改」),不經工作坊派工。
白話
主控台執行紀錄的 CINC/OpenVAS 等 HTML 報告現在不能線上看,只能下載。主系統早就能看:回應加 CSP sandbox,瀏覽器照樣畫版面與圖片,但報告裡的腳本不執行、也讀不到主站的登入資訊。主控台改用同一套隔離。
問題在哪
- core/console/inline_policy.py:HTML/SVG 刻意不在白名單,理由寫「主控台沒有 sandbox 那層」。_text_mime 看到開頭是 < 就回 None → 附件。
- core/console/routes.py:API 回應 setdefault CSP
default-src 'none'; frame-ancestors 'none'——連同來源都不准放進 iframe;頁面 CSP frame-src 'none'——主控台頁面不准嵌任何 iframe(PDF 的 blob 預覽也因此被擋,對話框退成不支援)。
怎麼做
- 【extend inline_policy】decide 回傳多一個 extra headers:判定為 HTML(文字可解、開頭 <、像 <!doctype html 或 <html,且不是二進位)時 inline=True、text/html; charset=utf-8,headers 帶主系統同一組隔離:
sandbox; default-src 'none'; img-src 'self' data:; style-src 'self' 'unsafe-inline'; font-src 'self' data:; frame-ancestors 'self'。SVG 仍不收(sandbox 對 SVG 內嵌腳本的保護不如 HTML 完整,主系統白名單也沒有 svg)。
- 【extend files.send_customer_file】inline 時把 extra headers 寫進回應;因 routes._headers 用 setdefault,端點自己設的 CSP 不會被全域值蓋掉。
- 【extend routes._CSP_PAGE】frame-src 'none' → frame-src 'self' blob:(只准嵌同來源預覽網址與 PDF blob)。frame-ancestors 'none' 不動(主控台頁面本身仍不准被別人嵌)。
改哪些檔
- core/console/inline_policy.py、core/console/files.py、core/console/routes.py、test/(inline_policy 與 console 預覽的既有測試檔)
驗證
- 單元:HTML 報告 → inline、text/html、CSP 含 sandbox 且不含 allow-scripts、含 frame-ancestors 'self';PDF 照舊;.html 檔名但內容是二進位 → 附件;SVG → 附件;其他 API 回應仍是 frame-ancestors 'none'。突變:拿掉 sandbox → 紅;HTML 回附件 → 紅。
- 實機:主控台執行紀錄點 CINC HTML 報告預覽 → 看得到報告,瀏覽器 console 出現 Blocked script execution(sandbox 生效)。
紀律
- 資安:sandbox 不得帶 allow-scripts/allow-same-origin;HTML 一律走預覽網址進 iframe,不得轉 blob(blob 繼承主控台來源且丟掉 sandbox)。回寫對到 .claude/rules/security/ 輸出那支。
- 顯式 git add,禁 -am;不切 branch(feature/FR-139)。
首腦第 6 棒實作回寫(2026-10-28)
commit:evidence-agent f9fc1af(主體)、0ccb44c(Content-Type 重複 charset),已 push;123 已升到 0ccb44c。