@pwddd/skills-scanner 3.0.6 → 3.0.7
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/README.md +12 -32
- package/index.ts +21 -17
- package/openclaw.plugin.json +1 -1
- package/package.json +1 -1
- package/skills/skills-scanner/SKILL.md +29 -34
- package/skills/skills-scanner/scan.py +2 -2
- package/src/commands.ts +302 -302
- package/src/deps.ts +77 -112
- package/src/report.ts +2 -2
- package/src/scanner.ts +6 -2
- package/src/watcher.ts +9 -9
package/src/deps.ts
CHANGED
|
@@ -1,112 +1,77 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* Dependency management module
|
|
3
|
-
*/
|
|
4
|
-
|
|
5
|
-
import { execSync } from "node:child_process";
|
|
6
|
-
import {
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
}
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
}
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
}
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
}
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
logger.error(
|
|
71
|
-
|
|
72
|
-
);
|
|
73
|
-
logger.error(
|
|
74
|
-
logger.error(
|
|
75
|
-
return false;
|
|
76
|
-
}
|
|
77
|
-
|
|
78
|
-
logger.info(`[skills-scanner] Installing Python dependencies using ${useUv ? "uv" : "python3"}...`);
|
|
79
|
-
|
|
80
|
-
try {
|
|
81
|
-
const venvDir = join(skillDir, ".venv");
|
|
82
|
-
|
|
83
|
-
if (existsSync(venvDir)) {
|
|
84
|
-
logger.info("[skills-scanner] Cleaning old virtual environment...");
|
|
85
|
-
rmSync(venvDir, { recursive: true, force: true });
|
|
86
|
-
}
|
|
87
|
-
|
|
88
|
-
if (useUv) {
|
|
89
|
-
// Use uv (faster)
|
|
90
|
-
await execAsync(`uv venv "${venvDir}" --python 3.10`);
|
|
91
|
-
logger.info("[skills-scanner] Virtual environment created (uv)");
|
|
92
|
-
|
|
93
|
-
logger.info("[skills-scanner] Installing requests...");
|
|
94
|
-
await execAsync(`uv pip install --python "${venvPython}" requests>=2.31.0`);
|
|
95
|
-
} else {
|
|
96
|
-
// Fallback to standard Python venv + pip
|
|
97
|
-
const pythonCmd = getPythonCommand();
|
|
98
|
-
await execAsync(`${pythonCmd} -m venv "${venvDir}"`);
|
|
99
|
-
logger.info("[skills-scanner] Virtual environment created (python -m venv)");
|
|
100
|
-
|
|
101
|
-
logger.info("[skills-scanner] Installing requests...");
|
|
102
|
-
await execAsync(`"${venvPython}" -m pip install --quiet requests>=2.31.0`);
|
|
103
|
-
}
|
|
104
|
-
|
|
105
|
-
execSync(`"${venvPython}" -c "import requests"`, { stdio: "ignore" });
|
|
106
|
-
logger.info("[skills-scanner] ✅ Dependencies installed successfully");
|
|
107
|
-
return true;
|
|
108
|
-
} catch (err: any) {
|
|
109
|
-
logger.error(`[skills-scanner] ⚠️ Dependency installation failed: ${err.message}`);
|
|
110
|
-
return false;
|
|
111
|
-
}
|
|
112
|
-
}
|
|
1
|
+
/**
|
|
2
|
+
* Dependency management module
|
|
3
|
+
*/
|
|
4
|
+
|
|
5
|
+
import { execSync, exec } from "node:child_process";
|
|
6
|
+
import { promisify } from "node:util";
|
|
7
|
+
|
|
8
|
+
const execAsync = promisify(exec);
|
|
9
|
+
|
|
10
|
+
function detectPythonCommand(): string | null {
|
|
11
|
+
try {
|
|
12
|
+
execSync("python3 --version", { stdio: "ignore" });
|
|
13
|
+
return "python3";
|
|
14
|
+
} catch {
|
|
15
|
+
try {
|
|
16
|
+
execSync("python --version", { stdio: "ignore" });
|
|
17
|
+
return "python";
|
|
18
|
+
} catch {
|
|
19
|
+
return null;
|
|
20
|
+
}
|
|
21
|
+
}
|
|
22
|
+
}
|
|
23
|
+
|
|
24
|
+
export const getPythonCommand = (): string | null => detectPythonCommand();
|
|
25
|
+
export const hasPython = (): boolean => detectPythonCommand() !== null;
|
|
26
|
+
|
|
27
|
+
export function isRequestsInstalled(pythonCmd: string | null): boolean {
|
|
28
|
+
if (!pythonCmd) return false;
|
|
29
|
+
try {
|
|
30
|
+
execSync(`${pythonCmd} -c "import requests"`, { stdio: "ignore" });
|
|
31
|
+
return true;
|
|
32
|
+
} catch {
|
|
33
|
+
return false;
|
|
34
|
+
}
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
export const isPythonReady = (pythonCmd: string | null): boolean =>
|
|
38
|
+
hasPython() && isRequestsInstalled(pythonCmd);
|
|
39
|
+
|
|
40
|
+
export async function ensureDeps(pythonCmd: string | null, logger: any): Promise<boolean> {
|
|
41
|
+
const resolvedPython = pythonCmd ?? getPythonCommand();
|
|
42
|
+
|
|
43
|
+
if (!resolvedPython) {
|
|
44
|
+
logger.error("[skills-scanner] Python not found. Please install Python 3.10+:");
|
|
45
|
+
logger.error("[skills-scanner] - macOS: brew install python3");
|
|
46
|
+
logger.error("[skills-scanner] - Linux: apt-get install python3 python3-pip");
|
|
47
|
+
logger.error("[skills-scanner] - Windows: https://www.python.org/downloads/");
|
|
48
|
+
return false;
|
|
49
|
+
}
|
|
50
|
+
|
|
51
|
+
if (isRequestsInstalled(resolvedPython)) {
|
|
52
|
+
logger.info("[skills-scanner] Python dependencies ready (requests installed)");
|
|
53
|
+
return true;
|
|
54
|
+
}
|
|
55
|
+
|
|
56
|
+
logger.info(`[skills-scanner] Installing requests package using ${resolvedPython}...`);
|
|
57
|
+
|
|
58
|
+
try {
|
|
59
|
+
await execAsync(`${resolvedPython} -m pip install --user --quiet "requests>=2.31.0"`, {
|
|
60
|
+
timeout: 120000,
|
|
61
|
+
});
|
|
62
|
+
|
|
63
|
+
if (isRequestsInstalled(resolvedPython)) {
|
|
64
|
+
logger.info("[skills-scanner] Dependencies installed successfully");
|
|
65
|
+
return true;
|
|
66
|
+
} else {
|
|
67
|
+
throw new Error("requests package not found after installation");
|
|
68
|
+
}
|
|
69
|
+
} catch (err: any) {
|
|
70
|
+
logger.error(`[skills-scanner] Dependency installation failed: ${err.message}`);
|
|
71
|
+
logger.error(`[skills-scanner] Please install manually:`);
|
|
72
|
+
logger.error(`[skills-scanner] ${resolvedPython} -m pip install --user requests`);
|
|
73
|
+
logger.error(`[skills-scanner] Or with sudo:`);
|
|
74
|
+
logger.error(`[skills-scanner] sudo ${resolvedPython} -m pip install requests`);
|
|
75
|
+
return false;
|
|
76
|
+
}
|
|
77
|
+
}
|
package/src/report.ts
CHANGED
|
@@ -15,7 +15,7 @@ export async function buildDailyReport(
|
|
|
15
15
|
useLLM: boolean,
|
|
16
16
|
policy: string,
|
|
17
17
|
logger: any,
|
|
18
|
-
|
|
18
|
+
pythonCmd: string | null,
|
|
19
19
|
scanScript: string
|
|
20
20
|
): Promise<string> {
|
|
21
21
|
const now = new Date();
|
|
@@ -41,7 +41,7 @@ export async function buildDailyReport(
|
|
|
41
41
|
for (const dir of dirs) {
|
|
42
42
|
if (!existsSync(dir)) continue;
|
|
43
43
|
const tmpJson = join(STATE_DIR, `tmp-${Date.now()}.json`);
|
|
44
|
-
await runScan(
|
|
44
|
+
await runScan(pythonCmd, scanScript, "batch", dir, {
|
|
45
45
|
behavioral,
|
|
46
46
|
recursive: true,
|
|
47
47
|
jsonOut: tmpJson,
|
package/src/scanner.ts
CHANGED
|
@@ -9,12 +9,16 @@ import type { ScanOptions, ScanResult } from "./types.js";
|
|
|
9
9
|
const execAsync = promisify(exec);
|
|
10
10
|
|
|
11
11
|
export async function runScan(
|
|
12
|
-
|
|
12
|
+
pythonCmd: string | null,
|
|
13
13
|
scanScript: string,
|
|
14
14
|
mode: "scan" | "batch" | "clawhub",
|
|
15
15
|
target: string,
|
|
16
16
|
opts: ScanOptions = {}
|
|
17
17
|
): Promise<ScanResult> {
|
|
18
|
+
if (!pythonCmd) {
|
|
19
|
+
return { exitCode: 1, output: "Python is not available on this host" };
|
|
20
|
+
}
|
|
21
|
+
|
|
18
22
|
const args = [mode, target];
|
|
19
23
|
if (opts.detailed) args.push("--detailed");
|
|
20
24
|
if (opts.behavioral) args.push("--behavioral");
|
|
@@ -24,7 +28,7 @@ export async function runScan(
|
|
|
24
28
|
if (opts.jsonOut) args.push("--json", opts.jsonOut);
|
|
25
29
|
if (opts.apiUrl) args.unshift("--api-url", opts.apiUrl);
|
|
26
30
|
|
|
27
|
-
const cmd = `"${
|
|
31
|
+
const cmd = `"${pythonCmd}" "${scanScript}" ${args.map((a) => `"${a}"`).join(" ")}`;
|
|
28
32
|
|
|
29
33
|
try {
|
|
30
34
|
const env = { ...process.env };
|
package/src/watcher.ts
CHANGED
|
@@ -17,7 +17,7 @@ export async function handleNewSkill(
|
|
|
17
17
|
policy: string,
|
|
18
18
|
notifyFn: (msg: string) => void,
|
|
19
19
|
logger: any,
|
|
20
|
-
|
|
20
|
+
pythonCmd: string | null,
|
|
21
21
|
scanScript: string,
|
|
22
22
|
quarantineDir: string
|
|
23
23
|
): Promise<void> {
|
|
@@ -27,7 +27,7 @@ export async function handleNewSkill(
|
|
|
27
27
|
logger.info(`[skills-scanner] 🔍 检测到新 Skill,开始安装前扫描: ${name}`);
|
|
28
28
|
notifyFn(`🔍 检测到新 Skill \`${name}\`,正在安全扫描...`);
|
|
29
29
|
|
|
30
|
-
const res = await runScan(
|
|
30
|
+
const res = await runScan(pythonCmd, scanScript, "scan", skillPath, {
|
|
31
31
|
behavioral,
|
|
32
32
|
detailed: true,
|
|
33
33
|
apiUrl,
|
|
@@ -77,7 +77,7 @@ export function startWatcher(
|
|
|
77
77
|
policy: string,
|
|
78
78
|
notifyFn: (msg: string) => void,
|
|
79
79
|
logger: any,
|
|
80
|
-
|
|
80
|
+
pythonCmd: string | null,
|
|
81
81
|
scanScript: string,
|
|
82
82
|
quarantineDir: string
|
|
83
83
|
): () => void {
|
|
@@ -105,12 +105,12 @@ export function startWatcher(
|
|
|
105
105
|
apiUrl,
|
|
106
106
|
useLLM,
|
|
107
107
|
policy,
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
108
|
+
notifyFn,
|
|
109
|
+
logger,
|
|
110
|
+
pythonCmd,
|
|
111
|
+
scanScript,
|
|
112
|
+
quarantineDir
|
|
113
|
+
);
|
|
114
114
|
}, 500)
|
|
115
115
|
);
|
|
116
116
|
});
|