name api-recon description 收集网站API接口时调用该skill。
API Recon(前端接口侦察)
在已授权 前提下,尽可能完整地发现:后端 API (路径、方法、参数、响应体)、前端路由 、UI 功能触发点 (Tab、弹窗、表格操作等)。
边界与禁止(Agent 必读 · 违反即越界)
本 skill 仅做 API / 参数面侦察 ,不是漏洞挖掘或渗透利用阶段。
任务边界
范围 允许 禁止 目标 枚举 path、method、参数、路由、UI 触发点 SQLi/XSS/越权/爆破/fuzz 漏洞、改包攻击、破坏性操作 鉴权 Hook + stub/mock 绕过客户端 登录门 向用户索要或猜测账号密码;尝试真实登录表单提交 运行时 无凭据下 hook 接口,用 mock 响应让 SPA 进入登录后壳层 依赖真实后端会话才能继续的流程
无凭据动态分析(Phase 3 默认)
通过 preload.js / runtime_harvest.js 拦截并 stub 登录、权限、菜单等 bootstrap 接口;
对业务查询接口返回 结构正确、业务码成功、数据可为空 的 mock body;
使前端在无后端或 401 环境下仍能渲染登录后页面,从而触发更多 XHR/fetch/WebSocket;
空数据、空白表格、占位 UI 均属预期 ——勿为此转向真实登录或漏洞测试。
一句话 :用 mock 撑开前端路由与组件挂载,只录 outbound 请求 ;后端返回什么不重要,重要的是前端还会发哪些接口 。
流程硬禁止
禁止 替代做法 Phase 1 完成前 grep/curl/Read 主 entry index-*.js 提取 API path 跑 OUTDIR/harvest_static.py 手写 extract_apis.py 等替代 harvest 的脚本 改 OUTDIR/harvest_static.py 后重跑 同一 grep/命令失败 ≥2 次仍重复 换策略:读 tool_logs、改 harvest、查 reference 跳过门禁 A/B,直接跑 scripts/ 原版 复制到 OUTDIR 并按目标改 真实用户名/密码、OTP、OAuth 等鉴权 stub/mock(见上文) 以「拿真实数据」为由跳过 stub,做越权/注入测试 只录 outbound,属 recon 边界 删除、导出敏感数据、批量写等不可逆操作 coverage 点击亦同 未完成 runtime + 动态枚举,声称已获全部页面和接口 见「完成定义」或标注局限 未完成参数触发矩阵 + diff,声称已掌握全部参数 Phase 3b 矩阵 + Phase 5 diff 用单一 runtime 样本推断必填/可选 多样本 diff 或校验规则/错误反推
两层模型 + 运行模式
层 产出 上限 静态 (JS bundle)全量 endpoint 路径、路由草案、组包点字段候选 无 HTTP 方法;参数须 Phase 1b;漏掉运行时拼接 URL 运行时 (活会话)方法 + body + 响应 + 动态 URL + WS/SSE;多样本 diff 补全参数 页面须实际渲染才会发请求;单样本不足以定必填/可选
运行模式 引擎 适用 depth runtime_harvest.js(Puppeteer)API 清单、METHOD/params/响应体、WS/SSE、可复现批量跑 coverage browser + preload.js 点 Tab/弹窗/表格,功能点覆盖更深 both 先 depth 再 coverage 最完整,耗时最长
参数方法论 (无通用脚本):path 用 harvest/正则;参数用 锚点扩窗 + UI 绑定链 + 多样本 diff + 错误反推 (grep 配方见 reference.md J 节)。
完成定义
全部满足方可声称 recon 完成:
脚本与门禁
scripts/ 仅为参考模板,禁止 直接跑原版并当最终结果。
规则 :先读 → 按目标改 → 写入 OUTDIR(如 recon/)→ 记 CHANGES.md;不匹配则按方法论重写,只借结构。
门禁 何时 参考脚本 → OUTDIR 副本 常见必改项 A(静态) Phase 0 后、第一次 跑 harvest/spider 前 harvest_static.py / spider_mpa.py多数站点默认 regex 可直接跑 ;仅 manifest/方言不匹配时改 endpoint 正则、webpack/Vite publicPath、MPA exclude/cookieB(运行时) Phase 2 后、跑 depth/coverage 前 runtime_harvest.js / preload.js + config.jsonCookie/localStorage 键、neutralize 成功值、stubs、login 正则、api 前缀、hash/history
SPA 强制顺序 (不可交换;Phase 编号优先于「先探索再脚本」):
步骤 必须 禁止 Phase 0 完成后 下一条 Bash = python3 OUTDIR/harvest_static.py <URL> OUTDIR curl/grep/Read 主 entry index-*.js(通常 >500KB) 门禁 A 复制脚本 → 按需小改 → 立刻运行 先手工提取 API 再决定是否 harvest Phase 1 完成前 wc -l 校验产出;404 改 harvest 重试手写 extract 脚本;对未下载 URL 反复 grep Phase 1b 起 grep 仅 OUTDIR/js/*.js 用主 bundle 代替 harvest
✅ 复制 harvest_static.py → (可选)改 regex → 立即运行
❌ curl 主 bundle → grep 多次 → 写临时 extract → 最后才 harvest
MPA :Phase 0 后下一条 Bash = python3 OUTDIR/spider_mpa.py ...
工具与输出约束
约束 说明 大文件 >100KB 的 index-*.js 禁止 Read/grep 进上下文;用 OUTDIR 脚本批处理 grep 输出 必须 | head -20 或 -m 5;对话只保留 path 摘要,勿贴 bundle 片段 校验 用 wc -l、ls | wc -l;勿 Read 整目录 regex 初探 可选、≤1 次、仅 ≤50KB 小 chunk 或 HTML;正式静态以 harvest 为准 reference 配方/模板/排障见 reference.md ,勿重复 inline 全文
执行路线图
Phase 0 分类 + OUTDIR
→ 门禁 A → Phase 1 harvest(★ 立刻运行 ★)
→ Phase 1b 参数逆向
→ Phase 2 鉴权三道门 → config.json
→ 门禁 B → Phase 3 运行时 + 参数矩阵
→ Phase 4 权限树(必要时)→ 重跑 Phase 3
→ Phase 5 合并报告 + insert_assets批量插入所有发现的服务、端点api资产,无论如何插入时不允许漏掉已发现的资产
按序勾选;前一项未完成不得进入下一 Phase 。
Phase 0 :初探 SPA/MPA;创建 OUTDIR → Phase 0
门禁 A + Phase 1 :复制脚本 → 立刻 harvest → wc -l 校验 → Phase 1
Phase 1b :锚点扩窗 + 绑定层 → param_candidates.json → Phase 1b
Phase 2 :鉴权三道门 → config.json → Phase 2
门禁 B :调整 runtime 脚本 → Phase 3
Phase 3 :depth / coverage / both;确认进壳;参数触发矩阵 → param_samples.json
Phase 4 (若需要):权限树 → patch stubs → 重跑 Phase 3 → Phase 4
Phase 5 :合并产出 + 报告 + insert_assets → Phase 5
Phase 0 — 分类
拉取入口 HTML,创建 OUTDIR (勿改 skill 内 scripts/):
SPA :空壳 + <div id=app> + chunk → Phase 1–5
MPA :SSR + <form>、无 endpoint bundle → 门禁 A 后:
python3 recon/spider_mpa.py <BASE_URL> <OUTDIR> [--cookie "session=..." ] [--max 300] [--depth 5] [--exclude "logout|delete|destroy" ]
产出 forms.txt、links.txt、api_inline.txt。SPA 若 forms ≈ 0 → 切 Phase 1。
Phase 1 — 静态
遵守 脚本与门禁 · 工具与输出约束 。
python3 recon/harvest_static.py <BASE_URL> <OUTDIR>
harvest:解析 HTML script → webpack/Vite manifest → 下载全部 lazy chunk → 产出 js/、api_static.txt、routes.txt、chunkmap.txt。
wc -l OUTDIR/api_static.txt OUTDIR/routes.txt
ls OUTDIR/js | wc -l
chunk 数 vs manifest:404 须改 harvest 重试,勿手工 curl 逐个 chunk
api_static.txt 过少 → 放宽 OUTDIR 内 endpoint 正则后重跑(见 reference)
Phase 1b — 参数逆向
path 来自 Phase 1;参数字段须单独 recon。grep 规则见 工具与输出约束 。
完成标准 :重要接口能答——字段名、传输位置、类型推断、是否必填、样本值、置信度。
1b.0 — 传输形态
形态 参数在哪 静态优先看 REST JSON body + query path 锚点旁 (params|data|body)\s*:\s*\{ GraphQL variablesgql 模板、$page: Int 传统 form urlencoded <form>、FormData文件上传 multipart FormData.append路径参数 /user/:id路由表 + useParams / $route.params 加密/签名 包进 sign/data Hook 加密函数入参(reference D 节)
产出:每接口标注 transport: query|json|form|graphql|encrypted。
1b.1 — 锚点扩窗
以已知 path 为锚,扩窗口找组包对象:
grep -n '"/api/user/list"' OUTDIR/js/*.js | head -20
grep -rhoaE '.{0,120}("/api[^"]+").{0,200}' OUTDIR/js/*.js | head -20
grep -rhoaE '(params|data|body|payload)\s*:\s*\{' OUTDIR/js/*.js | head -20
包装层 参数线索 axios 实例 data / params统一 request 拦截器注入全局字段 OpenAPI 客户端 生成 method 签名 React Query / SWR hook 第二参数 Vue composable composable 入参
类型残留:yup/zod/rules、Form.Item name=、内嵌 Swagger。
→ param_candidates.json:{ path, fields[], source: "static-callsite", confidence }
1b.2 — 绑定层
Form field → onFinish/handleSubmit → transform → API payload
绑定源 手法 表单 submit 跟 submit → transform → API 表格搜索 getFieldsValue() → params路由 :id / ?tab=拦截器 全局 tenantId、分页、sign 枚举 select options → API 枚举值
DevTools call stack 从 fetch/XHR.send 往上追组包函数。
1b.3 — 组包三问(≠ Phase 2 鉴权三门)
问 要答什么 组装 payload 在哪 build、transform 痕迹 校验 required、pattern、enum 传输 path / query / body / multipart / 头
拦截器门(Phase 2)顺带读全局注入字段(Authorization、X-Tenant-Id、sign)。
1b.4 — 与 Phase 3 衔接
候选字段来自静态/绑定层;必填/可选/条件依赖 须 Phase 3 参数矩阵 + diff + Phase 5 错误反推。
Phase 2 — 鉴权三道门
在 OUTDIR/js/ grep(带 head),写入 config.json(配方见 reference):
门 问题 关键词 渲染门 如何判断已登录? isLogin、getToken、Cookie/localStorage拦截器门 什么触发跳 /login? response_code、errno、axios interceptor内容门 菜单/权限从哪来? menu、permission、role、acl、routes
禁止把 localStorage 键名当凭据——须从 chunk/请求链确认。
出口 = 门禁 B :结论落到 config.json,并改 OUTDIR/runtime_harvest.js / preload.js。
Phase 2b — API 观察(可选)
用 OUTDIR 内 preload.js 确认会话键名、Authorization、嵌套 API URL:
配置 产出 recordDetail: true__API_RECON_DETAIL__observe.xhrHeaders: trueheaders 观察 extractUrlsFromResponse: true响应内子 API observe.storageReads/cookieReads: true回填 config neutralizeVueRouter: true__API_RECON_ROUTES__
coverage 每轮导出:__API_RECON_LOG__、__API_RECON_DETAIL__、__API_RECON_ROUTES__、__API_RECON_OBSERVE__。
Phase 3 — 运行时
须已过门禁 B;遵守 边界与禁止 · 无凭据 mock 策略。
config.json 设置 "runtimeMode": "depth" | "coverage" | "both"(模板见 reference)。
Hook 与 stub(depth + coverage 共用)
层 范围 目的 L1 精确 auth/权限/bootstrap stub 过首屏鉴权 L2 负向修正 所有 JSON 响应 未登录码 → 成功 L3 兜底 未命中 L1 的 /api 等 空成功体,撑开 UI
depth :fake auth + forward 改业务码 + stubs;遍历 routes(hash/history);产出 runtime_api.json
coverage :document-start 注入 preload.js(CDP addScriptToEvaluateOnNewDocument 或 Userscript)
验证:window.__API_RECON_PRELOAD__ 存在;业务 path 不回 /login。
cd recon && npm install
node runtime_harvest.js config.json
3b — coverage 动态枚举(必做)
主导航/侧栏 — 每项点击,等网络 1–3s
Tab — role=tab、.ant-tabs-tab
表格 — 首行查看/编辑/详情
工具栏 — 导出、筛选、新建(避免不可逆删除 )
每进模块 — 合并 API/路由
SPA — 对 routes.txt 未覆盖 path 受控 pushState(MPA 禁止)
参数触发矩阵 (必做):每模块按操作类型各录一次,diff 多样本 :
操作 通常多出的参数 列表首屏 分页 + 默认筛选 点搜索 keyword、filter 高级筛选 更多 optional 新建/编辑 完整 entity 批量/导出/排序 ids[]、exportType、sortField
stub 下 outbound body/headers 仍真实 ——以请求为准。录制 → scan_raw.json、param_samples.json、api_detail.json。
Vue :neutralizeVueRouter: true + document-start preload
React :routes.txt + 侧栏点击 + pushState
both :先 3a depth,再 3b coverage
Phase 4 — 权限树还原
触发 :模块页空白 / 每路由仅 bootstrap(如 locale)→ 内容门未过。
现象 含义 进壳成功 渲染门 + 拦截器门已过 侧栏缺项/点击空白 stub shape 或权限码不全 每路由 API 相同且极少 v-if permission 未通过routes.txt 远少于 bundle须从 auth 模块补全
grep -rhoaE '"/api[^"]*(permission|perm|role|menu|acl)[^"]*"' OUTDIR/js/*.js | sort -u | head -30
grep -rhoaE 'userRouteAuth|getResultTree|routeMap|routeLink|menuList|authList' OUTDIR/js/*.js | head -20
典型链:role_permissions(flat codes)+ permissions/all(tree)→ getResultTree → userRouteAuth[CODE].url。
python3 recon/extract_route_map.py recon/js recon/
python3 recon/build_perm_tree.py recon/js recon/ --config recon/config.json
中间产出:route_map.json、userRouteAuth.json、permissions_tree.json、*_stub.json、perm_codes_all.txt。
stub 检查:外层 response_code 与拦截器门一致;flat codes 与 tree 对齐;routes 覆盖 route_map 全部 link。
更新 config.json 后重跑 Phase 3 。大型 SPA 可调 waitUntil、routeTimeout、perRouteMs(见 reference A3/I 节)。
Phase 5 — 合并与报告
产出表
文件 阶段 内容 js/、api_static.txt、routes.txt、chunkmap.txt1 静态 bundle 与 path param_candidates.json1b 静态参数字段候选 config.json2 三道门 + runtime 配置 runtime_api.json3a depth 详细录制(含 WS/SSE) param_samples.json、scan_raw.json、api_detail.json3b 多样本、点击日志、detail route_map.json 等4 权限树中间文件(若执行) params_merged.json5 合并参数字段 + 置信度 api_merged.txt5 METHOD /path [params] [static|runtime|both]site_map.json5 路由、API、params、功能点、局限 insert_assets 5 将所有服务、端点资产写入资产库
5b — 参数合并
从 param_samples.json diff,无通用合并脚本 。置信度规则见 reference J7(高/中/低/待触发)。
5c — 错误反推
授权范围内可发不完整请求读 400(属参数 recon,非漏洞测试 ):field 'x' is required、枚举错误等。注意 data 包装、variables、加密前 bizData。
报告须注明:runtimeMode、静态/运行时 API 数、参数置信度、未覆盖模块、相对参考脚本的 CHANGES.md 摘要。
site_map.json 建议结构:
{
"site" : "https://example.com" ,
"runtimeMode" : "both" ,
"appType" : "vue-spa" ,
"routeGuardStrategy" : [ "nav-neutralize" , "L1-auth" , "L2-patch" , "forward" ] ,
"apisFromStatic" : [ ] ,
"apisFromRuntime" : [ ] ,
"apis" : [ ] ,
"params" : [ { "method" : "POST" , "path" : "/api/user/list" , "transport" : "json" , "fields" : [
更多字段与 grep 配方见 reference.md 。
通用说明
框架无关 :webpack/Vite/Angular lazy load 方法相同
传输 :REST/JSON、GraphQL、WebSocket、SSE;gRPC-web 不在范围
SSR :客户端 fetch 可录;RSC/Server Actions 不完全可枚举
盲区 :JSVMP、WASM、HMAC/mTLS 强校验 → 静态 + 标注局限
参数盲区 :条件联动、hidden params、WASM 组包 → 「待触发」/「不可达」
静态是安全网 :runtime 被挡时静态仍能枚举 endpoint
附加资源