opencode-api-security-testing 3.0.3 → 3.0.4
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 +3 -6
- package/postinstall.mjs +48 -59
- package/src/index.ts +83 -107
- package/examples/basic-usage.md +0 -127
- package/preuninstall.mjs +0 -89
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "opencode-api-security-testing",
|
|
3
|
-
"version": "3.0.
|
|
3
|
+
"version": "3.0.4",
|
|
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,14 +9,11 @@
|
|
|
9
9
|
"agents/",
|
|
10
10
|
"core/",
|
|
11
11
|
"references/",
|
|
12
|
-
"examples/",
|
|
13
12
|
"SKILL.md",
|
|
14
|
-
"postinstall.mjs"
|
|
15
|
-
"preuninstall.mjs"
|
|
13
|
+
"postinstall.mjs"
|
|
16
14
|
],
|
|
17
15
|
"scripts": {
|
|
18
|
-
"postinstall": "node postinstall.mjs"
|
|
19
|
-
"preuninstall": "node preuninstall.mjs"
|
|
16
|
+
"postinstall": "node postinstall.mjs"
|
|
20
17
|
},
|
|
21
18
|
"keywords": [
|
|
22
19
|
"opencode",
|
package/postinstall.mjs
CHANGED
|
@@ -4,17 +4,16 @@
|
|
|
4
4
|
* postinstall.mjs - API Security Testing Plugin
|
|
5
5
|
*
|
|
6
6
|
* Installs:
|
|
7
|
-
* 1.
|
|
8
|
-
* 2.
|
|
9
|
-
* 3. References to ~/.config/opencode/skills/api-security-testing/
|
|
7
|
+
* 1. agents to ~/.config/opencode/agents/
|
|
8
|
+
* 2. SKILL.md and references to ~/.config/opencode/skills/api-security-testing/
|
|
10
9
|
*/
|
|
11
10
|
|
|
12
11
|
import { copyFileSync, existsSync, mkdirSync, readdirSync } from "node:fs";
|
|
13
|
-
import { join
|
|
12
|
+
import { join } from "node:path";
|
|
14
13
|
import { fileURLToPath } from "node:url";
|
|
15
14
|
|
|
16
15
|
const __filename = fileURLToPath(import.meta.url);
|
|
17
|
-
const __dirname =
|
|
16
|
+
const __dirname = join(__filename, "..");
|
|
18
17
|
|
|
19
18
|
function getOpencodeBaseDir() {
|
|
20
19
|
const home = process.env.HOME || process.env.USERPROFILE || "/root";
|
|
@@ -29,100 +28,90 @@ function main() {
|
|
|
29
28
|
|
|
30
29
|
console.log("[api-security-testing] Installing...");
|
|
31
30
|
console.log(` Package root: ${packageRoot}`);
|
|
32
|
-
console.log(` Target base: ${getOpencodeBaseDir()}`);
|
|
33
31
|
|
|
34
|
-
let
|
|
35
|
-
let
|
|
32
|
+
let totalInstalled = 0;
|
|
33
|
+
let totalFailed = 0;
|
|
36
34
|
|
|
37
35
|
// 1. Install agents
|
|
38
36
|
console.log("\n[1/3] Installing agents...");
|
|
39
37
|
if (existsSync(agentsSourceDir)) {
|
|
40
|
-
const agentFiles = readdirSync(agentsSourceDir).filter(f => f.endsWith(".md"));
|
|
41
|
-
totalFiles += agentFiles.length;
|
|
42
|
-
|
|
43
38
|
if (!existsSync(agentsTargetDir)) {
|
|
44
39
|
mkdirSync(agentsTargetDir, { recursive: true });
|
|
45
40
|
}
|
|
46
41
|
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
const targetPath = join(agentsTargetDir, file);
|
|
42
|
+
const files = readdirSync(agentsSourceDir).filter(f => f.endsWith(".md"));
|
|
43
|
+
for (const file of files) {
|
|
50
44
|
try {
|
|
51
|
-
copyFileSync(
|
|
52
|
-
console.log(` ✓
|
|
53
|
-
|
|
45
|
+
copyFileSync(join(agentsSourceDir, file), join(agentsTargetDir, file));
|
|
46
|
+
console.log(` ✓ ${file}`);
|
|
47
|
+
totalInstalled++;
|
|
54
48
|
} catch (err) {
|
|
55
|
-
console.error(` ✗
|
|
49
|
+
console.error(` ✗ ${file}: ${err.message}`);
|
|
50
|
+
totalFailed++;
|
|
56
51
|
}
|
|
57
52
|
}
|
|
58
|
-
} else {
|
|
59
|
-
console.error(" ✗ agents/ directory not found");
|
|
60
53
|
}
|
|
61
54
|
|
|
62
55
|
// 2. Install SKILL.md
|
|
63
|
-
console.log("\n[2/3] Installing
|
|
56
|
+
console.log("\n[2/3] Installing SKILL.md...");
|
|
64
57
|
const skillSource = join(packageRoot, "SKILL.md");
|
|
65
|
-
const skillTarget = join(skillTargetDir, "SKILL.md");
|
|
66
|
-
totalFiles++;
|
|
67
|
-
|
|
68
58
|
if (existsSync(skillSource)) {
|
|
69
59
|
if (!existsSync(skillTargetDir)) {
|
|
70
60
|
mkdirSync(skillTargetDir, { recursive: true });
|
|
71
61
|
}
|
|
72
62
|
try {
|
|
73
|
-
copyFileSync(skillSource,
|
|
74
|
-
console.log(
|
|
75
|
-
|
|
63
|
+
copyFileSync(skillSource, join(skillTargetDir, "SKILL.md"));
|
|
64
|
+
console.log(" ✓ SKILL.md");
|
|
65
|
+
totalInstalled++;
|
|
76
66
|
} catch (err) {
|
|
77
|
-
console.error(` ✗
|
|
67
|
+
console.error(` ✗ SKILL.md: ${err.message}`);
|
|
68
|
+
totalFailed++;
|
|
78
69
|
}
|
|
79
|
-
} else {
|
|
80
|
-
console.error(" ✗ SKILL.md not found");
|
|
81
70
|
}
|
|
82
71
|
|
|
83
72
|
// 3. Install references
|
|
84
73
|
console.log("\n[3/3] Installing references...");
|
|
85
74
|
const refsSourceDir = join(packageRoot, "references");
|
|
86
75
|
const refsTargetDir = join(skillTargetDir, "references");
|
|
87
|
-
|
|
88
76
|
if (existsSync(refsSourceDir)) {
|
|
89
77
|
if (!existsSync(refsTargetDir)) {
|
|
90
78
|
mkdirSync(refsTargetDir, { recursive: true });
|
|
91
79
|
}
|
|
92
80
|
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
} catch (err) {
|
|
108
|
-
console.error(` ✗ Failed: references/${file} - ${err.message}`);
|
|
81
|
+
function copyDir(src, dest) {
|
|
82
|
+
const items = readdirSync(src);
|
|
83
|
+
for (const item of items) {
|
|
84
|
+
const srcPath = join(src, item);
|
|
85
|
+
const destPath = join(dest, item);
|
|
86
|
+
try {
|
|
87
|
+
copyFileSync(srcPath, destPath);
|
|
88
|
+
totalInstalled++;
|
|
89
|
+
} catch {
|
|
90
|
+
if (existsSync(srcPath) && !srcPath.endsWith(".md")) {
|
|
91
|
+
mkdirSync(destPath, { recursive: true });
|
|
92
|
+
copyDir(srcPath, destPath);
|
|
93
|
+
}
|
|
94
|
+
}
|
|
109
95
|
}
|
|
110
96
|
}
|
|
111
|
-
|
|
112
|
-
|
|
97
|
+
|
|
98
|
+
try {
|
|
99
|
+
copyDir(refsSourceDir, refsTargetDir);
|
|
100
|
+
console.log(" ✓ references/");
|
|
101
|
+
totalInstalled++;
|
|
102
|
+
} catch (err) {
|
|
103
|
+
console.error(` ✗ references/: ${err.message}`);
|
|
104
|
+
totalFailed++;
|
|
105
|
+
}
|
|
113
106
|
}
|
|
114
107
|
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
console.log(
|
|
119
|
-
console.log(
|
|
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");
|
|
108
|
+
console.log(`\n========================================`);
|
|
109
|
+
if (totalFailed === 0) {
|
|
110
|
+
console.log(`✓ Installed ${totalInstalled} file(s)`);
|
|
111
|
+
console.log(`\nAgents: ${agentsTargetDir}`);
|
|
112
|
+
console.log(`Skill: ${skillTargetDir}`);
|
|
124
113
|
} else {
|
|
125
|
-
console.log(`⚠
|
|
114
|
+
console.log(`⚠ Installed ${totalInstalled}, failed ${totalFailed}`);
|
|
126
115
|
process.exit(1);
|
|
127
116
|
}
|
|
128
117
|
}
|
package/src/index.ts
CHANGED
|
@@ -7,38 +7,23 @@ 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
|
+
|
|
10
14
|
function getCorePath(ctx: { directory: string }): string {
|
|
11
15
|
return join(ctx.directory, CORE_DIR);
|
|
12
16
|
}
|
|
13
17
|
|
|
14
|
-
function
|
|
15
|
-
const
|
|
16
|
-
const reqFile = join(
|
|
18
|
+
function checkDeps(ctx: { directory: string }): string {
|
|
19
|
+
const skillPath = getSkillPath(ctx);
|
|
20
|
+
const reqFile = join(skillPath, "requirements.txt");
|
|
17
21
|
if (existsSync(reqFile)) {
|
|
18
|
-
return `pip install -q -r "${reqFile}" 2>/dev/null
|
|
22
|
+
return `pip install -q -r "${reqFile}" 2>/dev/null; `;
|
|
19
23
|
}
|
|
20
24
|
return "";
|
|
21
25
|
}
|
|
22
26
|
|
|
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
|
-
|
|
42
27
|
const CYBER_SUPERVISOR_PROMPT = `你是 API 安全测试的**赛博监工**,代号"P9"。
|
|
43
28
|
|
|
44
29
|
## 职责
|
|
@@ -69,29 +54,7 @@ const CYBER_SUPERVISOR_PROMPT = `你是 API 安全测试的**赛博监工**,
|
|
|
69
54
|
| vuln_verify | 漏洞验证 |
|
|
70
55
|
| sqli_test | SQL注入测试 |
|
|
71
56
|
| 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
|
-
`;
|
|
57
|
+
| auth_test | 认证测试`;
|
|
95
58
|
|
|
96
59
|
const PROBING_MINER_PROMPT = `你是**API漏洞挖掘专家**,专注于发现和验证安全漏洞。
|
|
97
60
|
|
|
@@ -134,16 +97,7 @@ const RESOURCE_SPECIALIST_PROMPT = `你是**API资源探测专家**,专注于
|
|
|
134
97
|
使用 js_parse 解析 JS 文件
|
|
135
98
|
|
|
136
99
|
### 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`;
|
|
100
|
+
常见路径: /api/v1/*, /graphql, /swagger, /.well-known/*`;
|
|
147
101
|
|
|
148
102
|
const VULN_VERIFIER_PROMPT = `你是**漏洞验证专家**,专注于验证和确认安全漏洞。
|
|
149
103
|
|
|
@@ -151,18 +105,10 @@ const VULN_VERIFIER_PROMPT = `你是**漏洞验证专家**,专注于验证和
|
|
|
151
105
|
|
|
152
106
|
1. **快速验证** - 确认漏洞是否存在
|
|
153
107
|
2. **风险评估** - 判断实际影响
|
|
154
|
-
3. **PoC 生成** -
|
|
155
|
-
|
|
156
|
-
## 验证流程
|
|
157
|
-
|
|
158
|
-
1. 构造 payload
|
|
159
|
-
2. 发送测试请求
|
|
160
|
-
3. 分析响应
|
|
161
|
-
4. 判断结果
|
|
162
|
-
5. 生成 PoC`;
|
|
108
|
+
3. **PoC 生成** - 提供可执行的证明`;
|
|
163
109
|
|
|
164
110
|
const ApiSecurityTestingPlugin: Plugin = async (ctx) => {
|
|
165
|
-
console.log("[api-security-testing] Plugin loaded
|
|
111
|
+
console.log("[api-security-testing] Plugin loaded");
|
|
166
112
|
|
|
167
113
|
return {
|
|
168
114
|
tool: {
|
|
@@ -173,15 +119,18 @@ const ApiSecurityTestingPlugin: Plugin = async (ctx) => {
|
|
|
173
119
|
scan_type: tool.schema.enum(["full", "quick", "targeted"]).optional(),
|
|
174
120
|
},
|
|
175
121
|
async execute(args, ctx) {
|
|
176
|
-
const
|
|
122
|
+
const deps = checkDeps(ctx);
|
|
123
|
+
const corePath = getCorePath(ctx);
|
|
124
|
+
const cmd = `${deps}python3 -c "
|
|
177
125
|
import sys
|
|
178
|
-
sys.path.insert(0, '${
|
|
126
|
+
sys.path.insert(0, '${corePath}')
|
|
179
127
|
from deep_api_tester_v55 import DeepAPITesterV55
|
|
180
128
|
tester = DeepAPITesterV55(target='${args.target}', headless=True)
|
|
181
129
|
results = tester.run_test()
|
|
182
130
|
print(results)
|
|
183
|
-
`;
|
|
184
|
-
|
|
131
|
+
"`;
|
|
132
|
+
const result = await ctx.$`${cmd}`;
|
|
133
|
+
return result.toString();
|
|
185
134
|
},
|
|
186
135
|
}),
|
|
187
136
|
|
|
@@ -192,15 +141,18 @@ print(results)
|
|
|
192
141
|
method: tool.schema.enum(["GET", "POST", "PUT", "DELETE", "PATCH"]).optional(),
|
|
193
142
|
},
|
|
194
143
|
async execute(args, ctx) {
|
|
195
|
-
const
|
|
144
|
+
const deps = checkDeps(ctx);
|
|
145
|
+
const corePath = getCorePath(ctx);
|
|
146
|
+
const cmd = `${deps}python3 -c "
|
|
196
147
|
import sys
|
|
197
|
-
sys.path.insert(0, '${
|
|
148
|
+
sys.path.insert(0, '${corePath}')
|
|
198
149
|
from api_fuzzer import APIFuzzer
|
|
199
150
|
fuzzer = APIFuzzer('${args.endpoint}')
|
|
200
151
|
results = fuzzer.fuzz(method='${args.method || 'GET'}')
|
|
201
152
|
print(results)
|
|
202
|
-
`;
|
|
203
|
-
|
|
153
|
+
"`;
|
|
154
|
+
const result = await ctx.$`${cmd}`;
|
|
155
|
+
return result.toString();
|
|
204
156
|
},
|
|
205
157
|
}),
|
|
206
158
|
|
|
@@ -211,15 +163,18 @@ print(results)
|
|
|
211
163
|
endpoint: tool.schema.string(),
|
|
212
164
|
},
|
|
213
165
|
async execute(args, ctx) {
|
|
214
|
-
const
|
|
166
|
+
const deps = checkDeps(ctx);
|
|
167
|
+
const corePath = getCorePath(ctx);
|
|
168
|
+
const cmd = `${deps}python3 -c "
|
|
215
169
|
import sys
|
|
216
|
-
sys.path.insert(0, '${
|
|
170
|
+
sys.path.insert(0, '${corePath}')
|
|
217
171
|
from verifiers.vuln_verifier import VulnVerifier
|
|
218
172
|
verifier = VulnVerifier()
|
|
219
173
|
result = verifier.verify('${args.vuln_type}', '${args.endpoint}')
|
|
220
174
|
print(result)
|
|
221
|
-
`;
|
|
222
|
-
|
|
175
|
+
"`;
|
|
176
|
+
const result = await ctx.$`${cmd}`;
|
|
177
|
+
return result.toString();
|
|
223
178
|
},
|
|
224
179
|
}),
|
|
225
180
|
|
|
@@ -229,17 +184,20 @@ print(result)
|
|
|
229
184
|
url: tool.schema.string(),
|
|
230
185
|
},
|
|
231
186
|
async execute(args, ctx) {
|
|
232
|
-
const
|
|
187
|
+
const deps = checkDeps(ctx);
|
|
188
|
+
const corePath = getCorePath(ctx);
|
|
189
|
+
const cmd = `${deps}python3 -c "
|
|
233
190
|
import sys
|
|
234
|
-
sys.path.insert(0, '${
|
|
191
|
+
sys.path.insert(0, '${corePath}')
|
|
235
192
|
from collectors.browser_collect import BrowserCollector
|
|
236
193
|
collector = BrowserCollector(headless=True)
|
|
237
194
|
endpoints = collector.collect('${args.url}')
|
|
238
195
|
print(f'发现 {len(endpoints)} 个端点:')
|
|
239
196
|
for ep in endpoints:
|
|
240
197
|
print(ep)
|
|
241
|
-
`;
|
|
242
|
-
|
|
198
|
+
"`;
|
|
199
|
+
const result = await ctx.$`${cmd}`;
|
|
200
|
+
return result.toString();
|
|
243
201
|
},
|
|
244
202
|
}),
|
|
245
203
|
|
|
@@ -249,15 +207,18 @@ for ep in endpoints:
|
|
|
249
207
|
file_path: tool.schema.string(),
|
|
250
208
|
},
|
|
251
209
|
async execute(args, ctx) {
|
|
252
|
-
const
|
|
210
|
+
const deps = checkDeps(ctx);
|
|
211
|
+
const corePath = getCorePath(ctx);
|
|
212
|
+
const cmd = `${deps}python3 -c "
|
|
253
213
|
import sys
|
|
254
|
-
sys.path.insert(0, '${
|
|
214
|
+
sys.path.insert(0, '${corePath}')
|
|
255
215
|
from collectors.js_parser import JSParser
|
|
256
216
|
parser = JSParser()
|
|
257
217
|
endpoints = parser.parse_file('${args.file_path}')
|
|
258
218
|
print(f'从 JS 发现 {len(endpoints)} 个端点')
|
|
259
|
-
`;
|
|
260
|
-
|
|
219
|
+
"`;
|
|
220
|
+
const result = await ctx.$`${cmd}`;
|
|
221
|
+
return result.toString();
|
|
261
222
|
},
|
|
262
223
|
}),
|
|
263
224
|
|
|
@@ -267,15 +228,18 @@ print(f'从 JS 发现 {len(endpoints)} 个端点')
|
|
|
267
228
|
endpoint: tool.schema.string(),
|
|
268
229
|
},
|
|
269
230
|
async execute(args, ctx) {
|
|
270
|
-
const
|
|
231
|
+
const deps = checkDeps(ctx);
|
|
232
|
+
const corePath = getCorePath(ctx);
|
|
233
|
+
const cmd = `${deps}python3 -c "
|
|
271
234
|
import sys
|
|
272
|
-
sys.path.insert(0, '${
|
|
235
|
+
sys.path.insert(0, '${corePath}')
|
|
273
236
|
from smart_analyzer import SmartAnalyzer
|
|
274
237
|
analyzer = SmartAnalyzer()
|
|
275
238
|
result = analyzer.graphql_test('${args.endpoint}')
|
|
276
239
|
print(result)
|
|
277
|
-
`;
|
|
278
|
-
|
|
240
|
+
"`;
|
|
241
|
+
const result = await ctx.$`${cmd}`;
|
|
242
|
+
return result.toString();
|
|
279
243
|
},
|
|
280
244
|
}),
|
|
281
245
|
|
|
@@ -285,15 +249,18 @@ print(result)
|
|
|
285
249
|
bucket_url: tool.schema.string(),
|
|
286
250
|
},
|
|
287
251
|
async execute(args, ctx) {
|
|
288
|
-
const
|
|
252
|
+
const deps = checkDeps(ctx);
|
|
253
|
+
const corePath = getCorePath(ctx);
|
|
254
|
+
const cmd = `${deps}python3 -c "
|
|
289
255
|
import sys
|
|
290
|
-
sys.path.insert(0, '${
|
|
256
|
+
sys.path.insert(0, '${corePath}')
|
|
291
257
|
from cloud_storage_tester import CloudStorageTester
|
|
292
258
|
tester = CloudStorageTester()
|
|
293
259
|
result = tester.full_test('${args.bucket_url}')
|
|
294
260
|
print(result)
|
|
295
|
-
`;
|
|
296
|
-
|
|
261
|
+
"`;
|
|
262
|
+
const result = await ctx.$`${cmd}`;
|
|
263
|
+
return result.toString();
|
|
297
264
|
},
|
|
298
265
|
}),
|
|
299
266
|
|
|
@@ -304,15 +271,18 @@ print(result)
|
|
|
304
271
|
resource_id: tool.schema.string(),
|
|
305
272
|
},
|
|
306
273
|
async execute(args, ctx) {
|
|
307
|
-
const
|
|
274
|
+
const deps = checkDeps(ctx);
|
|
275
|
+
const corePath = getCorePath(ctx);
|
|
276
|
+
const cmd = `${deps}python3 -c "
|
|
308
277
|
import sys
|
|
309
|
-
sys.path.insert(0, '${
|
|
278
|
+
sys.path.insert(0, '${corePath}')
|
|
310
279
|
from testers.idor_tester import IDORTester
|
|
311
280
|
tester = IDORTester()
|
|
312
281
|
result = tester.test('${args.endpoint}', '${args.resource_id}')
|
|
313
282
|
print(result)
|
|
314
|
-
`;
|
|
315
|
-
|
|
283
|
+
"`;
|
|
284
|
+
const result = await ctx.$`${cmd}`;
|
|
285
|
+
return result.toString();
|
|
316
286
|
},
|
|
317
287
|
}),
|
|
318
288
|
|
|
@@ -323,15 +293,18 @@ print(result)
|
|
|
323
293
|
param: tool.schema.string(),
|
|
324
294
|
},
|
|
325
295
|
async execute(args, ctx) {
|
|
326
|
-
const
|
|
296
|
+
const deps = checkDeps(ctx);
|
|
297
|
+
const corePath = getCorePath(ctx);
|
|
298
|
+
const cmd = `${deps}python3 -c "
|
|
327
299
|
import sys
|
|
328
|
-
sys.path.insert(0, '${
|
|
300
|
+
sys.path.insert(0, '${corePath}')
|
|
329
301
|
from testers.sqli_tester import SQLiTester
|
|
330
302
|
tester = SQLiTester()
|
|
331
303
|
result = tester.test('${args.endpoint}', '${args.param}')
|
|
332
304
|
print(result)
|
|
333
|
-
`;
|
|
334
|
-
|
|
305
|
+
"`;
|
|
306
|
+
const result = await ctx.$`${cmd}`;
|
|
307
|
+
return result.toString();
|
|
335
308
|
},
|
|
336
309
|
}),
|
|
337
310
|
|
|
@@ -341,15 +314,18 @@ print(result)
|
|
|
341
314
|
endpoint: tool.schema.string(),
|
|
342
315
|
},
|
|
343
316
|
async execute(args, ctx) {
|
|
344
|
-
const
|
|
317
|
+
const deps = checkDeps(ctx);
|
|
318
|
+
const corePath = getCorePath(ctx);
|
|
319
|
+
const cmd = `${deps}python3 -c "
|
|
345
320
|
import sys
|
|
346
|
-
sys.path.insert(0, '${
|
|
321
|
+
sys.path.insert(0, '${corePath}')
|
|
347
322
|
from testers.auth_tester import AuthTester
|
|
348
323
|
tester = AuthTester()
|
|
349
324
|
result = tester.test('${args.endpoint}')
|
|
350
325
|
print(result)
|
|
351
|
-
`;
|
|
352
|
-
|
|
326
|
+
"`;
|
|
327
|
+
const result = await ctx.$`${cmd}`;
|
|
328
|
+
return result.toString();
|
|
353
329
|
},
|
|
354
330
|
}),
|
|
355
331
|
},
|
|
@@ -385,7 +361,7 @@ print(result)
|
|
|
385
361
|
prompt: VULN_VERIFIER_PROMPT,
|
|
386
362
|
};
|
|
387
363
|
|
|
388
|
-
console.log("[api-security-testing]
|
|
364
|
+
console.log("[api-security-testing] Tools registered");
|
|
389
365
|
},
|
|
390
366
|
};
|
|
391
367
|
};
|
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/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();
|