opencode-api-security-testing 3.0.3 → 3.0.5
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 +79 -221
- 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.5",
|
|
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
|
@@ -1,168 +1,30 @@
|
|
|
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
3
|
import { join } from "path";
|
|
5
4
|
import { existsSync } from "fs";
|
|
6
5
|
|
|
7
6
|
const SKILL_DIR = "skills/api-security-testing";
|
|
8
7
|
const CORE_DIR = `${SKILL_DIR}/core`;
|
|
9
8
|
|
|
9
|
+
function getSkillPath(ctx: { directory: string }): string {
|
|
10
|
+
return join(ctx.directory, SKILL_DIR);
|
|
11
|
+
}
|
|
12
|
+
|
|
10
13
|
function getCorePath(ctx: { directory: string }): string {
|
|
11
14
|
return join(ctx.directory, CORE_DIR);
|
|
12
15
|
}
|
|
13
16
|
|
|
14
|
-
function
|
|
15
|
-
const
|
|
16
|
-
const reqFile = join(
|
|
17
|
+
function checkDeps(ctx: { directory: string }): string {
|
|
18
|
+
const skillPath = getSkillPath(ctx);
|
|
19
|
+
const reqFile = join(skillPath, "requirements.txt");
|
|
17
20
|
if (existsSync(reqFile)) {
|
|
18
|
-
return `pip install -q -r "${reqFile}" 2>/dev/null
|
|
21
|
+
return `pip install -q -r "${reqFile}" 2>/dev/null; `;
|
|
19
22
|
}
|
|
20
23
|
return "";
|
|
21
24
|
}
|
|
22
25
|
|
|
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
|
-
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
26
|
const ApiSecurityTestingPlugin: Plugin = async (ctx) => {
|
|
165
|
-
console.log("[api-security-testing] Plugin loaded
|
|
27
|
+
console.log("[api-security-testing] Plugin loaded");
|
|
166
28
|
|
|
167
29
|
return {
|
|
168
30
|
tool: {
|
|
@@ -173,15 +35,18 @@ const ApiSecurityTestingPlugin: Plugin = async (ctx) => {
|
|
|
173
35
|
scan_type: tool.schema.enum(["full", "quick", "targeted"]).optional(),
|
|
174
36
|
},
|
|
175
37
|
async execute(args, ctx) {
|
|
176
|
-
const
|
|
38
|
+
const deps = checkDeps(ctx);
|
|
39
|
+
const corePath = getCorePath(ctx);
|
|
40
|
+
const cmd = `${deps}python3 -c "
|
|
177
41
|
import sys
|
|
178
|
-
sys.path.insert(0, '${
|
|
42
|
+
sys.path.insert(0, '${corePath}')
|
|
179
43
|
from deep_api_tester_v55 import DeepAPITesterV55
|
|
180
44
|
tester = DeepAPITesterV55(target='${args.target}', headless=True)
|
|
181
45
|
results = tester.run_test()
|
|
182
46
|
print(results)
|
|
183
|
-
`;
|
|
184
|
-
|
|
47
|
+
"`;
|
|
48
|
+
const result = await ctx.$`${cmd}`;
|
|
49
|
+
return result.toString();
|
|
185
50
|
},
|
|
186
51
|
}),
|
|
187
52
|
|
|
@@ -192,15 +57,18 @@ print(results)
|
|
|
192
57
|
method: tool.schema.enum(["GET", "POST", "PUT", "DELETE", "PATCH"]).optional(),
|
|
193
58
|
},
|
|
194
59
|
async execute(args, ctx) {
|
|
195
|
-
const
|
|
60
|
+
const deps = checkDeps(ctx);
|
|
61
|
+
const corePath = getCorePath(ctx);
|
|
62
|
+
const cmd = `${deps}python3 -c "
|
|
196
63
|
import sys
|
|
197
|
-
sys.path.insert(0, '${
|
|
64
|
+
sys.path.insert(0, '${corePath}')
|
|
198
65
|
from api_fuzzer import APIFuzzer
|
|
199
66
|
fuzzer = APIFuzzer('${args.endpoint}')
|
|
200
67
|
results = fuzzer.fuzz(method='${args.method || 'GET'}')
|
|
201
68
|
print(results)
|
|
202
|
-
`;
|
|
203
|
-
|
|
69
|
+
"`;
|
|
70
|
+
const result = await ctx.$`${cmd}`;
|
|
71
|
+
return result.toString();
|
|
204
72
|
},
|
|
205
73
|
}),
|
|
206
74
|
|
|
@@ -211,15 +79,18 @@ print(results)
|
|
|
211
79
|
endpoint: tool.schema.string(),
|
|
212
80
|
},
|
|
213
81
|
async execute(args, ctx) {
|
|
214
|
-
const
|
|
82
|
+
const deps = checkDeps(ctx);
|
|
83
|
+
const corePath = getCorePath(ctx);
|
|
84
|
+
const cmd = `${deps}python3 -c "
|
|
215
85
|
import sys
|
|
216
|
-
sys.path.insert(0, '${
|
|
86
|
+
sys.path.insert(0, '${corePath}')
|
|
217
87
|
from verifiers.vuln_verifier import VulnVerifier
|
|
218
88
|
verifier = VulnVerifier()
|
|
219
89
|
result = verifier.verify('${args.vuln_type}', '${args.endpoint}')
|
|
220
90
|
print(result)
|
|
221
|
-
`;
|
|
222
|
-
|
|
91
|
+
"`;
|
|
92
|
+
const result = await ctx.$`${cmd}`;
|
|
93
|
+
return result.toString();
|
|
223
94
|
},
|
|
224
95
|
}),
|
|
225
96
|
|
|
@@ -229,17 +100,20 @@ print(result)
|
|
|
229
100
|
url: tool.schema.string(),
|
|
230
101
|
},
|
|
231
102
|
async execute(args, ctx) {
|
|
232
|
-
const
|
|
103
|
+
const deps = checkDeps(ctx);
|
|
104
|
+
const corePath = getCorePath(ctx);
|
|
105
|
+
const cmd = `${deps}python3 -c "
|
|
233
106
|
import sys
|
|
234
|
-
sys.path.insert(0, '${
|
|
107
|
+
sys.path.insert(0, '${corePath}')
|
|
235
108
|
from collectors.browser_collect import BrowserCollector
|
|
236
109
|
collector = BrowserCollector(headless=True)
|
|
237
110
|
endpoints = collector.collect('${args.url}')
|
|
238
111
|
print(f'发现 {len(endpoints)} 个端点:')
|
|
239
112
|
for ep in endpoints:
|
|
240
113
|
print(ep)
|
|
241
|
-
`;
|
|
242
|
-
|
|
114
|
+
"`;
|
|
115
|
+
const result = await ctx.$`${cmd}`;
|
|
116
|
+
return result.toString();
|
|
243
117
|
},
|
|
244
118
|
}),
|
|
245
119
|
|
|
@@ -249,15 +123,18 @@ for ep in endpoints:
|
|
|
249
123
|
file_path: tool.schema.string(),
|
|
250
124
|
},
|
|
251
125
|
async execute(args, ctx) {
|
|
252
|
-
const
|
|
126
|
+
const deps = checkDeps(ctx);
|
|
127
|
+
const corePath = getCorePath(ctx);
|
|
128
|
+
const cmd = `${deps}python3 -c "
|
|
253
129
|
import sys
|
|
254
|
-
sys.path.insert(0, '${
|
|
130
|
+
sys.path.insert(0, '${corePath}')
|
|
255
131
|
from collectors.js_parser import JSParser
|
|
256
132
|
parser = JSParser()
|
|
257
133
|
endpoints = parser.parse_file('${args.file_path}')
|
|
258
134
|
print(f'从 JS 发现 {len(endpoints)} 个端点')
|
|
259
|
-
`;
|
|
260
|
-
|
|
135
|
+
"`;
|
|
136
|
+
const result = await ctx.$`${cmd}`;
|
|
137
|
+
return result.toString();
|
|
261
138
|
},
|
|
262
139
|
}),
|
|
263
140
|
|
|
@@ -267,15 +144,18 @@ print(f'从 JS 发现 {len(endpoints)} 个端点')
|
|
|
267
144
|
endpoint: tool.schema.string(),
|
|
268
145
|
},
|
|
269
146
|
async execute(args, ctx) {
|
|
270
|
-
const
|
|
147
|
+
const deps = checkDeps(ctx);
|
|
148
|
+
const corePath = getCorePath(ctx);
|
|
149
|
+
const cmd = `${deps}python3 -c "
|
|
271
150
|
import sys
|
|
272
|
-
sys.path.insert(0, '${
|
|
151
|
+
sys.path.insert(0, '${corePath}')
|
|
273
152
|
from smart_analyzer import SmartAnalyzer
|
|
274
153
|
analyzer = SmartAnalyzer()
|
|
275
154
|
result = analyzer.graphql_test('${args.endpoint}')
|
|
276
155
|
print(result)
|
|
277
|
-
`;
|
|
278
|
-
|
|
156
|
+
"`;
|
|
157
|
+
const result = await ctx.$`${cmd}`;
|
|
158
|
+
return result.toString();
|
|
279
159
|
},
|
|
280
160
|
}),
|
|
281
161
|
|
|
@@ -285,15 +165,18 @@ print(result)
|
|
|
285
165
|
bucket_url: tool.schema.string(),
|
|
286
166
|
},
|
|
287
167
|
async execute(args, ctx) {
|
|
288
|
-
const
|
|
168
|
+
const deps = checkDeps(ctx);
|
|
169
|
+
const corePath = getCorePath(ctx);
|
|
170
|
+
const cmd = `${deps}python3 -c "
|
|
289
171
|
import sys
|
|
290
|
-
sys.path.insert(0, '${
|
|
172
|
+
sys.path.insert(0, '${corePath}')
|
|
291
173
|
from cloud_storage_tester import CloudStorageTester
|
|
292
174
|
tester = CloudStorageTester()
|
|
293
175
|
result = tester.full_test('${args.bucket_url}')
|
|
294
176
|
print(result)
|
|
295
|
-
`;
|
|
296
|
-
|
|
177
|
+
"`;
|
|
178
|
+
const result = await ctx.$`${cmd}`;
|
|
179
|
+
return result.toString();
|
|
297
180
|
},
|
|
298
181
|
}),
|
|
299
182
|
|
|
@@ -304,15 +187,18 @@ print(result)
|
|
|
304
187
|
resource_id: tool.schema.string(),
|
|
305
188
|
},
|
|
306
189
|
async execute(args, ctx) {
|
|
307
|
-
const
|
|
190
|
+
const deps = checkDeps(ctx);
|
|
191
|
+
const corePath = getCorePath(ctx);
|
|
192
|
+
const cmd = `${deps}python3 -c "
|
|
308
193
|
import sys
|
|
309
|
-
sys.path.insert(0, '${
|
|
194
|
+
sys.path.insert(0, '${corePath}')
|
|
310
195
|
from testers.idor_tester import IDORTester
|
|
311
196
|
tester = IDORTester()
|
|
312
197
|
result = tester.test('${args.endpoint}', '${args.resource_id}')
|
|
313
198
|
print(result)
|
|
314
|
-
`;
|
|
315
|
-
|
|
199
|
+
"`;
|
|
200
|
+
const result = await ctx.$`${cmd}`;
|
|
201
|
+
return result.toString();
|
|
316
202
|
},
|
|
317
203
|
}),
|
|
318
204
|
|
|
@@ -323,15 +209,18 @@ print(result)
|
|
|
323
209
|
param: tool.schema.string(),
|
|
324
210
|
},
|
|
325
211
|
async execute(args, ctx) {
|
|
326
|
-
const
|
|
212
|
+
const deps = checkDeps(ctx);
|
|
213
|
+
const corePath = getCorePath(ctx);
|
|
214
|
+
const cmd = `${deps}python3 -c "
|
|
327
215
|
import sys
|
|
328
|
-
sys.path.insert(0, '${
|
|
216
|
+
sys.path.insert(0, '${corePath}')
|
|
329
217
|
from testers.sqli_tester import SQLiTester
|
|
330
218
|
tester = SQLiTester()
|
|
331
219
|
result = tester.test('${args.endpoint}', '${args.param}')
|
|
332
220
|
print(result)
|
|
333
|
-
`;
|
|
334
|
-
|
|
221
|
+
"`;
|
|
222
|
+
const result = await ctx.$`${cmd}`;
|
|
223
|
+
return result.toString();
|
|
335
224
|
},
|
|
336
225
|
}),
|
|
337
226
|
|
|
@@ -341,52 +230,21 @@ print(result)
|
|
|
341
230
|
endpoint: tool.schema.string(),
|
|
342
231
|
},
|
|
343
232
|
async execute(args, ctx) {
|
|
344
|
-
const
|
|
233
|
+
const deps = checkDeps(ctx);
|
|
234
|
+
const corePath = getCorePath(ctx);
|
|
235
|
+
const cmd = `${deps}python3 -c "
|
|
345
236
|
import sys
|
|
346
|
-
sys.path.insert(0, '${
|
|
237
|
+
sys.path.insert(0, '${corePath}')
|
|
347
238
|
from testers.auth_tester import AuthTester
|
|
348
239
|
tester = AuthTester()
|
|
349
240
|
result = tester.test('${args.endpoint}')
|
|
350
241
|
print(result)
|
|
351
|
-
`;
|
|
352
|
-
|
|
242
|
+
"`;
|
|
243
|
+
const result = await ctx.$`${cmd}`;
|
|
244
|
+
return result.toString();
|
|
353
245
|
},
|
|
354
246
|
}),
|
|
355
247
|
},
|
|
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
248
|
};
|
|
391
249
|
};
|
|
392
250
|
|
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();
|