opencode-api-security-testing 3.0.3 → 4.0.0
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/package.json +6 -21
- package/src/index.ts +84 -301
- package/examples/basic-usage.md +0 -127
- package/postinstall.mjs +0 -130
- package/preuninstall.mjs +0 -89
package/package.json
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "opencode-api-security-testing",
|
|
3
|
-
"version": "
|
|
4
|
-
"description": "API Security Testing Plugin for OpenCode -
|
|
3
|
+
"version": "4.0.0",
|
|
4
|
+
"description": "API Security Testing Plugin for OpenCode - Tools for vulnerability scanning",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"main": "src/index.ts",
|
|
7
7
|
"files": [
|
|
@@ -9,36 +9,21 @@
|
|
|
9
9
|
"agents/",
|
|
10
10
|
"core/",
|
|
11
11
|
"references/",
|
|
12
|
-
"
|
|
13
|
-
"SKILL.md",
|
|
14
|
-
"postinstall.mjs",
|
|
15
|
-
"preuninstall.mjs"
|
|
12
|
+
"SKILL.md"
|
|
16
13
|
],
|
|
17
|
-
"scripts": {
|
|
18
|
-
"postinstall": "node postinstall.mjs",
|
|
19
|
-
"preuninstall": "node preuninstall.mjs"
|
|
20
|
-
},
|
|
21
14
|
"keywords": [
|
|
22
15
|
"opencode",
|
|
23
16
|
"opencode-plugin",
|
|
24
17
|
"security",
|
|
25
|
-
"api-security"
|
|
26
|
-
"pentest",
|
|
27
|
-
"vulnerability-scanning"
|
|
18
|
+
"api-security"
|
|
28
19
|
],
|
|
29
20
|
"author": "steveopen1",
|
|
30
21
|
"license": "MIT",
|
|
31
22
|
"repository": {
|
|
32
23
|
"type": "git",
|
|
33
|
-
"url": "https://github.com/steveopen1/skill-play"
|
|
24
|
+
"url": "git+https://github.com/steveopen1/skill-play.git"
|
|
34
25
|
},
|
|
35
|
-
"homepage": "https://github.com/steveopen1/skill-play/tree/main/agent-plugins/OPENCODE/api-security-testing",
|
|
36
26
|
"peerDependencies": {
|
|
37
|
-
"@opencode-ai/plugin": "^1.1.19"
|
|
38
|
-
"@opencode-ai/sdk": "^1.1.19"
|
|
39
|
-
},
|
|
40
|
-
"dependencies": {
|
|
41
|
-
"@opencode-ai/plugin": "^1.1.19",
|
|
42
|
-
"@opencode-ai/sdk": "^1.1.19"
|
|
27
|
+
"@opencode-ai/plugin": "^1.1.19"
|
|
43
28
|
}
|
|
44
29
|
}
|
package/src/index.ts
CHANGED
|
@@ -1,392 +1,175 @@
|
|
|
1
1
|
import type { Plugin } from "@opencode-ai/plugin";
|
|
2
2
|
import { tool } from "@opencode-ai/plugin";
|
|
3
|
-
import type { AgentConfig } from "@opencode-ai/sdk";
|
|
4
|
-
import { join } from "path";
|
|
5
|
-
import { existsSync } from "fs";
|
|
6
3
|
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
function getCorePath(ctx: { directory: string }): string {
|
|
11
|
-
return join(ctx.directory, CORE_DIR);
|
|
12
|
-
}
|
|
13
|
-
|
|
14
|
-
function checkAndInstallDeps(ctx: { directory: string }): string {
|
|
15
|
-
const corePath = getCorePath(ctx);
|
|
16
|
-
const reqFile = join(corePath, "..", "requirements.txt");
|
|
17
|
-
if (existsSync(reqFile)) {
|
|
18
|
-
return `pip install -q -r "${reqFile}" 2>/dev/null || pip install -q requests beautifulsoup4 playwright 2>/dev/null; `;
|
|
19
|
-
}
|
|
20
|
-
return "";
|
|
21
|
-
}
|
|
22
|
-
|
|
23
|
-
async function execPython(ctx: { directory: string }, script: string, timeout = 60): Promise<string> {
|
|
24
|
-
const corePath = getCorePath(ctx);
|
|
25
|
-
const deps = checkAndInstallDeps(ctx);
|
|
26
|
-
|
|
27
|
-
try {
|
|
28
|
-
const result = await ctx.$`${deps}timeout ${timeout} python3 -c ${script}`;
|
|
29
|
-
return result.toString();
|
|
30
|
-
} catch (error) {
|
|
31
|
-
const errorMessage = error instanceof Error ? error.message : String(error);
|
|
32
|
-
if (errorMessage.includes("timeout")) {
|
|
33
|
-
return `错误: 执行超时 (${timeout}秒)。目标可能无响应或需要更长时间。`;
|
|
34
|
-
}
|
|
35
|
-
if (errorMessage.includes("ModuleNotFoundError")) {
|
|
36
|
-
return `错误: 缺少 Python 依赖模块。请运行: pip install -r requirements.txt`;
|
|
37
|
-
}
|
|
38
|
-
return `错误: ${errorMessage}`;
|
|
39
|
-
}
|
|
4
|
+
function getCorePath(directory: string): string {
|
|
5
|
+
return `${directory}/skills/api-security-testing/core`;
|
|
40
6
|
}
|
|
41
7
|
|
|
42
|
-
const CYBER_SUPERVISOR_PROMPT = `你是 API 安全测试的**赛博监工**,代号"P9"。
|
|
43
|
-
|
|
44
|
-
## 职责
|
|
45
|
-
|
|
46
|
-
1. **永不停止** - 任何线索都要追到底
|
|
47
|
-
2. **自动化编排** - 不等待用户,主动推进
|
|
48
|
-
3. **智能委派** - 识别任务类型,委派给最合适的子 agent
|
|
49
|
-
4. **压力升级** - 遇到失败自动换方法 (L1-L4)
|
|
50
|
-
|
|
51
|
-
## 可用子 Agent
|
|
52
|
-
|
|
53
|
-
| 子 Agent | 职责 |
|
|
54
|
-
|---------|------|
|
|
55
|
-
| @api-probing-miner | 漏洞挖掘 |
|
|
56
|
-
| @api-resource-specialist | 端点发现 |
|
|
57
|
-
| @api-vuln-verifier | 漏洞验证 |
|
|
58
|
-
|
|
59
|
-
## 可用工具
|
|
60
|
-
|
|
61
|
-
| 工具 | 用途 |
|
|
62
|
-
|------|------|
|
|
63
|
-
| api_security_scan | 完整扫描 |
|
|
64
|
-
| api_fuzz_test | 模糊测试 |
|
|
65
|
-
| browser_collect | 浏览器采集 |
|
|
66
|
-
| js_parse | JS分析 |
|
|
67
|
-
| graphql_test | GraphQL测试 |
|
|
68
|
-
| cloud_storage_test | 云存储测试 |
|
|
69
|
-
| vuln_verify | 漏洞验证 |
|
|
70
|
-
| sqli_test | SQL注入测试 |
|
|
71
|
-
| idor_test | IDOR测试 |
|
|
72
|
-
| auth_test | 认证测试
|
|
73
|
-
|
|
74
|
-
## 工作流程
|
|
75
|
-
|
|
76
|
-
1. 使用 browser_collect 采集端点
|
|
77
|
-
2. 使用 js_parse 分析 JS 文件
|
|
78
|
-
3. 使用 api_security_scan 进行全面扫描
|
|
79
|
-
4. 使用特定工具进行针对性测试
|
|
80
|
-
|
|
81
|
-
## 输出格式
|
|
82
|
-
|
|
83
|
-
\`\`\`markdown
|
|
84
|
-
## 安全测试报告
|
|
85
|
-
|
|
86
|
-
### 目标
|
|
87
|
-
- URL: {target}
|
|
88
|
-
|
|
89
|
-
### 发现漏洞
|
|
90
|
-
| 类型 | 端点 | 严重程度 |
|
|
91
|
-
|------|------|---------|
|
|
92
|
-
| SQL注入 | /api/user?id=1 | HIGH |
|
|
93
|
-
\`\`\`
|
|
94
|
-
`;
|
|
95
|
-
|
|
96
|
-
const PROBING_MINER_PROMPT = `你是**API漏洞挖掘专家**,专注于发现和验证安全漏洞。
|
|
97
|
-
|
|
98
|
-
## 职责
|
|
99
|
-
|
|
100
|
-
1. **针对性测试** - 根据端点特征选择最佳测试方法
|
|
101
|
-
2. **快速验证** - 确认漏洞存在
|
|
102
|
-
3. **PoC 生成** - 提供可执行的测试命令
|
|
103
|
-
|
|
104
|
-
## 测试方法库
|
|
105
|
-
|
|
106
|
-
### SQL 注入
|
|
107
|
-
- 布尔盲注: ' OR 1=1 --
|
|
108
|
-
- 联合查询: ' UNION SELECT NULL--
|
|
109
|
-
- 错误注入: ' AND 1=CONVERT(int,...)--
|
|
110
|
-
- 时间盲注: '; WAITFOR DELAY '00:00:05'--
|
|
111
|
-
|
|
112
|
-
### IDOR
|
|
113
|
-
- 替换 ID: /api/user/1 → /api/user/2
|
|
114
|
-
- 水平/垂直越权测试
|
|
115
|
-
|
|
116
|
-
### JWT
|
|
117
|
-
- 空算法: alg: none
|
|
118
|
-
- 密钥混淆: HS256 → HS512`;
|
|
119
|
-
|
|
120
|
-
const RESOURCE_SPECIALIST_PROMPT = `你是**API资源探测专家**,专注于发现和采集 API 端点。
|
|
121
|
-
|
|
122
|
-
## 职责
|
|
123
|
-
|
|
124
|
-
1. **全面发现** - 不遗漏任何端点
|
|
125
|
-
2. **动态采集** - 拦截真实请求
|
|
126
|
-
3. **静态分析** - 提取 API 模式
|
|
127
|
-
|
|
128
|
-
## 采集技术
|
|
129
|
-
|
|
130
|
-
### 1. 浏览器动态采集
|
|
131
|
-
使用 browser_collect 拦截 XHR/Fetch 请求
|
|
132
|
-
|
|
133
|
-
### 2. JS 静态分析
|
|
134
|
-
使用 js_parse 解析 JS 文件
|
|
135
|
-
|
|
136
|
-
### 3. 目录探测
|
|
137
|
-
常见路径: /api/v1/*, /graphql, /swagger, /.well-known/*
|
|
138
|
-
|
|
139
|
-
## 端点分类
|
|
140
|
-
|
|
141
|
-
| 风险 | 类型 | 示例 |
|
|
142
|
-
|------|------|------|
|
|
143
|
-
| 高 | 认证 | /login, /oauth/* |
|
|
144
|
-
| 高 | 数据 | /api/*/list |
|
|
145
|
-
| 中 | 用户 | /users, /profile |
|
|
146
|
-
| 极高 | 管理 | /admin, /manage`;
|
|
147
|
-
|
|
148
|
-
const VULN_VERIFIER_PROMPT = `你是**漏洞验证专家**,专注于验证和确认安全漏洞。
|
|
149
|
-
|
|
150
|
-
## 职责
|
|
151
|
-
|
|
152
|
-
1. **快速验证** - 确认漏洞是否存在
|
|
153
|
-
2. **风险评估** - 判断实际影响
|
|
154
|
-
3. **PoC 生成** - 提供可执行的证明
|
|
155
|
-
|
|
156
|
-
## 验证流程
|
|
157
|
-
|
|
158
|
-
1. 构造 payload
|
|
159
|
-
2. 发送测试请求
|
|
160
|
-
3. 分析响应
|
|
161
|
-
4. 判断结果
|
|
162
|
-
5. 生成 PoC`;
|
|
163
|
-
|
|
164
8
|
const ApiSecurityTestingPlugin: Plugin = async (ctx) => {
|
|
165
|
-
console.log("[api-security-testing] Plugin loaded, version: 3.0.2");
|
|
166
|
-
|
|
167
9
|
return {
|
|
168
10
|
tool: {
|
|
169
11
|
api_security_scan: tool({
|
|
170
|
-
description: "完整 API
|
|
12
|
+
description: "完整 API 安全扫描",
|
|
171
13
|
args: {
|
|
172
14
|
target: tool.schema.string(),
|
|
173
15
|
scan_type: tool.schema.enum(["full", "quick", "targeted"]).optional(),
|
|
174
16
|
},
|
|
175
17
|
async execute(args, ctx) {
|
|
18
|
+
const corePath = getCorePath(ctx.directory);
|
|
176
19
|
const script = `
|
|
177
20
|
import sys
|
|
178
|
-
sys.path.insert(0, '${
|
|
179
|
-
|
|
180
|
-
|
|
181
|
-
|
|
182
|
-
print(
|
|
21
|
+
sys.path.insert(0, '${corePath}')
|
|
22
|
+
try:
|
|
23
|
+
from deep_api_tester_v55 import DeepAPITesterV55
|
|
24
|
+
tester = DeepAPITesterV55(target='${args.target}', headless=True)
|
|
25
|
+
print(tester.run_test())
|
|
26
|
+
except Exception as e:
|
|
27
|
+
print(f"Error: {e}")
|
|
183
28
|
`;
|
|
184
|
-
|
|
29
|
+
const result = await ctx.$`python3 -c ${script}`;
|
|
30
|
+
return result.toString();
|
|
185
31
|
},
|
|
186
32
|
}),
|
|
187
33
|
|
|
188
34
|
api_fuzz_test: tool({
|
|
189
|
-
description: "API
|
|
35
|
+
description: "API 模糊测试",
|
|
190
36
|
args: {
|
|
191
37
|
endpoint: tool.schema.string(),
|
|
192
38
|
method: tool.schema.enum(["GET", "POST", "PUT", "DELETE", "PATCH"]).optional(),
|
|
193
39
|
},
|
|
194
40
|
async execute(args, ctx) {
|
|
41
|
+
const corePath = getCorePath(ctx.directory);
|
|
195
42
|
const script = `
|
|
196
43
|
import sys
|
|
197
|
-
sys.path.insert(0, '${
|
|
198
|
-
|
|
199
|
-
|
|
200
|
-
|
|
201
|
-
print(
|
|
44
|
+
sys.path.insert(0, '${corePath}')
|
|
45
|
+
try:
|
|
46
|
+
from api_fuzzer import APIFuzzer
|
|
47
|
+
fuzzer = APIFuzzer('${args.endpoint}')
|
|
48
|
+
print(fuzzer.fuzz(method='${args.method || "GET"}'))
|
|
49
|
+
except Exception as e:
|
|
50
|
+
print(f"Error: {e}")
|
|
202
51
|
`;
|
|
203
|
-
|
|
52
|
+
const result = await ctx.$`python3 -c ${script}`;
|
|
53
|
+
return result.toString();
|
|
204
54
|
},
|
|
205
55
|
}),
|
|
206
56
|
|
|
207
57
|
vuln_verify: tool({
|
|
208
|
-
description: "
|
|
58
|
+
description: "漏洞验证",
|
|
209
59
|
args: {
|
|
210
60
|
vuln_type: tool.schema.string(),
|
|
211
61
|
endpoint: tool.schema.string(),
|
|
212
62
|
},
|
|
213
63
|
async execute(args, ctx) {
|
|
64
|
+
const corePath = getCorePath(ctx.directory);
|
|
214
65
|
const script = `
|
|
215
66
|
import sys
|
|
216
|
-
sys.path.insert(0, '${
|
|
217
|
-
|
|
218
|
-
|
|
219
|
-
|
|
220
|
-
print(
|
|
67
|
+
sys.path.insert(0, '${corePath}')
|
|
68
|
+
try:
|
|
69
|
+
from verifiers.vuln_verifier import VulnVerifier
|
|
70
|
+
verifier = VulnVerifier()
|
|
71
|
+
print(verifier.verify('${args.vuln_type}', '${args.endpoint}'))
|
|
72
|
+
except Exception as e:
|
|
73
|
+
print(f"Error: {e}")
|
|
221
74
|
`;
|
|
222
|
-
|
|
75
|
+
const result = await ctx.$`python3 -c ${script}`;
|
|
76
|
+
return result.toString();
|
|
223
77
|
},
|
|
224
78
|
}),
|
|
225
79
|
|
|
226
80
|
browser_collect: tool({
|
|
227
|
-
description: "
|
|
81
|
+
description: "浏览器采集动态内容",
|
|
228
82
|
args: {
|
|
229
83
|
url: tool.schema.string(),
|
|
230
84
|
},
|
|
231
85
|
async execute(args, ctx) {
|
|
86
|
+
const corePath = getCorePath(ctx.directory);
|
|
232
87
|
const script = `
|
|
233
88
|
import sys
|
|
234
|
-
sys.path.insert(0, '${
|
|
235
|
-
|
|
236
|
-
|
|
237
|
-
|
|
238
|
-
|
|
239
|
-
|
|
240
|
-
|
|
241
|
-
|
|
242
|
-
|
|
243
|
-
|
|
244
|
-
}),
|
|
245
|
-
|
|
246
|
-
js_parse: tool({
|
|
247
|
-
description: "解析 JavaScript 文件。参数: file_path(文件路径)",
|
|
248
|
-
args: {
|
|
249
|
-
file_path: tool.schema.string(),
|
|
250
|
-
},
|
|
251
|
-
async execute(args, ctx) {
|
|
252
|
-
const script = `
|
|
253
|
-
import sys
|
|
254
|
-
sys.path.insert(0, '${getCorePath(ctx)}')
|
|
255
|
-
from collectors.js_parser import JSParser
|
|
256
|
-
parser = JSParser()
|
|
257
|
-
endpoints = parser.parse_file('${args.file_path}')
|
|
258
|
-
print(f'从 JS 发现 {len(endpoints)} 个端点')
|
|
89
|
+
sys.path.insert(0, '${corePath}')
|
|
90
|
+
try:
|
|
91
|
+
from collectors.browser_collect import BrowserCollector
|
|
92
|
+
collector = BrowserCollector(headless=True)
|
|
93
|
+
endpoints = collector.collect('${args.url}')
|
|
94
|
+
print(f'发现 {len(endpoints)} 个端点')
|
|
95
|
+
for ep in endpoints:
|
|
96
|
+
print(ep)
|
|
97
|
+
except Exception as e:
|
|
98
|
+
print(f"Error: {e}")
|
|
259
99
|
`;
|
|
260
|
-
|
|
100
|
+
const result = await ctx.$`python3 -c ${script}`;
|
|
101
|
+
return result.toString();
|
|
261
102
|
},
|
|
262
103
|
}),
|
|
263
104
|
|
|
264
105
|
graphql_test: tool({
|
|
265
|
-
description: "GraphQL
|
|
106
|
+
description: "GraphQL 安全测试",
|
|
266
107
|
args: {
|
|
267
108
|
endpoint: tool.schema.string(),
|
|
268
109
|
},
|
|
269
110
|
async execute(args, ctx) {
|
|
111
|
+
const corePath = getCorePath(ctx.directory);
|
|
270
112
|
const script = `
|
|
271
113
|
import sys
|
|
272
|
-
sys.path.insert(0, '${
|
|
273
|
-
|
|
274
|
-
|
|
275
|
-
|
|
276
|
-
print(
|
|
114
|
+
sys.path.insert(0, '${corePath}')
|
|
115
|
+
try:
|
|
116
|
+
from smart_analyzer import SmartAnalyzer
|
|
117
|
+
analyzer = SmartAnalyzer()
|
|
118
|
+
print(analyzer.graphql_test('${args.endpoint}'))
|
|
119
|
+
except Exception as e:
|
|
120
|
+
print(f"Error: {e}")
|
|
277
121
|
`;
|
|
278
|
-
|
|
279
|
-
|
|
280
|
-
}),
|
|
281
|
-
|
|
282
|
-
cloud_storage_test: tool({
|
|
283
|
-
description: "云存储安全测试。参数: bucket_url(存储桶URL)",
|
|
284
|
-
args: {
|
|
285
|
-
bucket_url: tool.schema.string(),
|
|
286
|
-
},
|
|
287
|
-
async execute(args, ctx) {
|
|
288
|
-
const script = `
|
|
289
|
-
import sys
|
|
290
|
-
sys.path.insert(0, '${getCorePath(ctx)}')
|
|
291
|
-
from cloud_storage_tester import CloudStorageTester
|
|
292
|
-
tester = CloudStorageTester()
|
|
293
|
-
result = tester.full_test('${args.bucket_url}')
|
|
294
|
-
print(result)
|
|
295
|
-
`;
|
|
296
|
-
return await execPython(ctx, script, 60);
|
|
297
|
-
},
|
|
298
|
-
}),
|
|
299
|
-
|
|
300
|
-
idor_test: tool({
|
|
301
|
-
description: "IDOR 越权测试。参数: endpoint, resource_id",
|
|
302
|
-
args: {
|
|
303
|
-
endpoint: tool.schema.string(),
|
|
304
|
-
resource_id: tool.schema.string(),
|
|
305
|
-
},
|
|
306
|
-
async execute(args, ctx) {
|
|
307
|
-
const script = `
|
|
308
|
-
import sys
|
|
309
|
-
sys.path.insert(0, '${getCorePath(ctx)}')
|
|
310
|
-
from testers.idor_tester import IDORTester
|
|
311
|
-
tester = IDORTester()
|
|
312
|
-
result = tester.test('${args.endpoint}', '${args.resource_id}')
|
|
313
|
-
print(result)
|
|
314
|
-
`;
|
|
315
|
-
return await execPython(ctx, script, 30);
|
|
122
|
+
const result = await ctx.$`python3 -c ${script}`;
|
|
123
|
+
return result.toString();
|
|
316
124
|
},
|
|
317
125
|
}),
|
|
318
126
|
|
|
319
127
|
sqli_test: tool({
|
|
320
|
-
description: "SQL
|
|
128
|
+
description: "SQL 注入测试",
|
|
321
129
|
args: {
|
|
322
130
|
endpoint: tool.schema.string(),
|
|
323
131
|
param: tool.schema.string(),
|
|
324
132
|
},
|
|
325
133
|
async execute(args, ctx) {
|
|
134
|
+
const corePath = getCorePath(ctx.directory);
|
|
326
135
|
const script = `
|
|
327
136
|
import sys
|
|
328
|
-
sys.path.insert(0, '${
|
|
329
|
-
|
|
330
|
-
|
|
331
|
-
|
|
332
|
-
print(
|
|
137
|
+
sys.path.insert(0, '${corePath}')
|
|
138
|
+
try:
|
|
139
|
+
from testers.sqli_tester import SQLiTester
|
|
140
|
+
tester = SQLiTester()
|
|
141
|
+
print(tester.test('${args.endpoint}', '${args.param}'))
|
|
142
|
+
except Exception as e:
|
|
143
|
+
print(f"Error: {e}")
|
|
333
144
|
`;
|
|
334
|
-
|
|
145
|
+
const result = await ctx.$`python3 -c ${script}`;
|
|
146
|
+
return result.toString();
|
|
335
147
|
},
|
|
336
148
|
}),
|
|
337
149
|
|
|
338
|
-
|
|
339
|
-
description: "
|
|
150
|
+
idor_test: tool({
|
|
151
|
+
description: "IDOR 越权测试",
|
|
340
152
|
args: {
|
|
341
153
|
endpoint: tool.schema.string(),
|
|
154
|
+
resource_id: tool.schema.string(),
|
|
342
155
|
},
|
|
343
156
|
async execute(args, ctx) {
|
|
157
|
+
const corePath = getCorePath(ctx.directory);
|
|
344
158
|
const script = `
|
|
345
159
|
import sys
|
|
346
|
-
sys.path.insert(0, '${
|
|
347
|
-
|
|
348
|
-
|
|
349
|
-
|
|
350
|
-
print(
|
|
160
|
+
sys.path.insert(0, '${corePath}')
|
|
161
|
+
try:
|
|
162
|
+
from testers.idor_tester import IDORTester
|
|
163
|
+
tester = IDORTester()
|
|
164
|
+
print(tester.test('${args.endpoint}', '${args.resource_id}'))
|
|
165
|
+
except Exception as e:
|
|
166
|
+
print(f"Error: {e}")
|
|
351
167
|
`;
|
|
352
|
-
|
|
168
|
+
const result = await ctx.$`python3 -c ${script}`;
|
|
169
|
+
return result.toString();
|
|
353
170
|
},
|
|
354
171
|
}),
|
|
355
172
|
},
|
|
356
|
-
|
|
357
|
-
config: async (config) => {
|
|
358
|
-
if (!config.agent) {
|
|
359
|
-
config.agent = {};
|
|
360
|
-
}
|
|
361
|
-
|
|
362
|
-
const agents = config.agent as Record<string, AgentConfig>;
|
|
363
|
-
|
|
364
|
-
agents["api-cyber-supervisor"] = {
|
|
365
|
-
description: "API安全测试编排者。协调完整扫描流程,永不停止。",
|
|
366
|
-
mode: "primary",
|
|
367
|
-
prompt: CYBER_SUPERVISOR_PROMPT,
|
|
368
|
-
};
|
|
369
|
-
|
|
370
|
-
agents["api-probing-miner"] = {
|
|
371
|
-
description: "漏洞挖掘专家。专注发现和验证 API 漏洞。",
|
|
372
|
-
mode: "subagent",
|
|
373
|
-
prompt: PROBING_MINER_PROMPT,
|
|
374
|
-
};
|
|
375
|
-
|
|
376
|
-
agents["api-resource-specialist"] = {
|
|
377
|
-
description: "资源探测专家。专注采集和发现 API 端点。",
|
|
378
|
-
mode: "subagent",
|
|
379
|
-
prompt: RESOURCE_SPECIALIST_PROMPT,
|
|
380
|
-
};
|
|
381
|
-
|
|
382
|
-
agents["api-vuln-verifier"] = {
|
|
383
|
-
description: "漏洞验证专家。验证和确认安全漏洞。",
|
|
384
|
-
mode: "subagent",
|
|
385
|
-
prompt: VULN_VERIFIER_PROMPT,
|
|
386
|
-
};
|
|
387
|
-
|
|
388
|
-
console.log("[api-security-testing] Agents registered:", Object.keys(agents));
|
|
389
|
-
},
|
|
390
173
|
};
|
|
391
174
|
};
|
|
392
175
|
|
package/examples/basic-usage.md
DELETED
|
@@ -1,127 +0,0 @@
|
|
|
1
|
-
# API Security Testing Examples
|
|
2
|
-
|
|
3
|
-
## 快速开始
|
|
4
|
-
|
|
5
|
-
### 1. 基础扫描
|
|
6
|
-
|
|
7
|
-
```
|
|
8
|
-
@api-cyber-supervisor 对 https://example.com 进行全面安全扫描
|
|
9
|
-
```
|
|
10
|
-
|
|
11
|
-
### 2. 针对特定端点扫描
|
|
12
|
-
|
|
13
|
-
```
|
|
14
|
-
@api-cyber-supervisor 扫描 https://api.example.com/v1 端点
|
|
15
|
-
```
|
|
16
|
-
|
|
17
|
-
### 3. 使用工具直接扫描
|
|
18
|
-
|
|
19
|
-
```
|
|
20
|
-
api_security_scan target="https://api.example.com" scan_type="full"
|
|
21
|
-
```
|
|
22
|
-
|
|
23
|
-
---
|
|
24
|
-
|
|
25
|
-
## 工具使用示例
|
|
26
|
-
|
|
27
|
-
### 浏览器采集
|
|
28
|
-
|
|
29
|
-
```
|
|
30
|
-
browser_collect url="https://example.com"
|
|
31
|
-
```
|
|
32
|
-
|
|
33
|
-
### SQL 注入测试
|
|
34
|
-
|
|
35
|
-
```
|
|
36
|
-
sqli_test endpoint="https://api.example.com/user?id=1" param="id"
|
|
37
|
-
```
|
|
38
|
-
|
|
39
|
-
### IDOR 测试
|
|
40
|
-
|
|
41
|
-
```
|
|
42
|
-
idor_test endpoint="https://api.example.com/user/1" resource_id="1"
|
|
43
|
-
```
|
|
44
|
-
|
|
45
|
-
### GraphQL 测试
|
|
46
|
-
|
|
47
|
-
```
|
|
48
|
-
graphql_test endpoint="https://api.example.com/graphql"
|
|
49
|
-
```
|
|
50
|
-
|
|
51
|
-
### 云存储测试
|
|
52
|
-
|
|
53
|
-
```
|
|
54
|
-
cloud_storage_test bucket_url="https://bucket.s3.amazonaws.com"
|
|
55
|
-
```
|
|
56
|
-
|
|
57
|
-
---
|
|
58
|
-
|
|
59
|
-
## Agent 委派示例
|
|
60
|
-
|
|
61
|
-
### 使用 Cyber Supervisor
|
|
62
|
-
|
|
63
|
-
```
|
|
64
|
-
@api-cyber-supervisor
|
|
65
|
-
对 https://api.example.com 进行完整的安全测试
|
|
66
|
-
包含以下漏洞测试:
|
|
67
|
-
- SQL 注入
|
|
68
|
-
- IDOR 越权
|
|
69
|
-
- JWT 安全
|
|
70
|
-
- 敏感数据泄露
|
|
71
|
-
```
|
|
72
|
-
|
|
73
|
-
### 使用资源探测专家
|
|
74
|
-
|
|
75
|
-
```
|
|
76
|
-
@api-resource-specialist
|
|
77
|
-
发现 https://example.com 的所有 API 端点
|
|
78
|
-
重点关注:
|
|
79
|
-
- 认证相关端点
|
|
80
|
-
- 用户数据端点
|
|
81
|
-
- 支付相关端点
|
|
82
|
-
```
|
|
83
|
-
|
|
84
|
-
### 使用漏洞挖掘专家
|
|
85
|
-
|
|
86
|
-
```
|
|
87
|
-
@api-probing-miner
|
|
88
|
-
针对发现的端点进行漏洞挖掘
|
|
89
|
-
优先测试:
|
|
90
|
-
- SQL 注入
|
|
91
|
-
- XSS
|
|
92
|
-
- IDOR
|
|
93
|
-
```
|
|
94
|
-
|
|
95
|
-
### 使用漏洞验证专家
|
|
96
|
-
|
|
97
|
-
```
|
|
98
|
-
@api-vuln-verifier
|
|
99
|
-
验证以下漏洞:
|
|
100
|
-
- 类型: SQL 注入
|
|
101
|
-
- 端点: https://api.example.com/user?id=1
|
|
102
|
-
```
|
|
103
|
-
|
|
104
|
-
---
|
|
105
|
-
|
|
106
|
-
## 报告输出示例
|
|
107
|
-
|
|
108
|
-
当扫描完成时,会输出类似以下的报告:
|
|
109
|
-
|
|
110
|
-
```markdown
|
|
111
|
-
## 安全测试报告
|
|
112
|
-
|
|
113
|
-
### 目标信息
|
|
114
|
-
- URL: https://api.example.com
|
|
115
|
-
- 端点总数: 42
|
|
116
|
-
- 发现漏洞: 5
|
|
117
|
-
|
|
118
|
-
### 漏洞详情
|
|
119
|
-
| # | 类型 | 端点 | 严重程度 |
|
|
120
|
-
|---|------|------|---------|
|
|
121
|
-
| 1 | SQL注入 | /api/user?id=1 | HIGH |
|
|
122
|
-
| 2 | IDOR | /api/user/:id | MEDIUM |
|
|
123
|
-
| 3 | 敏感数据 | /api/config | LOW |
|
|
124
|
-
|
|
125
|
-
### PoC
|
|
126
|
-
curl "https://api.example.com/api/user?id=1'%20OR%201=1--"
|
|
127
|
-
```
|
package/postinstall.mjs
DELETED
|
@@ -1,130 +0,0 @@
|
|
|
1
|
-
#!/usr/bin/env node
|
|
2
|
-
|
|
3
|
-
/**
|
|
4
|
-
* postinstall.mjs - API Security Testing Plugin
|
|
5
|
-
*
|
|
6
|
-
* Installs:
|
|
7
|
-
* 1. Agents to ~/.config/opencode/agents/
|
|
8
|
-
* 2. Skill (SKILL.md) to ~/.config/opencode/skills/api-security-testing/
|
|
9
|
-
* 3. References to ~/.config/opencode/skills/api-security-testing/
|
|
10
|
-
*/
|
|
11
|
-
|
|
12
|
-
import { copyFileSync, existsSync, mkdirSync, readdirSync } from "node:fs";
|
|
13
|
-
import { join, dirname } from "node:path";
|
|
14
|
-
import { fileURLToPath } from "node:url";
|
|
15
|
-
|
|
16
|
-
const __filename = fileURLToPath(import.meta.url);
|
|
17
|
-
const __dirname = dirname(__filename);
|
|
18
|
-
|
|
19
|
-
function getOpencodeBaseDir() {
|
|
20
|
-
const home = process.env.HOME || process.env.USERPROFILE || "/root";
|
|
21
|
-
return join(home, ".config", "opencode");
|
|
22
|
-
}
|
|
23
|
-
|
|
24
|
-
function main() {
|
|
25
|
-
const packageRoot = __dirname;
|
|
26
|
-
const agentsSourceDir = join(packageRoot, "agents");
|
|
27
|
-
const agentsTargetDir = join(getOpencodeBaseDir(), "agents");
|
|
28
|
-
const skillTargetDir = join(getOpencodeBaseDir(), "skills", "api-security-testing");
|
|
29
|
-
|
|
30
|
-
console.log("[api-security-testing] Installing...");
|
|
31
|
-
console.log(` Package root: ${packageRoot}`);
|
|
32
|
-
console.log(` Target base: ${getOpencodeBaseDir()}`);
|
|
33
|
-
|
|
34
|
-
let successCount = 0;
|
|
35
|
-
let totalFiles = 0;
|
|
36
|
-
|
|
37
|
-
// 1. Install agents
|
|
38
|
-
console.log("\n[1/3] Installing agents...");
|
|
39
|
-
if (existsSync(agentsSourceDir)) {
|
|
40
|
-
const agentFiles = readdirSync(agentsSourceDir).filter(f => f.endsWith(".md"));
|
|
41
|
-
totalFiles += agentFiles.length;
|
|
42
|
-
|
|
43
|
-
if (!existsSync(agentsTargetDir)) {
|
|
44
|
-
mkdirSync(agentsTargetDir, { recursive: true });
|
|
45
|
-
}
|
|
46
|
-
|
|
47
|
-
for (const file of agentFiles) {
|
|
48
|
-
const sourcePath = join(agentsSourceDir, file);
|
|
49
|
-
const targetPath = join(agentsTargetDir, file);
|
|
50
|
-
try {
|
|
51
|
-
copyFileSync(sourcePath, targetPath);
|
|
52
|
-
console.log(` ✓ Installed agent: ${file}`);
|
|
53
|
-
successCount++;
|
|
54
|
-
} catch (err) {
|
|
55
|
-
console.error(` ✗ Failed: ${file} - ${err.message}`);
|
|
56
|
-
}
|
|
57
|
-
}
|
|
58
|
-
} else {
|
|
59
|
-
console.error(" ✗ agents/ directory not found");
|
|
60
|
-
}
|
|
61
|
-
|
|
62
|
-
// 2. Install SKILL.md
|
|
63
|
-
console.log("\n[2/3] Installing skill...");
|
|
64
|
-
const skillSource = join(packageRoot, "SKILL.md");
|
|
65
|
-
const skillTarget = join(skillTargetDir, "SKILL.md");
|
|
66
|
-
totalFiles++;
|
|
67
|
-
|
|
68
|
-
if (existsSync(skillSource)) {
|
|
69
|
-
if (!existsSync(skillTargetDir)) {
|
|
70
|
-
mkdirSync(skillTargetDir, { recursive: true });
|
|
71
|
-
}
|
|
72
|
-
try {
|
|
73
|
-
copyFileSync(skillSource, skillTarget);
|
|
74
|
-
console.log(` ✓ Installed: SKILL.md`);
|
|
75
|
-
successCount++;
|
|
76
|
-
} catch (err) {
|
|
77
|
-
console.error(` ✗ Failed: SKILL.md - ${err.message}`);
|
|
78
|
-
}
|
|
79
|
-
} else {
|
|
80
|
-
console.error(" ✗ SKILL.md not found");
|
|
81
|
-
}
|
|
82
|
-
|
|
83
|
-
// 3. Install references
|
|
84
|
-
console.log("\n[3/3] Installing references...");
|
|
85
|
-
const refsSourceDir = join(packageRoot, "references");
|
|
86
|
-
const refsTargetDir = join(skillTargetDir, "references");
|
|
87
|
-
|
|
88
|
-
if (existsSync(refsSourceDir)) {
|
|
89
|
-
if (!existsSync(refsTargetDir)) {
|
|
90
|
-
mkdirSync(refsTargetDir, { recursive: true });
|
|
91
|
-
}
|
|
92
|
-
|
|
93
|
-
const refFiles = readdirSync(refsSourceDir, { recursive: true }).filter(f => typeof f === "string");
|
|
94
|
-
totalFiles += refFiles.length;
|
|
95
|
-
|
|
96
|
-
for (const file of refFiles) {
|
|
97
|
-
const sourcePath = join(refsSourceDir, file);
|
|
98
|
-
const targetPath = join(refsTargetDir, file);
|
|
99
|
-
const targetFileDir = join(refsTargetDir, file).replace(/\/[^\/]+$/, "");
|
|
100
|
-
if (!existsSync(targetFileDir)) {
|
|
101
|
-
mkdirSync(targetFileDir, { recursive: true });
|
|
102
|
-
}
|
|
103
|
-
try {
|
|
104
|
-
copyFileSync(sourcePath, targetPath);
|
|
105
|
-
console.log(` ✓ Installed: references/${file}`);
|
|
106
|
-
successCount++;
|
|
107
|
-
} catch (err) {
|
|
108
|
-
console.error(` ✗ Failed: references/${file} - ${err.message}`);
|
|
109
|
-
}
|
|
110
|
-
}
|
|
111
|
-
} else {
|
|
112
|
-
console.log(" (references/ not found, skipping)");
|
|
113
|
-
}
|
|
114
|
-
|
|
115
|
-
// Summary
|
|
116
|
-
console.log("\n========================================");
|
|
117
|
-
if (successCount === totalFiles) {
|
|
118
|
-
console.log(`✓ Successfully installed ${successCount} file(s)`);
|
|
119
|
-
console.log(`\nAgent location: ${agentsTargetDir}`);
|
|
120
|
-
console.log(`Skill location: ${skillTargetDir}`);
|
|
121
|
-
console.log("\nTo use:");
|
|
122
|
-
console.log(" @api-cyber-supervisor - Start security testing");
|
|
123
|
-
console.log(" skill({ name: \"api-security-testing\" }) - Load skill");
|
|
124
|
-
} else {
|
|
125
|
-
console.log(`⚠ Partially installed: ${successCount}/${totalFiles}`);
|
|
126
|
-
process.exit(1);
|
|
127
|
-
}
|
|
128
|
-
}
|
|
129
|
-
|
|
130
|
-
main();
|
package/preuninstall.mjs
DELETED
|
@@ -1,89 +0,0 @@
|
|
|
1
|
-
#!/usr/bin/env node
|
|
2
|
-
|
|
3
|
-
/**
|
|
4
|
-
* preuninstall.mjs - API Security Testing Plugin
|
|
5
|
-
*
|
|
6
|
-
* Removes installed files when the package is uninstalled.
|
|
7
|
-
*/
|
|
8
|
-
|
|
9
|
-
import { existsSync, mkdirSync, readdirSync, rmSync } from "node:fs";
|
|
10
|
-
import { join, dirname } from "node:path";
|
|
11
|
-
import { fileURLToPath } from "node:url";
|
|
12
|
-
|
|
13
|
-
const __filename = fileURLToPath(import.meta.url);
|
|
14
|
-
const __dirname = dirname(__filename);
|
|
15
|
-
|
|
16
|
-
function getOpencodeBaseDir() {
|
|
17
|
-
const home = process.env.HOME || process.env.USERPROFILE || "/root";
|
|
18
|
-
return join(home, ".config", "opencode");
|
|
19
|
-
}
|
|
20
|
-
|
|
21
|
-
function deleteFile(filePath) {
|
|
22
|
-
try {
|
|
23
|
-
if (existsSync(filePath)) {
|
|
24
|
-
rmSync(filePath);
|
|
25
|
-
console.log(` ✓ Removed: ${filePath}`);
|
|
26
|
-
return true;
|
|
27
|
-
}
|
|
28
|
-
} catch (err) {
|
|
29
|
-
console.error(` ✗ Failed to remove: ${filePath} - ${err.message}`);
|
|
30
|
-
}
|
|
31
|
-
return false;
|
|
32
|
-
}
|
|
33
|
-
|
|
34
|
-
function deleteDirectory(dirPath) {
|
|
35
|
-
try {
|
|
36
|
-
if (existsSync(dirPath)) {
|
|
37
|
-
rmSync(dirPath, { recursive: true });
|
|
38
|
-
console.log(` ✓ Removed directory: ${dirPath}`);
|
|
39
|
-
return true;
|
|
40
|
-
}
|
|
41
|
-
} catch (err) {
|
|
42
|
-
console.error(` ✗ Failed to remove directory: ${dirPath} - ${err.message}`);
|
|
43
|
-
}
|
|
44
|
-
return false;
|
|
45
|
-
}
|
|
46
|
-
|
|
47
|
-
function main() {
|
|
48
|
-
const baseDir = getOpencodeBaseDir();
|
|
49
|
-
const agentsDir = join(baseDir, "agents");
|
|
50
|
-
const skillDir = join(baseDir, "skills", "api-security-testing");
|
|
51
|
-
|
|
52
|
-
console.log("[api-security-testing] Uninstalling...");
|
|
53
|
-
console.log(` Base directory: ${baseDir}`);
|
|
54
|
-
|
|
55
|
-
let removedCount = 0;
|
|
56
|
-
let totalCount = 0;
|
|
57
|
-
|
|
58
|
-
// 1. Remove agent files
|
|
59
|
-
console.log("\n[1/2] Removing agents...");
|
|
60
|
-
if (existsSync(agentsDir)) {
|
|
61
|
-
const agentFiles = readdirSync(agentsDir).filter(f => f.endsWith(".md"));
|
|
62
|
-
totalCount += agentFiles.length;
|
|
63
|
-
|
|
64
|
-
for (const file of agentFiles) {
|
|
65
|
-
const filePath = join(agentsDir, file);
|
|
66
|
-
// Only remove our agents (prefix with our plugin name)
|
|
67
|
-
if (file.startsWith("api-")) {
|
|
68
|
-
if (deleteFile(filePath)) removedCount++;
|
|
69
|
-
} else {
|
|
70
|
-
console.log(` - Skipped (not our agent): ${file}`);
|
|
71
|
-
}
|
|
72
|
-
}
|
|
73
|
-
}
|
|
74
|
-
|
|
75
|
-
// 2. Remove skill directory
|
|
76
|
-
console.log("\n[2/2] Removing skill...");
|
|
77
|
-
if (existsSync(skillDir)) {
|
|
78
|
-
if (deleteDirectory(skillDir)) {
|
|
79
|
-
removedCount++;
|
|
80
|
-
}
|
|
81
|
-
}
|
|
82
|
-
|
|
83
|
-
// Summary
|
|
84
|
-
console.log("\n========================================");
|
|
85
|
-
console.log(`Uninstall complete.`);
|
|
86
|
-
console.log("\nNote: If you reinstall the package, the postinstall script will re-install the files.");
|
|
87
|
-
}
|
|
88
|
-
|
|
89
|
-
main();
|