opencode-api-security-testing 3.0.2 → 3.0.3

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.
@@ -0,0 +1,127 @@
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/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "opencode-api-security-testing",
3
- "version": "3.0.2",
3
+ "version": "3.0.3",
4
4
  "description": "API Security Testing Plugin for OpenCode - Automated vulnerability scanning and penetration testing",
5
5
  "type": "module",
6
6
  "main": "src/index.ts",
@@ -9,11 +9,14 @@
9
9
  "agents/",
10
10
  "core/",
11
11
  "references/",
12
+ "examples/",
12
13
  "SKILL.md",
13
- "postinstall.mjs"
14
+ "postinstall.mjs",
15
+ "preuninstall.mjs"
14
16
  ],
15
17
  "scripts": {
16
- "postinstall": "node postinstall.mjs"
18
+ "postinstall": "node postinstall.mjs",
19
+ "preuninstall": "node preuninstall.mjs"
17
20
  },
18
21
  "keywords": [
19
22
  "opencode",
@@ -0,0 +1,89 @@
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();
package/src/index.ts CHANGED
@@ -7,23 +7,38 @@ import { existsSync } from "fs";
7
7
  const SKILL_DIR = "skills/api-security-testing";
8
8
  const CORE_DIR = `${SKILL_DIR}/core`;
9
9
 
10
- function getSkillPath(ctx: { directory: string }): string {
11
- return join(ctx.directory, SKILL_DIR);
12
- }
13
-
14
10
  function getCorePath(ctx: { directory: string }): string {
15
11
  return join(ctx.directory, CORE_DIR);
16
12
  }
17
13
 
18
- function checkDeps(ctx: { directory: string }): string {
19
- const skillPath = getSkillPath(ctx);
20
- const reqFile = join(skillPath, "requirements.txt");
14
+ function checkAndInstallDeps(ctx: { directory: string }): string {
15
+ const corePath = getCorePath(ctx);
16
+ const reqFile = join(corePath, "..", "requirements.txt");
21
17
  if (existsSync(reqFile)) {
22
- return `pip install -q -r "${reqFile}" 2>/dev/null; `;
18
+ return `pip install -q -r "${reqFile}" 2>/dev/null || pip install -q requests beautifulsoup4 playwright 2>/dev/null; `;
23
19
  }
24
20
  return "";
25
21
  }
26
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
+ }
40
+ }
41
+
27
42
  const CYBER_SUPERVISOR_PROMPT = `你是 API 安全测试的**赛博监工**,代号"P9"。
28
43
 
29
44
  ## 职责
@@ -54,7 +69,29 @@ const CYBER_SUPERVISOR_PROMPT = `你是 API 安全测试的**赛博监工**,
54
69
  | vuln_verify | 漏洞验证 |
55
70
  | sqli_test | SQL注入测试 |
56
71
  | idor_test | IDOR测试 |
57
- | auth_test | 认证测试`;
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
+ `;
58
95
 
59
96
  const PROBING_MINER_PROMPT = `你是**API漏洞挖掘专家**,专注于发现和验证安全漏洞。
60
97
 
@@ -97,7 +134,16 @@ const RESOURCE_SPECIALIST_PROMPT = `你是**API资源探测专家**,专注于
97
134
  使用 js_parse 解析 JS 文件
98
135
 
99
136
  ### 3. 目录探测
100
- 常见路径: /api/v1/*, /graphql, /swagger, /.well-known/*`;
137
+ 常见路径: /api/v1/*, /graphql, /swagger, /.well-known/*
138
+
139
+ ## 端点分类
140
+
141
+ | 风险 | 类型 | 示例 |
142
+ |------|------|------|
143
+ | 高 | 认证 | /login, /oauth/* |
144
+ | 高 | 数据 | /api/*/list |
145
+ | 中 | 用户 | /users, /profile |
146
+ | 极高 | 管理 | /admin, /manage`;
101
147
 
102
148
  const VULN_VERIFIER_PROMPT = `你是**漏洞验证专家**,专注于验证和确认安全漏洞。
103
149
 
