opencode-api-security-testing 4.0.1 → 5.2.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/SKILL.md +6 -0
- package/agents/api-cyber-supervisor.md +5 -3
- package/package.json +48 -47
- package/postinstall.mjs +243 -39
- package/src/index.ts +318 -25
- package/src/tools/endpoint-discover.ts +325 -0
- package/src/tools/report-generator.ts +355 -0
- package/src/utils/env-checker.ts +264 -0
|
@@ -0,0 +1,264 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Environment Checker & Auto-Installer
|
|
3
|
+
*
|
|
4
|
+
* Detects missing dependencies and installs them automatically.
|
|
5
|
+
* Supports: Python, pip packages, Playwright, Node.js modules
|
|
6
|
+
*/
|
|
7
|
+
|
|
8
|
+
import { execSync } from "child_process";
|
|
9
|
+
import { existsSync } from "fs";
|
|
10
|
+
import { join } from "path";
|
|
11
|
+
|
|
12
|
+
export interface EnvCheckResult {
|
|
13
|
+
python: boolean;
|
|
14
|
+
pythonVersion: string | null;
|
|
15
|
+
pip: boolean;
|
|
16
|
+
playwright: boolean;
|
|
17
|
+
playwrightInstalled: boolean;
|
|
18
|
+
missingPackages: string[];
|
|
19
|
+
autoInstalled: string[];
|
|
20
|
+
errors: string[];
|
|
21
|
+
}
|
|
22
|
+
|
|
23
|
+
/**
|
|
24
|
+
* Run shell command safely
|
|
25
|
+
*/
|
|
26
|
+
function runCommand(cmd: string, timeout = 30000): { success: boolean; output: string; error: string } {
|
|
27
|
+
try {
|
|
28
|
+
const output = execSync(cmd, {
|
|
29
|
+
encoding: "utf-8",
|
|
30
|
+
timeout,
|
|
31
|
+
stdio: ["pipe", "pipe", "pipe"]
|
|
32
|
+
});
|
|
33
|
+
return { success: true, output: output.trim(), error: "" };
|
|
34
|
+
} catch (error) {
|
|
35
|
+
const err = error as { stdout?: string; stderr?: string; message?: string };
|
|
36
|
+
return {
|
|
37
|
+
success: false,
|
|
38
|
+
output: err.stdout || "",
|
|
39
|
+
error: err.stderr || err.message || "Unknown error"
|
|
40
|
+
};
|
|
41
|
+
}
|
|
42
|
+
}
|
|
43
|
+
|
|
44
|
+
/**
|
|
45
|
+
* Check if Python is available
|
|
46
|
+
*/
|
|
47
|
+
function checkPython(): { available: boolean; version: string | null } {
|
|
48
|
+
// Try python3 first, then python
|
|
49
|
+
for (const cmd of ["python3 --version", "python --version"]) {
|
|
50
|
+
const result = runCommand(cmd, 5000);
|
|
51
|
+
if (result.success) {
|
|
52
|
+
const versionMatch = result.output.match(/Python\s+(\d+\.\d+\.\d+)/i) ||
|
|
53
|
+
result.error.match(/Python\s+(\d+\.\d+\.\d+)/i);
|
|
54
|
+
return {
|
|
55
|
+
available: true,
|
|
56
|
+
version: versionMatch ? versionMatch[1] : "unknown"
|
|
57
|
+
};
|
|
58
|
+
}
|
|
59
|
+
}
|
|
60
|
+
return { available: false, version: null };
|
|
61
|
+
}
|
|
62
|
+
|
|
63
|
+
/**
|
|
64
|
+
* Check if pip is available
|
|
65
|
+
*/
|
|
66
|
+
function checkPip(): boolean {
|
|
67
|
+
for (const cmd of ["pip3 --version", "pip --version", "python3 -m pip --version", "python -m pip --version"]) {
|
|
68
|
+
const result = runCommand(cmd, 5000);
|
|
69
|
+
if (result.success) return true;
|
|
70
|
+
}
|
|
71
|
+
return false;
|
|
72
|
+
}
|
|
73
|
+
|
|
74
|
+
/**
|
|
75
|
+
* Check if a Python package is installed
|
|
76
|
+
*/
|
|
77
|
+
function checkPythonPackage(packageName: string): boolean {
|
|
78
|
+
const result = runCommand(`python3 -c "import ${packageName}" 2>/dev/null || python -c "import ${packageName}" 2>/dev/null`, 5000);
|
|
79
|
+
return result.success;
|
|
80
|
+
}
|
|
81
|
+
|
|
82
|
+
/**
|
|
83
|
+
* Install a Python package
|
|
84
|
+
*/
|
|
85
|
+
function installPythonPackage(packageName: string): { success: boolean; error: string } {
|
|
86
|
+
for (const cmd of [
|
|
87
|
+
`pip3 install -q ${packageName}`,
|
|
88
|
+
`pip install -q ${packageName}`,
|
|
89
|
+
`python3 -m pip install -q ${packageName}`,
|
|
90
|
+
`python -m pip install -q ${packageName}`
|
|
91
|
+
]) {
|
|
92
|
+
const result = runCommand(cmd, 60000);
|
|
93
|
+
if (result.success) return { success: true, error: "" };
|
|
94
|
+
}
|
|
95
|
+
return { success: false, error: `Failed to install ${packageName}` };
|
|
96
|
+
}
|
|
97
|
+
|
|
98
|
+
/**
|
|
99
|
+
* Check if Playwright browsers are installed
|
|
100
|
+
*/
|
|
101
|
+
function checkPlaywright(): { installed: boolean; browsersInstalled: boolean } {
|
|
102
|
+
// Check if playwright Python package is installed
|
|
103
|
+
const hasPackage = checkPythonPackage("playwright");
|
|
104
|
+
|
|
105
|
+
// Check if browsers are installed
|
|
106
|
+
const homeDir = process.env.HOME || process.env.USERPROFILE || "/root";
|
|
107
|
+
const pwCachePath = join(homeDir, ".cache", "ms-playwright");
|
|
108
|
+
const browsersInstalled = existsSync(pwCachePath);
|
|
109
|
+
|
|
110
|
+
return { installed: hasPackage, browsersInstalled };
|
|
111
|
+
}
|
|
112
|
+
|
|
113
|
+
/**
|
|
114
|
+
* Install Playwright
|
|
115
|
+
*/
|
|
116
|
+
function installPlaywright(): { success: boolean; error: string } {
|
|
117
|
+
console.log("[env-checker] Installing Playwright Python package...");
|
|
118
|
+
const pkgResult = installPythonPackage("playwright");
|
|
119
|
+
if (!pkgResult.success) return pkgResult;
|
|
120
|
+
|
|
121
|
+
console.log("[env-checker] Installing Playwright browsers (chromium)...");
|
|
122
|
+
const browserResult = runCommand("python3 -m playwright install chromium", 300000);
|
|
123
|
+
if (browserResult.success) {
|
|
124
|
+
return { success: true, error: "" };
|
|
125
|
+
}
|
|
126
|
+
|
|
127
|
+
// Try with python fallback
|
|
128
|
+
const browserResult2 = runCommand("python -m playwright install chromium", 300000);
|
|
129
|
+
if (browserResult2.success) {
|
|
130
|
+
return { success: true, error: "" };
|
|
131
|
+
}
|
|
132
|
+
|
|
133
|
+
return { success: false, error: `Failed to install Playwright browsers: ${browserResult.error}` };
|
|
134
|
+
}
|
|
135
|
+
|
|
136
|
+
/**
|
|
137
|
+
* Check if Node.js is available
|
|
138
|
+
*/
|
|
139
|
+
function checkNode(): { available: boolean; version: string | null } {
|
|
140
|
+
const result = runCommand("node --version", 5000);
|
|
141
|
+
if (result.success) {
|
|
142
|
+
return { available: true, version: result.output };
|
|
143
|
+
}
|
|
144
|
+
return { available: false, version: null };
|
|
145
|
+
}
|
|
146
|
+
|
|
147
|
+
/**
|
|
148
|
+
* Run full environment check and auto-install missing dependencies
|
|
149
|
+
*/
|
|
150
|
+
export function checkAndFixEnvironment(): EnvCheckResult {
|
|
151
|
+
const result: EnvCheckResult = {
|
|
152
|
+
python: false,
|
|
153
|
+
pythonVersion: null,
|
|
154
|
+
pip: false,
|
|
155
|
+
playwright: false,
|
|
156
|
+
playwrightInstalled: false,
|
|
157
|
+
missingPackages: [],
|
|
158
|
+
autoInstalled: [],
|
|
159
|
+
errors: []
|
|
160
|
+
};
|
|
161
|
+
|
|
162
|
+
console.log("[env-checker] Starting environment check...");
|
|
163
|
+
|
|
164
|
+
// Check Python
|
|
165
|
+
const pythonCheck = checkPython();
|
|
166
|
+
result.python = pythonCheck.available;
|
|
167
|
+
result.pythonVersion = pythonCheck.version;
|
|
168
|
+
|
|
169
|
+
if (!result.python) {
|
|
170
|
+
result.errors.push("Python 3 is not installed. Please install Python 3.8+ from https://python.org");
|
|
171
|
+
return result;
|
|
172
|
+
}
|
|
173
|
+
|
|
174
|
+
console.log(`[env-checker] Python ${result.pythonVersion} detected`);
|
|
175
|
+
|
|
176
|
+
// Check pip
|
|
177
|
+
result.pip = checkPip();
|
|
178
|
+
if (!result.pip) {
|
|
179
|
+
result.errors.push("pip is not available. Please install pip.");
|
|
180
|
+
return result;
|
|
181
|
+
}
|
|
182
|
+
|
|
183
|
+
// Check required Python packages
|
|
184
|
+
const requiredPackages = ["requests", "beautifulsoup4"];
|
|
185
|
+
const optionalPackages = ["playwright", "websocket-client", "urllib3"];
|
|
186
|
+
|
|
187
|
+
for (const pkg of requiredPackages) {
|
|
188
|
+
if (!checkPythonPackage(pkg)) {
|
|
189
|
+
console.log(`[env-checker] Installing missing package: ${pkg}`);
|
|
190
|
+
const installResult = installPythonPackage(pkg);
|
|
191
|
+
if (installResult.success) {
|
|
192
|
+
result.autoInstalled.push(pkg);
|
|
193
|
+
console.log(`[env-checker] ✓ ${pkg} installed`);
|
|
194
|
+
} else {
|
|
195
|
+
result.missingPackages.push(pkg);
|
|
196
|
+
result.errors.push(`Failed to install ${pkg}: ${installResult.error}`);
|
|
197
|
+
}
|
|
198
|
+
}
|
|
199
|
+
}
|
|
200
|
+
|
|
201
|
+
// Check optional packages
|
|
202
|
+
for (const pkg of optionalPackages) {
|
|
203
|
+
if (!checkPythonPackage(pkg)) {
|
|
204
|
+
console.log(`[env-checker] Installing optional package: ${pkg}`);
|
|
205
|
+
const installResult = installPythonPackage(pkg);
|
|
206
|
+
if (installResult.success) {
|
|
207
|
+
result.autoInstalled.push(pkg);
|
|
208
|
+
console.log(`[env-checker] ✓ ${pkg} installed`);
|
|
209
|
+
} else {
|
|
210
|
+
result.missingPackages.push(pkg);
|
|
211
|
+
}
|
|
212
|
+
}
|
|
213
|
+
}
|
|
214
|
+
|
|
215
|
+
// Check Playwright
|
|
216
|
+
const pwCheck = checkPlaywright();
|
|
217
|
+
result.playwright = pwCheck.installed;
|
|
218
|
+
result.playwrightInstalled = pwCheck.browsersInstalled;
|
|
219
|
+
|
|
220
|
+
if (!pwCheck.installed || !pwCheck.browsersInstalled) {
|
|
221
|
+
console.log("[env-checker] Playwright not fully installed, installing...");
|
|
222
|
+
const pwInstallResult = installPlaywright();
|
|
223
|
+
if (pwInstallResult.success) {
|
|
224
|
+
result.playwright = true;
|
|
225
|
+
result.playwrightInstalled = true;
|
|
226
|
+
result.autoInstalled.push("playwright+browsers");
|
|
227
|
+
console.log("[env-checker] ✓ Playwright installed");
|
|
228
|
+
} else {
|
|
229
|
+
result.errors.push(`Playwright installation failed: ${pwInstallResult.error}`);
|
|
230
|
+
console.log(`[env-checker] ⚠ Playwright installation failed: ${pwInstallResult.error}`);
|
|
231
|
+
console.log("[env-checker] browser_collect tool will not work until Playwright is installed");
|
|
232
|
+
}
|
|
233
|
+
}
|
|
234
|
+
|
|
235
|
+
// Summary
|
|
236
|
+
console.log("\n[env-checker] Environment check complete:");
|
|
237
|
+
console.log(` Python: ${result.python ? "✓ " + result.pythonVersion : "✗"}`);
|
|
238
|
+
console.log(` pip: ${result.pip ? "✓" : "✗"}`);
|
|
239
|
+
console.log(` Playwright: ${result.playwrightInstalled ? "✓" : "✗"}`);
|
|
240
|
+
if (result.autoInstalled.length > 0) {
|
|
241
|
+
console.log(` Auto-installed: ${result.autoInstalled.join(", ")}`);
|
|
242
|
+
}
|
|
243
|
+
if (result.errors.length > 0) {
|
|
244
|
+
console.log(` Errors: ${result.errors.join("; ")}`);
|
|
245
|
+
}
|
|
246
|
+
|
|
247
|
+
return result;
|
|
248
|
+
}
|
|
249
|
+
|
|
250
|
+
/**
|
|
251
|
+
* Generate a shell command prefix that ensures dependencies are installed
|
|
252
|
+
*/
|
|
253
|
+
export function getDepCheckCommand(ctx: { directory: string }): string {
|
|
254
|
+
const { existsSync } = require("fs");
|
|
255
|
+
const { join } = require("path");
|
|
256
|
+
|
|
257
|
+
const skillPath = join(ctx.directory, "skills/api-security-testing");
|
|
258
|
+
const reqFile = join(skillPath, "requirements.txt");
|
|
259
|
+
|
|
260
|
+
if (existsSync(reqFile)) {
|
|
261
|
+
return `pip install -q -r "${reqFile}" 2>/dev/null; `;
|
|
262
|
+
}
|
|
263
|
+
return "";
|
|
264
|
+
}
|