# Discovery Agent（流程文档）

> 本文件由主 agent 在阶段 1 入口 Read 后按其流程执行

**目标：** 建立初始情报基线——读懂前端代码、找到所有静态可知的接口、生成初始权限矩阵、建立会话池。Discovery 阶段只处理**静态可知**的信息；运行时才会暴露的接口（如创建订单后才出现的 callback URL、支付后才有的 sign）由 Attack 阶段边测边发现、边补充。

`api_inventory.json` 和 `permission_matrix.json` 是**活文件**，Attack 阶段每发现新接口就追加，不是一次性生成后冻结。

---

## 流程

### Step 0：项目初始化(多项目隔离)

阶段 1 最先执行。确定 project-id、建项目目录、登记到项目清单。

1. **确定 project-id**:
   - 默认从 target hostname 第一段提取(小写):`bi-fine-sit.agibot.com` → `bi-fine-sit`
   - 用户 `--project <id>` 覆盖
   - 校验只含 `[a-z0-9-]`,否则要求用户手动指定

2. **建项目目录**:`mkdir -p pentest-data/{project-id}/`

3. **登记项目清单** `pentest-data/index.json`(**根目录,不在子目录内**):
   - 文件不存在 → 建 `{"_note": "项目清单", "projects": []}`
   - 查 `projects[]` 是否已有此 `project_id`:
     - 有 + `target` 相同 → **续测**:Read `pentest-data/{id}/state.json` 断点恢复,跳到对应阶段
     - 有 + `target` 不同 → 冲突,提示用户换 `--project` 名
     - 无 → append 一条 `{project_id, target, dir, created, last_active, phase:1, status:"in_progress"}`(时间用 `date -Iseconds`)

4. 之后所有状态文件写到 `pentest-data/{project-id}/` 下。

### Step 1：全量 JS 深度阅读

登录前先抓取所有前端 JS 文件，**完整阅读每一个**，不只是 grep 接口路径。

需要提取并记录：

**接口层面**
- 所有 `fetch()`、`xmlhttprequest`、`jsonPost()`、`axios` 调用的 URL
- 每个接口传递的**完整参数列表**（不只是显式的，还有注释掉的、条件分支里的）
- 参数的构造方式：是直接从输入框取值，还是经过计算/哈希/签名

**业务逻辑层面**
- 所有签名/哈希逻辑（如密码 SHA256、支付签名生成方式）
- 前端状态机：页面在什么条件下展示什么操作，背后接口是什么
- 前端存储的敏感数据：localStorage、全局变量、window.xxx 里有什么
- 任何注释，特别是中文注释、TODO、FIXME、"注意"、"漏洞"等关键词

**可控参数层面**
- 哪些"应该由服务端决定"的值实际上由前端传入（金额、概率、角色、状态）
- 前端做了哪些校验，这些校验是不是后端也做了（前端限制 ≠ 后端限制）

**资产密钥扫描（必做）**
- 对所有 JS 内容（含独立 .js、内联 `<script>`、引用的资产文件如 config.js）grep 硬编码密钥模式：`access_key|secret|api_key|app_secret|token|password|passwd|AKIA|appId|app_id|private_key|client_secret`。命中写入 `js_analysis.hardcoded_secrets`。config.js / 配置类资产常藏 AK/SK,别漏。

把这些记录到 `pentest-data/{project-id}/js_analysis.json`：
```json
{
  "login_mechanism": "SHA256(password) before submit",
  "payment_sign": "HMAC of order_no+amount+timestamp, key unknown",
  "client_controlled_params": [
    {"api": "/api/user/lottery.php", "param": "probabilities", "note": "概率数组完全由客户端传入，服务端未重新计算"},
    {"api": "/api/user/create-batch-order.php", "param": "use_points", "note": "积分抵扣量客户端控制"}
  ],
  "sensitive_comments": [
    {"file": "user.js", "line": 1157, "comment": "复用原有 recharge-callback 接口，保持竞态漏洞不变"}
  ],
  "hardcoded_secrets": [
    {"file": "admin/js/config.js", "line": 12, "match": "accessKey=AKIAXXXX...", "note": "管理端配置泄露 AK/SK"}
  ],
  "frontend_only_validation": [
    {"field": "quantity", "note": "前端限制正整数，未确认后端是否同样验证"}
  ],
  "channels_covered": ["js_ref", "inline_script", "asset_ref", "page_link", "path_inference", "response_body"]
}
```

> `channels_covered` 是反遗漏的六个 bool（见 a01.txt G7）：Discovery 末尾自问每个渠道走过没，走过就加入数组。**不建台账、不每参数记来源**——就这六个标记。

### Step 2：接口全量发现

**不止依赖 JS 文件中引用的接口。** 组合多种方式：

1. **JS grep**：从所有 JS 文件提取接口路径（已知方式）

2. **页面内联脚本**：每个 HTML 页面单独读，提取 `<script>` 标签内的接口调用（与独立 JS 文件分开处理）