@@ -105,10 +151,18 @@ const VULN_VERIFIER_PROMPT = `你是**漏洞验证专家**,专注于验证和
105
151
 
106
152
  1. **快速验证** - 确认漏洞是否存在
107
153
  2. **风险评估** - 判断实际影响
108
- 3. **PoC 生成** - 提供可执行的证明`;
154
+ 3. **PoC 生成** - 提供可执行的证明
155
+
156
+ ## 验证流程
157
+
158
+ 1. 构造 payload
159
+ 2. 发送测试请求
160
+ 3. 分析响应
161
+ 4. 判断结果
162
+ 5. 生成 PoC`;
109
163
 
110
164
  const ApiSecurityTestingPlugin: Plugin = async (ctx) => {
111
- console.log("[api-security-testing] Plugin loaded");
165
+ console.log("[api-security-testing] Plugin loaded, version: 3.0.2");
112
166
 
113
167
  return {
114
168
  tool: {
@@ -119,18 +173,15 @@ const ApiSecurityTestingPlugin: Plugin = async (ctx) => {
119
173
  scan_type: tool.schema.enum(["full", "quick", "targeted"]).optional(),
120
174
  },
121
175
  async execute(args, ctx) {
122
- const deps = checkDeps(ctx);
123
- const corePath = getCorePath(ctx);
124
- const cmd = `${deps}python3 -c "
176
+ const script = `
125
177
  import sys
126
- sys.path.insert(0, '${corePath}')
178
+ sys.path.insert(0, '${getCorePath(ctx)}')
127
179
  from deep_api_tester_v55 import DeepAPITesterV55
128
180
  tester = DeepAPITesterV55(target='${args.target}', headless=True)
129
181
  results = tester.run_test()
130
182
  print(results)
131
- "`;
132
- const result = await ctx.$`${cmd}`;
133
- return result.toString();
183
+ `;
184
+ return await execPython(ctx, script, 120);
134
185
  },
135
186
  }),
136
187
 
@@ -141,18 +192,15 @@ print(results)
141
192
  method: tool.schema.enum(["GET", "POST", "PUT", "DELETE", "PATCH"]).optional(),
142
193
  },
143
194
  async execute(args, ctx) {
144
- const deps = checkDeps(ctx);
145
- const corePath = getCorePath(ctx);
146
- const cmd = `${deps}python3 -c "
195
+ const script = `
147
196
  import sys
148
- sys.path.insert(0, '${corePath}')
197
+ sys.path.insert(0, '${getCorePath(ctx)}')
149
198
  from api_fuzzer import APIFuzzer
150
199
  fuzzer = APIFuzzer('${args.endpoint}')
151
200
  results = fuzzer.fuzz(method='${args.method || 'GET'}')
152
201
  print(results)
153
- "`;
154
- const result = await ctx.$`${cmd}`;
155
- return result.toString();
202
+ `;
203
+ return await execPython(ctx, script, 60);
156
204
  },
157
205
  }),
158
206
 
@@ -163,18 +211,15 @@ print(results)
163
211
  endpoint: tool.schema.string(),
164
212
  },
165
213
  async execute(args, ctx) {
166
- const deps = checkDeps(ctx);
167
- const corePath = getCorePath(ctx);
168
- const cmd = `${deps}python3 -c "
214
+ const script = `
169
215
  import sys
170
- sys.path.insert(0, '${corePath}')
216
+ sys.path.insert(0, '${getCorePath(ctx)}')
171
217
  from verifiers.vuln_verifier import VulnVerifier
172
218
  verifier = VulnVerifier()
173
219
  result = verifier.verify('${args.vuln_type}', '${args.endpoint}')
174
220
  print(result)
175
- "`;
176
- const result = await ctx.$`${cmd}`;
177
- return result.toString();
221
+ `;
222
+ return await execPython(ctx, script, 30);
178
223
  },
179
224
  }),
180
225
 
@@ -184,20 +229,17 @@ print(result)
184
229
  url: tool.schema.string(),
185
230
  },
186
231
  async execute(args, ctx) {
187
- const deps = checkDeps(ctx);
188
- const corePath = getCorePath(ctx);
189
- const cmd = `${deps}python3 -c "
232
+ const script = `
190
233
  import sys
191
- sys.path.insert(0, '${corePath}')
234
+ sys.path.insert(0, '${getCorePath(ctx)}')
192
235
  from collectors.browser_collect import BrowserCollector
193
236
  collector = BrowserCollector(headless=True)
194
237
  endpoints = collector.collect('${args.url}')
195
238
  print(f'发现 {len(endpoints)} 个端点:')
196
239
  for ep in endpoints:
197
240
  print(ep)
198
- "`;
199
- const result = await ctx.$`${cmd}`;
200
- return result.toString();
241
+ `;
242
+ return await execPython(ctx, script, 90);
201
243
  },
202
244
  }),
203
245
 
@@ -207,18 +249,15 @@ for ep in endpoints:
207
249
  file_path: tool.schema.string(),
208
250
  },
209
251
  async execute(args, ctx) {
210
- const deps = checkDeps(ctx);
211
- const corePath = getCorePath(ctx);
212
- const cmd = `${deps}python3 -c "
252
+ const script = `
213
253
  import sys
214
- sys.path.insert(0, '${corePath}')
254
+ sys.path.insert(0, '${getCorePath(ctx)}')
215
255
  from collectors.js_parser import JSParser
216
256
  parser = JSParser()
217
257
  endpoints = parser.parse_file('${args.file_path}')
218
258
  print(f'从 JS 发现 {len(endpoints)} 个端点')
219
- "`;
220
- const result = await ctx.$`${cmd}`;
221
- return result.toString();
259
+ `;
260
+ return await execPython(ctx, script, 30);
222
261
  },
223
262
  }),
224
263
 
@@ -228,18 +267,15 @@ print(f'从 JS 发现 {len(endpoints)} 个端点')
228
267
  endpoint: tool.schema.string(),
229
268
  },
230
269
  async execute(args, ctx) {
231
- const deps = checkDeps(ctx);
232
- const corePath = getCorePath(ctx);
233
- const cmd = `${deps}python3 -c "
270
+ const script = `
234
271
  import sys
235
- sys.path.insert(0, '${corePath}')
272
+ sys.path.insert(0, '${getCorePath(ctx)}')
236
273
  from smart_analyzer import SmartAnalyzer
237
274
  analyzer = SmartAnalyzer()
238
275
  result = analyzer.graphql_test('${args.endpoint}')
239
276
  print(result)
240
- "`;
241
- const result = await ctx.$`${cmd}`;
242
- return result.toString();
277
+ `;
278
+ return await execPython(ctx, script, 60);
243
279
  },
244
280
  }),
245
281
 
@@ -249,18 +285,15 @@ print(result)
249
285
  bucket_url: tool.schema.string(),
250
286
  },
251
287
  async execute(args, ctx) {
252
- const deps = checkDeps(ctx);
253
- const corePath = getCorePath(ctx);
254
- const cmd = `${deps}python3 -c "
288
+ const script = `
255
289
  import sys
256
- sys.path.insert(0, '${corePath}')
290
+ sys.path.insert(0, '${getCorePath(ctx)}')
257
291
  from cloud_storage_tester import CloudStorageTester
258
292
  tester = CloudStorageTester()
259
293
  result = tester.full_test('${args.bucket_url}')
260
294
  print(result)
261
- "`;
262
- const result = await ctx.$`${cmd}`;
263
- return result.toString();
295
+ `;
296
+ return await execPython(ctx, script, 60);
264
297
  },
265
298
  }),
266
299
 
@@ -271,18 +304,15 @@ print(result)
271
304
  resource_id: tool.schema.string(),
272
305
  },
273
306
  async execute(args, ctx) {
274
- const deps = checkDeps(ctx);
275
- const corePath = getCorePath(ctx);
276
- const cmd = `${deps}python3 -c "
307
+ const script = `
277
308
  import sys
278
- sys.path.insert(0, '${corePath}')
309
+ sys.path.insert(0, '${getCorePath(ctx)}')
279
310
  from testers.idor_tester import IDORTester
280
311
  tester = IDORTester()
281
312
  result = tester.test('${args.endpoint}', '${args.resource_id}')
282
313
  print(result)
283
- "`;
284
- const result = await ctx.$`${cmd}`;
285
- return result.toString();
314
+ `;
315
+ return await execPython(ctx, script, 30);
286
316
  },
287
317
  }),
288
318
 
@@ -293,18 +323,15 @@ print(result)
293
323
  param: tool.schema.string(),
294
324
  },
295
325
  async execute(args, ctx) {
296
- const deps = checkDeps(ctx);
297
- const corePath = getCorePath(ctx);
298
- const cmd = `${deps}python3 -c "
326
+ const script = `
299
327
  import sys
300
- sys.path.insert(0, '${corePath}')
328
+ sys.path.insert(0, '${getCorePath(ctx)}')
301
329
  from testers.sqli_tester import SQLiTester
302
330
  tester = SQLiTester()
303
331
  result = tester.test('${args.endpoint}', '${args.param}')
304
332
  print(result)
305
- "`;
306
- const result = await ctx.$`${cmd}`;
307
- return result.toString();
333
+ `;
334
+ return await execPython(ctx, script, 30);
308
335
  },
309
336
  }),
310
337
 
@@ -314,18 +341,15 @@ print(result)
314
341
  endpoint: tool.schema.string(),
315
342
  },
316
343
  async execute(args, ctx) {
317
- const deps = checkDeps(ctx);
318
- const corePath = getCorePath(ctx);
319
- const cmd = `${deps}python3 -c "
344
+ const script = `
320
345
  import sys
321
- sys.path.insert(0, '${corePath}')
346
+ sys.path.insert(0, '${getCorePath(ctx)}')
322
347
  from testers.auth_tester import AuthTester
323
348
  tester = AuthTester()
324
349
  result = tester.test('${args.endpoint}')
325
350
  print(result)
326
- "`;
327
- const result = await ctx.$`${cmd}`;
328
- return result.toString();
351
+ `;
352
+ return await execPython(ctx, script, 30);
329
353
  },
330
354
  }),
331
355
  },