Day 14,我把同一種探針放在網址 # 後的 q。三個本機頁面的原始 HTTP 回應都沒有探針,但 Chrome 讀得到 fragment:弱點頁把它交給 innerHTML,建立 <img> 並觸發 alert(1);文字頁透過 textContent 顯示完整探針,沒有建立圖片元素;第三頁的腳本根本沒有讀取 fragment,目標元素只顯示固定內容。
這與先前的伺服器反射情境不同。fragment 不會隨資源請求送到伺服器。因此,「原始回應沒有探針」不能直接推出前端沒有 XSS。今天我讀取 Day 14 已保存的結果,把 HTTP、片段來源、DOM 寫入及探針執行分開交給 AI,檢查風險報告能否保留三種結果。
我沿用 Day 7 的九欄 JSON 報告結構,包括 tool_verdict、summary、evidence、risk_rating 等欄位;但原本的分類只涵蓋早期的伺服器回應測試,沒有 Day 14 的三種 DOM 結果。因此,Day 15 把 tool_verdict 的允許值改成 confirmed_dom_xss、fragment_rendered_as_text 和 fragment_not_rendered,並要求模型逐字保留底層工具分類。
每組報告收到 5 份具名來源。我沒有把「瀏覽器讀得到片段」直接翻成「頁面已使用片段」,而是另列頁面腳本的實際寫入語句:
| 來源 | 提供給報告的事實 |
|---|---|
| E1 | HTTP request target、原始回應是否含探針、Day 14 工具分類 |
| E2 | 瀏覽器讀回的 q,以及頁面腳本是否讀取 fragment |
| E3 | innerHTML、textContent 或固定內容的寫入語句與實際 DOM |
| E4 | alert(1)、相符 DOM 識別碼及固定探針的執行結果 |
| E5 | 本機實驗的適用範圍與未測項目 |
弱點組的關鍵證據可以縮成以下幾行。E1 的 false 與 E4 的 true 同時成立,因為探針不是從原始 HTTP 回應進入頁面,而是在瀏覽器端由 fragment 進入 DOM:
E1 response_probe_in_body: false
E2 source_value_matches_payload: true
E2 page_reads_fragment: true
E3 sink_statement: output.innerHTML = value;
E3 img_created: true
E4 dialog_message: 1
E4 dom_marker_matches_token: true
E4 execution_observed: true
我先從專案根目錄執行乾跑:
.venv/bin/python day-15/run_reports.py --dry-run
程式核對 Day 14 的三組 JSON、完整 HTML 回應、SHA-256、固定探針、fragment、DOM 結果、伺服器請求路徑和執行摘要,再產生模型提示。乾跑三組都通過,但 api_called: false,沒有模型回答。這次沒有重新啟動網站,也沒有再次送出 XSS 探針。
我另外執行 7 個離線測試,包括修改原始回應、偽造執行訊號、把 # 片段塞進 HTTP 請求紀錄,以及在報告中漏掉必要引用;測試均通過。這些測試只驗證資料轉換與檢查規則,不能證明模型的自由文字正確。
接著我使用 gemini-3.5-flash-lite 對三組已核對的證據各送出一次請求,保存每組的來源副本、提示、模型原始回答和九欄報告。三份回答都通過 Schema、分類、必要逐字引用與本機評級限制。
| 組別 | Day 14 分類 | Gemini 保留的分類 | 模型對 DOM/執行的描述 | 自動檢查 |
|---|---|---|---|---|
innerHTML |
confirmed_dom_xss |
confirmed_dom_xss |
建立 <img>;雙重訊號相符 |
通過 |
textContent |
fragment_rendered_as_text |
fragment_rendered_as_text |
只顯示文字;未觀察到探針執行 | 通過 |
| 忽略片段 | fragment_not_rendered |
fragment_not_rendered |
頁面未讀取片段;目標元素是固定內容 | 通過 |
三組 risk_rating 都是 needs_context。文字頁和忽略頁的 possible_impacts、remediation 都是空陣列;這避免把一次未執行寫成「無風險」或推測未測試的影響。
弱點組的摘要與分類符合證據:原始回應沒有探針,瀏覽器把 fragment 交給 innerHTML,建立圖片元素,並觀察到 alert(1) 與相符旗標。不過,它在「可能影響」寫出「任意腳本」,條件又提到「受害者」;原文還有語意不清的「事件處理常數」。Day 14 只在本機 Chrome 測到固定 img onerror 探針寫入旗標與顯示對話框,沒有測試任意程式碼、真實使用者或正式環境。我保留這份模型原文,將越界之處記在作者核對紀錄,沒有把它當成本文結論。
模型對弱點頁提出以 textContent 顯示純文字,方向符合這個頁面的需求;若產品真的需要顯示 HTML,仍須依實際情境設計可信的清理或安全建構。
文字頁和忽略頁的敘述則保住了差異:前者是頁面使用片段,但只當文字顯示;後者是瀏覽器持有片段,頁面腳本卻未讀取它。兩者都沒有觀察到本次固定探針執行,也都不能據此推論整頁或整站安全。
這次三份 AI 報告的技術分類都正確,必要引用也能回到指定來源。分層證據讓我看出模型沒有把「原始回應不含探針」誤解為「前端沒有 XSS」,也沒有把「瀏覽器讀到片段」誤解為三頁都已執行。但弱點組的可能影響仍超出本機固定探針,說明通過 Schema 與逐字引用後,仍須核對每句自由文字。
本次結論只限於 Day 14 保存的本機 URL fragment q、3 個 DOM 寫入情境和固定 img onerror 探針;沒有測試其他 DOM 來源、寫入點、CSP、正式網站或實際業務影響。
那就…
明天見!