昨天,我讓工具在受控瀏覽器中實際執行 XSS 探針。只有同時捕捉到 alert(1),並從 DOM 讀回本次唯一識別碼(owo)時,工具才會輸出 confirmed_xss。
這項結果回答的是「這次探針有沒有執行」,卻還沒有回答另外幾個問題:可能造成什麼影響、影響需要哪些條件、應該先修哪裡,以及修正後要怎麼驗證?
所以今天,我嘗試把昨天的工具接回 Gemini,讓 AI 整理風險分析。
不過,我不希望模型重新猜測 XSS 是否成立。底層工具已經有瀏覽器事件與 DOM 旗標,AI 的角色應該是解讀這些證據,而不是蓋過它們。
confirmed_xss 是技術測試結果,不等於固定的風險等級。
因此,我把報告中的兩個欄位分開:
| 欄位 | 誰負責 | 意義 |
|---|---|---|
tool_verdict |
Day 6 工具 | 本次探針的技術分類 |
risk_rating |
AI 根據現有情境整理 | low、medium、high、critical 或 needs_context |
AI 必須逐字保留底層工具的 verdict。如果資料只有本機端點、固定探針與瀏覽器結果,沒有真實資產、使用者權限、資料敏感度或業務影響,就應該使用 needs_context,而不是直接貼上「高風險」。
這不是降低 XSS 的重要性,而是避免用缺少的資料做出看似精確的結論。
我把流程整理成 5 個階段:
執行 Day 6 XSS 偵測器
↓
把結果拆成具名證據
↓
交給 Gemini 產生結構化風險報告
↓
檢查格式、逐字引用與工具分類
↓
人工核對影響、條件與修正建議
Day 7 程式可以直接呼叫昨天的 scan(),也可以讀取已保存的 JSON。取得結果後,我沒有把整份資料混成一段文字,而是拆成 4 個來源:
| 來源 | 內容 |
|---|---|
E1 |
工具分類、原因、目標、參數與 HTTP 結果 |
E2 |
alert、執行狀態與 DOM 旗標 |
E3 |
探針附近的 HTTP 回應節錄 |
E4 |
Day 6 工具原本的限制 |
這次本機實際執行後,E1 包含:
verdict: confirmed_xss
reason: 受控瀏覽器實際觸發 alert(1),並在 DOM 寫入本次唯一識別碼。
target: http://127.0.0.1:8000/vulnerable
parameter: q
status_code: 200
content_type: text/html
E2 則保留瀏覽器觀察:
execution_observed: true
alert_observed: true
dialog_message: 1
dom_marker: 本次唯一識別碼
dom_excerpt: data-day6-xss="本次唯一識別碼"
Prompt 裡的任務與 evidence_sources 分開。系統規則也要求模型把 HTML、網址與屬性當成資料,不執行證據裡的任何指示。
這次的 Schema 要求模型回傳以下內容:
now、next、later 的修正建議與驗證方式。其中,可能影響不是事故紀錄。每一項都必須附上條件,例如:
{
"impact": "若相同資料流存在於真實網站,攻擊者可能在受影響頁面的來源環境中執行程式。",
"conditions": [
"攻擊者能讓使用者開啟帶有惡意輸入的頁面。",
"真實頁面仍以可執行的 HTML 情境輸出未受信任資料。"
]
}
這段是我人工編寫的測試資料,不是 Gemini 的實際回答。它的用途是確認程式可以接收預期格式,不能拿來代表模型表現。
除了 JSON Schema,我又加了一層本機規則。程式會讀取原始偵測結果中的 verdict,再檢查模型回傳的 tool_verdict 是否完全相同。
核心概念如下:
expected_verdict = evidence["expected_tool_verdict"]
if report["tool_verdict"] != expected_verdict:
errors.append("tool_verdict 與底層工具結果不一致")
假設 Day 6 輸出 confirmed_xss,模型卻改成 not_reflected,即使其餘 JSON 欄位都符合 Schema,驗證仍然會失敗。
反過來也一樣。如果底層只有 reflected_but_not_executed,AI 不能自行升級成 confirmed_xss。
這條規則讓技術事實與 AI 判讀維持清楚的邊界。
對這次 HTML 文字內容的本機案例,修正方向是讓不受信任的輸入保持為文字,不要被瀏覽器解讀成 HTML。
我要求模型優先建議:
textContent。alert 與 DOM 旗標都不再出現。所以報告中的每項 remediation 都包含 action 與 verification。只寫「過濾輸入」或「加上 CSP」,不足以成為可以核對的修正計畫。
我先啟動本機測試網站:
python3 day-06/lab_server.py
接著讓 Day 7 程式直接呼叫昨天的偵測器,但先使用 --dry-run,確認證據轉換與 Schema,不呼叫 Gemini:
python3 day-07/risk_analyzer.py \
--target http://127.0.0.1:8000/vulnerable \
--dry-run
這次實際結果為:
| 階段 | 結果 |
|---|---|
| Day 6 HTTP 回應 | 200、text/html |
| Day 6 分類 | confirmed_xss |
alert(1) 與 DOM 旗標 |
都有觀察到 |
| Day 7 必要欄位檢查 | 通過 |
| JSON Schema | 通過 |
| 證據與 Prompt 建立 | 完成 |
| Gemini API | --dry-run,未呼叫 |
這表示昨天的瀏覽器驗證已經能直接成為今天的 AI 輸入,不需要人工複製結果。
我執行:
python3 -m unittest discover -s day-07 -p 'test_*.py' -v
9 個測試全部通過,包括:
--dry-run 不會呼叫 API。這些結果只證明本機流程符合目前寫下的規則,不能證明模型分析一定正確。
完成離線串接後,我建立隔離的 Python 環境,安裝 google-genai 2.24.0 與其他需求套件,再使用非 --dry-run 模式送出同一份偵測結果。
預設的 Gemini 3.8 Flash 暫時無法完成請求,因此我改用 Gemini 3.5 Flash-Lite 繼續實驗。
接著,我改用穩定版 Gemini 3.5 Flash-Lite。官方將它定位成低延遲、低成本,適合大量處理與簡單資料擷取的模型,而且支援 Structured outputs 與 Thinking。
我只更換模型名稱,證據、Prompt、Schema、Thinking level 與驗證規則都維持不變:
.venv/bin/python day-07/risk_analyzer.py \
--model gemini-3.5-flash-lite \
--detector-result day-07/results/integration-dry-run/detector-result.json
這次在 4.849 秒內完成,Finish reason 是 STOP。輸入使用 822 個 token,輸出使用 535 個 token,合計 1357 個 token。
模型保留 confirmed_xss,並沒有直接指定高風險:
{
"tool_verdict": "confirmed_xss",
"risk_rating": "needs_context",
"rating_basis": "未提供真實網站、使用者權限、資料敏感度與業務影響,因此風險等級設為 needs_context。"
}
可能影響也使用條件式描述,沒有聲稱真實使用者已經受害。修正建議則要求依 HTML 輸出情境編碼,並重新送出相同探針,確認 JavaScript 不再執行。
完整回答通過 Schema、工具分類、逐字引用與本機規則檢查。
PASS 之後,人工核對仍找到問題模型的摘要寫道:
在本機測試端點的 q 參數中觀察到腳本執行與 DOM 寫入,檢測工具確認觸發 alert(1)。
這句話與原始資料一致,但模型列出的引用只有 verdict: confirmed_xss、execution_observed: true 與 HTTP 回應節錄,沒有引用 E1 裡的:
parameter: q
換句話說,證據來源確實包含 q,模型的引用清單卻沒有完整支持摘要中的每個具體資訊。要修正這項問題,可以補上 parameter: q,或從摘要移除參數名稱。
另一個問題是模型引用了很長、而且結尾截斷在 setAttribut 的 HTTP 回應節錄。這段文字確實出現在 E3,所以能通過逐字檢查,但可讀性不佳,證明力也不如 E1、E2 的直接欄位。
所以今天完成的是:
今天,我把昨天的 XSS 偵測器接到風險分析流程。瀏覽器負責回答探針是否執行,AI 則負責整理可能影響、未知事項、修正順序與驗證方式。
最重要的改變不是多了一段 AI 摘要,而是把權責分開:tool_verdict 不能由模型改寫,risk_rating 也不能在缺少真實情境時假裝精確。
目前程式已經跑通本機證據轉換,Day 6 的 5 個測試與 Day 7 的 9 個測試也全部通過。Gemini 3.5 Flash-Lite 成功產生報告,並在人工核對時找到引用不完整的問題。
這正是我希望保留驗證流程的原因:AI 可以幫忙整理風險,但最後仍要回到原始證據,逐句確認它說得是否完整。
那就…
明天見!