opencode-api-security-testing 3.0.2 → 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 -18
- package/src/index.ts +84 -277
- package/postinstall.mjs +0 -130
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,33 +9,21 @@
|
|
|
9
9
|
"agents/",
|
|
10
10
|
"core/",
|
|
11
11
|
"references/",
|
|
12
|
-
"SKILL.md"
|
|
13
|
-
"postinstall.mjs"
|
|
12
|
+
"SKILL.md"
|
|
14
13
|
],
|
|
15
|
-
"scripts": {
|
|
16
|
-
"postinstall": "node postinstall.mjs"
|
|
17
|
-
},
|
|
18
14
|
"keywords": [
|
|
19
15
|
"opencode",
|
|
20
16
|
"opencode-plugin",
|
|
21
17
|
"security",
|
|
22
|
-
"api-security"
|
|
23
|
-
"pentest",
|
|
24
|
-
"vulnerability-scanning"
|
|
18
|
+
"api-security"
|
|
25
19
|
],
|
|
26
20
|
"author": "steveopen1",
|
|
27
21
|
"license": "MIT",
|
|
28
22
|
"repository": {
|
|
29
23
|
"type": "git",
|
|
30
|
-
"url": "https://github.com/steveopen1/skill-play"
|
|
24
|
+
"url": "git+https://github.com/steveopen1/skill-play.git"
|
|
31
25
|
},
|
|
32
|
-
"homepage": "https://github.com/steveopen1/skill-play/tree/main/agent-plugins/OPENCODE/api-security-testing",
|
|
33
26
|
"peerDependencies": {
|
|
34
|
-
"@opencode-ai/plugin": "^1.1.19"
|
|
35
|
-
"@opencode-ai/sdk": "^1.1.19"
|
|
36
|
-
},
|
|
37
|
-
"dependencies": {
|
|
38
|
-
"@opencode-ai/plugin": "^1.1.19",
|
|
39
|
-
"@opencode-ai/sdk": "^1.1.19"
|
|
27
|
+
"@opencode-ai/plugin": "^1.1.19"
|
|
40
28
|
}
|
|
41
29
|
}
|
package/src/index.ts
CHANGED
|
@@ -1,368 +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 getSkillPath(ctx: { directory: string }): string {
|
|
11
|
-
return join(ctx.directory, SKILL_DIR);
|
|
12
|
-
}
|
|
13
|
-
|
|
14
|
-
function getCorePath(ctx: { directory: string }): string {
|
|
15
|
-
return join(ctx.directory, CORE_DIR);
|
|
16
|
-
}
|
|
17
|
-
|
|
18
|
-
function checkDeps(ctx: { directory: string }): string {
|
|
19
|
-
const skillPath = getSkillPath(ctx);
|
|
20
|
-
const reqFile = join(skillPath, "requirements.txt");
|
|
21
|
-
if (existsSync(reqFile)) {
|
|
22
|
-
return `pip install -q -r "${reqFile}" 2>/dev/null; `;
|
|
23
|
-
}
|
|
24
|
-
return "";
|
|
4
|
+
function getCorePath(directory: string): string {
|
|
5
|
+
return `${directory}/skills/api-security-testing/core`;
|
|
25
6
|
}
|
|
26
7
|
|
|
27
|
-
const CYBER_SUPERVISOR_PROMPT = `你是 API 安全测试的**赛博监工**,代号"P9"。
|
|
28
|
-
|
|
29
|
-
## 职责
|
|
30
|
-
|
|
31
|
-
1. **永不停止** - 任何线索都要追到底
|
|
32
|
-
2. **自动化编排** - 不等待用户,主动推进
|
|
33
|
-
3. **智能委派** - 识别任务类型,委派给最合适的子 agent
|
|
34
|
-
4. **压力升级** - 遇到失败自动换方法 (L1-L4)
|
|
35
|
-
|
|
36
|
-
## 可用子 Agent
|
|
37
|
-
|
|
38
|
-
| 子 Agent | 职责 |
|
|
39
|
-
|---------|------|
|
|
40
|
-
| @api-probing-miner | 漏洞挖掘 |
|
|
41
|
-
| @api-resource-specialist | 端点发现 |
|
|
42
|
-
| @api-vuln-verifier | 漏洞验证 |
|
|
43
|
-
|
|
44
|
-
## 可用工具
|
|
45
|
-
|
|
46
|
-
| 工具 | 用途 |
|
|
47
|
-
|------|------|
|
|
48
|
-
| api_security_scan | 完整扫描 |
|
|
49
|
-
| api_fuzz_test | 模糊测试 |
|
|
50
|
-
| browser_collect | 浏览器采集 |
|
|
51
|
-
| js_parse | JS分析 |
|
|
52
|
-
| graphql_test | GraphQL测试 |
|
|
53
|
-
| cloud_storage_test | 云存储测试 |
|
|
54
|
-
| vuln_verify | 漏洞验证 |
|
|
55
|
-
| sqli_test | SQL注入测试 |
|
|
56
|
-
| idor_test | IDOR测试 |
|
|
57
|
-
| auth_test | 认证测试`;
|
|
58
|
-
|
|
59
|
-
const PROBING_MINER_PROMPT = `你是**API漏洞挖掘专家**,专注于发现和验证安全漏洞。
|
|
60
|
-
|
|
61
|
-
## 职责
|
|
62
|
-
|
|
63
|
-
1. **针对性测试** - 根据端点特征选择最佳测试方法
|
|
64
|
-
2. **快速验证** - 确认漏洞存在
|
|
65
|
-
3. **PoC 生成** - 提供可执行的测试命令
|
|
66
|
-
|
|
67
|
-
## 测试方法库
|
|
68
|
-
|
|
69
|
-
### SQL 注入
|
|
70
|
-
- 布尔盲注: ' OR 1=1 --
|
|
71
|
-
- 联合查询: ' UNION SELECT NULL--
|
|
72
|
-
- 错误注入: ' AND 1=CONVERT(int,...)--
|
|
73
|
-
- 时间盲注: '; WAITFOR DELAY '00:00:05'--
|
|
74
|
-
|
|
75
|
-
### IDOR
|
|
76
|
-
- 替换 ID: /api/user/1 → /api/user/2
|
|
77
|
-
- 水平/垂直越权测试
|
|
78
|
-
|
|
79
|
-
### JWT
|
|
80
|
-
- 空算法: alg: none
|
|
81
|
-
- 密钥混淆: HS256 → HS512`;
|
|
82
|
-
|
|
83
|
-
const RESOURCE_SPECIALIST_PROMPT = `你是**API资源探测专家**,专注于发现和采集 API 端点。
|
|
84
|
-
|
|
85
|
-
## 职责
|
|
86
|
-
|
|
87
|
-
1. **全面发现** - 不遗漏任何端点
|
|
88
|
-
2. **动态采集** - 拦截真实请求
|
|
89
|
-
3. **静态分析** - 提取 API 模式
|
|
90
|
-
|
|
91
|
-
## 采集技术
|
|
92
|
-
|
|
93
|
-
### 1. 浏览器动态采集
|
|
94
|
-
使用 browser_collect 拦截 XHR/Fetch 请求
|
|
95
|
-
|
|
96
|
-
### 2. JS 静态分析
|
|
97
|
-
使用 js_parse 解析 JS 文件
|
|
98
|
-
|
|
99
|
-
### 3. 目录探测
|
|
100
|
-
常见路径: /api/v1/*, /graphql, /swagger, /.well-known/*`;
|
|
101
|
-
|
|
102
|
-
const VULN_VERIFIER_PROMPT = `你是**漏洞验证专家**,专注于验证和确认安全漏洞。
|
|
103
|
-
|
|
104
|
-
## 职责
|
|
105
|
-
|
|
106
|
-
1. **快速验证** - 确认漏洞是否存在
|
|
107
|
-
2. **风险评估** - 判断实际影响
|
|
108
|
-
3. **PoC 生成** - 提供可执行的证明`;
|
|
109
|
-
|
|
110
8
|
const ApiSecurityTestingPlugin: Plugin = async (ctx) => {
|
|
111
|
-
console.log("[api-security-testing] Plugin loaded");
|
|
112
|
-
|
|
113
9
|
return {
|
|
114
10
|
tool: {
|
|
115
11
|
api_security_scan: tool({
|
|
116
|
-
description: "完整 API
|
|
12
|
+
description: "完整 API 安全扫描",
|
|
117
13
|
args: {
|
|
118
14
|
target: tool.schema.string(),
|
|
119
15
|
scan_type: tool.schema.enum(["full", "quick", "targeted"]).optional(),
|
|
120
16
|
},
|
|
121
17
|
async execute(args, ctx) {
|
|
122
|
-
const
|
|
123
|
-
const
|
|
124
|
-
const cmd = `${deps}python3 -c "
|
|
18
|
+
const corePath = getCorePath(ctx.directory);
|
|
19
|
+
const script = `
|
|
125
20
|
import sys
|
|
126
21
|
sys.path.insert(0, '${corePath}')
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
print(
|
|
131
|
-
|
|
132
|
-
|
|
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}")
|
|
28
|
+
`;
|
|
29
|
+
const result = await ctx.$`python3 -c ${script}`;
|
|
133
30
|
return result.toString();
|
|
134
31
|
},
|
|
135
32
|
}),
|
|
136
33
|
|
|
137
34
|
api_fuzz_test: tool({
|
|
138
|
-
description: "API
|
|
35
|
+
description: "API 模糊测试",
|
|
139
36
|
args: {
|
|
140
37
|
endpoint: tool.schema.string(),
|
|
141
38
|
method: tool.schema.enum(["GET", "POST", "PUT", "DELETE", "PATCH"]).optional(),
|
|
142
39
|
},
|
|
143
40
|
async execute(args, ctx) {
|
|
144
|
-
const
|
|
145
|
-
const
|
|
146
|
-
const cmd = `${deps}python3 -c "
|
|
41
|
+
const corePath = getCorePath(ctx.directory);
|
|
42
|
+
const script = `
|
|
147
43
|
import sys
|
|
148
44
|
sys.path.insert(0, '${corePath}')
|
|
149
|
-
|
|
150
|
-
|
|
151
|
-
|
|
152
|
-
print(
|
|
153
|
-
|
|
154
|
-
|
|
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}")
|
|
51
|
+
`;
|
|
52
|
+
const result = await ctx.$`python3 -c ${script}`;
|
|
155
53
|
return result.toString();
|
|
156
54
|
},
|
|
157
55
|
}),
|
|
158
56
|
|
|
159
57
|
vuln_verify: tool({
|
|
160
|
-
description: "
|
|
58
|
+
description: "漏洞验证",
|
|
161
59
|
args: {
|
|
162
60
|
vuln_type: tool.schema.string(),
|
|
163
61
|
endpoint: tool.schema.string(),
|
|
164
62
|
},
|
|
165
63
|
async execute(args, ctx) {
|
|
166
|
-
const
|
|
167
|
-
const
|
|
168
|
-
const cmd = `${deps}python3 -c "
|
|
64
|
+
const corePath = getCorePath(ctx.directory);
|
|
65
|
+
const script = `
|
|
169
66
|
import sys
|
|
170
67
|
sys.path.insert(0, '${corePath}')
|
|
171
|
-
|
|
172
|
-
|
|
173
|
-
|
|
174
|
-
print(
|
|
175
|
-
|
|
176
|
-
|
|
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}")
|
|
74
|
+
`;
|
|
75
|
+
const result = await ctx.$`python3 -c ${script}`;
|
|
177
76
|
return result.toString();
|
|
178
77
|
},
|
|
179
78
|
}),
|
|
180
79
|
|
|
181
80
|
browser_collect: tool({
|
|
182
|
-
description: "
|
|
81
|
+
description: "浏览器采集动态内容",
|
|
183
82
|
args: {
|
|
184
83
|
url: tool.schema.string(),
|
|
185
84
|
},
|
|
186
85
|
async execute(args, ctx) {
|
|
187
|
-
const
|
|
188
|
-
const
|
|
189
|
-
const cmd = `${deps}python3 -c "
|
|
190
|
-
import sys
|
|
191
|
-
sys.path.insert(0, '${corePath}')
|
|
192
|
-
from collectors.browser_collect import BrowserCollector
|
|
193
|
-
collector = BrowserCollector(headless=True)
|
|
194
|
-
endpoints = collector.collect('${args.url}')
|
|
195
|
-
print(f'发现 {len(endpoints)} 个端点:')
|
|
196
|
-
for ep in endpoints:
|
|
197
|
-
print(ep)
|
|
198
|
-
"`;
|
|
199
|
-
const result = await ctx.$`${cmd}`;
|
|
200
|
-
return result.toString();
|
|
201
|
-
},
|
|
202
|
-
}),
|
|
203
|
-
|
|
204
|
-
js_parse: tool({
|
|
205
|
-
description: "解析 JavaScript 文件。参数: file_path(文件路径)",
|
|
206
|
-
args: {
|
|
207
|
-
file_path: tool.schema.string(),
|
|
208
|
-
},
|
|
209
|
-
async execute(args, ctx) {
|
|
210
|
-
const deps = checkDeps(ctx);
|
|
211
|
-
const corePath = getCorePath(ctx);
|
|
212
|
-
const cmd = `${deps}python3 -c "
|
|
86
|
+
const corePath = getCorePath(ctx.directory);
|
|
87
|
+
const script = `
|
|
213
88
|
import sys
|
|
214
89
|
sys.path.insert(0, '${corePath}')
|
|
215
|
-
|
|
216
|
-
|
|
217
|
-
|
|
218
|
-
|
|
219
|
-
|
|
220
|
-
|
|
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}")
|
|
99
|
+
`;
|
|
100
|
+
const result = await ctx.$`python3 -c ${script}`;
|
|
221
101
|
return result.toString();
|
|
222
102
|
},
|
|
223
103
|
}),
|
|
224
104
|
|
|
225
105
|
graphql_test: tool({
|
|
226
|
-
description: "GraphQL
|
|
227
|
-
args: {
|
|
228
|
-
endpoint: tool.schema.string(),
|
|
229
|
-
},
|
|
230
|
-
async execute(args, ctx) {
|
|
231
|
-
const deps = checkDeps(ctx);
|
|
232
|
-
const corePath = getCorePath(ctx);
|
|
233
|
-
const cmd = `${deps}python3 -c "
|
|
234
|
-
import sys
|
|
235
|
-
sys.path.insert(0, '${corePath}')
|
|
236
|
-
from smart_analyzer import SmartAnalyzer
|
|
237
|
-
analyzer = SmartAnalyzer()
|
|
238
|
-
result = analyzer.graphql_test('${args.endpoint}')
|
|
239
|
-
print(result)
|
|
240
|
-
"`;
|
|
241
|
-
const result = await ctx.$`${cmd}`;
|
|
242
|
-
return result.toString();
|
|
243
|
-
},
|
|
244
|
-
}),
|
|
245
|
-
|
|
246
|
-
cloud_storage_test: tool({
|
|
247
|
-
description: "云存储安全测试。参数: bucket_url(存储桶URL)",
|
|
248
|
-
args: {
|
|
249
|
-
bucket_url: tool.schema.string(),
|
|
250
|
-
},
|
|
251
|
-
async execute(args, ctx) {
|
|
252
|
-
const deps = checkDeps(ctx);
|
|
253
|
-
const corePath = getCorePath(ctx);
|
|
254
|
-
const cmd = `${deps}python3 -c "
|
|
255
|
-
import sys
|
|
256
|
-
sys.path.insert(0, '${corePath}')
|
|
257
|
-
from cloud_storage_tester import CloudStorageTester
|
|
258
|
-
tester = CloudStorageTester()
|
|
259
|
-
result = tester.full_test('${args.bucket_url}')
|
|
260
|
-
print(result)
|
|
261
|
-
"`;
|
|
262
|
-
const result = await ctx.$`${cmd}`;
|
|
263
|
-
return result.toString();
|
|
264
|
-
},
|
|
265
|
-
}),
|
|
266
|
-
|
|
267
|
-
idor_test: tool({
|
|
268
|
-
description: "IDOR 越权测试。参数: endpoint, resource_id",
|
|
106
|
+
description: "GraphQL 安全测试",
|
|
269
107
|
args: {
|
|
270
108
|
endpoint: tool.schema.string(),
|
|
271
|
-
resource_id: tool.schema.string(),
|
|
272
109
|
},
|
|
273
110
|
async execute(args, ctx) {
|
|
274
|
-
const
|
|
275
|
-
const
|
|
276
|
-
const cmd = `${deps}python3 -c "
|
|
111
|
+
const corePath = getCorePath(ctx.directory);
|
|
112
|
+
const script = `
|
|
277
113
|
import sys
|
|
278
114
|
sys.path.insert(0, '${corePath}')
|
|
279
|
-
|
|
280
|
-
|
|
281
|
-
|
|
282
|
-
print(
|
|
283
|
-
|
|
284
|
-
|
|
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}")
|
|
121
|
+
`;
|
|
122
|
+
const result = await ctx.$`python3 -c ${script}`;
|
|
285
123
|
return result.toString();
|
|
286
124
|
},
|
|
287
125
|
}),
|
|
288
126
|
|
|
289
127
|
sqli_test: tool({
|
|
290
|
-
description: "SQL
|
|
128
|
+
description: "SQL 注入测试",
|
|
291
129
|
args: {
|
|
292
130
|
endpoint: tool.schema.string(),
|
|
293
131
|
param: tool.schema.string(),
|
|
294
132
|
},
|
|
295
133
|
async execute(args, ctx) {
|
|
296
|
-
const
|
|
297
|
-
const
|
|
298
|
-
const cmd = `${deps}python3 -c "
|
|
134
|
+
const corePath = getCorePath(ctx.directory);
|
|
135
|
+
const script = `
|
|
299
136
|
import sys
|
|
300
137
|
sys.path.insert(0, '${corePath}')
|
|
301
|
-
|
|
302
|
-
|
|
303
|
-
|
|
304
|
-
print(
|
|
305
|
-
|
|
306
|
-
|
|
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}")
|
|
144
|
+
`;
|
|
145
|
+
const result = await ctx.$`python3 -c ${script}`;
|
|
307
146
|
return result.toString();
|
|
308
147
|
},
|
|
309
148
|
}),
|
|
310
149
|
|
|
311
|
-
|
|
312
|
-
description: "
|
|
150
|
+
idor_test: tool({
|
|
151
|
+
description: "IDOR 越权测试",
|
|
313
152
|
args: {
|
|
314
153
|
endpoint: tool.schema.string(),
|
|
154
|
+
resource_id: tool.schema.string(),
|
|
315
155
|
},
|
|
316
156
|
async execute(args, ctx) {
|
|
317
|
-
const
|
|
318
|
-
const
|
|
319
|
-
const cmd = `${deps}python3 -c "
|
|
157
|
+
const corePath = getCorePath(ctx.directory);
|
|
158
|
+
const script = `
|
|
320
159
|
import sys
|
|
321
160
|
sys.path.insert(0, '${corePath}')
|
|
322
|
-
|
|
323
|
-
|
|
324
|
-
|
|
325
|
-
print(
|
|
326
|
-
|
|
327
|
-
|
|
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}")
|
|
167
|
+
`;
|
|
168
|
+
const result = await ctx.$`python3 -c ${script}`;
|
|
328
169
|
return result.toString();
|
|
329
170
|
},
|
|
330
171
|
}),
|
|
331
172
|
},
|
|
332
|
-
|
|
333
|
-
config: async (config) => {
|
|
334
|
-
if (!config.agent) {
|
|
335
|
-
config.agent = {};
|
|
336
|
-
}
|
|
337
|
-
|
|
338
|
-
const agents = config.agent as Record<string, AgentConfig>;
|
|
339
|
-
|
|
340
|
-
agents["api-cyber-supervisor"] = {
|
|
341
|
-
description: "API安全测试编排者。协调完整扫描流程,永不停止。",
|
|
342
|
-
mode: "primary",
|
|
343
|
-
prompt: CYBER_SUPERVISOR_PROMPT,
|
|
344
|
-
};
|
|
345
|
-
|
|
346
|
-
agents["api-probing-miner"] = {
|
|
347
|
-
description: "漏洞挖掘专家。专注发现和验证 API 漏洞。",
|
|
348
|
-
mode: "subagent",
|
|
349
|
-
prompt: PROBING_MINER_PROMPT,
|
|
350
|
-
};
|
|
351
|
-
|
|
352
|
-
agents["api-resource-specialist"] = {
|
|
353
|
-
description: "资源探测专家。专注采集和发现 API 端点。",
|
|
354
|
-
mode: "subagent",
|
|
355
|
-
prompt: RESOURCE_SPECIALIST_PROMPT,
|
|
356
|
-
};
|
|
357
|
-
|
|
358
|
-
agents["api-vuln-verifier"] = {
|
|
359
|
-
description: "漏洞验证专家。验证和确认安全漏洞。",
|
|
360
|
-
mode: "subagent",
|
|
361
|
-
prompt: VULN_VERIFIER_PROMPT,
|
|
362
|
-
};
|
|
363
|
-
|
|
364
|
-
console.log("[api-security-testing] Agents registered:", Object.keys(agents));
|
|
365
|
-
},
|
|
366
173
|
};
|
|
367
174
|
};
|
|
368
175
|
|
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();
|