Day 17,我們替 Confirmed Finding 定義了六道證據門檻。
不過,Validator 要判斷攻擊者是否跨越邊界,前提是 Agent 已經知道系統有哪些邊界。
如果一開始就把 Repository 丟給 AI,要求它「找出所有漏洞」,模型很容易把看到的程式碼片段直接套進常見漏洞清單:
看到 API Key → Secret 洩漏
看到 innerHTML → XSS
沒有 Rate Limit Middleware → DoS
只檢查已登入 → 授權安全
這些判斷可能成立,也可能完全忽略執行環境、資料來源與其他防護層。
今天會替 Vibe Guard 實作 Recon 的第一版,讓後續 Hunter 開始找問題前,先取得:
最終產物不是一段「看起來合理」的系統介紹,而是一份每項事實都能連回檔案與行號的 architecture.json。
目錄清單可以告訴 Agent 專案有哪些檔案,卻無法回答:
Cloudflare security-audit-skill 的 Reconnaissance 階段把工作分成三個方向:
| 方向 | 要回答的問題 |
|---|---|
| Overview | 這是什麼應用程式、使用什麼技術、入口在哪裡? |
| Trust Model | 有哪些角色、信任邊界、認證與授權控制? |
| Input Surface | 外部輸入從哪裡進入,又會到達哪些 Sink? |
最後才把三者合併成 architecture.md,提供給後續每一個 Hunter。
這個順序很重要。Recon 的目標不是找漏洞,而是建立後續判斷共用的系統模型。
LLM 很容易把合理推論寫成確定事實。
例如 Repository 中有 firebase.json,只能證明專案存在 Firebase Hosting 設定,不能直接證明:
Production 正部署在 Firebase Hosting。
可能的情況還包含:
因此 Recon 產物應區分三種狀態:
| 狀態 | 定義 | 範例 |
|---|---|---|
| Observed | Repository 有直接證據 | Client 呼叫 connectFirestoreEmulator |
| Inferred | 由多項證據推導,仍需標示假設 | 應用程式看起來是單頁 Web App |
| Unknown | 目前沒有足夠資料 | 正式環境是否啟用 App Check |
第一版 Demo 會優先保存 Observed Fact,並把 Repository 外的資訊列入 unknowns,不讓模型自行補完。
Vibe Guard 的測試專案是一個只允許在本機執行的 Firebase Web App。
使用者可以:
projects Collection。這段描述目前只是人類理解。Agent 還需要知道每項結論的證據位置。
今天的程式放在:
demo-app/recon-demo/scenario.js
進入 Demo:
cd /media/mickey/777/ithome/demo-app
執行:
npm run recon:demo
程式會讀取:
package.json
firebase.json
firestore.rules
src/main.js
再輸出:
recon-demo/architecture.json
Recon 最危險的失敗,不是少寫一段介紹,而是產生無法核對的結論。
Demo 使用 evidence() 尋找來源:
function evidence(path, snippet) {
const lines = sources[path].split("\n");
const lineIndex = lines.findIndex((line) => line.includes(snippet));
if (lineIndex === -1) {
throw new Error(`Missing Recon evidence in ${path}: ${snippet}`);
}
return {
path,
line: lineIndex + 1,
snippet: lines[lineIndex].trim()
};
}
每項 Observed Fact 都保存:
path
line
snippet
如果程式改動後找不到原始證據,Recon 會直接失敗,不會繼續輸出一份過期架構。
這還不是完整的 Semantic Code Analysis,但已經建立一個重要規則:
沒有來源的架構描述,不能被後續 Agent 當成事實。
Agent 先確認應用程式類型、用途與主要入口。
Demo 的輸出包含:
{
"type": "Local-only Firebase web application",
"purpose": "Users sign in and save project launch goals.",
"stack": [
"Vite",
"Firebase Authentication",
"Cloud Firestore"
]
}
package.json 的 Vite Script、src/main.js 的 initializeApp(),以及 Firebase 設定共同支持這項判斷。
知道技術棧後,Hunter 才能選擇正確的檢查方式。
例如這個專案:
如果 Agent 只搜尋 Server Middleware,會錯過真正的授權邊界。
Recon 先列出三種角色:
| Actor | 預期能力 |
|---|---|
| Visitor | 註冊或登入 |
| Authenticated User | 建立與查看 Project |
| Operator | 未知,Repository 沒有正式 IAM 或 Runbook |
第三列刻意保留 Unknown。
Repository 沒有足夠資料判斷:
如果 Agent 自行假設「只有管理員可以」,後續隱私與維運檢查就會建立在虛構前提上。
只列出 Form 不夠。Recon 還要描述輸入最後到哪裡。
Demo 找到三個主要輸入面:
| Input | Sink | 後續應檢查什麼? |
|---|---|---|
| Email、Password | Firebase Authentication | 憑證處理、錯誤訊息、濫用防護 |
| Project Name、Launch Goal | Firestore projects |
驗證、授權、資料最小化 |
| Firestore 中的 Project 欄位 | Browser innerHTML |
Stored XSS、安全輸出 |
第三項特別容易被忽略。
外部輸入不一定在同一個 Request 中到達 Sink。使用者先把內容存進 Database,另一個使用者之後再讀取並渲染,仍然是一條完整資料流:
Project Form
→ Firestore SDK
→ Security Rules
→ projects Collection
→ Snapshot Listener
→ innerHTML
Recon 不在此階段直接宣布 XSS。
它只把 Source、Storage、Control 與 Sink 連起來,交給 Hunter 建立攻擊假設,再由 Validator 實際重現。
Demo 使用:
onAuthStateChanged(auth, (user) => {
// 切換登入後介面
});
這能證明 Browser 會追蹤登入狀態。
真正控制 Database 存取的是:
firestore.rules
目前 Rule 為:
allow read, write: if request.auth != null;
Firebase 官方文件說明,未登入時 request.auth 是 null;登入後可以使用 request.auth.uid 對照資料擁有者。
因此 Recon 應記錄:
Authentication:
Browser 使用 Firebase Authentication 取得身分。
Authorization:
Firestore Security Rules 決定 projects 是否可讀寫。
Observed policy:
任何已登入身分都符合目前 read/write 條件。
最後一句仍然不是完整漏洞報告。是否能跨帳號讀寫,還要結合 Collection Query、資料所有權模型與動態測試。
信任邊界是資料從一個控制範圍進入另一個控制範圍的位置。
第一版 Recon 找到三條:
Browser
→ Firebase Authentication Emulator
Browser
→ Cloud Firestore Emulator
Repository Configuration
→ Firebase Hosting
每條邊界都要記錄:
from
to
以 Browser 到 Firestore 為例:
{
"from": "Browser",
"to": "Cloud Firestore emulator",
"data": "Project records and authenticated identity",
"control": {
"path": "firestore.rules",
"line": 6,
"snippet": "allow read, write: if request.auth != null;"
}
}
這份資訊會直接影響 Hunter 的優先順序。
如果高價值資料跨越 Browser 與 Database 邊界,而唯一控制只確認「已登入」,Access Control Hunter 就應優先追蹤跨帳號操作。
Repository 中沒有 WAF 設定,不代表正式環境沒有 WAF。
Repository 中沒有 Backup Script,也不代表平台沒有 Managed Backup。
反過來,Agent 也不能假設這些防護一定存在。
Demo 把無法回答的問題列成:
{
"question": "Are CDN, WAF, rate limits, quotas, or App Check enabled?",
"requiredEvidence": "Firebase and Google Cloud project configuration"
}
完整 Unknown 清單包含:
這些項目不是 Recon 失敗,而是 Recon 的重要產物。
後續遇到依賴這些資訊的 Candidate 時,Validator 應輸出 requires_evidence,而不是自行選擇最危險或最安全的假設。
執行後會看到:
RECON SUMMARY
Application: Local-only Firebase web application
Actors: 3
Trust boundaries: 3
Input surfaces: 3
Data flows: 3
Unknowns: 4
並列出三條信任邊界與四個待補問題。
產生的 architecture.json 可以成為下一階段的共用 Context:
Repository
→ Recon
→ architecture.json
→ Security Hunter
→ Privacy Hunter
→ Reliability Hunter
不同 Hunter 不需要各自猜一次系統用途,也比較不容易產生互相矛盾的架構假設。
今天的 Demo 使用明確規則讀取已知檔案,目的是先定義 Recon 產物與證據格式。
要處理任意 Repository,還需要加入:
其中「使用 Gemini 合併」不能取代程式證據。
比較安全的分工是:
Deterministic Tools
收集檔案、Symbol、設定、引用位置
Gemini
分類元件、解釋資料流、提出需要補查的問題
Validator
確認引用存在,並把無證據結論降級為 Inferred 或 Unknown
這樣能利用模型理解不同技術棧的能力,又不必相信它記住的每一個架構細節。
第一版可以使用以下停止條件:
Application
用途、技術棧與主要 Entry Point 已確認。
Actors
外部使用者、服務與維運角色已列出。
Inputs
Network、Form、File、Environment 與 External Integration 已盤點。
Data Flows
重要輸入已追蹤到 Storage、Output 或危險 Sink。
Trust Boundaries
每條邊界都有資料、控制與證據位置。
Unknowns
Repository 外的必要資訊已列出取得方式。
如果專案包含多租戶、Plugin、管理後台、多個部署目標或複雜權限繼承,就不應強迫 Recon 在固定 Token 用完時結束。
正確做法是把複雜區域拆開調查,再合併成同一份架構模型。
AI 在找漏洞前,必須先知道它正在檢查什麼系統。
一份可用的 Recon 至少要回答:
今天的 Demo 將這些內容輸出為帶有檔案、行號與原始片段的 architecture.json。
Recon 不是替 Repository 寫一段摘要,而是替後續每個 Finding 建立共同、可核對的世界觀。
明天,我們會設計資安、隱私與 SRE 的檢查規則,讓 Agent 根據架構選擇適用規則,而不是對所有專案套用同一份 Checklist。