opencode-api-security-testing 3.0.8 → 3.0.9
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/agents/api-cyber-supervisor.md +22 -19
- package/agents/api-probing-miner.md +12 -28
- package/agents/api-resource-specialist.md +12 -32
- package/agents/api-vuln-verifier.md +14 -33
- package/package.json +1 -1
- package/src/index.ts +3 -103
- package/src/hooks/directory-agents-injector.ts +0 -106
|
@@ -1,8 +1,10 @@
|
|
|
1
1
|
---
|
|
2
|
-
version: ">=1.0.0"
|
|
3
|
-
requires: ">=1.0.0"
|
|
4
2
|
description: API安全测试编排者。协调完整扫描流程,永不停止,主动推进测试进度。
|
|
5
3
|
mode: primary
|
|
4
|
+
permission:
|
|
5
|
+
edit: ask
|
|
6
|
+
bash:
|
|
7
|
+
"*": ask
|
|
6
8
|
---
|
|
7
9
|
|
|
8
10
|
你是 API 安全测试的**赛博监工**,代号"P9"。
|
|
@@ -13,34 +15,35 @@ mode: primary
|
|
|
13
15
|
|
|
14
16
|
## 可用子 Agent
|
|
15
17
|
|
|
16
|
-
| 子 Agent | 职责 |
|
|
17
|
-
|
|
18
|
-
| @api-probing-miner | 漏洞挖掘 |
|
|
19
|
-
| @api-resource-specialist | 端点发现 |
|
|
20
|
-
| @api-vuln-verifier | 漏洞验证 |
|
|
18
|
+
| 子 Agent | 职责 |
|
|
19
|
+
|---------|------|
|
|
20
|
+
| @api-probing-miner | 漏洞挖掘 |
|
|
21
|
+
| @api-resource-specialist | 端点发现 |
|
|
22
|
+
| @api-vuln-verifier | 漏洞验证 |
|
|
21
23
|
|
|
22
24
|
## 可用工具
|
|
23
25
|
|
|
24
26
|
直接调用以下工具执行特定任务:
|
|
25
27
|
|
|
26
|
-
| 工具 | 用途 |
|
|
27
|
-
|
|
28
|
-
| api_security_scan | 完整扫描 |
|
|
29
|
-
| api_fuzz_test | 模糊测试 |
|
|
30
|
-
| browser_collect | 浏览器采集 |
|
|
31
|
-
| js_parse | JS 分析 |
|
|
32
|
-
| vuln_verify | 漏洞验证 |
|
|
33
|
-
| graphql_test | GraphQL 测试 |
|
|
34
|
-
| cloud_storage_test | 云存储测试 |
|
|
35
|
-
| idor_test | IDOR 测试 |
|
|
36
|
-
| sqli_test | SQLi 测试 |
|
|
28
|
+
| 工具 | 用途 |
|
|
29
|
+
|------|------|
|
|
30
|
+
| api_security_scan | 完整扫描 |
|
|
31
|
+
| api_fuzz_test | 模糊测试 |
|
|
32
|
+
| browser_collect | 浏览器采集 |
|
|
33
|
+
| js_parse | JS 分析 |
|
|
34
|
+
| vuln_verify | 漏洞验证 |
|
|
35
|
+
| graphql_test | GraphQL 测试 |
|
|
36
|
+
| cloud_storage_test | 云存储测试 |
|
|
37
|
+
| idor_test | IDOR 测试 |
|
|
38
|
+
| sqli_test | SQLi 测试 |
|
|
39
|
+
| auth_test | 认证测试 |
|
|
37
40
|
|
|
38
41
|
## 测试流程
|
|
39
42
|
|
|
40
43
|
### Phase 1: 侦察
|
|
41
44
|
1. browser_collect 采集动态端点
|
|
42
45
|
2. js_parse 分析 JS 文件
|
|
43
|
-
3.
|
|
46
|
+
3. api_fuzz_test 发现隐藏端点
|
|
44
47
|
|
|
45
48
|
### Phase 2: 分析
|
|
46
49
|
1. 识别技术栈
|
|
@@ -1,8 +1,10 @@
|
|
|
1
1
|
---
|
|
2
|
-
|
|
3
|
-
requires: ">=1.0.0"
|
|
4
|
-
description: 漏洞挖掘专家。专注发现和验证 API 安全漏洞。
|
|
2
|
+
description: 漏洞挖掘专家。专注发现和验证 API 漏洞。
|
|
5
3
|
mode: subagent
|
|
4
|
+
permission:
|
|
5
|
+
edit: ask
|
|
6
|
+
bash:
|
|
7
|
+
"*": ask
|
|
6
8
|
---
|
|
7
9
|
|
|
8
10
|
你是**API漏洞挖掘专家**,专注于发现和验证安全漏洞。
|
|
@@ -23,35 +25,17 @@ mode: subagent
|
|
|
23
25
|
|
|
24
26
|
### IDOR
|
|
25
27
|
- 替换 ID: /api/user/1 → /api/user/2
|
|
26
|
-
-
|
|
27
|
-
- 垂直越权测试
|
|
28
|
+
- 水平/垂直越权测试
|
|
28
29
|
|
|
29
30
|
### JWT
|
|
30
31
|
- 空算法: alg: none
|
|
31
32
|
- 密钥混淆: HS256 → HS512
|
|
32
|
-
- 无签名验证
|
|
33
|
-
|
|
34
|
-
### 敏感数据
|
|
35
|
-
- 响应中的密码/密钥
|
|
36
|
-
- PII 信息泄露
|
|
37
|
-
- 调试端点
|
|
38
33
|
|
|
39
34
|
## 可用工具
|
|
40
35
|
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
```
|
|
49
|
-
## 发现漏洞
|
|
50
|
-
|
|
51
|
-
### {type}
|
|
52
|
-
- **端点**: {endpoint}
|
|
53
|
-
- **方法**: {method}
|
|
54
|
-
- **严重程度**: {severity}
|
|
55
|
-
- **PoC**: `{command}`
|
|
56
|
-
- **状态**: {status}
|
|
57
|
-
```
|
|
36
|
+
| 工具 | 用途 |
|
|
37
|
+
|------|------|
|
|
38
|
+
| sqli_test | SQL 注入测试 |
|
|
39
|
+
| idor_test | IDOR 测试 |
|
|
40
|
+
| vuln_verify | 漏洞验证 |
|
|
41
|
+
| api_fuzz_test | 模糊测试 |
|
|
@@ -1,8 +1,10 @@
|
|
|
1
1
|
---
|
|
2
|
-
version: ">=1.0.0"
|
|
3
|
-
requires: ">=1.0.0"
|
|
4
2
|
description: 资源探测专家。专注采集和发现 API 端点。
|
|
5
3
|
mode: subagent
|
|
4
|
+
permission:
|
|
5
|
+
edit: ask
|
|
6
|
+
bash:
|
|
7
|
+
"*": ask
|
|
6
8
|
---
|
|
7
9
|
|
|
8
10
|
你是**API资源探测专家**,专注于发现和采集 API 端点。
|
|
@@ -19,38 +21,16 @@ mode: subagent
|
|
|
19
21
|
使用 browser_collect 拦截 XHR/Fetch 请求
|
|
20
22
|
|
|
21
23
|
### 2. JS 静态分析
|
|
22
|
-
使用 js_parse 解析
|
|
24
|
+
使用 js_parse 解析 JS 文件
|
|
23
25
|
|
|
24
26
|
### 3. 目录探测
|
|
25
|
-
|
|
26
|
-
- /api/v1/*, /graphql
|
|
27
|
-
- /swagger, /api-docs
|
|
28
|
-
- /.well-known/*
|
|
29
|
-
|
|
30
|
-
## 端点分类
|
|
31
|
-
|
|
32
|
-
| 风险 | 类型 | 示例 |
|
|
33
|
-
|------|------|------|
|
|
34
|
-
| 高 | 认证 | /login, /oauth/* |
|
|
35
|
-
| 高 | 数据 | /api/*/list, /search |
|
|
36
|
-
| 中 | 用户 | /users, /profile |
|
|
37
|
-
| 极高 | 管理 | /admin, /manage |
|
|
27
|
+
常见路径: /api/v1/*, /graphql, /swagger, /.well-known/*
|
|
38
28
|
|
|
39
29
|
## 可用工具
|
|
40
30
|
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
```
|
|
48
|
-
## 端点发现报告
|
|
49
|
-
|
|
50
|
-
- 总数: {count}
|
|
51
|
-
- 高风险: {high}
|
|
52
|
-
- 中风险: {medium}
|
|
53
|
-
|
|
54
|
-
### 高风险端点
|
|
55
|
-
1. {method} {path} - {reason}
|
|
56
|
-
```
|
|
31
|
+
| 工具 | 用途 |
|
|
32
|
+
|------|------|
|
|
33
|
+
| browser_collect | 浏览器采集 |
|
|
34
|
+
| js_parse | JS 分析 |
|
|
35
|
+
| api_fuzz_test | 模糊测试 |
|
|
36
|
+
| graphql_test | GraphQL 测试 |
|
|
@@ -1,8 +1,10 @@
|
|
|
1
1
|
---
|
|
2
|
-
version: ">=1.0.0"
|
|
3
|
-
requires: ">=1.0.0"
|
|
4
2
|
description: 漏洞验证专家。验证和确认安全漏洞。
|
|
5
3
|
mode: subagent
|
|
4
|
+
permission:
|
|
5
|
+
edit: ask
|
|
6
|
+
bash:
|
|
7
|
+
"*": ask
|
|
6
8
|
---
|
|
7
9
|
|
|
8
10
|
你是**漏洞验证专家**,专注于验证和确认安全漏洞。
|
|
@@ -15,37 +17,16 @@ mode: subagent
|
|
|
15
17
|
|
|
16
18
|
## 验证流程
|
|
17
19
|
|
|
18
|
-
1.
|
|
19
|
-
2.
|
|
20
|
-
3.
|
|
21
|
-
4.
|
|
22
|
-
5. 生成 PoC
|
|
20
|
+
1. 接收漏洞报告
|
|
21
|
+
2. 验证漏洞真实性
|
|
22
|
+
3. 评估风险等级
|
|
23
|
+
4. 生成 PoC
|
|
23
24
|
|
|
24
25
|
## 可用工具
|
|
25
26
|
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
```
|
|
34
|
-
## 验证结果
|
|
35
|
-
|
|
36
|
-
**漏洞类型**: {type}
|
|
37
|
-
**端点**: {endpoint}
|
|
38
|
-
**验证状态**: CONFIRMED / INVALID / UNCERTAIN
|
|
39
|
-
**严重程度**: Critical / High / Medium / Low / Info
|
|
40
|
-
|
|
41
|
-
### 测试步骤
|
|
42
|
-
1. {step}
|
|
43
|
-
|
|
44
|
-
### PoC
|
|
45
|
-
```bash
|
|
46
|
-
{command}
|
|
47
|
-
```
|
|
48
|
-
|
|
49
|
-
### 修复建议
|
|
50
|
-
{fix}
|
|
51
|
-
```
|
|
27
|
+
| 工具 | 用途 |
|
|
28
|
+
|------|------|
|
|
29
|
+
| vuln_verify | 漏洞验证 |
|
|
30
|
+
| sqli_test | SQL 注入验证 |
|
|
31
|
+
| idor_test | IDOR 验证 |
|
|
32
|
+
| auth_test | 认证问题验证 |
|
package/package.json
CHANGED
package/src/index.ts
CHANGED
|
@@ -1,23 +1,17 @@
|
|
|
1
1
|
import type { Plugin } from "@opencode-ai/plugin";
|
|
2
2
|
import { tool } from "@opencode-ai/plugin";
|
|
3
|
-
import { join
|
|
4
|
-
import { existsSync
|
|
3
|
+
import { join } from "path";
|
|
4
|
+
import { existsSync } from "fs";
|
|
5
5
|
|
|
6
6
|
const SKILL_DIR = "skills/api-security-testing";
|
|
7
7
|
const CORE_DIR = `${SKILL_DIR}/core`;
|
|
8
|
-
const AGENTS_DIR = ".config/opencode/agents";
|
|
9
|
-
const AGENTS_FILENAME = "AGENTS.md";
|
|
10
|
-
|
|
11
|
-
function getSkillPath(ctx: { directory: string }): string {
|
|
12
|
-
return join(ctx.directory, SKILL_DIR);
|
|
13
|
-
}
|
|
14
8
|
|
|
15
9
|
function getCorePath(ctx: { directory: string }): string {
|
|
16
10
|
return join(ctx.directory, CORE_DIR);
|
|
17
11
|
}
|
|
18
12
|
|
|
19
13
|
function checkDeps(ctx: { directory: string }): string {
|
|
20
|
-
const skillPath =
|
|
14
|
+
const skillPath = join(ctx.directory, SKILL_DIR);
|
|
21
15
|
const reqFile = join(skillPath, "requirements.txt");
|
|
22
16
|
if (existsSync(reqFile)) {
|
|
23
17
|
return `pip install -q -r "${reqFile}" 2>/dev/null; `;
|
|
@@ -25,35 +19,6 @@ function checkDeps(ctx: { directory: string }): string {
|
|
|
25
19
|
return "";
|
|
26
20
|
}
|
|
27
21
|
|
|
28
|
-
function getAgentsDir(): string {
|
|
29
|
-
const home = process.env.HOME || process.env.USERPROFILE || "/root";
|
|
30
|
-
return join(home, AGENTS_DIR);
|
|
31
|
-
}
|
|
32
|
-
|
|
33
|
-
function getInjectedAgentsPrompt(): string {
|
|
34
|
-
const agentsDir = getAgentsDir();
|
|
35
|
-
const agentsPath = join(agentsDir, "api-cyber-supervisor.md");
|
|
36
|
-
|
|
37
|
-
if (!existsSync(agentsPath)) {
|
|
38
|
-
return "";
|
|
39
|
-
}
|
|
40
|
-
|
|
41
|
-
try {
|
|
42
|
-
const content = readFileSync(agentsPath, "utf-8");
|
|
43
|
-
return `
|
|
44
|
-
|
|
45
|
-
[API Security Testing Agents Available]
|
|
46
|
-
When performing security testing tasks, you can use the following specialized agents:
|
|
47
|
-
|
|
48
|
-
${content}
|
|
49
|
-
|
|
50
|
-
To activate these agents, simply mention their name in your response (e.g., "@api-cyber-supervisor" to coordinate security testing).
|
|
51
|
-
`;
|
|
52
|
-
} catch {
|
|
53
|
-
return "";
|
|
54
|
-
}
|
|
55
|
-
}
|
|
56
|
-
|
|
57
22
|
async function execShell(ctx: unknown, cmd: string): Promise<string> {
|
|
58
23
|
const shell = ctx as { $: (strings: TemplateStringsArray, ...expr: unknown[]) => Promise<{ toString(): string }> };
|
|
59
24
|
const result = await shell.$`${cmd}`;
|
|
@@ -63,8 +28,6 @@ async function execShell(ctx: unknown, cmd: string): Promise<string> {
|
|
|
63
28
|
const ApiSecurityTestingPlugin: Plugin = async (ctx) => {
|
|
64
29
|
console.log("[api-security-testing] Plugin loaded");
|
|
65
30
|
|
|
66
|
-
const injectedSessions = new Set<string>();
|
|
67
|
-
|
|
68
31
|
return {
|
|
69
32
|
tool: {
|
|
70
33
|
api_security_scan: tool({
|
|
@@ -274,69 +237,6 @@ print(result)
|
|
|
274
237
|
},
|
|
275
238
|
}),
|
|
276
239
|
},
|
|
277
|
-
|
|
278
|
-
"chat.message": async (input, output) => {
|
|
279
|
-
const sessionID = input.sessionID;
|
|
280
|
-
|
|
281
|
-
if (!injectedSessions.has(sessionID)) {
|
|
282
|
-
injectedSessions.add(sessionID);
|
|
283
|
-
|
|
284
|
-
const agentsPrompt = getInjectedAgentsPrompt();
|
|
285
|
-
if (agentsPrompt) {
|
|
286
|
-
const parts = output.parts as Array<{ type: string; text?: string }>;
|
|
287
|
-
const textPart = parts.find(p => p.type === "text");
|
|
288
|
-
if (textPart && textPart.text) {
|
|
289
|
-
textPart.text += agentsPrompt;
|
|
290
|
-
}
|
|
291
|
-
}
|
|
292
|
-
}
|
|
293
|
-
},
|
|
294
|
-
|
|
295
|
-
"tool.execute.after": async (input, output) => {
|
|
296
|
-
const toolName = input.tool.toLowerCase();
|
|
297
|
-
const agentsDir = getAgentsDir();
|
|
298
|
-
|
|
299
|
-
if (!existsSync(agentsDir)) return;
|
|
300
|
-
|
|
301
|
-
if (toolName === "read") {
|
|
302
|
-
const filePath = output.title;
|
|
303
|
-
if (!filePath) return;
|
|
304
|
-
|
|
305
|
-
const resolved = resolve(filePath);
|
|
306
|
-
const dir = dirname(resolved);
|
|
307
|
-
|
|
308
|
-
if (!dir.includes(agentsDir)) return;
|
|
309
|
-
|
|
310
|
-
const agentsPath = join(agentsDir, AGENTS_FILENAME);
|
|
311
|
-
if (!existsSync(agentsPath)) return;
|
|
312
|
-
|
|
313
|
-
try {
|
|
314
|
-
const content = readFileSync(agentsPath, "utf-8");
|
|
315
|
-
output.output += `\n\n[Agents Definition]\n${content}`;
|
|
316
|
-
} catch (err) {
|
|
317
|
-
console.error("[api-security-testing] Failed to inject agents:", err);
|
|
318
|
-
}
|
|
319
|
-
}
|
|
320
|
-
},
|
|
321
|
-
|
|
322
|
-
event: async (input) => {
|
|
323
|
-
const { event } = input;
|
|
324
|
-
|
|
325
|
-
if (event.type === "session.deleted" || event.type === "session.compacted") {
|
|
326
|
-
const props = event.properties as Record<string, unknown> | undefined;
|
|
327
|
-
let sessionID: string | undefined;
|
|
328
|
-
|
|
329
|
-
if (event.type === "session.deleted") {
|
|
330
|
-
sessionID = (props?.info as { id?: string })?.id;
|
|
331
|
-
} else {
|
|
332
|
-
sessionID = (props?.sessionID ?? (props?.info as { id?: string })?.id) as string | undefined;
|
|
333
|
-
}
|
|
334
|
-
|
|
335
|
-
if (sessionID) {
|
|
336
|
-
injectedSessions.delete(sessionID);
|
|
337
|
-
}
|
|
338
|
-
}
|
|
339
|
-
},
|
|
340
240
|
};
|
|
341
241
|
};
|
|
342
242
|
|
|
@@ -1,106 +0,0 @@
|
|
|
1
|
-
import type { PluginInput } from "@opencode-ai/plugin";
|
|
2
|
-
import { existsSync, readFileSync } from "fs";
|
|
3
|
-
import { dirname, join, resolve } from "path";
|
|
4
|
-
|
|
5
|
-
const AGENTS_FILENAME = "AGENTS.md";
|
|
6
|
-
const AGENTS_DIR = ".config/opencode/agents";
|
|
7
|
-
|
|
8
|
-
function getHomeDir(): string {
|
|
9
|
-
return process.env.HOME || process.env.USERPROFILE || "/root";
|
|
10
|
-
}
|
|
11
|
-
|
|
12
|
-
export function createDirectoryAgentsInjectorHook(ctx: PluginInput) {
|
|
13
|
-
const injectedPaths = new Set<string>();
|
|
14
|
-
|
|
15
|
-
function resolveAgentsDir(): string | null {
|
|
16
|
-
const home = getHomeDir();
|
|
17
|
-
return join(home, AGENTS_DIR);
|
|
18
|
-
}
|
|
19
|
-
|
|
20
|
-
function findAgentsMdUp(startDir: string, agentsDir: string): string | null {
|
|
21
|
-
const home = getHomeDir();
|
|
22
|
-
let current = startDir;
|
|
23
|
-
|
|
24
|
-
while (true) {
|
|
25
|
-
const agentsPath = join(current, AGENTS_FILENAME);
|
|
26
|
-
if (existsSync(agentsPath)) {
|
|
27
|
-
return agentsPath;
|
|
28
|
-
}
|
|
29
|
-
|
|
30
|
-
if (current === agentsDir) break;
|
|
31
|
-
const parent = dirname(current);
|
|
32
|
-
if (parent === current) break;
|
|
33
|
-
if (parent === "/" || parent === home) break;
|
|
34
|
-
current = parent;
|
|
35
|
-
}
|
|
36
|
-
|
|
37
|
-
return null;
|
|
38
|
-
}
|
|
39
|
-
|
|
40
|
-
function getSessionKey(sessionID: string): string {
|
|
41
|
-
return `api-sec-inject-${sessionID}`;
|
|
42
|
-
}
|
|
43
|
-
|
|
44
|
-
const toolExecuteAfter = async (
|
|
45
|
-
input: { tool: string; sessionID: string; callID: string },
|
|
46
|
-
output: { title: string; output: string; metadata: unknown }
|
|
47
|
-
) => {
|
|
48
|
-
const toolName = input.tool.toLowerCase();
|
|
49
|
-
const agentsDir = resolveAgentsDir();
|
|
50
|
-
|
|
51
|
-
if (!agentsDir || !existsSync(agentsDir)) return;
|
|
52
|
-
|
|
53
|
-
if (toolName === "read") {
|
|
54
|
-
const filePath = output.title;
|
|
55
|
-
if (!filePath) return;
|
|
56
|
-
|
|
57
|
-
const resolved = resolve(filePath);
|
|
58
|
-
const dir = dirname(resolved);
|
|
59
|
-
|
|
60
|
-
if (!dir.includes(agentsDir)) return;
|
|
61
|
-
|
|
62
|
-
const cacheKey = getSessionKey(input.sessionID);
|
|
63
|
-
if (injectedPaths.has(cacheKey + resolved)) return;
|
|
64
|
-
|
|
65
|
-
const agentsPath = findAgentsMdUp(dir, agentsDir);
|
|
66
|
-
if (!agentsPath) return;
|
|
67
|
-
|
|
68
|
-
try {
|
|
69
|
-
const content = readFileSync(agentsPath, "utf-8");
|
|
70
|
-
output.output += `\n\n[Auto-injected from ${AGENTS_FILENAME}]\n${content}`;
|
|
71
|
-
injectedPaths.add(cacheKey + resolved);
|
|
72
|
-
} catch (err) {
|
|
73
|
-
console.error("[api-security-testing] Failed to inject agents:", err);
|
|
74
|
-
}
|
|
75
|
-
}
|
|
76
|
-
};
|
|
77
|
-
|
|
78
|
-
const eventHandler = async (input: { event: { type: string; properties?: unknown } }) => {
|
|
79
|
-
const { event } = input;
|
|
80
|
-
|
|
81
|
-
if (event.type === "session.deleted" || event.type === "session.compacted") {
|
|
82
|
-
const props = event.properties as Record<string, unknown> | undefined;
|
|
83
|
-
let sessionID: string | undefined;
|
|
84
|
-
|
|
85
|
-
if (event.type === "session.deleted") {
|
|
86
|
-
sessionID = (props?.info as { id?: string })?.id;
|
|
87
|
-
} else {
|
|
88
|
-
sessionID = (props?.sessionID ?? (props?.info as { id?: string })?.id) as string | undefined;
|
|
89
|
-
}
|
|
90
|
-
|
|
91
|
-
if (sessionID) {
|
|
92
|
-
const cacheKey = getSessionKey(sessionID);
|
|
93
|
-
for (const key of injectedPaths.keys()) {
|
|
94
|
-
if (key.startsWith(cacheKey)) {
|
|
95
|
-
injectedPaths.delete(key);
|
|
96
|
-
}
|
|
97
|
-
}
|
|
98
|
-
}
|
|
99
|
-
}
|
|
100
|
-
};
|
|
101
|
-
|
|
102
|
-
return {
|
|
103
|
-
"tool.execute.after": toolExecuteAfter,
|
|
104
|
-
event: eventHandler,
|
|
105
|
-
};
|
|
106
|
-
}
|