3. **前端渲染页面**：前端页面本身也有访问控制，同样需要纳入 `api_inventory`（`source: "page"`）。来源：首页导航的 `href` 链接、各角色路径规律（如 `/user/*.php`、`/merchant/*.php`、`/admin/*.php`）。这样 Step 3 的权限矩阵扫描会自动覆盖页面级越权。

4. **路径推断**：对已发现的接口做模式扩展
   - 有 `product-add.php` → 猜 `product-edit.php`、`product-delete.php`、`product-detail.php`、`product-list.php`
   - 有 `user/login.php` → 猜 `user/logout.php`、`user/register.php`、`user/profile.php`、`user/change-password.php`
   - 有 `xxx-batch-yyy.php` → 猜 `xxx-yyy.php`（批量接口通常有对应的单品版本）
   - 有 `create-xxx.php` → 猜 `submit-xxx.php`、`confirm-xxx.php`、`cancel-xxx.php`
   - 有 `xxx-list.php` → 猜 `xxx-export.php`、`xxx-search.php`、`xxx-count.php`
   - 用 `curl -s URL -o /dev/null -w "%{http_code}"` 批量探测是否存在（200/301/302/403 都算存在，404 跳过）

5. **公共路径探测**：无论业务类型，都探测：
   ```
   /.env  /config.php  /phpinfo.php  /info.php
   /api/  /swagger.json  /swagger-ui.html
   /.git/config  /backup.zip  /db.sql
   /actuator  /actuator/env  /_debug  /debug
   /admin/  /administrator/
   ```

6. **引用资产抓取(asset_ref)**：每个 `<script src>` / `<link href>` 引用的资产文件都抓取并阅读。**src 相对其所在页面解析**——页面在 `/admin/login.php` 引用 `js/config.js` → 取 `/admin/js/config.js`（不是按站点根 `/js/config.js`）；一次 404 时换 base 重试，别轻易判定不存在（config.js 类 404 误判是漏 AK/SK 的常见原因）。资产内容同样做密钥扫描（见 Step 1）。config.js / .json 配置类资产是 AK/SK 泄露高发点。

把所有发现的接口写入 `pentest-data/{project-id}/api_inventory.json`：
```json
{
  "discovered_apis": [
    {"url": "/api/user/login.php", "method": "POST", "source": "js:user.js", "auth_required": "none"},
    {"url": "/api/admin/user-list.php", "method": "GET", "source": "js:admin.js", "auth_required": "admin?"}
  ]
}
```

### Step 3：会话池建立 + 初始权限矩阵

用可用账号登录建立会话池（账号 0 或 1 个也不阻塞，正常进行后续步骤）。注意从 JS 分析中已知的登录机制（如密码哈希方式），直接应用，不要浪费时间试错。

每个账号登录后立即做一件事：**用该账号的 session 访问 api_inventory 里的所有接口**，记录每个接口对该角色的响应状态码。

这会生成**初始**权限矩阵，写入 `pentest-data/{project-id}/permission_matrix.json`：

```json
{
  "_note": "活文件，Attack 阶段每发现新接口立即追加",
  "/api/admin/user-list.php": {
    "admin": 200,
    "user_maijia1": 200,
    "merchant_shanghu1": 200
  },
  "/api/admin/order-list.php": {
    "admin": 200,
    "user_maijia1": 403,
    "merchant_shanghu1": 403
  }
}
```

**权限矩阵直接揭示垂直越权**：任何非预期角色能以 200 访问的接口，立即标记为高优先攻击目标，写入 module_queue 的首位。

> Attack 阶段每发现一个新接口（从响应体、重定向 URL、业务流程中），立即用所有角色 session 测一遍权限，追加进矩阵。

**注册页探测**：探测是否存在注册页（`/register`、`/signup`、`/sign-up`、登录页上的「注册」链接）。发现则记入 `state.json.registration_page`。账号不足 2 个时，这决定阶段 3 是否提示用户注册补全。

### Step 4：模块队列构建

基于以上信息构建初始模块队列：
- 主菜单导航发现的模块
- 权限矩阵中发现的异常访问点（标记 `priority: critical`）
- JS 分析中发现的高风险参数对应的功能模块

**seed `pages.json`**：把一级模块对应的页面写入 `pentest-data/{project-id}/pages.json`（`source: "nav_menu"`，`visited: true`）。**不在此阶段穷举所有功能**——页面内的功能点在阶段 2 边测边补进每个页面的 `functions[]`。

**完成条件：** `api_inventory.json` 存在、`permission_matrix.json` 存在（初始版本即可）、`module_queue` 非空、`js_analysis.json` 存在且 `client_controlled_params` 字段已填写、`pages.json` 存在。

Discovery 完成后进入 Attack 阶段，两个文件继续被增量更新。

---

## 跨角色 fetch 模板（供 Phase 2 使用）

```javascript
// 直接用 curl，无需浏览器
// 从 pool.json 取各角色 cookie，批量测试
for role in admin user1 user2 merchant1; do
  SESS=$(jq -r ".accounts[] | select(.role==\"$role\") | .cookie_value" pentest-data/{project-id}/sessions/pool.json)
  curl -s -H "Cookie: SESSION_NAME=$SESS" "$TARGET_URL"
done
```
