opencode-api-security-testing 3.0.3 → 4.0.0

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