clawvet 0.12.1 → 0.12.3
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 +51 -0
- package/dist/index.js +33 -5
- package/dist/index.js.map +1 -1
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -54,6 +54,57 @@ clawvet audit
|
|
|
54
54
|
clawvet watch --threshold 50
|
|
55
55
|
```
|
|
56
56
|
|
|
57
|
+
## Install-time enforcement
|
|
58
|
+
|
|
59
|
+
`clawvet gate` is an OpenClaw [`security.installPolicy`](https://docs.openclaw.ai/tools/skills-config)
|
|
60
|
+
hook. OpenClaw stages the source, writes the install metadata to the command's
|
|
61
|
+
stdin, and reads back one JSON verdict before the install completes. It runs
|
|
62
|
+
whether or not an agent remembers to scan anything.
|
|
63
|
+
|
|
64
|
+
Print a ready-to-paste config with the paths already resolved:
|
|
65
|
+
|
|
66
|
+
```bash
|
|
67
|
+
clawvet gate --print-config
|
|
68
|
+
```
|
|
69
|
+
|
|
70
|
+
Use that rather than writing the paths by hand. OpenClaw requires the policy
|
|
71
|
+
command and any interpreter script argument to be regular files, and rejects
|
|
72
|
+
symlinks. `npm i -g clawvet` installs a symlink into `bin/`, so pointing
|
|
73
|
+
`installPolicy` at `which clawvet` fails. `--print-config` resolves through to
|
|
74
|
+
the real `dist/index.js` and invokes it via an absolute `node` path.
|
|
75
|
+
|
|
76
|
+
`targets` is `["skill"]`. ClawVet reads `SKILL.md` and the files it references,
|
|
77
|
+
so a plugin that ships no `SKILL.md` has no instruction layer to inspect and is
|
|
78
|
+
allowed through. Do not add `"plugin"` until that is a real scanner.
|
|
79
|
+
|
|
80
|
+
Verdicts map onto ClawVet's own vocabulary:
|
|
81
|
+
|
|
82
|
+
| Risk score | Grade | ClawVet | installPolicy |
|
|
83
|
+
|-----------|-------|---------|---------------|
|
|
84
|
+
| 0-25 | A / B | `approve` | `allow` |
|
|
85
|
+
| 26-75 | C / D | `warn` | `warn` |
|
|
86
|
+
| 76-100 | F | `block` | `block` |
|
|
87
|
+
|
|
88
|
+
A `warn` is not a pass. OpenClaw's docs are explicit: "A warning stops the
|
|
89
|
+
install before commit." An interactive CLI install asks the operator to confirm,
|
|
90
|
+
and Gateway-backed or automatic installs stay blocked without an
|
|
91
|
+
operator-confirmation path.
|
|
92
|
+
|
|
93
|
+
**Choosing a threshold.** `--block-at <score>` moves the blocking line, default
|
|
94
|
+
76. ClawHavoc campaign fixtures score 28-36, so at the default they warn, which
|
|
95
|
+
stops the install pending review rather than denying it outright. `--block-at
|
|
96
|
+
26` denies them outright at the cost of denying dual-use skills that score above
|
|
97
|
+
26. A finding marked `disqualifying`, such as a known-malicious C2 address,
|
|
98
|
+
blocks at any threshold.
|
|
99
|
+
|
|
100
|
+
Static passes only, so it fits the install timeout: 119 ms end to end including
|
|
101
|
+
node startup, against the 10 s default. The semantic pass is never reached, so
|
|
102
|
+
no API key and no network round trip.
|
|
103
|
+
|
|
104
|
+
Anything the host cannot parse fails closed. A malformed payload, an unreadable
|
|
105
|
+
staged path, or a scanner error returns `block` with a reason rather than a bare
|
|
106
|
+
non-zero exit, so the operator sees why the install stopped.
|
|
107
|
+
|
|
57
108
|
## What it detects
|
|
58
109
|
|
|
59
110
|
ClawVet runs a 6-pass analysis on every skill:
|
package/dist/index.js
CHANGED
|
@@ -4,7 +4,7 @@
|
|
|
4
4
|
import { Command } from "commander";
|
|
5
5
|
import { readFileSync as readFileSync8 } from "fs";
|
|
6
6
|
import { execFile } from "child_process";
|
|
7
|
-
import { fileURLToPath } from "url";
|
|
7
|
+
import { fileURLToPath as fileURLToPath2 } from "url";
|
|
8
8
|
import { dirname as dirname5, join as join8 } from "path";
|
|
9
9
|
|
|
10
10
|
// src/commands/scan.ts
|
|
@@ -1890,8 +1890,9 @@ async function badgeCommand(target, options) {
|
|
|
1890
1890
|
}
|
|
1891
1891
|
|
|
1892
1892
|
// src/commands/gate.ts
|
|
1893
|
-
import { readFileSync as readFileSync7, existsSync as existsSync6, statSync as statSync4 } from "fs";
|
|
1893
|
+
import { readFileSync as readFileSync7, existsSync as existsSync6, statSync as statSync4, realpathSync } from "fs";
|
|
1894
1894
|
import { join as join7, basename as basename6, dirname as dirname4 } from "path";
|
|
1895
|
+
import { fileURLToPath } from "url";
|
|
1895
1896
|
var PROTOCOL_VERSION = 1;
|
|
1896
1897
|
var DECISION = {
|
|
1897
1898
|
approve: "allow",
|
|
@@ -1927,8 +1928,35 @@ function summarize(name, grade, score, findings) {
|
|
|
1927
1928
|
const head = `ClawVet graded "${name}" ${grade} (risk ${score}/100).`;
|
|
1928
1929
|
return worst.length ? `${head} ${worst.join("; ")}.` : head;
|
|
1929
1930
|
}
|
|
1931
|
+
function printConfig(blockAt) {
|
|
1932
|
+
const self = realpathSync(fileURLToPath(import.meta.url));
|
|
1933
|
+
const args = [self, "gate"];
|
|
1934
|
+
if (blockAt !== DEFAULT_BLOCK_AT) args.push("--block-at", String(blockAt));
|
|
1935
|
+
const config = {
|
|
1936
|
+
security: {
|
|
1937
|
+
installPolicy: {
|
|
1938
|
+
enabled: true,
|
|
1939
|
+
// Only "skill". ClawVet reads SKILL.md and the files it references. A
|
|
1940
|
+
// plugin with no SKILL.md has no instruction layer to inspect and is
|
|
1941
|
+
// allowed through, so listing "plugin" here would claim a protection
|
|
1942
|
+
// that does not exist yet.
|
|
1943
|
+
targets: ["skill"],
|
|
1944
|
+
exec: {
|
|
1945
|
+
command: realpathSync(process.execPath),
|
|
1946
|
+
args,
|
|
1947
|
+
timeoutMs: 1e4
|
|
1948
|
+
}
|
|
1949
|
+
}
|
|
1950
|
+
}
|
|
1951
|
+
};
|
|
1952
|
+
process.stdout.write(JSON.stringify(config, null, 2) + "\n");
|
|
1953
|
+
}
|
|
1930
1954
|
async function gateCommand(options = {}) {
|
|
1931
1955
|
const blockAt = Number.isFinite(options.blockAt) ? options.blockAt : DEFAULT_BLOCK_AT;
|
|
1956
|
+
if (options.printConfig) {
|
|
1957
|
+
printConfig(blockAt);
|
|
1958
|
+
return;
|
|
1959
|
+
}
|
|
1932
1960
|
let req;
|
|
1933
1961
|
try {
|
|
1934
1962
|
const raw = await readStdin();
|
|
@@ -2000,7 +2028,7 @@ function openUrl(url) {
|
|
|
2000
2028
|
}
|
|
2001
2029
|
function readPackageVersion() {
|
|
2002
2030
|
try {
|
|
2003
|
-
const here = dirname5(
|
|
2031
|
+
const here = dirname5(fileURLToPath2(import.meta.url));
|
|
2004
2032
|
const pkg = JSON.parse(readFileSync8(join8(here, "..", "package.json"), "utf-8"));
|
|
2005
2033
|
return pkg.version;
|
|
2006
2034
|
} catch {
|
|
@@ -2022,14 +2050,14 @@ program.command("scan").description("Scan a skill for security threats").argumen
|
|
|
2022
2050
|
quiet: opts.quiet
|
|
2023
2051
|
});
|
|
2024
2052
|
});
|
|
2025
|
-
program.command("gate").alias("policy").description("OpenClaw install-policy hook: staged install metadata on stdin, JSON verdict on stdout").option("--block-at <score>", "Risk score at or above which to block the install", "76").action(async (opts) => {
|
|
2053
|
+
program.command("gate").alias("policy").description("OpenClaw install-policy hook: staged install metadata on stdin, JSON verdict on stdout").option("--block-at <score>", "Risk score at or above which to block the install", "76").option("--print-config", "Print a ready-to-paste OpenClaw installPolicy config with resolved paths").action(async (opts) => {
|
|
2026
2054
|
if (process.argv[2] === "policy") {
|
|
2027
2055
|
process.stderr.write(
|
|
2028
2056
|
`clawvet: 'policy' is deprecated, use 'gate'. Update args to ["gate"] in your installPolicy config.
|
|
2029
2057
|
`
|
|
2030
2058
|
);
|
|
2031
2059
|
}
|
|
2032
|
-
await gateCommand({ blockAt: Number(opts.blockAt) });
|
|
2060
|
+
await gateCommand({ blockAt: Number(opts.blockAt), printConfig: opts.printConfig });
|
|
2033
2061
|
});
|
|
2034
2062
|
program.command("audit").description("Scan all installed OpenClaw skills").option("--dir <path>", "Custom skills directory to scan").action(async (opts) => {
|
|
2035
2063
|
await auditCommand({ dir: opts.dir });
|
package/dist/index.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../src/index.ts","../src/commands/scan.ts","../../shared/src/patterns.ts","../../shared/src/scanner/skill-parser.ts","../../shared/src/scanner/static-analysis.ts","../../shared/src/scanner/metadata-validator.ts","../../shared/src/scanner/dependency-checker.ts","../../shared/src/scanner/typosquat-detector.ts","../../shared/src/scanner/context-classifier.ts","../../shared/src/scanner/risk-scorer.ts","../../shared/src/scanner/cache.ts","../../shared/src/scanner/index.ts","../src/output/terminal.ts","../src/output/json.ts","../src/output/sarif.ts","../src/telemetry.ts","../src/assemble.ts","../src/feedback.ts","../src/commands/audit.ts","../src/commands/watch.ts","../src/commands/badge.ts","../src/commands/gate.ts"],"sourcesContent":["import { Command } from \"commander\";\nimport { readFileSync } from \"node:fs\";\nimport { execFile } from \"node:child_process\";\nimport { fileURLToPath } from \"node:url\";\nimport { dirname, join } from \"node:path\";\nimport { scanCommand } from \"./commands/scan.js\";\nimport { auditCommand } from \"./commands/audit.js\";\nimport { watchCommand } from \"./commands/watch.js\";\nimport { badgeCommand } from \"./commands/badge.js\";\nimport { gateCommand } from \"./commands/gate.js\";\nimport { FEEDBACK_URL, FEEDBACK_DISPLAY_URL } from \"./feedback.js\";\n\n// Open a URL in the user's browser without going through a shell. Using\n// execFile (not exec) means the URL is passed as an argument, never\n// interpolated into a command string a shell would parse — no shell-exec\n// surface even though the URL here is a constant.\nfunction openUrl(url: string): void {\n const child =\n process.platform === \"win32\"\n ? execFile(\"cmd\", [\"/c\", \"start\", \"\", url])\n : process.platform === \"darwin\"\n ? execFile(\"open\", [url])\n : execFile(\"xdg-open\", [url]);\n // Opening a browser is best-effort — never crash the CLI if the opener is\n // missing (e.g. a headless Linux box without xdg-open).\n child.on(\"error\", () => {});\n}\n\nfunction readPackageVersion(): string {\n try {\n const here = dirname(fileURLToPath(import.meta.url));\n const pkg = JSON.parse(readFileSync(join(here, \"..\", \"package.json\"), \"utf-8\"));\n return pkg.version;\n } catch {\n return \"0.0.0\";\n }\n}\n\nconst program = new Command();\n\nprogram\n .name(\"clawvet\")\n .description(\"Skill vetting & supply chain security for OpenClaw\")\n .version(readPackageVersion());\n\nprogram\n .command(\"scan\")\n .description(\"Scan a skill for security threats\")\n .argument(\"<target>\", \"Path to skill folder or SKILL.md file\")\n .option(\"--format <format>\", \"Output format: terminal, json, or sarif\", \"terminal\")\n .option(\"--fail-on <severity>\", \"Exit 1 if findings at this severity or above\")\n .option(\"--semantic\", \"Enable AI semantic analysis (requires ANTHROPIC_API_KEY)\")\n .option(\"--remote\", \"Fetch skill from ClawHub by name instead of local path\")\n .option(\"-q, --quiet\", \"Suppress all output, exit code only (0=pass, 1=fail)\")\n .option(\"--subscribe\", \"Open a prefilled GitHub issue to send feedback\")\n .action(async (target, opts) => {\n if (opts.subscribe) {\n console.log(`Opening ${FEEDBACK_DISPLAY_URL} ...`);\n openUrl(FEEDBACK_URL);\n }\n await scanCommand(target, {\n format: opts.format,\n failOn: opts.failOn,\n semantic: opts.semantic,\n remote: opts.remote,\n quiet: opts.quiet,\n });\n });\n\nprogram\n .command(\"gate\")\n .alias(\"policy\")\n .description(\"OpenClaw install-policy hook: staged install metadata on stdin, JSON verdict on stdout\")\n .option(\"--block-at <score>\", \"Risk score at or above which to block the install\", \"76\")\n .action(async (opts) => {\n // `policy` was the name in 0.12.0. It collides with `openclaw policy`,\n // which lints workspace config rather than gating installs. Kept as an\n // alias so a config written against 0.12.0 keeps working; the notice goes\n // to stderr because stdout carries the JSON verdict the host parses.\n if (process.argv[2] === \"policy\") {\n process.stderr.write(\n \"clawvet: 'policy' is deprecated, use 'gate'. Update args to [\\\"gate\\\"] in your installPolicy config.\\n\"\n );\n }\n await gateCommand({ blockAt: Number(opts.blockAt) });\n });\n\nprogram\n .command(\"audit\")\n .description(\"Scan all installed OpenClaw skills\")\n .option(\"--dir <path>\", \"Custom skills directory to scan\")\n .action(async (opts) => {\n await auditCommand({ dir: opts.dir });\n });\n\nprogram\n .command(\"watch\")\n .description(\"Pre-install hook — blocks risky skill installs\")\n .option(\"--threshold <score>\", \"Risk score threshold (default 50)\", \"50\")\n .option(\"--dir <path>\", \"Custom skills directory to watch\")\n .action(async (opts) => {\n await watchCommand({ threshold: parseInt(opts.threshold), dir: opts.dir });\n });\n\nprogram\n .command(\"badge\")\n .description(\"Generate a trust badge for a skill's README\")\n .argument(\"<target>\", \"Path to skill folder or SKILL.md file\")\n .option(\"--md\", \"Output only the markdown snippet\")\n .action(async (target, opts) => {\n await badgeCommand(target, { markdown: opts.md });\n });\n\nprogram\n .command(\"feedback\")\n .description(\"Open a prefilled GitHub issue to send feedback\")\n .action(async () => {\n console.log(`Opening ${FEEDBACK_DISPLAY_URL} ...`);\n openUrl(FEEDBACK_URL);\n });\n\nprogram.parse();\n","import { readFileSync, existsSync, statSync } from \"node:fs\";\nimport { resolve, join, basename, dirname } from \"node:path\";\nimport chalk from \"chalk\";\nimport { scanSkill } from \"@clawvet/shared\";\nimport { printScanResult } from \"../output/terminal.js\";\nimport { printJsonResult } from \"../output/json.js\";\nimport { printSarifResult } from \"../output/sarif.js\";\nimport { sendTelemetry, hasBeenAsked, setTelemetry, isTelemetryEnabled, getScanCount } from \"../telemetry.js\";\nimport { assembleSkill } from \"../assemble.js\";\nimport { FEEDBACK_DISPLAY_URL } from \"../feedback.js\";\n\nexport interface ScanOptions {\n format?: \"terminal\" | \"json\" | \"sarif\";\n failOn?: \"critical\" | \"high\" | \"medium\" | \"low\";\n semantic?: boolean;\n remote?: boolean;\n quiet?: boolean;\n}\n\nconst SLUG_PATTERN = /^[a-z0-9][a-z0-9_-]{0,63}$/i;\n\nasync function fetchRemoteSkill(slug: string): Promise<string> {\n if (!SLUG_PATTERN.test(slug)) {\n throw new Error(\n `Invalid skill name \"${slug}\". Must be 1-64 chars, alphanumeric + dash/underscore.`\n );\n }\n\n const encoded = encodeURIComponent(slug);\n // The ClawHub catalog API returns the skill record as JSON, with the full\n // SKILL.md content in `skill.description`. The other sources serve raw\n // markdown. Try the catalog first, then fall back to raw endpoints.\n const sources: Array<{ url: string; json: boolean }> = [\n { url: `https://clawhub.ai/api/v1/skills/${encoded}`, json: true },\n { url: `https://clawhub.ai/api/v1/skills/${encoded}/raw`, json: false },\n {\n url: `https://raw.githubusercontent.com/openclaw/skills/main/${encoded}/SKILL.md`,\n json: false,\n },\n ];\n\n for (const { url, json } of sources) {\n try {\n const res = await fetch(url, { signal: AbortSignal.timeout(10000) });\n if (!res.ok) continue;\n\n if (!json) return await res.text();\n\n const body = (await res.json()) as {\n skill?: { description?: string };\n };\n const content = body?.skill?.description;\n if (typeof content === \"string\" && content.includes(\"---\")) {\n return content;\n }\n } catch {\n // try next\n }\n }\n\n throw new Error(\n `Could not fetch skill \"${slug}\" from ClawHub. Check the skill name and try again.`\n );\n}\n\nexport async function scanCommand(\n target: string,\n options: ScanOptions\n): Promise<void> {\n let content: string;\n let fallbackName: string | undefined;\n\n if (options.remote) {\n try {\n process.stderr.write(`Fetching \"${target}\" from ClawHub...\\n`);\n content = await fetchRemoteSkill(target);\n fallbackName = target;\n } catch (err) {\n console.error(\n err instanceof Error ? err.message : \"Failed to fetch remote skill\"\n );\n process.exit(1);\n }\n } else {\n const skillPath = resolve(target);\n let skillFile = skillPath;\n let skillDir: string | undefined;\n\n if (\n existsSync(skillPath) &&\n statSync(skillPath).isDirectory() &&\n existsSync(join(skillPath, \"SKILL.md\"))\n ) {\n skillDir = skillPath;\n skillFile = join(skillPath, \"SKILL.md\");\n }\n\n if (!existsSync(skillFile) || statSync(skillFile).isDirectory()) {\n console.error(`Error: Cannot find SKILL.md at ${skillFile}`);\n console.error(`Hint: If this is a directory of skills, use 'clawvet audit --dir ${target}' instead.`);\n process.exit(1);\n }\n\n const skillMd = readFileSync(skillFile, \"utf-8\");\n content = skillDir ? assembleSkill(skillDir, skillMd) : skillMd;\n fallbackName = basename(dirname(skillFile));\n }\n\n // Load .clawvetban — block skills by name, author, or slug\n const banFile = join(process.cwd(), \".clawvetban\");\n if (existsSync(banFile)) {\n const banEntries = readFileSync(banFile, \"utf-8\")\n .split(\"\\n\")\n .map((l) => l.trim().toLowerCase())\n .filter((l) => l && !l.startsWith(\"#\"));\n\n // Quick parse frontmatter to check name/author before full scan\n const fmMatch = content.match(/^---\\r?\\n([\\s\\S]*?)\\r?\\n---/);\n if (fmMatch) {\n const fmText = fmMatch[1].toLowerCase();\n for (const ban of banEntries) {\n const targetLower = target.toLowerCase();\n if (\n targetLower.includes(ban) ||\n fmText.includes(`name: ${ban}`) ||\n fmText.includes(`author: ${ban}`) ||\n fmText.includes(`slug: ${ban}`)\n ) {\n console.error(\n chalk.bgRed.white.bold(` BANNED `) +\n chalk.red(` Skill matches ban list entry: ${ban}`)\n );\n console.error(chalk.dim(` Source: ${banFile}`));\n process.exit(1);\n }\n }\n }\n }\n\n // Load .clawvetignore\n const ignoreFile = join(process.cwd(), \".clawvetignore\");\n const ignorePatterns: string[] = [];\n if (existsSync(ignoreFile)) {\n const lines = readFileSync(ignoreFile, \"utf-8\").split(\"\\n\");\n for (const line of lines) {\n const trimmed = line.trim();\n if (trimmed && !trimmed.startsWith(\"#\")) {\n ignorePatterns.push(trimmed);\n }\n }\n }\n\n const result = await scanSkill(content, {\n semantic: options.semantic ?? false,\n ignorePatterns: ignorePatterns.length ? ignorePatterns : undefined,\n skillName: fallbackName,\n });\n\n if (!options.quiet) {\n if (options.format === \"sarif\") {\n printSarifResult(result);\n } else if (options.format === \"json\") {\n printJsonResult(result);\n } else {\n printScanResult(result);\n }\n }\n\n // Telemetry: first-run opt-in prompt (only in interactive TTY)\n const isInteractive = !options.quiet && options.format !== \"json\" && options.format !== \"sarif\";\n if (isInteractive) {\n if (!hasBeenAsked() && !isTelemetryEnabled() && process.stdin.isTTY) {\n const readline = await import(\"node:readline\");\n const rl = readline.createInterface({ input: process.stdin, output: process.stderr });\n const answer = await new Promise<string>((resolve) => {\n rl.question(\n chalk.dim(\"Help improve ClawVet — send anonymous usage stats? (y/n) \"),\n (a) => { rl.close(); resolve(a.trim().toLowerCase()); }\n );\n });\n setTelemetry(answer === \"y\" || answer === \"yes\");\n }\n\n }\n\n // Await telemetry so it completes before any process.exit()\n await sendTelemetry(result);\n\n // Show feedback CTA every 5th scan (after increment)\n if (isInteractive && getScanCount() % 5 === 0) {\n console.log(\n chalk.dim(\" \") +\n chalk.cyan(\"Got feedback? → \") +\n chalk.underline.cyan(FEEDBACK_DISPLAY_URL)\n );\n console.log();\n }\n\n const failOn = options.failOn || (options.quiet ? \"high\" : undefined);\n if (failOn) {\n const severityOrder = [\"low\", \"medium\", \"high\", \"critical\"];\n const threshold = severityOrder.indexOf(failOn);\n const hasFailure = result.findings.some(\n (f) => severityOrder.indexOf(f.severity) >= threshold\n );\n if (hasFailure) {\n process.exit(1);\n }\n }\n}\n","import type { ThreatPattern } from \"./types.js\";\n\n// Build regex from parts at runtime to avoid AV false positives on signature strings\nfunction re(parts: string[], flags: string): RegExp {\n return new RegExp(parts.join(\"\"), flags);\n}\n\nexport const THREAT_PATTERNS: ThreatPattern[] = [\n // ═══════════════════════════════════════════════════════\n // CRITICAL: Remote code execution\n // ═══════════════════════════════════════════════════════\n {\n name: \"CURL_PIPE_BASH\",\n pattern: re([\"curl\\\\s+.*\\\\|\\\\s*(ba)?\", \"sh\"], \"gi\"),\n severity: \"critical\",\n category: \"remote_code_execution\",\n title: \"Curl piped to shell\",\n description: \"Downloads and executes remote code directly — classic supply chain attack vector.\",\n codeOnly: true,\n fix: \"Download the script first, inspect it, then execute: `curl -o setup.sh URL && cat setup.sh && bash setup.sh`\",\n },\n {\n name: \"WGET_EXECUTE\",\n pattern: re([\"wget\\\\s+.*&&\\\\s*(ba)?\", \"sh\"], \"gi\"),\n severity: \"critical\",\n category: \"remote_code_execution\",\n title: \"Wget with shell execution\",\n description: \"Downloads and executes remote code via wget.\",\n codeOnly: true,\n fix: \"Download the file first with `wget -O script.sh URL`, review it, then execute.\",\n },\n {\n name: \"EVAL_DYNAMIC\",\n pattern: re([\"ev\", \"al\\\\s*\\\\(\"], \"gi\"),\n severity: \"critical\",\n category: \"remote_code_execution\",\n title: \"Dynamic code evaluation\",\n description: \"Uses dynamic code evaluation which can run arbitrary code.\",\n codeOnly: true,\n fix: \"Replace dynamic evaluation with a safer alternative like JSON.parse() or a sandboxed environment.\",\n },\n {\n name: \"BASE64_DECODE\",\n pattern: re([\"base\", \"64\\\\s+(-d|--dec\", \"ode)\"], \"gi\"),\n severity: \"critical\",\n category: \"obfuscation\",\n title: \"Base64 decode execution\",\n description: \"Decodes base64 content, often used to hide malicious payloads.\",\n codeOnly: true,\n fix: \"Decode and include the command directly so users can review it.\",\n },\n {\n name: \"PYTHON_EXEC\",\n pattern: re([\"pyth\", \"on[3]?\\\\s+-c\"], \"gi\"),\n severity: \"critical\",\n category: \"remote_code_execution\",\n title: \"Python inline execution\",\n description: \"Executes inline Python code which may contain hidden payloads.\",\n codeOnly: true,\n fix: \"Move inline code to a separate .py file so users can review it before execution.\",\n },\n {\n name: \"REVERSE_SHELL\",\n pattern: re([\"\\\\/dev\\\\/tc\", \"p\\\\/|nc\\\\s+-[elp]|nca\", \"t\\\\s+-|mkfi\", \"fo\\\\s+.*\\\\/tmp\"], \"gi\"),\n severity: \"critical\",\n category: \"remote_code_execution\",\n title: \"Reverse shell\",\n description: \"Creates a reverse connection back to an attacker-controlled server.\",\n codeOnly: true,\n fix: \"Remove reverse connection commands — these are almost never legitimate in skills.\",\n },\n {\n name: \"CRON_PERSISTENCE\",\n pattern: re([\"cron\", \"tab\\\\s+-|\\\\/etc\\\\/cro\", \"n|system\", \"ctl\\\\s+enable\"], \"gi\"),\n severity: \"critical\",\n category: \"persistence\",\n title: \"Scheduled task persistence\",\n description: \"Installs a cron job or systemd service for persistent execution after reboot.\",\n codeOnly: true,\n fix: \"Document the scheduled task in the skill description and require explicit user consent before installing.\",\n },\n {\n name: \"PERL_EXEC\",\n pattern: re([\"per\", \"l\\\\s+-e\"], \"gi\"),\n severity: \"critical\",\n category: \"remote_code_execution\",\n title: \"Perl inline execution\",\n description: \"Executes inline Perl code which may contain obfuscated payloads.\",\n codeOnly: true,\n fix: \"Move inline code to a separate .pl file so users can review it before execution.\",\n },\n {\n name: \"NODE_EVAL\",\n pattern: re([\"no\", \"de\\\\s+-e\\\\s\"], \"gi\"),\n severity: \"critical\",\n category: \"remote_code_execution\",\n title: \"Node.js inline execution\",\n description: \"Executes inline Node.js code, often used to hide malicious logic.\",\n codeOnly: true,\n fix: \"Move inline code to a separate .js file so users can review it before execution.\",\n },\n {\n name: \"RUBY_EXEC\",\n pattern: re([\"rub\", \"y\\\\s+-e\"], \"gi\"),\n severity: \"critical\",\n category: \"remote_code_execution\",\n title: \"Ruby inline execution\",\n description: \"Executes inline Ruby code which may contain hidden payloads.\",\n codeOnly: true,\n fix: \"Move inline code to a separate .rb file so users can review it before execution.\",\n },\n\n // ═══════════════════════════════════════════════════════\n // HIGH: Credential theft\n // ═══════════════════════════════════════════════════════\n {\n name: \"ENV_FILE_READ\",\n // Match credential files, not the words. A leading letter before \".env\"\n // means it is a property access like `process.env`, and bare \"credentials\"\n // is ordinary English (\"store your credentials\"); both were the top two\n // false-positive sources on real skills.\n pattern: /(?<![A-Za-z0-9_])\\.env\\b|[./\\\\-]credentials\\b|credentials\\.(?:json|ya?ml)|\\.aws\\b|\\.ssh\\b|keychain/gi,\n severity: \"high\",\n category: \"credential_theft\",\n title: \"Sensitive file access\",\n description: \"Accesses credential files (.env, .aws, .ssh, keychain).\",\n fix: \"Declare required env vars in frontmatter under `metadata.openclaw.requires.env`.\",\n },\n {\n name: \"API_KEY_EXFIL\",\n pattern: /(ANTHROPIC|OPENAI|SLACK|DISCORD|TELEGRAM|STRIPE|GITHUB|GITLAB|AWS_SECRET|GROQ|OPENROUTER).*(_KEY|_TOKEN|_SECRET)/gi,\n severity: \"high\",\n category: \"credential_theft\",\n title: \"API key reference\",\n description: \"References specific API keys/tokens that could be exfiltrated.\",\n fix: \"Use environment variable references ($VAR) instead of hardcoding keys, and declare them in requires.env.\",\n },\n {\n name: \"DOTFILE_ACCESS\",\n pattern: /~\\/\\.(openclaw|clawdbot|moltbot)\\//gi,\n severity: \"high\",\n category: \"credential_theft\",\n title: \"OpenClaw config access\",\n description: \"Accesses OpenClaw/Clawdbot/Moltbot configuration directories.\",\n fix: \"Use the official OpenClaw SDK/API instead of directly reading config directories.\",\n },\n {\n name: \"SESSION_THEFT\",\n pattern: /sessions\\/\\*\\.jsonl/gi,\n severity: \"high\",\n category: \"credential_theft\",\n title: \"Session data access\",\n description: \"Accesses session transcript files which may contain sensitive data.\",\n fix: \"Remove session file access — skills should not read conversation transcripts.\",\n },\n {\n name: \"SSH_KEY_ACCESS\",\n pattern: /~\\/\\.ssh\\/id_|\\.pem\\b|BEGIN\\s+(RSA\\s+)?PRIVATE\\s+KEY/gi,\n severity: \"high\",\n category: \"credential_theft\",\n title: \"SSH/private key access\",\n description: \"Accesses SSH keys or private key files that could be stolen.\",\n fix: \"Use ssh-agent or a credential manager instead of directly reading key files.\",\n },\n {\n name: \"BROWSER_DATA\",\n pattern: /\\.config\\/google-chrome|\\.mozilla\\/firefox|Login\\s*Data|Cookies\\.sqlite/gi,\n severity: \"high\",\n category: \"credential_theft\",\n title: \"Browser data access\",\n description: \"Accesses browser profiles which contain saved passwords, cookies, and tokens.\",\n fix: \"Remove browser data access — skills should not read browser profiles.\",\n },\n {\n name: \"GIT_CREDENTIALS\",\n pattern: /\\.git-credentials|\\.gitconfig|git\\s+config.*credential/gi,\n severity: \"high\",\n category: \"credential_theft\",\n title: \"Git credential access\",\n description: \"Accesses git credential storage which may contain auth tokens.\",\n fix: \"Use `git` CLI commands instead of directly reading credential files.\",\n },\n {\n name: \"NPM_TOKEN\",\n pattern: /\\.npmrc|npm_token|NPM_AUTH_TOKEN/gi,\n severity: \"high\",\n category: \"credential_theft\",\n title: \"npm token access\",\n description: \"Accesses npm auth tokens which could be used to publish malicious packages.\",\n fix: \"Use `npm whoami` or `npm config get` instead of directly reading .npmrc.\",\n },\n {\n name: \"KUBE_CONFIG\",\n pattern: /~\\/\\.kube\\/config|KUBECONFIG/gi,\n severity: \"high\",\n category: \"credential_theft\",\n title: \"Kubernetes config access\",\n description: \"Accesses Kubernetes configuration which contains cluster credentials.\",\n fix: \"Use `kubectl` CLI commands instead of directly reading kubeconfig.\",\n },\n {\n name: \"DOCKER_SOCKET\",\n pattern: /\\/var\\/run\\/docker\\.sock|docker\\s+exec/gi,\n severity: \"high\",\n category: \"container_escape\",\n title: \"Docker socket/exec access\",\n description: \"Accesses Docker socket or runs exec — could enable container escape.\",\n fix: \"Use Docker SDK or CLI with limited permissions instead of direct socket access.\",\n },\n\n // ═══════════════════════════════════════════════════════\n // HIGH: Network exfiltration\n // ═══════════════════════════════════════════════════════\n {\n name: \"WEBHOOK_SEND\",\n pattern: /webhook\\.(site|url)|discord\\.com\\/api\\/webhooks|hooks\\.slack\\.com|api\\.telegram\\.org\\/bot/gi,\n severity: \"high\",\n category: \"data_exfiltration\",\n title: \"Webhook data exfiltration\",\n description: \"Sends data to webhook endpoints (Discord, Slack, Telegram) — common exfiltration channel.\",\n fix: \"If webhook integration is needed, declare it in the skill description and let users configure their own webhook URL.\",\n },\n {\n name: \"BORE_TUNNEL\",\n pattern: /bore\\.pub|ngrok|localtunnel|serveo\\.net|localhost\\.run/gi,\n severity: \"high\",\n category: \"data_exfiltration\",\n title: \"Tunnel service usage\",\n description: \"Uses tunneling services to expose local services or exfiltrate data.\",\n fix: \"Document tunnel usage in the skill description and require explicit user consent.\",\n },\n {\n name: \"SUSPICIOUS_IP\",\n pattern: /\\b(?:91\\.92\\.242\\.\\d+|45\\.61\\.\\d+\\.\\d+)\\b/g,\n severity: \"critical\",\n category: \"data_exfiltration\",\n title: \"Known malicious IP\",\n description: \"Contains IP addresses associated with known ClawHavoc C2 infrastructure.\",\n fix: \"Remove references to known malicious IP addresses.\",\n // Curated indicator of compromise: an exact match against known C2\n // infrastructure is disqualifying on its own, not a signal to be averaged.\n disqualifying: true,\n },\n {\n name: \"DNS_EXFIL\",\n pattern: /dig\\s+.*TXT|nslookup\\s+.*\\$|dns.*exfil|\\.burpcollaborator\\.|\\.oastify\\./gi,\n severity: \"high\",\n category: \"data_exfiltration\",\n title: \"DNS exfiltration\",\n description: \"Uses DNS queries to exfiltrate data — bypasses most firewalls.\",\n fix: \"Remove DNS exfiltration patterns — use standard HTTP APIs for data transfer.\",\n },\n {\n name: \"PASTEBIN_FETCH\",\n pattern: /pastebin\\.com|paste\\.ee|hastebin\\.com|ghostbin\\.|dpaste\\./gi,\n severity: \"high\",\n category: \"data_exfiltration\",\n title: \"Pastebin service usage\",\n description: \"References paste services commonly used to host malicious payloads or receive exfiltrated data.\",\n fix: \"Host code in a version-controlled repository (GitHub, GitLab) instead of paste services.\",\n },\n {\n name: \"SUSPICIOUS_TLD\",\n pattern: /https?:\\/\\/[^\\s\"']*\\.(tk|ml|ga|cf|gq|top|xyz|pw|cc|ws|buzz)\\b/gi,\n severity: \"high\",\n category: \"data_exfiltration\",\n title: \"Suspicious TLD\",\n description: \"URL uses a top-level domain frequently associated with malicious infrastructure.\",\n fix: \"Use URLs from well-known, reputable domains instead of suspicious TLDs.\",\n },\n {\n name: \"URL_SHORTENER\",\n pattern: /bit\\.ly|tinyurl\\.com|t\\.co\\/|goo\\.gl|is\\.gd|buff\\.ly|ow\\.ly|rb\\.gy/gi,\n severity: \"high\",\n category: \"obfuscation\",\n title: \"URL shortener\",\n description: \"Uses URL shorteners to hide the real destination of links.\",\n fix: \"Use the full, unshortened URL so users can verify the destination.\",\n },\n {\n name: \"RAW_SOCKET\",\n pattern: re([\"new\\\\s+Soc\", \"ket|net\\\\.conn\", \"ect|dgram\\\\.create\", \"Socket\"], \"gi\"),\n severity: \"high\",\n category: \"data_exfiltration\",\n title: \"Raw socket connection\",\n description: \"Creates raw network sockets which can bypass HTTP monitoring.\",\n codeOnly: true,\n fix: \"Use standard HTTP libraries (fetch, axios) instead of raw sockets for network communication.\",\n },\n\n // ═══════════════════════════════════════════════════════\n // MEDIUM: Social engineering\n // ═══════════════════════════════════════════════════════\n {\n name: \"PREREQUISITE_INSTALL\",\n pattern: /prerequisite|install.*first|run.*before|required.*dependency/gi,\n severity: \"medium\",\n category: \"social_engineering\",\n title: \"Prerequisite install trick\",\n description: \"Instructs users to install prerequisites — common social engineering tactic.\",\n fix: \"Declare dependencies in `metadata.openclaw.requires.bins` instead of instructing manual installs.\",\n },\n {\n name: \"COPY_PASTE_COMMAND\",\n pattern: /copy.*paste.*terminal|run.*this.*command/gi,\n severity: \"medium\",\n category: \"social_engineering\",\n title: \"Copy-paste command instruction\",\n description: \"Instructs users to copy-paste commands into their terminal.\",\n fix: \"Put commands in code blocks with proper context instead of copy-paste instructions.\",\n },\n {\n name: \"FAKE_DEPENDENCY\",\n pattern: /openclaw-core|moltbot-runtime|clawdbot-helper/gi,\n severity: \"medium\",\n category: \"social_engineering\",\n title: \"Fake dependency reference\",\n description: \"References fake packages that mimic official OpenClaw components.\",\n fix: \"Use only official OpenClaw packages from the verified registry.\",\n },\n {\n name: \"AUTHORITY_SPOOFING\",\n pattern: /official\\s+(openclaw|clawhub)|endorsed\\s+by|verified\\s+(skill|publisher)|from\\s+the\\s+openclaw\\s+team/gi,\n severity: \"medium\",\n category: \"social_engineering\",\n title: \"Authority spoofing\",\n description: \"Claims official endorsement or verification to gain trust.\",\n fix: \"Remove false authority claims — let the skill's quality speak for itself.\",\n },\n {\n name: \"URGENCY_MANIPULATION\",\n pattern: /critical\\s+update|security\\s+patch|must\\s+install\\s+immediately|urgent.*update/gi,\n severity: \"medium\",\n category: \"social_engineering\",\n title: \"Urgency manipulation\",\n description: \"Creates false urgency to pressure users into installing without review.\",\n fix: \"Remove urgency language — let users evaluate the skill at their own pace.\",\n },\n\n // ═══════════════════════════════════════════════════════\n // MEDIUM: Prompt injection\n // ═══════════════════════════════════════════════════════\n {\n name: \"IGNORE_INSTRUCTIONS\",\n pattern: /ignore\\s+(all\\s+)?previous\\s+instructions/gi,\n severity: \"medium\",\n category: \"prompt_injection\",\n title: \"Prompt injection — ignore instructions\",\n description: \"Attempts to override the AI agent's existing instructions.\",\n fix: \"Remove prompt injection attempts — skills should not try to override agent instructions.\",\n },\n {\n name: \"SYSTEM_OVERRIDE\",\n pattern: /you\\s+are\\s+now|new\\s+instructions|forget\\s+everything/gi,\n severity: \"medium\",\n category: \"prompt_injection\",\n title: \"Prompt injection — system override\",\n description: \"Attempts to redefine the AI agent's identity or instructions.\",\n fix: \"Remove system override attempts — skills should not alter agent identity.\",\n },\n {\n name: \"MEMORY_MANIPULATION\",\n pattern: /SOUL\\.md|MEMORY\\.md|AGENTS\\.md/gi,\n severity: \"medium\",\n category: \"persistence\",\n title: \"Memory/personality file manipulation\",\n description: \"References core personality or memory files, may attempt persistence.\",\n fix: \"Remove references to agent memory/personality files — skills should not modify agent state.\",\n },\n {\n name: \"JAILBREAK_ATTEMPT\",\n pattern: /\\bDAN\\b|do\\s+anything\\s+now|developer\\s+mode|evil\\s+mode|bypass.*safety/gi,\n severity: \"medium\",\n category: \"prompt_injection\",\n title: \"Jailbreak attempt\",\n description: \"Uses known jailbreak techniques (DAN, developer mode) to bypass safety constraints.\",\n fix: \"Remove jailbreak attempts — skills should work within the agent's safety constraints.\",\n },\n {\n name: \"ROLE_HIJACK\",\n pattern: /(?:pretend|act|behave)\\s+(?:you\\s+are|as\\s+if|to\\s+be)\\s+(?:a\\s+)?(?:different|new|hacker|evil)/gi,\n severity: \"medium\",\n category: \"prompt_injection\",\n title: \"Role hijacking\",\n description: \"Attempts to change the agent's persona to bypass safety restrictions.\",\n fix: \"Remove role hijacking attempts — skills should not alter the agent's persona.\",\n },\n {\n name: \"PROMPT_EXTRACTION\",\n pattern: /(?:reveal|show|print|output|tell\\s+me)\\s+(?:your\\s+)?(?:system\\s+)?(?:prompt|instructions|rules)/gi,\n severity: \"medium\",\n category: \"prompt_injection\",\n title: \"System prompt extraction\",\n description: \"Attempts to extract the agent's system prompt or configuration.\",\n fix: \"Remove prompt extraction attempts — skills should not try to access system prompts.\",\n },\n\n // ═══════════════════════════════════════════════════════\n // MEDIUM: Obfuscation\n // ═══════════════════════════════════════════════════════\n {\n name: \"HEX_ENCODING\",\n pattern: /\\\\x[0-9a-f]{2}(?:\\\\x[0-9a-f]{2}){3,}/gi,\n severity: \"medium\",\n category: \"obfuscation\",\n title: \"Hex-encoded payload\",\n description: \"Contains hex-encoded strings commonly used to hide malicious commands.\",\n codeOnly: true,\n fix: \"Replace hex-encoded strings with readable text so users can review the content.\",\n },\n {\n name: \"JS_OBFUSCATOR\",\n pattern: /_0x[a-f0-9]{4,}|var\\s+_0x/gi,\n severity: \"medium\",\n category: \"obfuscation\",\n title: \"JavaScript obfuscator output\",\n description: \"Contains patterns from JavaScript obfuscation tools used to hide malicious code.\",\n codeOnly: true,\n fix: \"Provide readable, unobfuscated source code instead of obfuscated JavaScript.\",\n },\n {\n name: \"UNICODE_STEGANOGRAPHY\",\n pattern: /[\\u200B\\u200C\\u200D\\u2060\\uFEFF]{3,}/g,\n severity: \"medium\",\n category: \"obfuscation\",\n title: \"Hidden zero-width characters\",\n description: \"Contains clusters of invisible zero-width Unicode characters that may hide instructions.\",\n fix: \"Remove zero-width characters — all content should be visible to users.\",\n },\n {\n name: \"RTL_OVERRIDE\",\n pattern: /[\\u202A\\u202B\\u202C\\u202D\\u202E\\u2066\\u2067\\u2068\\u2069]/g,\n severity: \"medium\",\n category: \"obfuscation\",\n title: \"Bidirectional text override\",\n description: \"Contains Unicode bidi override characters that can reverse displayed text to hide real content.\",\n fix: \"Remove bidirectional text override characters — text direction should be natural.\",\n },\n {\n name: \"HTML_COMMENT_INJECTION\",\n pattern: /<!--[\\s\\S]*?(?:ignore|instructions|system|override|secret)[\\s\\S]*?-->/gi,\n severity: \"medium\",\n category: \"obfuscation\",\n title: \"Hidden HTML comment instruction\",\n description: \"Embeds instructions inside HTML comments that are invisible to users but read by agents.\",\n fix: \"Move instructions from HTML comments into visible content.\",\n },\n {\n name: \"STRING_CONCAT_OBFUSC\",\n pattern: /[\"'][a-z]{1,3}[\"']\\s*\\+\\s*[\"'][a-z]{1,3}[\"']\\s*\\+\\s*[\"'][a-z]{1,3}[\"']/gi,\n severity: \"medium\",\n category: \"obfuscation\",\n title: \"String concatenation obfuscation\",\n description: \"Builds commands via single-character string concatenation to evade pattern detection.\",\n codeOnly: true,\n fix: \"Use complete string literals instead of character-by-character concatenation.\",\n },\n {\n name: \"BUFFER_BASE64_DECODE\",\n pattern: re([\"Buf\", \"fer\\\\.from\\\\s*\\\\(.*['\\\"]base\", \"64['\\\"]\\\\)|at\", \"ob\\\\s*\\\\(\"], \"gi\"),\n severity: \"critical\",\n category: \"obfuscation\",\n title: \"Buffer/atob encoded payload\",\n description: \"Decodes encoded content via Buffer.from() or atob(), often used to hide malicious payloads.\",\n codeOnly: true,\n fix: \"Include the decoded content directly so users can review it.\",\n },\n {\n name: \"STRING_FROMCHARCODE\",\n pattern: re([\"String\\\\.from\", \"CharCode\\\\s*\\\\(\"], \"gi\"),\n severity: \"medium\",\n category: \"obfuscation\",\n title: \"String.fromCharCode usage\",\n description: \"Builds strings from character codes to evade static pattern detection.\",\n codeOnly: true,\n fix: \"Use plain string literals instead of String.fromCharCode().\",\n },\n {\n name: \"DYNAMIC_PROPERTY_ACCESS\",\n pattern: /(?:process|global|window|globalThis)\\s*\\[\\s*['\"`]?\\w*['\"`]?\\s*\\+/gi,\n severity: \"medium\",\n category: \"obfuscation\",\n title: \"Dynamic property access on globals\",\n description: \"Dynamically accesses global object properties via string concatenation to hide intent.\",\n codeOnly: true,\n fix: \"Use direct property access (e.g., `process.env`) instead of dynamic bracket notation.\",\n },\n\n // ═══════════════════════════════════════════════════════\n // MEDIUM: Privilege escalation & system access\n // ═══════════════════════════════════════════════════════\n {\n name: \"SUDO_USAGE\",\n pattern: /sudo\\s+(?!apt|dnf|yum|brew)/gi,\n severity: \"medium\",\n category: \"privilege_escalation\",\n title: \"Sudo usage\",\n description: \"Requests elevated privileges — check if actually required for the task.\",\n codeOnly: true,\n fix: \"Remove sudo if not strictly necessary, or document why elevated privileges are required.\",\n },\n {\n name: \"CHMOD_DANGEROUS\",\n pattern: /chmod\\s+(?:777|a\\+[rwx]|[+]s)/gi,\n severity: \"medium\",\n category: \"privilege_escalation\",\n title: \"Dangerous file permissions\",\n description: \"Sets overly permissive file permissions (777) or setuid/setgid bits.\",\n codeOnly: true,\n fix: \"Use least-privilege permissions (e.g., `chmod 755` or `chmod 644`) instead of 777.\",\n },\n {\n name: \"PATH_TRAVERSAL\",\n pattern: /\\.\\.\\//g,\n severity: \"medium\",\n category: \"file_system\",\n title: \"Path traversal\",\n description: \"Uses relative path traversal (../) which could access files outside expected directories.\",\n codeOnly: true,\n fix: \"Use absolute paths or paths relative to the skill's working directory.\",\n },\n\n // ═══════════════════════════════════════════════════════\n // LOW: Suspicious but not necessarily malicious\n // ═══════════════════════════════════════════════════════\n {\n name: \"SHELL_EXEC\",\n pattern: re([\"child_\", \"process|ex\", \"ec\\\\(|spa\", \"wn\\\\(\"], \"gi\"),\n severity: \"low\",\n category: \"code_execution\",\n title: \"Shell execution API\",\n description: \"Uses shell execution APIs — legitimate but worth noting.\",\n codeOnly: true,\n fix: \"If shell execution is needed, use execFile() with explicit arguments instead of exec() with string commands.\",\n },\n {\n name: \"NETWORK_REQUEST\",\n pattern: /fetch\\(|axios|node-fetch|got\\(/gi,\n severity: \"low\",\n category: \"network\",\n title: \"Network request API\",\n description: \"Makes network requests — legitimate but worth reviewing targets.\",\n codeOnly: true,\n fix: \"Document all network endpoints in the skill description so users can review them.\",\n },\n {\n name: \"FILE_WRITE\",\n pattern: /fs\\.write|writeFileSync/gi,\n severity: \"low\",\n category: \"file_system\",\n title: \"File write operation\",\n description: \"Writes to the filesystem — check what files are being modified.\",\n codeOnly: true,\n fix: \"Document which files are written and why in the skill description.\",\n },\n {\n name: \"ENV_MODIFICATION\",\n pattern: /process\\.env\\[|export\\s+[A-Z_]+=|setenv/gi,\n severity: \"low\",\n category: \"environment\",\n title: \"Environment variable modification\",\n description: \"Modifies environment variables which could affect other tools or processes.\",\n codeOnly: true,\n fix: \"Document env var modifications in the skill description and declare them in requires.env.\",\n },\n {\n name: \"WILDCARD_FILE_ACCESS\",\n pattern: /\\*\\.(pem|key|p12|pfx|jks|keystore|ovpn|rdp)/gi,\n severity: \"low\",\n category: \"credential_theft\",\n title: \"Sensitive file extension glob\",\n description: \"Globs for files with sensitive extensions (keys, certificates, VPN configs).\",\n fix: \"Reference specific files by name instead of using wildcard patterns on sensitive extensions.\",\n },\n {\n name: \"LARGE_BASE64_LITERAL\",\n pattern: /[A-Za-z0-9+/=]{100,}/g,\n severity: \"low\",\n category: \"obfuscation\",\n title: \"Large base64-like string\",\n description: \"Contains a long base64-like string that may be an encoded payload.\",\n fix: \"Include the decoded content directly or explain what the base64 string contains.\",\n },\n];\n\nexport const POPULAR_SKILLS = [\n \"todoist-cli\",\n \"github-manager\",\n \"slack-assistant\",\n \"email-composer\",\n \"calendar-sync\",\n \"weather-forecast\",\n \"news-reader\",\n \"code-reviewer\",\n \"docker-helper\",\n \"aws-manager\",\n \"notion-sync\",\n \"jira-tracker\",\n \"spotify-controller\",\n \"home-assistant\",\n \"file-organizer\",\n \"pdf-reader\",\n \"translate-text\",\n \"image-generator\",\n \"web-scraper\",\n \"database-query\",\n \"git-assistant\",\n \"linux-admin\",\n \"python-helper\",\n \"react-builder\",\n \"api-tester\",\n \"markdown-editor\",\n \"csv-analyzer\",\n \"ssh-manager\",\n \"cron-scheduler\",\n \"log-analyzer\",\n];\n","import { parse as parseYaml } from \"yaml\";\nimport type { ParsedSkill, SkillFrontmatter, CodeBlock } from \"../types.js\";\n\nconst FRONTMATTER_RE = /^---\\r?\\n([\\s\\S]*?)\\r?\\n---/;\nconst CODE_BLOCK_RE = /```(\\w*)\\r?\\n([\\s\\S]*?)```/g;\nconst URL_RE = /https?:\\/\\/[^\\s\"'<>\\])+]+/gi;\nconst IP_RE = /\\b(?:\\d{1,3}\\.){3}\\d{1,3}\\b/g;\nconst DOMAIN_RE = /\\b(?:[a-z0-9](?:[a-z0-9-]{0,61}[a-z0-9])?\\.)+[a-z]{2,}\\b/gi;\n\nexport function parseSkill(content: string): ParsedSkill {\n let frontmatter: SkillFrontmatter = {};\n let body = content;\n\n const fmMatch = content.match(FRONTMATTER_RE);\n if (fmMatch) {\n try {\n frontmatter = parseYaml(fmMatch[1]) as SkillFrontmatter;\n } catch {\n frontmatter = {};\n }\n body = content.slice(fmMatch[0].length).trim();\n }\n\n const codeBlocks: CodeBlock[] = [];\n let match: RegExpExecArray | null;\n const cbRe = new RegExp(CODE_BLOCK_RE.source, CODE_BLOCK_RE.flags);\n\n while ((match = cbRe.exec(content)) !== null) {\n const before = content.slice(0, match.index);\n const lineStart = before.split(\"\\n\").length;\n const blockLines = match[0].split(\"\\n\").length;\n codeBlocks.push({\n language: match[1] || \"unknown\",\n content: match[2],\n lineStart,\n lineEnd: lineStart + blockLines - 1,\n });\n }\n\n const urls = [...new Set(content.match(URL_RE) || [])];\n const ipAddresses = [...new Set(content.match(IP_RE) || [])];\n const domains = [...new Set(content.match(DOMAIN_RE) || [])];\n\n return {\n frontmatter,\n body,\n codeBlocks,\n urls,\n ipAddresses,\n domains,\n rawContent: content,\n };\n}\n","import { THREAT_PATTERNS } from \"../patterns.js\";\nimport type { Finding, ParsedSkill, Severity } from \"../types.js\";\n\nfunction isInCodeBlock(lineNumber: number, skill: ParsedSkill): boolean {\n return skill.codeBlocks.some(\n (block) => lineNumber >= block.lineStart && lineNumber <= block.lineEnd\n );\n}\n\nfunction isInHeading(lineNumber: number, rawContent: string): boolean {\n const lines = rawContent.split(\"\\n\");\n const line = lines[lineNumber - 1] || \"\";\n return /^\\s*#{1,6}\\s/.test(line);\n}\n\nconst BASE_CONFIDENCE: Record<Severity, number> = {\n critical: 0.9,\n high: 0.8,\n medium: 0.6,\n low: 0.5,\n};\n\nexport function runStaticAnalysis(skill: ParsedSkill): Finding[] {\n const findings: Finding[] = [];\n\n for (const threat of THREAT_PATTERNS) {\n const re = new RegExp(threat.pattern.source, threat.pattern.flags);\n let match: RegExpExecArray | null;\n\n while ((match = re.exec(skill.rawContent)) !== null) {\n const before = skill.rawContent.slice(0, match.index);\n const lineNumber = before.split(\"\\n\").length;\n\n if (threat.codeOnly && !isInCodeBlock(lineNumber, skill)) {\n continue;\n }\n\n const inCode = isInCodeBlock(lineNumber, skill);\n const inHeading = isInHeading(lineNumber, skill.rawContent);\n\n let contextMultiplier: number;\n if (inCode && threat.codeOnly) {\n contextMultiplier = 1.0;\n } else if (inCode) {\n contextMultiplier = 0.95;\n } else if (inHeading) {\n contextMultiplier = 0.5;\n } else if (threat.codeOnly) {\n // codeOnly pattern somehow in prose (shouldn't happen due to skip above)\n contextMultiplier = 0.4;\n } else {\n // Non-codeOnly patterns are designed to match in prose\n contextMultiplier = 0.9;\n }\n\n const baseConfidence = BASE_CONFIDENCE[threat.severity];\n // A curated indicator of compromise is an exact match, not a fuzzy\n // heuristic, where it appears does not make it less certain.\n const confidence = threat.disqualifying\n ? 1.0\n : Math.min(1.0, baseConfidence * contextMultiplier);\n\n findings.push({\n category: threat.category,\n severity: threat.severity,\n title: threat.title,\n description: threat.description,\n evidence: match[0],\n lineNumber,\n analysisPass: \"static-analysis\",\n confidence: Math.round(confidence * 100) / 100,\n fix: threat.fix,\n disqualifying: threat.disqualifying,\n });\n }\n }\n\n return findings;\n}\n","import type { Finding, ParsedSkill } from \"../types.js\";\n\nconst SEMVER_RE = /^\\d+\\.\\d+\\.\\d+/;\n\nconst KNOWN_BINS = [\n \"curl\", \"wget\", \"git\", \"python\", \"python3\", \"node\", \"npm\", \"npx\",\n \"brew\", \"apt\", \"pip\", \"docker\", \"kubectl\", \"ssh\", \"scp\", \"rsync\",\n \"ffmpeg\", \"jq\", \"sed\", \"awk\", \"grep\", \"find\",\n];\n\nexport function validateMetadata(skill: ParsedSkill): Finding[] {\n const findings: Finding[] = [];\n const fm = skill.frontmatter;\n const pass = \"metadata-validator\";\n\n if (!fm.name) {\n findings.push({\n category: \"metadata\",\n severity: \"medium\",\n title: \"Missing skill name\",\n description: \"SKILL.md frontmatter does not declare a name.\",\n analysisPass: pass,\n fix: \"Add `name:` to the YAML frontmatter.\",\n });\n }\n\n if (!fm.description) {\n findings.push({\n category: \"metadata\",\n severity: \"medium\",\n title: \"Missing description\",\n description: \"SKILL.md frontmatter does not declare a description.\",\n analysisPass: pass,\n fix: \"Add `description:` to the YAML frontmatter.\",\n });\n } else if (fm.description.length < 10) {\n findings.push({\n category: \"metadata\",\n severity: \"low\",\n title: \"Vague description\",\n description: \"Skill description is suspiciously short.\",\n evidence: fm.description,\n analysisPass: pass,\n fix: \"Write a more detailed description (at least 10 characters).\",\n });\n }\n\n if (fm.version && !SEMVER_RE.test(fm.version)) {\n findings.push({\n category: \"metadata\",\n severity: \"low\",\n title: \"Invalid version format\",\n description: \"Version does not follow semver format.\",\n evidence: fm.version,\n analysisPass: pass,\n fix: \"Use semver format: `version: X.Y.Z` (e.g., `1.0.0`).\",\n });\n }\n\n // Real-world frontmatter sometimes gives bins/env as a scalar string instead\n // of a list; guard so `new Set(...)` doesn't throw on it.\n const rawBins = fm.metadata?.openclaw?.requires?.bins;\n const declaredBins = new Set(Array.isArray(rawBins) ? rawBins : []);\n\n for (const bin of KNOWN_BINS) {\n const binRe = new RegExp(`\\\\b${bin}\\\\b`, \"i\");\n if (binRe.test(skill.rawContent) && !declaredBins.has(bin)) {\n const usedInCode = skill.codeBlocks.some((cb) => binRe.test(cb.content));\n if (usedInCode) {\n findings.push({\n category: \"metadata\",\n severity: \"low\",\n title: `Undeclared binary: ${bin}`,\n description: `Skill uses '${bin}' in code but does not declare it in requires.bins.`,\n analysisPass: pass,\n fix: `Add '${bin}' to \\`metadata.openclaw.requires.bins\\` in frontmatter.`,\n });\n }\n }\n }\n\n const rawEnv = fm.metadata?.openclaw?.requires?.env;\n const declaredEnv = new Set(Array.isArray(rawEnv) ? rawEnv : []);\n\n // A variable the skill assigns itself is a local, not an environment\n // dependency. Without this, every `RESULT=$(curl ...)` in a shell block gets\n // reported as an undeclared env var, which buries the real ones.\n const assigned = new Set<string>();\n for (const re of [\n /^\\s*(?:export\\s+|local\\s+|declare\\s+(?:-\\w+\\s+)?)?([A-Z][A-Z0-9_]+)=/gm,\n /\\bread\\s+(?:-\\w+\\s+)*([A-Z][A-Z0-9_]+)/g,\n /\\bfor\\s+([A-Z][A-Z0-9_]+)\\s+in\\b/g,\n ]) {\n for (const m of skill.rawContent.matchAll(re)) assigned.add(m[1]);\n }\n\n const envRe = /\\$\\{?([A-Z][A-Z0-9_]+)\\}?/g;\n let match: RegExpExecArray | null;\n\n while ((match = envRe.exec(skill.rawContent)) !== null) {\n const envVar = match[1];\n if (!declaredEnv.has(envVar) && !assigned.has(envVar) && envVar.length > 2) {\n findings.push({\n category: \"metadata\",\n severity: \"low\",\n title: `Undeclared env var: ${envVar}`,\n description: `References environment variable $${envVar} but does not declare it in requires.env.`,\n evidence: match[0],\n analysisPass: pass,\n fix: `Add '${envVar}' to \\`metadata.openclaw.requires.env\\` in frontmatter.`,\n });\n }\n }\n\n return findings;\n}\n","import type { Finding, ParsedSkill } from \"../types.js\";\n\nconst NPX_AUTO_INSTALL_RE = /npx\\s+-y\\s+/gi;\nconst NPM_INSTALL_RE = /npm\\s+install\\s+(-g\\s+)?(\\S+)/gi;\n\nexport function checkDependencies(skill: ParsedSkill): Finding[] {\n const findings: Finding[] = [];\n const pass = \"dependency-checker\";\n\n let match: RegExpExecArray | null;\n const npxRe = new RegExp(NPX_AUTO_INSTALL_RE.source, NPX_AUTO_INSTALL_RE.flags);\n\n while ((match = npxRe.exec(skill.rawContent)) !== null) {\n const before = skill.rawContent.slice(0, match.index);\n const lineNumber = before.split(\"\\n\").length;\n\n findings.push({\n category: \"dependency_risk\",\n severity: \"medium\",\n title: \"npx auto-install (-y flag)\",\n description: \"Uses 'npx -y' which auto-installs packages without user confirmation.\",\n evidence: match[0],\n lineNumber,\n analysisPass: pass,\n fix: \"Remove the `-y` flag from npx to require user confirmation before installing.\",\n });\n }\n\n const npmRe = new RegExp(NPM_INSTALL_RE.source, NPM_INSTALL_RE.flags);\n while ((match = npmRe.exec(skill.rawContent)) !== null) {\n if (match[1]) {\n findings.push({\n category: \"dependency_risk\",\n severity: \"medium\",\n title: \"Global npm package install\",\n description: `Installs npm package globally: ${match[2]}`,\n evidence: match[0],\n analysisPass: pass,\n fix: \"Use a local install (`npm install` without `-g`) or declare the dependency in requires.bins.\",\n });\n }\n }\n\n return findings;\n}\n","import { distance } from \"fastest-levenshtein\";\nimport { POPULAR_SKILLS } from \"../patterns.js\";\nimport type { Finding } from \"../types.js\";\n\nconst MAX_EDIT_DISTANCE = 2;\n\nexport function detectTyposquats(skillName: string): Finding[] {\n if (!skillName) return [];\n\n const findings: Finding[] = [];\n const normalized = skillName.toLowerCase().trim();\n\n for (const popular of POPULAR_SKILLS) {\n if (normalized === popular) continue;\n\n const d = distance(normalized, popular);\n if (d > 0 && d <= MAX_EDIT_DISTANCE) {\n findings.push({\n category: \"typosquatting\",\n severity: \"high\",\n title: `Possible typosquat of \"${popular}\"`,\n description: `Skill name \"${skillName}\" is ${d} edit(s) away from popular skill \"${popular}\". This may be an attempt to impersonate a trusted skill.`,\n evidence: `\"${skillName}\" ≈ \"${popular}\" (distance: ${d})`,\n analysisPass: \"typosquat-detector\",\n });\n }\n }\n\n const patterns = [\n { re: /-{2,}/, desc: \"extra hyphens\" },\n { re: /(.)\\1{2,}/, desc: \"repeated characters\" },\n ];\n\n for (const p of patterns) {\n if (p.re.test(normalized) && !POPULAR_SKILLS.includes(normalized)) {\n findings.push({\n category: \"typosquatting\",\n severity: \"medium\",\n title: `Suspicious naming pattern: ${p.desc}`,\n description: `Skill name \"${skillName}\" has ${p.desc}, which is a common typosquatting technique.`,\n analysisPass: \"typosquat-detector\",\n });\n }\n }\n\n return findings;\n}\n","import type { Finding } from \"../types.js\";\n\n// A static context pass over the whole finding set, run before scoring.\n//\n// Many rules fire on a capability that is only dangerous once it is paired with\n// a way to get data out or run remote code. Reading ~/.aws/credentials is theft\n// when it is piped to a webhook and configuration when it is not. The regex\n// stage can't see that difference; this pass can, because it sees every finding\n// in the skill at once.\n//\n// Rule: if the skill contains no exfiltration/remote-exec SINK, downweight the\n// dual-use capability findings so a lone capability no longer reaches the warn\n// band. Skills that pair the same capability with a sink keep full weight, so\n// this costs no recall on the corpus (every credential/persistence-based\n// malicious fixture also carries an exfil or curl-pipe-bash sink).\n\n// Capabilities that are common in legitimate skills and only incriminating in\n// combination. Deliberately excludes remote_code_execution / obfuscation:\n// a lone inline interpreter or eval is left for the semantic stage to judge,\n// because static rules can't tell a REPL from an obfuscated dropper.\nconst DUAL_USE_CATEGORIES = new Set([\n \"credential_theft\",\n \"container_escape\",\n \"privilege_escalation\",\n \"persistence\",\n]);\n\n// Signals that a capability is actually being weaponised: data leaving the box,\n// or remote code being pulled and run.\nconst SINK_CATEGORIES = new Set([\"data_exfiltration\"]);\nconst SINK_TITLES = new Set([\n \"Curl piped to shell\",\n \"Wget with shell execution\",\n \"Shell execution API\",\n \"Reverse shell\",\n \"Known malicious IP\",\n]);\n\nconst DOWNWEIGHT = 0.3;\n\nfunction inCode(line: number | null | undefined, codeLines: Set<number>): boolean {\n return line !== null && line !== undefined && codeLines.has(line);\n}\n\n// The install-me envelope. Real malicious skills keep the payload in a\n// referenced script or binary and leave only the instructions that get a user\n// to run it in the markdown. Each half is common on its own: 48 of 400 clean\n// ClawHub skills say \"install X first\", and legitimate READMEs tell you to run\n// a command. Together they are not: on the 500-skill real corpus this pair\n// fires on 48 of 50 malicious skills and 0 of 450 benign ones.\n//\n// Each half is medium severity, so an envelope-only skill tops out at 24 and\n// never crosses the warn line at 26. Promoting the pair is what closes that\n// two-point gap on the threat class where the payload is out of file.\nconst ENVELOPE_TITLES = [\"Prerequisite install trick\", \"Copy-paste command instruction\"];\n\n// Hosts whose whole purpose is a documented one-line installer. `curl | sh` off\n// one of these is the vendor's own published instruction, not a dropper, and it\n// was the single most common cause of a real clean skill being flagged. An\n// attacker cannot use this without first compromising the vendor, in which case\n// the install script is the least of anyone's problems.\nconst TRUSTED_INSTALLER_HOSTS = [\n \"astral.sh\",\n \"sh.rustup.rs\",\n \"get.docker.com\",\n \"install.python-poetry.org\",\n \"get.pnpm.io\",\n \"bun.sh\",\n \"ollama.com\",\n \"deb.nodesource.com\",\n \"raw.githubusercontent.com/Homebrew\",\n \"get.volta.sh\",\n];\n\nconst PIPE_TO_SHELL = new Set([\"Curl piped to shell\", \"Wget with shell execution\"]);\n\nfunction fromTrustedInstaller(f: Finding): boolean {\n if (!PIPE_TO_SHELL.has(f.title)) return false;\n const ev = f.evidence ?? \"\";\n return TRUSTED_INSTALLER_HOSTS.some((h) => ev.includes(h));\n}\n\nfunction isSink(f: Finding): boolean {\n if (fromTrustedInstaller(f)) return false;\n return SINK_CATEGORIES.has(f.category) || SINK_TITLES.has(f.title);\n}\n\nfunction isDualUse(f: Finding): boolean {\n return DUAL_USE_CATEGORIES.has(f.category);\n}\n\nfunction promoteEnvelope(findings: Finding[]): Finding[] {\n const titles = new Set(findings.map((f) => f.title));\n if (!ENVELOPE_TITLES.every((t) => titles.has(t))) return findings;\n\n // The pair is its own concern, not a louder version of either half, so it is\n // reported as a separate finding and the halves stay as the evidence for it.\n // Confidence is 1.0 for the same reason a curated indicator of compromise is:\n // this is an exact co-occurrence, not a fuzzy heuristic that gets less\n // certain depending on where in the file it matched.\n const anchor = findings.find((f) => f.title === ENVELOPE_TITLES[0])!;\n const envelope: Finding = {\n category: \"social_engineering\",\n severity: \"high\",\n title: \"Install-me envelope\",\n description:\n \"The skill tells the user to install a prerequisite and run a command, without the payload being in SKILL.md. This is how a skill gets code it does not contain executed.\",\n evidence: anchor.evidence,\n lineNumber: anchor.lineNumber,\n analysisPass: \"context-classifier\",\n confidence: 1.0,\n fix: \"Declare dependencies in `metadata.openclaw.requires.bins` and ship the code you run, so it can be reviewed before it executes.\",\n };\n return [...findings, envelope];\n}\n\n// A credential read on its own is configuration, and an outbound request on its\n// own is an API call. Together in one skill they are the exfiltration pattern:\n// a secret is read and something sends data out. This is the mirror of the\n// downweight below, and the reason it can be stated with confidence 1.0 is the\n// same: the co-occurrence is exact, not a guess about any single line.\nfunction taintExfiltration(findings: Finding[], codeLines: Set<number>): Finding[] {\n const source = findings.find((f) => f.category === \"credential_theft\");\n const sink = findings.find((f) => f.category === \"data_exfiltration\");\n if (!source || !sink) return findings;\n // Both halves must be actual uses: a declaration in frontmatter or a prose\n // threat-table mention is documenting, not doing. An OAuth client declaring\n // its API key and posting to the user's own webhook, or a security scanner\n // listing exfiltration patterns, is not the exfiltration pattern.\n if (!inCode(source.lineNumber, codeLines) || !inCode(sink.lineNumber, codeLines)) {\n return findings;\n }\n\n return [\n ...findings,\n {\n category: \"data_exfiltration\",\n severity: \"critical\",\n title: \"Credential exfiltration\",\n description: `The skill reads credentials (${source.title}) and sends data out (${sink.title}). Together these are the pattern used to steal secrets.`,\n evidence: source.evidence,\n lineNumber: source.lineNumber,\n analysisPass: \"context-classifier\",\n confidence: 1.0,\n fix: \"Remove the outbound send, or document exactly what is transmitted and let the user supply their own endpoint.\",\n },\n ];\n}\n\nexport function applyContext(findings: Finding[], codeLines?: Set<number>): Finding[] {\n const withEnvelope = promoteEnvelope(findings).map((f) =>\n fromTrustedInstaller(f)\n ? { ...f, severity: \"low\" as const, confidence: 0.3, description: `${f.description} This one points at a well-known vendor installer.` }\n : f\n );\n const lines = codeLines ?? new Set<number>();\n if (withEnvelope.some(isSink)) return taintExfiltration(withEnvelope, lines);\n return withEnvelope.map((f) =>\n isDualUse(f) && !f.disqualifying\n ? { ...f, confidence: Math.round((f.confidence ?? 1.0) * DOWNWEIGHT * 100) / 100 }\n : f\n );\n}\n","import type { Finding, FindingsCount, RiskGrade } from \"../types.js\";\n\nconst SEVERITY_WEIGHTS = {\n critical: 30,\n high: 15,\n medium: 7,\n low: 3,\n} as const;\n\n// A disqualifying indicator of compromise pins the score to the bottom of the\n// F band regardless of aggregate, enough benign signal must never dilute a\n// known-bad match down into a passing grade.\nconst DISQUALIFYING_FLOOR = 90;\n\n// Identical matches of the same rule count with diminishing returns. A rule\n// matching the same evidence on four lines is one concern repeated, and letting\n// it stack linearly is what pushes legitimate skills (an ssh helper that reads\n// ~/.ssh a few times) into the block band. The first hit counts full; each\n// extra identical hit counts at this fraction, so repetition still adds signal\n// without dominating.\nconst REPEAT_FACTOR = 0.25;\n\nfunction weight(f: Finding): number {\n return SEVERITY_WEIGHTS[f.severity] * (f.confidence ?? 1.0);\n}\n\n// Key on title plus evidence, not title alone. Five \"prerequisite install\"\n// matches on five different lines are five separate malicious instructions and\n// each must count full; only identical matches on different lines discount.\nfunction repeatKey(f: Finding): string {\n return `${f.title}\\u0000${f.evidence ?? \"\"}`;\n}\n\nexport function calculateRiskScore(findings: Finding[]): number {\n const byKey = new Map<string, Finding[]>();\n for (const f of findings) {\n // Metadata findings are documentation hygiene, not risk. An undeclared\n // `grep` says the frontmatter is incomplete, not that the skill is\n // dangerous, and a skill using eight ordinary unix tools would otherwise\n // accumulate enough of them to be flagged on its own. They are still\n // reported, they just do not move the score.\n if (f.category === \"metadata\") continue;\n const key = repeatKey(f);\n const arr = byKey.get(key);\n if (arr) arr.push(f);\n else byKey.set(key, [f]);\n }\n\n let score = 0;\n for (const group of byKey.values()) {\n group.sort((a, b) => weight(b) - weight(a));\n group.forEach((f, i) => {\n score += i === 0 ? weight(f) : weight(f) * REPEAT_FACTOR;\n });\n }\n if (findings.some((f) => f.disqualifying)) {\n score = Math.max(score, DISQUALIFYING_FLOOR);\n }\n return Math.round(Math.min(score, 100));\n}\n\nexport function getRiskGrade(score: number): RiskGrade {\n if (score <= 10) return \"A\";\n if (score <= 25) return \"B\";\n if (score <= 50) return \"C\";\n if (score <= 75) return \"D\";\n return \"F\";\n}\n\nexport function countFindings(findings: Finding[]): FindingsCount {\n const counts: FindingsCount = { critical: 0, high: 0, medium: 0, low: 0 };\n for (const f of findings) {\n counts[f.severity]++;\n }\n return counts;\n}\n","import { createHash } from \"node:crypto\";\nimport type { ScanResult } from \"../types.js\";\n\nconst MAX_ENTRIES = 100;\nconst cache = new Map<string, ScanResult>();\n\nfunction hashContent(content: string): string {\n return createHash(\"sha256\").update(content).digest(\"hex\");\n}\n\nexport function getCached(content: string): ScanResult | undefined {\n const key = hashContent(content);\n const result = cache.get(key);\n if (result) {\n // Move to end (most recently used)\n cache.delete(key);\n cache.set(key, result);\n }\n return result;\n}\n\nexport function setCached(content: string, result: ScanResult): void {\n const key = hashContent(content);\n if (cache.has(key)) {\n cache.delete(key);\n } else if (cache.size >= MAX_ENTRIES) {\n // Evict oldest (first entry)\n const oldest = cache.keys().next().value!;\n cache.delete(oldest);\n }\n cache.set(key, result);\n}\n","import type { Finding, ParsedSkill, ScanResult } from \"../types.js\";\nimport { parseSkill } from \"./skill-parser.js\";\nimport { runStaticAnalysis } from \"./static-analysis.js\";\nimport { validateMetadata } from \"./metadata-validator.js\";\nimport { checkDependencies } from \"./dependency-checker.js\";\nimport { detectTyposquats } from \"./typosquat-detector.js\";\nimport { applyContext } from \"./context-classifier.js\";\nimport { calculateRiskScore, getRiskGrade, countFindings } from \"./risk-scorer.js\";\nimport { getCached, setCached } from \"./cache.js\";\n\n// Line numbers that fall inside a fenced code block. The context pass uses this\n// to tell a real use of a credential or exfil channel from a mention of one in\n// prose or frontmatter.\nfunction codeLinesOf(skill: ParsedSkill): Set<number> {\n const lines = new Set<number>();\n for (const cb of skill.codeBlocks) {\n for (let l = cb.lineStart; l <= cb.lineEnd; l++) lines.add(l);\n }\n return lines;\n}\n\nexport interface ScanOptions {\n semantic?: boolean;\n semanticAnalyzer?: (content: string) => Promise<Finding[]>;\n ignorePatterns?: string[];\n skipCache?: boolean;\n /** Fallback name when SKILL.md frontmatter is missing `name`. Typically the folder basename. */\n skillName?: string;\n}\n\nexport async function scanSkill(\n content: string,\n options: ScanOptions = {}\n): Promise<ScanResult> {\n if (!options.skipCache) {\n const cached = getCached(content);\n if (cached) {\n return { ...cached, cached: true };\n }\n }\n\n const skill = parseSkill(content);\n const allFindings: Finding[] = [];\n\n allFindings.push(...runStaticAnalysis(skill));\n allFindings.push(...validateMetadata(skill));\n\n if (options.semantic && options.semanticAnalyzer) {\n const semanticFindings = await options.semanticAnalyzer(content);\n allFindings.push(...semanticFindings);\n }\n\n allFindings.push(...checkDependencies(skill));\n\n if (skill.frontmatter.name) {\n allFindings.push(...detectTyposquats(skill.frontmatter.name));\n }\n\n // Filter out ignored patterns\n const filteredFindings = options.ignorePatterns?.length\n ? allFindings.filter(\n (f) => !options.ignorePatterns!.some((ig) => f.title === ig || f.category === ig)\n )\n : allFindings;\n\n // Context pass: downweight dual-use capabilities that have no exfil/exec sink.\n const contextFindings = applyContext(filteredFindings, codeLinesOf(skill));\n\n const riskScore = calculateRiskScore(contextFindings);\n const riskGrade = getRiskGrade(riskScore);\n // Report what was scored. Showing the pre-context findings would explain the\n // score wrong: a promoted envelope pair would read as two mediums next to a\n // score only a high can produce.\n const findingsCount = countFindings(contextFindings);\n\n const recommendation =\n riskScore >= 76 ? \"block\" : riskScore >= 26 ? \"warn\" : \"approve\";\n\n const result: ScanResult = {\n skillName: skill.frontmatter.name || options.skillName || \"unknown\",\n skillVersion: skill.frontmatter.version,\n skillSource: \"local\",\n status: \"complete\",\n riskScore,\n riskGrade,\n findingsCount,\n findings: contextFindings,\n recommendation,\n };\n\n if (!options.skipCache) {\n setCached(content, result);\n }\n\n return result;\n}\n\nexport { parseSkill } from \"./skill-parser.js\";\nexport { runStaticAnalysis } from \"./static-analysis.js\";\nexport { validateMetadata } from \"./metadata-validator.js\";\nexport { checkDependencies } from \"./dependency-checker.js\";\nexport { detectTyposquats } from \"./typosquat-detector.js\";\nexport { calculateRiskScore, getRiskGrade, countFindings } from \"./risk-scorer.js\";\n","import chalk from \"chalk\";\nimport type { ScanResult, Finding, Severity } from \"@clawvet/shared\";\n\nconst SEVERITY_COLORS: Record<Severity, (s: string) => string> = {\n critical: chalk.bgRed.white.bold,\n high: chalk.red.bold,\n medium: chalk.yellow,\n low: chalk.blue,\n};\n\nconst GRADE_COLORS: Record<string, (s: string) => string> = {\n A: chalk.green.bold,\n B: chalk.greenBright,\n C: chalk.yellow.bold,\n D: chalk.redBright.bold,\n F: chalk.bgRed.white.bold,\n};\n\nexport function printScanResult(result: ScanResult): void {\n console.log();\n console.log(chalk.bold(\"━\".repeat(60)));\n console.log(chalk.bold(\" ClawVet Scan Report\"));\n console.log(chalk.bold(\"━\".repeat(60)));\n console.log();\n\n console.log(` Skill: ${chalk.bold(result.skillName)}`);\n if (result.skillVersion) {\n console.log(` Version: ${result.skillVersion}`);\n }\n console.log();\n\n // Risk score\n const gradeColor = GRADE_COLORS[result.riskGrade] || chalk.white;\n console.log(\n ` Risk Score: ${gradeColor(`${Math.round(result.riskScore)}/100`)} Grade: ${gradeColor(result.riskGrade)}`\n );\n console.log();\n\n // Findings summary\n const fc = result.findingsCount;\n console.log(\" Findings:\");\n if (fc.critical)\n console.log(\n ` ${SEVERITY_COLORS.critical(` CRITICAL `)} ${fc.critical}`\n );\n if (fc.high)\n console.log(` ${SEVERITY_COLORS.high(\"HIGH\")} ${fc.high}`);\n if (fc.medium)\n console.log(` ${SEVERITY_COLORS.medium(\"MEDIUM\")} ${fc.medium}`);\n if (fc.low) console.log(` ${SEVERITY_COLORS.low(\"LOW\")} ${fc.low}`);\n if (!fc.critical && !fc.high && !fc.medium && !fc.low) {\n console.log(` ${chalk.green(\"No findings — skill looks clean!\")}`);\n }\n console.log();\n\n // Detailed findings\n if (result.findings.length > 0) {\n console.log(chalk.bold(\" Details:\"));\n console.log();\n for (const f of result.findings) {\n const color = SEVERITY_COLORS[f.severity];\n const confStr = f.confidence != null ? ` ${Math.round(f.confidence * 100)}%` : \"\";\n console.log(` ${color(`[${f.severity.toUpperCase()}${confStr}]`)} ${f.title}`);\n console.log(` ${chalk.dim(f.description)}`);\n if (f.evidence) {\n console.log(` Evidence: ${chalk.italic(f.evidence)}`);\n }\n if (f.lineNumber) {\n console.log(` Line: ${f.lineNumber}`);\n }\n if (f.fix) {\n console.log(` Fix: ${chalk.green(f.fix)}`);\n }\n console.log();\n }\n }\n\n // Recommendation\n const recColors: Record<string, (s: string) => string> = {\n block: chalk.bgRed.white.bold,\n warn: chalk.bgYellow.black.bold,\n approve: chalk.bgGreen.black.bold,\n };\n const rec = result.recommendation || \"approve\";\n console.log(\n ` Recommendation: ${(recColors[rec] || chalk.white)(` ${rec.toUpperCase()} `)}`\n );\n console.log();\n console.log(chalk.bold(\"━\".repeat(60)));\n console.log();\n}\n","import type { ScanResult } from \"@clawvet/shared\";\n\nexport function printJsonResult(result: ScanResult): void {\n console.log(JSON.stringify(result, null, 2));\n}\n","import type { ScanResult, Finding, Severity } from \"@clawvet/shared\";\n\nconst SEVERITY_TO_SARIF: Record<Severity, string> = {\n critical: \"error\",\n high: \"error\",\n medium: \"warning\",\n low: \"note\",\n};\n\nconst SEVERITY_TO_LEVEL: Record<Severity, string> = {\n critical: \"9.0\",\n high: \"7.0\",\n medium: \"4.0\",\n low: \"1.0\",\n};\n\nexport function printSarifResult(result: ScanResult): void {\n const rules = new Map<string, { id: string; finding: Finding }>();\n\n for (const f of result.findings) {\n const ruleId = f.category + \"/\" + f.title.toLowerCase().replace(/[^a-z0-9]+/g, \"-\");\n if (!rules.has(ruleId)) {\n rules.set(ruleId, { id: ruleId, finding: f });\n }\n }\n\n const sarif = {\n $schema: \"https://raw.githubusercontent.com/oasis-tcs/sarif-spec/main/sarif-2.1/schema/sarif-schema-2.1.0.json\",\n version: \"2.1.0\",\n runs: [\n {\n tool: {\n driver: {\n name: \"clawvet\",\n informationUri: \"https://github.com/clawvet/clawvet\",\n rules: [...rules.values()].map((r) => ({\n id: r.id,\n shortDescription: { text: r.finding.title },\n fullDescription: { text: r.finding.description },\n defaultConfiguration: {\n level: SEVERITY_TO_SARIF[r.finding.severity],\n },\n properties: {\n security_severity: SEVERITY_TO_LEVEL[r.finding.severity],\n },\n })),\n },\n },\n results: result.findings.map((f) => {\n const ruleId = f.category + \"/\" + f.title.toLowerCase().replace(/[^a-z0-9]+/g, \"-\");\n return {\n ruleId,\n level: SEVERITY_TO_SARIF[f.severity],\n message: {\n text: f.description + (f.evidence ? ` Evidence: ${f.evidence}` : \"\"),\n ...(f.fix ? { markdown: `${f.description}\\n\\n**Fix:** ${f.fix}` } : {}),\n },\n locations: [\n {\n physicalLocation: {\n artifactLocation: { uri: \"SKILL.md\" },\n region: { startLine: f.lineNumber ?? 1 },\n },\n },\n ],\n };\n }),\n },\n ],\n };\n\n console.log(JSON.stringify(sarif, null, 2));\n}\n","import { readFileSync, writeFileSync, mkdirSync, existsSync } from \"node:fs\";\nimport { join } from \"node:path\";\nimport { homedir, platform, release } from \"node:os\";\nimport { randomUUID, createHash } from \"node:crypto\";\nimport type { ScanResult } from \"@clawvet/shared\";\n\nconst CONFIG_DIR = join(homedir(), \".clawvet\");\nconst CONFIG_FILE = join(CONFIG_DIR, \"config.json\");\nconst TELEMETRY_ENDPOINT = \"https://bazzzz--0ab7a9301f3911f1ab9942dde27851f2.web.val.run\";\n\n// Never send raw skill names — that would leak what skills a user has\n// installed (including private/internal ones) to the telemetry endpoint.\n// A SHA-256 hash still lets us correlate a *known public* skill across devices\n// (hash the public name and match), but arbitrary/private names stay\n// unrecoverable, so nothing sensitive leaves the machine in cleartext.\nfunction hashSkillName(name: string): string {\n return createHash(\"sha256\").update(name).digest(\"hex\").slice(0, 16);\n}\n\n// Tag traffic so dev/CI runs can be excluded from product metrics server-side\n// instead of polluting them (the \"dev-local\" rows problem).\nfunction detectEnvironment(): \"ci\" | \"development\" | \"production\" {\n if (process.env.CI || process.env.GITHUB_ACTIONS) return \"ci\";\n if (process.env.CLAWVET_ENV === \"development\" || process.env.NODE_ENV === \"development\") {\n return \"development\";\n }\n return \"production\";\n}\n\nfunction readCliVersion(): string {\n try {\n const pkg = JSON.parse(\n readFileSync(new URL(\"../package.json\", import.meta.url), \"utf-8\")\n );\n return pkg.version ?? \"0.0.0\";\n } catch {\n return \"0.0.0\";\n }\n}\n\ninterface Config {\n telemetry?: \"on\" | \"off\" | undefined; // undefined = not yet asked\n deviceId?: string;\n scanCount?: number;\n}\n\nfunction loadConfig(): Config {\n try {\n if (existsSync(CONFIG_FILE)) {\n return JSON.parse(readFileSync(CONFIG_FILE, \"utf-8\"));\n }\n } catch {\n // corrupted config, start fresh\n }\n return {};\n}\n\nfunction saveConfig(config: Config): void {\n try {\n mkdirSync(CONFIG_DIR, { recursive: true });\n writeFileSync(CONFIG_FILE, JSON.stringify(config, null, 2));\n } catch {\n // non-critical, ignore\n }\n}\n\nexport function isTelemetryEnabled(): boolean {\n const env = process.env.CLAWVET_TELEMETRY;\n if (env === \"0\" || env === \"off\") return false;\n if (env === \"1\" || env === \"on\") return true;\n const config = loadConfig();\n return config.telemetry === \"on\";\n}\n\nexport function setTelemetry(enabled: boolean): void {\n const config = loadConfig();\n config.telemetry = enabled ? \"on\" : \"off\";\n saveConfig(config);\n}\n\nexport function hasBeenAsked(): boolean {\n const config = loadConfig();\n return config.telemetry !== undefined;\n}\n\nfunction getDeviceId(): string {\n const config = loadConfig();\n if (!config.deviceId) {\n config.deviceId = randomUUID();\n saveConfig(config);\n }\n return config.deviceId;\n}\n\nfunction incrementScanCount(): number {\n const config = loadConfig();\n config.scanCount = (config.scanCount || 0) + 1;\n saveConfig(config);\n return config.scanCount;\n}\n\nexport function getScanCount(): number {\n return loadConfig().scanCount || 0;\n}\n\nexport function sendTelemetry(result: ScanResult): Promise<void> {\n if (!isTelemetryEnabled()) return Promise.resolve();\n\n const scanCount = incrementScanCount();\n\n const payload = {\n event: \"scan_completed\",\n deviceId: getDeviceId(),\n scanCount,\n ts: new Date().toISOString(),\n os: platform(),\n osVersion: release(),\n cliVersion: readCliVersion(),\n environment: detectEnvironment(),\n skillHash: hashSkillName(result.skillName),\n riskScore: result.riskScore,\n riskGrade: result.riskGrade,\n findingsCount: result.findingsCount,\n cached: result.cached ?? false,\n };\n\n return post(payload);\n}\n\nexport interface AuditSummary {\n skillsScanned: number;\n findingsTotal: number;\n grades: Record<string, number>;\n durationMs: number;\n}\n\n/**\n * One session-level event summarising a whole `clawvet audit` run, instead of\n * one event per scanned skill. Lets an audit of N skills register as a single\n * data point (the strongest usage signal) without inflating scan counts.\n */\nexport function sendAuditTelemetry(summary: AuditSummary): Promise<void> {\n if (!isTelemetryEnabled()) return Promise.resolve();\n\n const payload = {\n event: \"audit_completed\",\n deviceId: getDeviceId(),\n ts: new Date().toISOString(),\n os: platform(),\n osVersion: release(),\n cliVersion: readCliVersion(),\n environment: detectEnvironment(),\n skillsScanned: summary.skillsScanned,\n findingsTotal: summary.findingsTotal,\n grades: summary.grades,\n durationMs: summary.durationMs,\n };\n\n return post(payload);\n}\n\nfunction post(payload: unknown): Promise<void> {\n return fetch(TELEMETRY_ENDPOINT, {\n method: \"POST\",\n headers: { \"Content-Type\": \"application/json\" },\n body: JSON.stringify(payload),\n signal: AbortSignal.timeout(3000),\n })\n .then(() => {})\n .catch(() => {\n // silently ignore — telemetry is best-effort\n });\n}\n","import { readFileSync, readdirSync, statSync } from \"node:fs\";\nimport { basename, join } from \"node:path\";\n\nconst MAX_REFERENCED_FILE_SIZE = 256 * 1024;\nconst SHALLOW_DIRECTORIES = new Set([\"lib\", \"scripts\"]);\n\ninterface CandidateFile {\n absolutePath: string;\n relativePath: string;\n}\n\nfunction escapeRegExp(value: string): string {\n return value.replace(/[.*+?^${}()|[\\]\\\\]/g, \"\\\\$&\");\n}\n\nfunction isReferenced(skillMd: string, relativePath: string): boolean {\n // ponytail: basename substring match; upgrade to real reference parsing\n // (Markdown links + shell tokens) if benchmark false-negatives appear.\n const names = new Set([basename(relativePath), relativePath]);\n return [...names].some((name) =>\n new RegExp(`\\\\b${escapeRegExp(name)}\\\\b`).test(skillMd)\n );\n}\n\nfunction listCandidates(skillDir: string): CandidateFile[] {\n const candidates: CandidateFile[] = [];\n\n let entries;\n try {\n entries = readdirSync(skillDir, { withFileTypes: true });\n } catch {\n return candidates;\n }\n\n for (const entry of entries) {\n if (entry.isFile()) {\n if (entry.name !== \"SKILL.md\") {\n candidates.push({\n absolutePath: join(skillDir, entry.name),\n relativePath: entry.name,\n });\n }\n continue;\n }\n\n if (!entry.isDirectory() || !SHALLOW_DIRECTORIES.has(entry.name)) {\n continue;\n }\n\n try {\n for (const child of readdirSync(join(skillDir, entry.name), {\n withFileTypes: true,\n })) {\n if (child.isFile()) {\n candidates.push({\n absolutePath: join(skillDir, entry.name, child.name),\n relativePath: `${entry.name}/${child.name}`,\n });\n }\n }\n } catch {\n // A missing or unreadable optional directory should not abort the scan.\n }\n }\n\n return candidates.sort((a, b) =>\n a.relativePath.localeCompare(b.relativePath)\n );\n}\n\n/**\n * Appends local files explicitly referenced by a skill manifest so the shared\n * string-only scanner can inspect cross-file payloads without filesystem access.\n */\nexport function assembleSkill(skillDir: string, skillMd: string): string {\n let assembled = skillMd;\n\n for (const candidate of listCandidates(skillDir)) {\n if (!isReferenced(skillMd, candidate.relativePath)) {\n continue;\n }\n\n try {\n const stat = statSync(candidate.absolutePath);\n if (stat.size > MAX_REFERENCED_FILE_SIZE) {\n continue;\n }\n\n const contents = readFileSync(candidate.absolutePath);\n if (contents.includes(0)) {\n continue;\n }\n\n const separator = assembled.endsWith(\"\\n\") ? \"\\n\" : \"\\n\\n\";\n assembled += `${separator}# [clawvet] referenced file: ${candidate.relativePath}\\n${contents.toString(\"utf-8\")}`;\n } catch {\n // Files may disappear or become unreadable between listing and reading.\n }\n }\n\n return assembled;\n}\n","/**\n * Feedback goes to a prefilled GitHub issue rather than a form.\n *\n * The old Tally form got 11 visits and 0 submissions in 12 months (2s average\n * dwell — people landed and bounced). A prefilled issue is one click, needs no\n * account switch for anyone already on GitHub, and lands somewhere public where\n * it helps other users instead of a private form inbox.\n */\nconst ISSUE_BODY = [\n \"**What were you scanning?**\",\n \"\",\n \"\",\n \"**What happened, and what did you expect instead?**\",\n \"\",\n \"\",\n \"---\",\n \"_Filed from the ClawVet CLI._\",\n].join(\"\\n\");\n\n/** Full prefilled URL — used when opening a browser. */\nexport const FEEDBACK_URL =\n \"https://github.com/MohibShaikh/clawvet/issues/new\" +\n \"?labels=feedback\" +\n `&title=${encodeURIComponent(\"Feedback: \")}` +\n `&body=${encodeURIComponent(ISSUE_BODY)}`;\n\n/** Short form for terminal output — the encoded URL is unreadable when wrapped. */\nexport const FEEDBACK_DISPLAY_URL =\n \"https://github.com/MohibShaikh/clawvet/issues/new\";\n","import { readdirSync, existsSync, readFileSync } from \"node:fs\";\nimport { join, basename } from \"node:path\";\nimport { homedir } from \"node:os\";\nimport { scanSkill } from \"@clawvet/shared\";\nimport { printScanResult } from \"../output/terminal.js\";\nimport { sendAuditTelemetry } from \"../telemetry.js\";\nimport chalk from \"chalk\";\n\nconst DEFAULT_SKILL_DIRS = [\n join(homedir(), \".openclaw\", \"skills\"),\n join(homedir(), \".openclaw\", \"workspace\", \"skills\"),\n];\n\nexport async function auditCommand(options: { dir?: string } = {}): Promise<void> {\n const SKILL_DIRS = options.dir ? [options.dir] : DEFAULT_SKILL_DIRS;\n console.log(chalk.bold(\"\\nClawVet Audit — Scanning all installed skills\\n\"));\n\n const startedAt = Date.now();\n let totalScanned = 0;\n let totalThreats = 0;\n const grades: Record<string, number> = { A: 0, B: 0, C: 0, D: 0, F: 0 };\n\n for (const dir of SKILL_DIRS) {\n if (!existsSync(dir)) {\n if (options.dir) {\n console.error(chalk.yellow(`Warning: Directory not found: ${dir}\\n`));\n process.exit(1);\n }\n continue;\n }\n\n // If the dir itself contains a SKILL.md, scan it directly\n const directSkillFile = join(dir, \"SKILL.md\");\n if (existsSync(directSkillFile)) {\n const content = readFileSync(directSkillFile, \"utf-8\");\n const result = await scanSkill(content, { skillName: basename(dir) });\n totalScanned++;\n totalThreats += result.findings.length;\n grades[result.riskGrade] = (grades[result.riskGrade] ?? 0) + 1;\n printScanResult(result);\n continue;\n }\n\n const entries = readdirSync(dir, { withFileTypes: true });\n for (const entry of entries) {\n if (!entry.isDirectory()) continue;\n const skillFile = join(dir, entry.name, \"SKILL.md\");\n if (!existsSync(skillFile)) continue;\n\n const content = readFileSync(skillFile, \"utf-8\");\n const result = await scanSkill(content, { skillName: entry.name });\n totalScanned++;\n totalThreats += result.findings.length;\n grades[result.riskGrade] = (grades[result.riskGrade] ?? 0) + 1;\n\n printScanResult(result);\n }\n }\n\n const gradeColors: Record<string, (s: string) => string> = {\n A: chalk.green.bold,\n B: chalk.greenBright,\n C: chalk.yellow.bold,\n D: chalk.redBright.bold,\n F: chalk.bgRed.white.bold,\n };\n const gradeSummary = ([\"A\", \"B\", \"C\", \"D\", \"F\"] as const)\n .filter((g) => grades[g] > 0)\n .map((g) => `${gradeColors[g](g)} ${grades[g]}`)\n .join(\" \");\n\n console.log(\n chalk.bold(\n `\\nAudit complete: ${totalScanned} skills scanned, ${totalThreats} findings`\n )\n );\n if (totalScanned > 0) {\n console.log(` Grades: ${gradeSummary}`);\n }\n const blocked = grades.D + grades.F;\n if (blocked > 0) {\n console.log(\n chalk.red(` ${blocked} skill${blocked > 1 ? \"s\" : \"\"} graded D or F — review before use`)\n );\n }\n console.log();\n\n // One session-level telemetry event for the whole audit (best-effort).\n await sendAuditTelemetry({\n skillsScanned: totalScanned,\n findingsTotal: totalThreats,\n grades,\n durationMs: Date.now() - startedAt,\n });\n}\n","import { readFileSync, existsSync, watch } from \"node:fs\";\nimport { join, dirname, basename } from \"node:path\";\nimport { homedir } from \"node:os\";\nimport chalk from \"chalk\";\nimport { scanSkill } from \"@clawvet/shared\";\nimport { printScanResult } from \"../output/terminal.js\";\n\nconst DEFAULT_SKILL_DIRS = [\n join(homedir(), \".openclaw\", \"skills\"),\n join(homedir(), \".openclaw\", \"workspace\", \"skills\"),\n];\n\nexport async function watchCommand(options: {\n threshold?: number;\n dir?: string;\n}): Promise<void> {\n const threshold = options.threshold || 50;\n const SKILL_DIRS = options.dir ? [options.dir] : DEFAULT_SKILL_DIRS;\n console.log(\n chalk.bold(\n `\\nClawVet Watch — monitoring skill directories (threshold: ${threshold})\\n`\n )\n );\n\n const watchDirs: string[] = [];\n for (const dir of SKILL_DIRS) {\n if (existsSync(dir)) {\n watchDirs.push(dir);\n }\n }\n\n if (watchDirs.length === 0) {\n console.log(\n chalk.yellow(\n \"No OpenClaw skill directories found. Watching will start when directories are created.\\n\"\n )\n );\n console.log(chalk.dim(\"Expected directories:\"));\n for (const dir of SKILL_DIRS) {\n console.log(chalk.dim(` ${dir}`));\n }\n console.log();\n process.exit(1);\n }\n\n console.log(chalk.dim(\"Watching:\"));\n for (const dir of watchDirs) {\n console.log(chalk.dim(` ${dir}`));\n }\n console.log();\n\n for (const dir of watchDirs) {\n const watcher = watch(dir, { recursive: true }, async (event, filename) => {\n if (!filename?.endsWith(\"SKILL.md\")) return;\n\n const skillFile = join(dir, filename);\n if (!existsSync(skillFile)) return;\n\n console.log(chalk.dim(`\\nDetected change: ${filename}`));\n\n try {\n const content = readFileSync(skillFile, \"utf-8\");\n const result = await scanSkill(content, {\n skillName: basename(dirname(skillFile)),\n });\n\n if (result.cached) {\n console.log(chalk.dim(\"(cached)\"));\n }\n printScanResult(result);\n\n if (result.riskScore > threshold) {\n console.log(\n chalk.bgRed.white.bold(\n ` BLOCKED — Risk score ${result.riskScore} exceeds threshold ${threshold} `\n )\n );\n console.log(\n chalk.red(\n `This skill should not be installed. Run 'clawvet scan ${skillFile}' for details.\\n`\n )\n );\n }\n } catch (err) {\n console.error(chalk.red(`Error scanning ${filename}:`), err);\n }\n });\n\n process.on(\"SIGINT\", () => {\n watcher.close();\n console.log(chalk.dim(\"\\nWatch stopped.\"));\n process.exit(0);\n });\n }\n\n console.log(chalk.dim(\"Press Ctrl+C to stop watching.\\n\"));\n await new Promise(() => {});\n}\n","import { readFileSync, existsSync, statSync } from \"node:fs\";\nimport { resolve, join, dirname, basename } from \"node:path\";\nimport chalk from \"chalk\";\nimport { scanSkill } from \"@clawvet/shared\";\nimport type { RiskGrade } from \"@clawvet/shared\";\n\nconst GRADE_COLORS: Record<RiskGrade, string> = {\n A: \"brightgreen\",\n B: \"green\",\n C: \"yellow\",\n D: \"orange\",\n F: \"red\",\n};\n\nconst GRADE_LABELS: Record<RiskGrade, string> = {\n A: \"safe\",\n B: \"safe\",\n C: \"review\",\n D: \"risky\",\n F: \"dangerous\",\n};\n\nexport async function badgeCommand(\n target: string,\n options: { markdown?: boolean }\n): Promise<void> {\n const skillPath = resolve(target);\n let skillFile = skillPath;\n\n if (\n existsSync(skillPath) &&\n !skillPath.endsWith(\".md\") &&\n existsSync(join(skillPath, \"SKILL.md\"))\n ) {\n skillFile = join(skillPath, \"SKILL.md\");\n }\n\n if (!existsSync(skillFile) || statSync(skillFile).isDirectory()) {\n console.error(`Error: Cannot find SKILL.md at ${skillFile}`);\n process.exit(1);\n }\n\n const content = readFileSync(skillFile, \"utf-8\");\n const result = await scanSkill(content, {\n skillName: basename(dirname(skillFile)),\n });\n\n const label = GRADE_LABELS[result.riskGrade];\n const color = GRADE_COLORS[result.riskGrade];\n const badgeUrl = `https://img.shields.io/badge/clawvet-${result.riskGrade}%20${label}-${color}`;\n const linkUrl = \"https://github.com/MohibShaikh/clawvet\";\n\n if (options.markdown) {\n console.log(`[](${linkUrl})`);\n } else {\n console.log();\n console.log(chalk.bold(\" ClawVet Trust Badge\"));\n console.log();\n console.log(` Skill: ${chalk.bold(result.skillName)}`);\n console.log(` Grade: ${result.riskGrade} (${label})`);\n console.log(` Score: ${result.riskScore}/100`);\n console.log();\n console.log(chalk.dim(\" Markdown (paste in README):\"));\n console.log();\n console.log(` [](${linkUrl})`);\n console.log();\n console.log(chalk.dim(\" HTML:\"));\n console.log();\n console.log(` <a href=\"${linkUrl}\"><img src=\"${badgeUrl}\" alt=\"ClawVet ${result.riskGrade}\"></a>`);\n console.log();\n }\n}\n","import { readFileSync, existsSync, statSync } from \"node:fs\";\nimport { join, basename, dirname } from \"node:path\";\nimport { scanSkill } from \"@clawvet/shared\";\nimport type { Finding, Recommendation } from \"@clawvet/shared\";\nimport { assembleSkill } from \"../assemble.js\";\n\n// OpenClaw's security.installPolicy hook. It writes staged install metadata to\n// our stdin and reads a single JSON verdict from our stdout, after the source\n// is staged and before the install completes. Anything it cannot parse fails\n// closed, so this path must emit exactly one JSON object on stdout and put\n// every diagnostic on stderr.\n//\n// Named `gate`, not `policy`. `openclaw policy` is a different thing in the\n// same ecosystem: a workspace-config conformance linter a human runs. This is\n// an install-time admission gate the host runs. Two commands called policy\n// meaning two different things is a trap, and it would also collide with a\n// future declarative `clawvet policy` that reads a rules file.\n//\n// This is the enforcement half of ClawVet. The clawvet skill asks an agent to\n// remember to scan; this runs whether or not it remembers.\n\nconst PROTOCOL_VERSION = 1;\n\n// Only the fields we use. OpenClaw sends more and may add more.\ninterface PolicyRequest {\n protocolVersion?: number;\n targetType?: string;\n targetName?: string;\n sourcePath?: string;\n sourcePathKind?: \"directory\" | \"file\";\n origin?: { slug?: string; version?: string; registry?: string };\n}\n\ntype Decision = \"allow\" | \"warn\" | \"block\";\n\ninterface PolicyFinding {\n ruleId: string;\n message: string;\n severity: \"info\" | \"warn\" | \"critical\";\n evidence?: string;\n line?: number;\n}\n\ninterface PolicyResponse {\n protocolVersion: number;\n decision: Decision;\n reason?: string;\n findings?: PolicyFinding[];\n}\n\n// approve/warn/block is already ClawVet's own vocabulary, so the mapping is\n// identity. Kept explicit so a change on either side is a compile error here\n// rather than a silently wrong verdict.\nconst DECISION: Record<Recommendation, Decision> = {\n approve: \"allow\",\n warn: \"warn\",\n block: \"block\",\n};\n\n// ClawVet grades severity for a human reading a report; installPolicy grades it\n// for a gate. medium and low are advisory either way.\nconst SEVERITY: Record<Finding[\"severity\"], PolicyFinding[\"severity\"]> = {\n critical: \"critical\",\n high: \"critical\",\n medium: \"warn\",\n low: \"info\",\n};\n\nconst REASON_MAX = 1000;\n\n// ClawVet carries two thresholds for two jobs. `recommendation` blocks at 76,\n// which is the conservative posture for a report a human reads. The scanner's\n// warn line is 26, and that is the threshold the paper validates detection at.\n// A hard install gate inherits whichever one it is wired to, so the operator\n// picks: default to 76 so ordinary dual-use skills still install, and let a\n// stricter deployment lower it. See --block-at.\nconst DEFAULT_BLOCK_AT = 76;\n\nfunction emit(res: PolicyResponse): never {\n process.stdout.write(JSON.stringify(res) + \"\\n\");\n process.exit(0);\n}\n\n// A scanner that cannot read the target has not cleared it. Block with a\n// reason the user can act on, rather than exiting non-zero and leaving them\n// with a bare install failure.\nfunction blockWith(reason: string): never {\n emit({\n protocolVersion: PROTOCOL_VERSION,\n decision: \"block\",\n reason: reason.slice(0, REASON_MAX),\n });\n}\n\nasync function readStdin(): Promise<string> {\n const chunks: Buffer[] = [];\n for await (const chunk of process.stdin) chunks.push(chunk as Buffer);\n return Buffer.concat(chunks).toString(\"utf-8\");\n}\n\nfunction summarize(\n name: string,\n grade: string,\n score: number,\n findings: Finding[]\n): string {\n const worst = findings\n .filter((f) => f.severity === \"critical\" || f.severity === \"high\")\n .slice(0, 3)\n .map((f) => f.title);\n const head = `ClawVet graded \"${name}\" ${grade} (risk ${score}/100).`;\n return worst.length ? `${head} ${worst.join(\"; \")}.` : head;\n}\n\nexport interface GateOptions {\n blockAt?: number;\n}\n\nexport async function gateCommand(options: GateOptions = {}): Promise<void> {\n const blockAt = Number.isFinite(options.blockAt)\n ? (options.blockAt as number)\n : DEFAULT_BLOCK_AT;\n let req: PolicyRequest;\n try {\n const raw = await readStdin();\n if (!raw.trim()) blockWith(\"ClawVet gate received no install metadata on stdin.\");\n req = JSON.parse(raw) as PolicyRequest;\n } catch {\n blockWith(\"ClawVet gate could not parse the install metadata on stdin.\");\n }\n\n if (req.protocolVersion !== undefined && req.protocolVersion !== PROTOCOL_VERSION) {\n blockWith(\n `ClawVet gate speaks protocol ${PROTOCOL_VERSION}, host sent ${req.protocolVersion}. Upgrade clawvet.`\n );\n }\n\n const sourcePath = req.sourcePath;\n if (!sourcePath || !existsSync(sourcePath)) {\n blockWith(`ClawVet gate could not read the staged source at ${sourcePath ?? \"(none)\"}.`);\n }\n\n let skillFile = sourcePath;\n let skillDir: string | undefined;\n if (statSync(sourcePath).isDirectory()) {\n skillDir = sourcePath;\n skillFile = join(sourcePath, \"SKILL.md\");\n }\n\n if (!existsSync(skillFile) || statSync(skillFile).isDirectory()) {\n // No SKILL.md means no instruction layer to vet. Plugins can legitimately\n // ship without one, so this is not on its own a reason to fail an install.\n emit({ protocolVersion: PROTOCOL_VERSION, decision: \"allow\" });\n }\n\n let result;\n try {\n const skillMd = readFileSync(skillFile, \"utf-8\");\n // Assemble referenced files so a payload split across them cannot hide.\n const content = skillDir ? assembleSkill(skillDir, skillMd) : skillMd;\n // Static passes only. The semantic pass needs a key and a network round\n // trip, and this runs inside the host's install timeout.\n result = await scanSkill(content, {\n skillName: req.targetName || req.origin?.slug || basename(dirname(skillFile)),\n });\n } catch (err) {\n blockWith(\n `ClawVet gate failed to scan the staged skill: ${err instanceof Error ? err.message : \"unknown error\"}`\n );\n }\n\n // A disqualifying indicator is a verdict on its own, independent of score.\n const disqualified = result.findings.some((f) => f.disqualifying);\n const decision: Decision =\n disqualified || result.riskScore >= blockAt\n ? \"block\"\n : DECISION[result.recommendation ?? \"warn\"] === \"allow\"\n ? \"allow\"\n : \"warn\";\n const findings: PolicyFinding[] = result.findings.slice(0, 20).map((f) => ({\n ruleId: f.id ?? f.category,\n message: f.description || f.title,\n severity: SEVERITY[f.severity],\n ...(f.evidence ? { evidence: f.evidence } : {}),\n ...(f.lineNumber ? { line: f.lineNumber } : {}),\n }));\n\n if (decision === \"allow\") {\n emit({ protocolVersion: PROTOCOL_VERSION, decision, findings });\n }\n\n emit({\n protocolVersion: PROTOCOL_VERSION,\n decision,\n reason: summarize(\n result.skillName,\n result.riskGrade,\n result.riskScore,\n result.findings\n ).slice(0, REASON_MAX),\n findings,\n });\n}\n"],"mappings":"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","names":["readFileSync","dirname","join","readFileSync","existsSync","statSync","join","basename","chalk","re","inCode","re","createHash","readFileSync","join","existsSync","statSync","join","readFileSync","basename","chalk","resolve","readdirSync","existsSync","readFileSync","join","basename","homedir","chalk","join","homedir","existsSync","readFileSync","basename","readdirSync","readFileSync","existsSync","join","dirname","basename","homedir","chalk","DEFAULT_SKILL_DIRS","join","homedir","chalk","existsSync","readFileSync","basename","dirname","readFileSync","existsSync","statSync","resolve","join","dirname","basename","chalk","GRADE_COLORS","resolve","existsSync","join","statSync","readFileSync","basename","dirname","chalk","readFileSync","existsSync","statSync","join","basename","dirname","existsSync","statSync","join","readFileSync","basename","dirname","dirname","readFileSync","join"]}
|
|
1
|
+
{"version":3,"sources":["../src/index.ts","../src/commands/scan.ts","../../shared/src/patterns.ts","../../shared/src/scanner/skill-parser.ts","../../shared/src/scanner/static-analysis.ts","../../shared/src/scanner/metadata-validator.ts","../../shared/src/scanner/dependency-checker.ts","../../shared/src/scanner/typosquat-detector.ts","../../shared/src/scanner/context-classifier.ts","../../shared/src/scanner/risk-scorer.ts","../../shared/src/scanner/cache.ts","../../shared/src/scanner/index.ts","../src/output/terminal.ts","../src/output/json.ts","../src/output/sarif.ts","../src/telemetry.ts","../src/assemble.ts","../src/feedback.ts","../src/commands/audit.ts","../src/commands/watch.ts","../src/commands/badge.ts","../src/commands/gate.ts"],"sourcesContent":["import { Command } from \"commander\";\nimport { readFileSync } from \"node:fs\";\nimport { execFile } from \"node:child_process\";\nimport { fileURLToPath } from \"node:url\";\nimport { dirname, join } from \"node:path\";\nimport { scanCommand } from \"./commands/scan.js\";\nimport { auditCommand } from \"./commands/audit.js\";\nimport { watchCommand } from \"./commands/watch.js\";\nimport { badgeCommand } from \"./commands/badge.js\";\nimport { gateCommand } from \"./commands/gate.js\";\nimport { FEEDBACK_URL, FEEDBACK_DISPLAY_URL } from \"./feedback.js\";\n\n// Open a URL in the user's browser without going through a shell. Using\n// execFile (not exec) means the URL is passed as an argument, never\n// interpolated into a command string a shell would parse — no shell-exec\n// surface even though the URL here is a constant.\nfunction openUrl(url: string): void {\n const child =\n process.platform === \"win32\"\n ? execFile(\"cmd\", [\"/c\", \"start\", \"\", url])\n : process.platform === \"darwin\"\n ? execFile(\"open\", [url])\n : execFile(\"xdg-open\", [url]);\n // Opening a browser is best-effort — never crash the CLI if the opener is\n // missing (e.g. a headless Linux box without xdg-open).\n child.on(\"error\", () => {});\n}\n\nfunction readPackageVersion(): string {\n try {\n const here = dirname(fileURLToPath(import.meta.url));\n const pkg = JSON.parse(readFileSync(join(here, \"..\", \"package.json\"), \"utf-8\"));\n return pkg.version;\n } catch {\n return \"0.0.0\";\n }\n}\n\nconst program = new Command();\n\nprogram\n .name(\"clawvet\")\n .description(\"Skill vetting & supply chain security for OpenClaw\")\n .version(readPackageVersion());\n\nprogram\n .command(\"scan\")\n .description(\"Scan a skill for security threats\")\n .argument(\"<target>\", \"Path to skill folder or SKILL.md file\")\n .option(\"--format <format>\", \"Output format: terminal, json, or sarif\", \"terminal\")\n .option(\"--fail-on <severity>\", \"Exit 1 if findings at this severity or above\")\n .option(\"--semantic\", \"Enable AI semantic analysis (requires ANTHROPIC_API_KEY)\")\n .option(\"--remote\", \"Fetch skill from ClawHub by name instead of local path\")\n .option(\"-q, --quiet\", \"Suppress all output, exit code only (0=pass, 1=fail)\")\n .option(\"--subscribe\", \"Open a prefilled GitHub issue to send feedback\")\n .action(async (target, opts) => {\n if (opts.subscribe) {\n console.log(`Opening ${FEEDBACK_DISPLAY_URL} ...`);\n openUrl(FEEDBACK_URL);\n }\n await scanCommand(target, {\n format: opts.format,\n failOn: opts.failOn,\n semantic: opts.semantic,\n remote: opts.remote,\n quiet: opts.quiet,\n });\n });\n\nprogram\n .command(\"gate\")\n .alias(\"policy\")\n .description(\"OpenClaw install-policy hook: staged install metadata on stdin, JSON verdict on stdout\")\n .option(\"--block-at <score>\", \"Risk score at or above which to block the install\", \"76\")\n .option(\"--print-config\", \"Print a ready-to-paste OpenClaw installPolicy config with resolved paths\")\n .action(async (opts) => {\n // `policy` was the name in 0.12.0. It collides with `openclaw policy`,\n // which lints workspace config rather than gating installs. Kept as an\n // alias so a config written against 0.12.0 keeps working; the notice goes\n // to stderr because stdout carries the JSON verdict the host parses.\n if (process.argv[2] === \"policy\") {\n process.stderr.write(\n \"clawvet: 'policy' is deprecated, use 'gate'. Update args to [\\\"gate\\\"] in your installPolicy config.\\n\"\n );\n }\n await gateCommand({ blockAt: Number(opts.blockAt), printConfig: opts.printConfig });\n });\n\nprogram\n .command(\"audit\")\n .description(\"Scan all installed OpenClaw skills\")\n .option(\"--dir <path>\", \"Custom skills directory to scan\")\n .action(async (opts) => {\n await auditCommand({ dir: opts.dir });\n });\n\nprogram\n .command(\"watch\")\n .description(\"Pre-install hook — blocks risky skill installs\")\n .option(\"--threshold <score>\", \"Risk score threshold (default 50)\", \"50\")\n .option(\"--dir <path>\", \"Custom skills directory to watch\")\n .action(async (opts) => {\n await watchCommand({ threshold: parseInt(opts.threshold), dir: opts.dir });\n });\n\nprogram\n .command(\"badge\")\n .description(\"Generate a trust badge for a skill's README\")\n .argument(\"<target>\", \"Path to skill folder or SKILL.md file\")\n .option(\"--md\", \"Output only the markdown snippet\")\n .action(async (target, opts) => {\n await badgeCommand(target, { markdown: opts.md });\n });\n\nprogram\n .command(\"feedback\")\n .description(\"Open a prefilled GitHub issue to send feedback\")\n .action(async () => {\n console.log(`Opening ${FEEDBACK_DISPLAY_URL} ...`);\n openUrl(FEEDBACK_URL);\n });\n\nprogram.parse();\n","import { readFileSync, existsSync, statSync } from \"node:fs\";\nimport { resolve, join, basename, dirname } from \"node:path\";\nimport chalk from \"chalk\";\nimport { scanSkill } from \"@clawvet/shared\";\nimport { printScanResult } from \"../output/terminal.js\";\nimport { printJsonResult } from \"../output/json.js\";\nimport { printSarifResult } from \"../output/sarif.js\";\nimport { sendTelemetry, hasBeenAsked, setTelemetry, isTelemetryEnabled, getScanCount } from \"../telemetry.js\";\nimport { assembleSkill } from \"../assemble.js\";\nimport { FEEDBACK_DISPLAY_URL } from \"../feedback.js\";\n\nexport interface ScanOptions {\n format?: \"terminal\" | \"json\" | \"sarif\";\n failOn?: \"critical\" | \"high\" | \"medium\" | \"low\";\n semantic?: boolean;\n remote?: boolean;\n quiet?: boolean;\n}\n\nconst SLUG_PATTERN = /^[a-z0-9][a-z0-9_-]{0,63}$/i;\n\nasync function fetchRemoteSkill(slug: string): Promise<string> {\n if (!SLUG_PATTERN.test(slug)) {\n throw new Error(\n `Invalid skill name \"${slug}\". Must be 1-64 chars, alphanumeric + dash/underscore.`\n );\n }\n\n const encoded = encodeURIComponent(slug);\n // The ClawHub catalog API returns the skill record as JSON, with the full\n // SKILL.md content in `skill.description`. The other sources serve raw\n // markdown. Try the catalog first, then fall back to raw endpoints.\n const sources: Array<{ url: string; json: boolean }> = [\n { url: `https://clawhub.ai/api/v1/skills/${encoded}`, json: true },\n { url: `https://clawhub.ai/api/v1/skills/${encoded}/raw`, json: false },\n {\n url: `https://raw.githubusercontent.com/openclaw/skills/main/${encoded}/SKILL.md`,\n json: false,\n },\n ];\n\n for (const { url, json } of sources) {\n try {\n const res = await fetch(url, { signal: AbortSignal.timeout(10000) });\n if (!res.ok) continue;\n\n if (!json) return await res.text();\n\n const body = (await res.json()) as {\n skill?: { description?: string };\n };\n const content = body?.skill?.description;\n if (typeof content === \"string\" && content.includes(\"---\")) {\n return content;\n }\n } catch {\n // try next\n }\n }\n\n throw new Error(\n `Could not fetch skill \"${slug}\" from ClawHub. Check the skill name and try again.`\n );\n}\n\nexport async function scanCommand(\n target: string,\n options: ScanOptions\n): Promise<void> {\n let content: string;\n let fallbackName: string | undefined;\n\n if (options.remote) {\n try {\n process.stderr.write(`Fetching \"${target}\" from ClawHub...\\n`);\n content = await fetchRemoteSkill(target);\n fallbackName = target;\n } catch (err) {\n console.error(\n err instanceof Error ? err.message : \"Failed to fetch remote skill\"\n );\n process.exit(1);\n }\n } else {\n const skillPath = resolve(target);\n let skillFile = skillPath;\n let skillDir: string | undefined;\n\n if (\n existsSync(skillPath) &&\n statSync(skillPath).isDirectory() &&\n existsSync(join(skillPath, \"SKILL.md\"))\n ) {\n skillDir = skillPath;\n skillFile = join(skillPath, \"SKILL.md\");\n }\n\n if (!existsSync(skillFile) || statSync(skillFile).isDirectory()) {\n console.error(`Error: Cannot find SKILL.md at ${skillFile}`);\n console.error(`Hint: If this is a directory of skills, use 'clawvet audit --dir ${target}' instead.`);\n process.exit(1);\n }\n\n const skillMd = readFileSync(skillFile, \"utf-8\");\n content = skillDir ? assembleSkill(skillDir, skillMd) : skillMd;\n fallbackName = basename(dirname(skillFile));\n }\n\n // Load .clawvetban — block skills by name, author, or slug\n const banFile = join(process.cwd(), \".clawvetban\");\n if (existsSync(banFile)) {\n const banEntries = readFileSync(banFile, \"utf-8\")\n .split(\"\\n\")\n .map((l) => l.trim().toLowerCase())\n .filter((l) => l && !l.startsWith(\"#\"));\n\n // Quick parse frontmatter to check name/author before full scan\n const fmMatch = content.match(/^---\\r?\\n([\\s\\S]*?)\\r?\\n---/);\n if (fmMatch) {\n const fmText = fmMatch[1].toLowerCase();\n for (const ban of banEntries) {\n const targetLower = target.toLowerCase();\n if (\n targetLower.includes(ban) ||\n fmText.includes(`name: ${ban}`) ||\n fmText.includes(`author: ${ban}`) ||\n fmText.includes(`slug: ${ban}`)\n ) {\n console.error(\n chalk.bgRed.white.bold(` BANNED `) +\n chalk.red(` Skill matches ban list entry: ${ban}`)\n );\n console.error(chalk.dim(` Source: ${banFile}`));\n process.exit(1);\n }\n }\n }\n }\n\n // Load .clawvetignore\n const ignoreFile = join(process.cwd(), \".clawvetignore\");\n const ignorePatterns: string[] = [];\n if (existsSync(ignoreFile)) {\n const lines = readFileSync(ignoreFile, \"utf-8\").split(\"\\n\");\n for (const line of lines) {\n const trimmed = line.trim();\n if (trimmed && !trimmed.startsWith(\"#\")) {\n ignorePatterns.push(trimmed);\n }\n }\n }\n\n const result = await scanSkill(content, {\n semantic: options.semantic ?? false,\n ignorePatterns: ignorePatterns.length ? ignorePatterns : undefined,\n skillName: fallbackName,\n });\n\n if (!options.quiet) {\n if (options.format === \"sarif\") {\n printSarifResult(result);\n } else if (options.format === \"json\") {\n printJsonResult(result);\n } else {\n printScanResult(result);\n }\n }\n\n // Telemetry: first-run opt-in prompt (only in interactive TTY)\n const isInteractive = !options.quiet && options.format !== \"json\" && options.format !== \"sarif\";\n if (isInteractive) {\n if (!hasBeenAsked() && !isTelemetryEnabled() && process.stdin.isTTY) {\n const readline = await import(\"node:readline\");\n const rl = readline.createInterface({ input: process.stdin, output: process.stderr });\n const answer = await new Promise<string>((resolve) => {\n rl.question(\n chalk.dim(\"Help improve ClawVet — send anonymous usage stats? (y/n) \"),\n (a) => { rl.close(); resolve(a.trim().toLowerCase()); }\n );\n });\n setTelemetry(answer === \"y\" || answer === \"yes\");\n }\n\n }\n\n // Await telemetry so it completes before any process.exit()\n await sendTelemetry(result);\n\n // Show feedback CTA every 5th scan (after increment)\n if (isInteractive && getScanCount() % 5 === 0) {\n console.log(\n chalk.dim(\" \") +\n chalk.cyan(\"Got feedback? → \") +\n chalk.underline.cyan(FEEDBACK_DISPLAY_URL)\n );\n console.log();\n }\n\n const failOn = options.failOn || (options.quiet ? \"high\" : undefined);\n if (failOn) {\n const severityOrder = [\"low\", \"medium\", \"high\", \"critical\"];\n const threshold = severityOrder.indexOf(failOn);\n const hasFailure = result.findings.some(\n (f) => severityOrder.indexOf(f.severity) >= threshold\n );\n if (hasFailure) {\n process.exit(1);\n }\n }\n}\n","import type { ThreatPattern } from \"./types.js\";\n\n// Build regex from parts at runtime to avoid AV false positives on signature strings\nfunction re(parts: string[], flags: string): RegExp {\n return new RegExp(parts.join(\"\"), flags);\n}\n\nexport const THREAT_PATTERNS: ThreatPattern[] = [\n // ═══════════════════════════════════════════════════════\n // CRITICAL: Remote code execution\n // ═══════════════════════════════════════════════════════\n {\n name: \"CURL_PIPE_BASH\",\n pattern: re([\"curl\\\\s+.*\\\\|\\\\s*(ba)?\", \"sh\"], \"gi\"),\n severity: \"critical\",\n category: \"remote_code_execution\",\n title: \"Curl piped to shell\",\n description: \"Downloads and executes remote code directly — classic supply chain attack vector.\",\n codeOnly: true,\n fix: \"Download the script first, inspect it, then execute: `curl -o setup.sh URL && cat setup.sh && bash setup.sh`\",\n },\n {\n name: \"WGET_EXECUTE\",\n pattern: re([\"wget\\\\s+.*&&\\\\s*(ba)?\", \"sh\"], \"gi\"),\n severity: \"critical\",\n category: \"remote_code_execution\",\n title: \"Wget with shell execution\",\n description: \"Downloads and executes remote code via wget.\",\n codeOnly: true,\n fix: \"Download the file first with `wget -O script.sh URL`, review it, then execute.\",\n },\n {\n name: \"EVAL_DYNAMIC\",\n pattern: re([\"ev\", \"al\\\\s*\\\\(\"], \"gi\"),\n severity: \"critical\",\n category: \"remote_code_execution\",\n title: \"Dynamic code evaluation\",\n description: \"Uses dynamic code evaluation which can run arbitrary code.\",\n codeOnly: true,\n fix: \"Replace dynamic evaluation with a safer alternative like JSON.parse() or a sandboxed environment.\",\n },\n {\n name: \"BASE64_DECODE\",\n pattern: re([\"base\", \"64\\\\s+(-d|--dec\", \"ode)\"], \"gi\"),\n severity: \"critical\",\n category: \"obfuscation\",\n title: \"Base64 decode execution\",\n description: \"Decodes base64 content, often used to hide malicious payloads.\",\n codeOnly: true,\n fix: \"Decode and include the command directly so users can review it.\",\n },\n {\n name: \"PYTHON_EXEC\",\n pattern: re([\"pyth\", \"on[3]?\\\\s+-c\"], \"gi\"),\n severity: \"critical\",\n category: \"remote_code_execution\",\n title: \"Python inline execution\",\n description: \"Executes inline Python code which may contain hidden payloads.\",\n codeOnly: true,\n fix: \"Move inline code to a separate .py file so users can review it before execution.\",\n },\n {\n name: \"REVERSE_SHELL\",\n pattern: re([\"\\\\/dev\\\\/tc\", \"p\\\\/|nc\\\\s+-[elp]|nca\", \"t\\\\s+-|mkfi\", \"fo\\\\s+.*\\\\/tmp\"], \"gi\"),\n severity: \"critical\",\n category: \"remote_code_execution\",\n title: \"Reverse shell\",\n description: \"Creates a reverse connection back to an attacker-controlled server.\",\n codeOnly: true,\n fix: \"Remove reverse connection commands — these are almost never legitimate in skills.\",\n },\n {\n name: \"CRON_PERSISTENCE\",\n pattern: re([\"cron\", \"tab\\\\s+-|\\\\/etc\\\\/cro\", \"n|system\", \"ctl\\\\s+enable\"], \"gi\"),\n severity: \"critical\",\n category: \"persistence\",\n title: \"Scheduled task persistence\",\n description: \"Installs a cron job or systemd service for persistent execution after reboot.\",\n codeOnly: true,\n fix: \"Document the scheduled task in the skill description and require explicit user consent before installing.\",\n },\n {\n name: \"PERL_EXEC\",\n pattern: re([\"per\", \"l\\\\s+-e\"], \"gi\"),\n severity: \"critical\",\n category: \"remote_code_execution\",\n title: \"Perl inline execution\",\n description: \"Executes inline Perl code which may contain obfuscated payloads.\",\n codeOnly: true,\n fix: \"Move inline code to a separate .pl file so users can review it before execution.\",\n },\n {\n name: \"NODE_EVAL\",\n pattern: re([\"no\", \"de\\\\s+-e\\\\s\"], \"gi\"),\n severity: \"critical\",\n category: \"remote_code_execution\",\n title: \"Node.js inline execution\",\n description: \"Executes inline Node.js code, often used to hide malicious logic.\",\n codeOnly: true,\n fix: \"Move inline code to a separate .js file so users can review it before execution.\",\n },\n {\n name: \"RUBY_EXEC\",\n pattern: re([\"rub\", \"y\\\\s+-e\"], \"gi\"),\n severity: \"critical\",\n category: \"remote_code_execution\",\n title: \"Ruby inline execution\",\n description: \"Executes inline Ruby code which may contain hidden payloads.\",\n codeOnly: true,\n fix: \"Move inline code to a separate .rb file so users can review it before execution.\",\n },\n\n // ═══════════════════════════════════════════════════════\n // HIGH: Credential theft\n // ═══════════════════════════════════════════════════════\n {\n name: \"ENV_FILE_READ\",\n // Match credential files, not the words. A leading letter before \".env\"\n // means it is a property access like `process.env`, and bare \"credentials\"\n // is ordinary English (\"store your credentials\"); both were the top two\n // false-positive sources on real skills.\n pattern: /(?<![A-Za-z0-9_])\\.env\\b|[./\\\\-]credentials\\b|credentials\\.(?:json|ya?ml)|\\.aws\\b|\\.ssh\\b|keychain/gi,\n severity: \"high\",\n category: \"credential_theft\",\n title: \"Sensitive file access\",\n description: \"Accesses credential files (.env, .aws, .ssh, keychain).\",\n fix: \"Declare required env vars in frontmatter under `metadata.openclaw.requires.env`.\",\n },\n {\n name: \"API_KEY_EXFIL\",\n pattern: /(ANTHROPIC|OPENAI|SLACK|DISCORD|TELEGRAM|STRIPE|GITHUB|GITLAB|AWS_SECRET|GROQ|OPENROUTER).*(_KEY|_TOKEN|_SECRET)/gi,\n severity: \"high\",\n category: \"credential_theft\",\n title: \"API key reference\",\n description: \"References specific API keys/tokens that could be exfiltrated.\",\n fix: \"Use environment variable references ($VAR) instead of hardcoding keys, and declare them in requires.env.\",\n },\n {\n name: \"DOTFILE_ACCESS\",\n pattern: /~\\/\\.(openclaw|clawdbot|moltbot)\\//gi,\n severity: \"high\",\n category: \"credential_theft\",\n title: \"OpenClaw config access\",\n description: \"Accesses OpenClaw/Clawdbot/Moltbot configuration directories.\",\n fix: \"Use the official OpenClaw SDK/API instead of directly reading config directories.\",\n },\n {\n name: \"SESSION_THEFT\",\n pattern: /sessions\\/\\*\\.jsonl/gi,\n severity: \"high\",\n category: \"credential_theft\",\n title: \"Session data access\",\n description: \"Accesses session transcript files which may contain sensitive data.\",\n fix: \"Remove session file access — skills should not read conversation transcripts.\",\n },\n {\n name: \"SSH_KEY_ACCESS\",\n pattern: /~\\/\\.ssh\\/id_|\\.pem\\b|BEGIN\\s+(RSA\\s+)?PRIVATE\\s+KEY/gi,\n severity: \"high\",\n category: \"credential_theft\",\n title: \"SSH/private key access\",\n description: \"Accesses SSH keys or private key files that could be stolen.\",\n fix: \"Use ssh-agent or a credential manager instead of directly reading key files.\",\n },\n {\n name: \"BROWSER_DATA\",\n pattern: /\\.config\\/google-chrome|\\.mozilla\\/firefox|Login\\s*Data|Cookies\\.sqlite/gi,\n severity: \"high\",\n category: \"credential_theft\",\n title: \"Browser data access\",\n description: \"Accesses browser profiles which contain saved passwords, cookies, and tokens.\",\n fix: \"Remove browser data access — skills should not read browser profiles.\",\n },\n {\n name: \"GIT_CREDENTIALS\",\n pattern: /\\.git-credentials|\\.gitconfig|git\\s+config.*credential/gi,\n severity: \"high\",\n category: \"credential_theft\",\n title: \"Git credential access\",\n description: \"Accesses git credential storage which may contain auth tokens.\",\n fix: \"Use `git` CLI commands instead of directly reading credential files.\",\n },\n {\n name: \"NPM_TOKEN\",\n pattern: /\\.npmrc|npm_token|NPM_AUTH_TOKEN/gi,\n severity: \"high\",\n category: \"credential_theft\",\n title: \"npm token access\",\n description: \"Accesses npm auth tokens which could be used to publish malicious packages.\",\n fix: \"Use `npm whoami` or `npm config get` instead of directly reading .npmrc.\",\n },\n {\n name: \"KUBE_CONFIG\",\n pattern: /~\\/\\.kube\\/config|KUBECONFIG/gi,\n severity: \"high\",\n category: \"credential_theft\",\n title: \"Kubernetes config access\",\n description: \"Accesses Kubernetes configuration which contains cluster credentials.\",\n fix: \"Use `kubectl` CLI commands instead of directly reading kubeconfig.\",\n },\n {\n name: \"DOCKER_SOCKET\",\n pattern: /\\/var\\/run\\/docker\\.sock|docker\\s+exec/gi,\n severity: \"high\",\n category: \"container_escape\",\n title: \"Docker socket/exec access\",\n description: \"Accesses Docker socket or runs exec — could enable container escape.\",\n fix: \"Use Docker SDK or CLI with limited permissions instead of direct socket access.\",\n },\n\n // ═══════════════════════════════════════════════════════\n // HIGH: Network exfiltration\n // ═══════════════════════════════════════════════════════\n {\n name: \"WEBHOOK_SEND\",\n pattern: /webhook\\.(site|url)|discord\\.com\\/api\\/webhooks|hooks\\.slack\\.com|api\\.telegram\\.org\\/bot/gi,\n severity: \"high\",\n category: \"data_exfiltration\",\n title: \"Webhook data exfiltration\",\n description: \"Sends data to webhook endpoints (Discord, Slack, Telegram) — common exfiltration channel.\",\n fix: \"If webhook integration is needed, declare it in the skill description and let users configure their own webhook URL.\",\n },\n {\n name: \"BORE_TUNNEL\",\n pattern: /bore\\.pub|ngrok|localtunnel|serveo\\.net|localhost\\.run/gi,\n severity: \"high\",\n category: \"data_exfiltration\",\n title: \"Tunnel service usage\",\n description: \"Uses tunneling services to expose local services or exfiltrate data.\",\n fix: \"Document tunnel usage in the skill description and require explicit user consent.\",\n },\n {\n name: \"SUSPICIOUS_IP\",\n pattern: /\\b(?:91\\.92\\.242\\.\\d+|45\\.61\\.\\d+\\.\\d+)\\b/g,\n severity: \"critical\",\n category: \"data_exfiltration\",\n title: \"Known malicious IP\",\n description: \"Contains IP addresses associated with known ClawHavoc C2 infrastructure.\",\n fix: \"Remove references to known malicious IP addresses.\",\n // Curated indicator of compromise: an exact match against known C2\n // infrastructure is disqualifying on its own, not a signal to be averaged.\n disqualifying: true,\n },\n {\n name: \"DNS_EXFIL\",\n pattern: /dig\\s+.*TXT|nslookup\\s+.*\\$|dns.*exfil|\\.burpcollaborator\\.|\\.oastify\\./gi,\n severity: \"high\",\n category: \"data_exfiltration\",\n title: \"DNS exfiltration\",\n description: \"Uses DNS queries to exfiltrate data — bypasses most firewalls.\",\n fix: \"Remove DNS exfiltration patterns — use standard HTTP APIs for data transfer.\",\n },\n {\n name: \"PASTEBIN_FETCH\",\n pattern: /pastebin\\.com|paste\\.ee|hastebin\\.com|ghostbin\\.|dpaste\\./gi,\n severity: \"high\",\n category: \"data_exfiltration\",\n title: \"Pastebin service usage\",\n description: \"References paste services commonly used to host malicious payloads or receive exfiltrated data.\",\n fix: \"Host code in a version-controlled repository (GitHub, GitLab) instead of paste services.\",\n },\n {\n name: \"SUSPICIOUS_TLD\",\n pattern: /https?:\\/\\/[^\\s\"']*\\.(tk|ml|ga|cf|gq|top|xyz|pw|cc|ws|buzz)\\b/gi,\n severity: \"high\",\n category: \"data_exfiltration\",\n title: \"Suspicious TLD\",\n description: \"URL uses a top-level domain frequently associated with malicious infrastructure.\",\n fix: \"Use URLs from well-known, reputable domains instead of suspicious TLDs.\",\n },\n {\n name: \"URL_SHORTENER\",\n pattern: /bit\\.ly|tinyurl\\.com|t\\.co\\/|goo\\.gl|is\\.gd|buff\\.ly|ow\\.ly|rb\\.gy/gi,\n severity: \"high\",\n category: \"obfuscation\",\n title: \"URL shortener\",\n description: \"Uses URL shorteners to hide the real destination of links.\",\n fix: \"Use the full, unshortened URL so users can verify the destination.\",\n },\n {\n name: \"RAW_SOCKET\",\n pattern: re([\"new\\\\s+Soc\", \"ket|net\\\\.conn\", \"ect|dgram\\\\.create\", \"Socket\"], \"gi\"),\n severity: \"high\",\n category: \"data_exfiltration\",\n title: \"Raw socket connection\",\n description: \"Creates raw network sockets which can bypass HTTP monitoring.\",\n codeOnly: true,\n fix: \"Use standard HTTP libraries (fetch, axios) instead of raw sockets for network communication.\",\n },\n\n // ═══════════════════════════════════════════════════════\n // MEDIUM: Social engineering\n // ═══════════════════════════════════════════════════════\n {\n name: \"PREREQUISITE_INSTALL\",\n pattern: /prerequisite|install.*first|run.*before|required.*dependency/gi,\n severity: \"medium\",\n category: \"social_engineering\",\n title: \"Prerequisite install trick\",\n description: \"Instructs users to install prerequisites — common social engineering tactic.\",\n fix: \"Declare dependencies in `metadata.openclaw.requires.bins` instead of instructing manual installs.\",\n },\n {\n name: \"COPY_PASTE_COMMAND\",\n pattern: /copy.*paste.*terminal|run.*this.*command/gi,\n severity: \"medium\",\n category: \"social_engineering\",\n title: \"Copy-paste command instruction\",\n description: \"Instructs users to copy-paste commands into their terminal.\",\n fix: \"Put commands in code blocks with proper context instead of copy-paste instructions.\",\n },\n {\n name: \"FAKE_DEPENDENCY\",\n pattern: /openclaw-core|moltbot-runtime|clawdbot-helper/gi,\n severity: \"medium\",\n category: \"social_engineering\",\n title: \"Fake dependency reference\",\n description: \"References fake packages that mimic official OpenClaw components.\",\n fix: \"Use only official OpenClaw packages from the verified registry.\",\n },\n {\n name: \"AUTHORITY_SPOOFING\",\n pattern: /official\\s+(openclaw|clawhub)|endorsed\\s+by|verified\\s+(skill|publisher)|from\\s+the\\s+openclaw\\s+team/gi,\n severity: \"medium\",\n category: \"social_engineering\",\n title: \"Authority spoofing\",\n description: \"Claims official endorsement or verification to gain trust.\",\n fix: \"Remove false authority claims — let the skill's quality speak for itself.\",\n },\n {\n name: \"URGENCY_MANIPULATION\",\n pattern: /critical\\s+update|security\\s+patch|must\\s+install\\s+immediately|urgent.*update/gi,\n severity: \"medium\",\n category: \"social_engineering\",\n title: \"Urgency manipulation\",\n description: \"Creates false urgency to pressure users into installing without review.\",\n fix: \"Remove urgency language — let users evaluate the skill at their own pace.\",\n },\n\n // ═══════════════════════════════════════════════════════\n // MEDIUM: Prompt injection\n // ═══════════════════════════════════════════════════════\n {\n name: \"IGNORE_INSTRUCTIONS\",\n pattern: /ignore\\s+(all\\s+)?previous\\s+instructions/gi,\n severity: \"medium\",\n category: \"prompt_injection\",\n title: \"Prompt injection — ignore instructions\",\n description: \"Attempts to override the AI agent's existing instructions.\",\n fix: \"Remove prompt injection attempts — skills should not try to override agent instructions.\",\n },\n {\n name: \"SYSTEM_OVERRIDE\",\n pattern: /you\\s+are\\s+now|new\\s+instructions|forget\\s+everything/gi,\n severity: \"medium\",\n category: \"prompt_injection\",\n title: \"Prompt injection — system override\",\n description: \"Attempts to redefine the AI agent's identity or instructions.\",\n fix: \"Remove system override attempts — skills should not alter agent identity.\",\n },\n {\n name: \"MEMORY_MANIPULATION\",\n pattern: /SOUL\\.md|MEMORY\\.md|AGENTS\\.md/gi,\n severity: \"medium\",\n category: \"persistence\",\n title: \"Memory/personality file manipulation\",\n description: \"References core personality or memory files, may attempt persistence.\",\n fix: \"Remove references to agent memory/personality files — skills should not modify agent state.\",\n },\n {\n name: \"JAILBREAK_ATTEMPT\",\n pattern: /\\bDAN\\b|do\\s+anything\\s+now|developer\\s+mode|evil\\s+mode|bypass.*safety/gi,\n severity: \"medium\",\n category: \"prompt_injection\",\n title: \"Jailbreak attempt\",\n description: \"Uses known jailbreak techniques (DAN, developer mode) to bypass safety constraints.\",\n fix: \"Remove jailbreak attempts — skills should work within the agent's safety constraints.\",\n },\n {\n name: \"ROLE_HIJACK\",\n pattern: /(?:pretend|act|behave)\\s+(?:you\\s+are|as\\s+if|to\\s+be)\\s+(?:a\\s+)?(?:different|new|hacker|evil)/gi,\n severity: \"medium\",\n category: \"prompt_injection\",\n title: \"Role hijacking\",\n description: \"Attempts to change the agent's persona to bypass safety restrictions.\",\n fix: \"Remove role hijacking attempts — skills should not alter the agent's persona.\",\n },\n {\n name: \"PROMPT_EXTRACTION\",\n pattern: /(?:reveal|show|print|output|tell\\s+me)\\s+(?:your\\s+)?(?:system\\s+)?(?:prompt|instructions|rules)/gi,\n severity: \"medium\",\n category: \"prompt_injection\",\n title: \"System prompt extraction\",\n description: \"Attempts to extract the agent's system prompt or configuration.\",\n fix: \"Remove prompt extraction attempts — skills should not try to access system prompts.\",\n },\n\n // ═══════════════════════════════════════════════════════\n // MEDIUM: Obfuscation\n // ═══════════════════════════════════════════════════════\n {\n name: \"HEX_ENCODING\",\n pattern: /\\\\x[0-9a-f]{2}(?:\\\\x[0-9a-f]{2}){3,}/gi,\n severity: \"medium\",\n category: \"obfuscation\",\n title: \"Hex-encoded payload\",\n description: \"Contains hex-encoded strings commonly used to hide malicious commands.\",\n codeOnly: true,\n fix: \"Replace hex-encoded strings with readable text so users can review the content.\",\n },\n {\n name: \"JS_OBFUSCATOR\",\n pattern: /_0x[a-f0-9]{4,}|var\\s+_0x/gi,\n severity: \"medium\",\n category: \"obfuscation\",\n title: \"JavaScript obfuscator output\",\n description: \"Contains patterns from JavaScript obfuscation tools used to hide malicious code.\",\n codeOnly: true,\n fix: \"Provide readable, unobfuscated source code instead of obfuscated JavaScript.\",\n },\n {\n name: \"UNICODE_STEGANOGRAPHY\",\n pattern: /[\\u200B\\u200C\\u200D\\u2060\\uFEFF]{3,}/g,\n severity: \"medium\",\n category: \"obfuscation\",\n title: \"Hidden zero-width characters\",\n description: \"Contains clusters of invisible zero-width Unicode characters that may hide instructions.\",\n fix: \"Remove zero-width characters — all content should be visible to users.\",\n },\n {\n name: \"RTL_OVERRIDE\",\n pattern: /[\\u202A\\u202B\\u202C\\u202D\\u202E\\u2066\\u2067\\u2068\\u2069]/g,\n severity: \"medium\",\n category: \"obfuscation\",\n title: \"Bidirectional text override\",\n description: \"Contains Unicode bidi override characters that can reverse displayed text to hide real content.\",\n fix: \"Remove bidirectional text override characters — text direction should be natural.\",\n },\n {\n name: \"HTML_COMMENT_INJECTION\",\n pattern: /<!--[\\s\\S]*?(?:ignore|instructions|system|override|secret)[\\s\\S]*?-->/gi,\n severity: \"medium\",\n category: \"obfuscation\",\n title: \"Hidden HTML comment instruction\",\n description: \"Embeds instructions inside HTML comments that are invisible to users but read by agents.\",\n fix: \"Move instructions from HTML comments into visible content.\",\n },\n {\n name: \"STRING_CONCAT_OBFUSC\",\n pattern: /[\"'][a-z]{1,3}[\"']\\s*\\+\\s*[\"'][a-z]{1,3}[\"']\\s*\\+\\s*[\"'][a-z]{1,3}[\"']/gi,\n severity: \"medium\",\n category: \"obfuscation\",\n title: \"String concatenation obfuscation\",\n description: \"Builds commands via single-character string concatenation to evade pattern detection.\",\n codeOnly: true,\n fix: \"Use complete string literals instead of character-by-character concatenation.\",\n },\n {\n name: \"BUFFER_BASE64_DECODE\",\n pattern: re([\"Buf\", \"fer\\\\.from\\\\s*\\\\(.*['\\\"]base\", \"64['\\\"]\\\\)|at\", \"ob\\\\s*\\\\(\"], \"gi\"),\n severity: \"critical\",\n category: \"obfuscation\",\n title: \"Buffer/atob encoded payload\",\n description: \"Decodes encoded content via Buffer.from() or atob(), often used to hide malicious payloads.\",\n codeOnly: true,\n fix: \"Include the decoded content directly so users can review it.\",\n },\n {\n name: \"STRING_FROMCHARCODE\",\n pattern: re([\"String\\\\.from\", \"CharCode\\\\s*\\\\(\"], \"gi\"),\n severity: \"medium\",\n category: \"obfuscation\",\n title: \"String.fromCharCode usage\",\n description: \"Builds strings from character codes to evade static pattern detection.\",\n codeOnly: true,\n fix: \"Use plain string literals instead of String.fromCharCode().\",\n },\n {\n name: \"DYNAMIC_PROPERTY_ACCESS\",\n pattern: /(?:process|global|window|globalThis)\\s*\\[\\s*['\"`]?\\w*['\"`]?\\s*\\+/gi,\n severity: \"medium\",\n category: \"obfuscation\",\n title: \"Dynamic property access on globals\",\n description: \"Dynamically accesses global object properties via string concatenation to hide intent.\",\n codeOnly: true,\n fix: \"Use direct property access (e.g., `process.env`) instead of dynamic bracket notation.\",\n },\n\n // ═══════════════════════════════════════════════════════\n // MEDIUM: Privilege escalation & system access\n // ═══════════════════════════════════════════════════════\n {\n name: \"SUDO_USAGE\",\n pattern: /sudo\\s+(?!apt|dnf|yum|brew)/gi,\n severity: \"medium\",\n category: \"privilege_escalation\",\n title: \"Sudo usage\",\n description: \"Requests elevated privileges — check if actually required for the task.\",\n codeOnly: true,\n fix: \"Remove sudo if not strictly necessary, or document why elevated privileges are required.\",\n },\n {\n name: \"CHMOD_DANGEROUS\",\n pattern: /chmod\\s+(?:777|a\\+[rwx]|[+]s)/gi,\n severity: \"medium\",\n category: \"privilege_escalation\",\n title: \"Dangerous file permissions\",\n description: \"Sets overly permissive file permissions (777) or setuid/setgid bits.\",\n codeOnly: true,\n fix: \"Use least-privilege permissions (e.g., `chmod 755` or `chmod 644`) instead of 777.\",\n },\n {\n name: \"PATH_TRAVERSAL\",\n pattern: /\\.\\.\\//g,\n severity: \"medium\",\n category: \"file_system\",\n title: \"Path traversal\",\n description: \"Uses relative path traversal (../) which could access files outside expected directories.\",\n codeOnly: true,\n fix: \"Use absolute paths or paths relative to the skill's working directory.\",\n },\n\n // ═══════════════════════════════════════════════════════\n // LOW: Suspicious but not necessarily malicious\n // ═══════════════════════════════════════════════════════\n {\n name: \"SHELL_EXEC\",\n pattern: re([\"child_\", \"process|ex\", \"ec\\\\(|spa\", \"wn\\\\(\"], \"gi\"),\n severity: \"low\",\n category: \"code_execution\",\n title: \"Shell execution API\",\n description: \"Uses shell execution APIs — legitimate but worth noting.\",\n codeOnly: true,\n fix: \"If shell execution is needed, use execFile() with explicit arguments instead of exec() with string commands.\",\n },\n {\n name: \"NETWORK_REQUEST\",\n pattern: /fetch\\(|axios|node-fetch|got\\(/gi,\n severity: \"low\",\n category: \"network\",\n title: \"Network request API\",\n description: \"Makes network requests — legitimate but worth reviewing targets.\",\n codeOnly: true,\n fix: \"Document all network endpoints in the skill description so users can review them.\",\n },\n {\n name: \"FILE_WRITE\",\n pattern: /fs\\.write|writeFileSync/gi,\n severity: \"low\",\n category: \"file_system\",\n title: \"File write operation\",\n description: \"Writes to the filesystem — check what files are being modified.\",\n codeOnly: true,\n fix: \"Document which files are written and why in the skill description.\",\n },\n {\n name: \"ENV_MODIFICATION\",\n pattern: /process\\.env\\[|export\\s+[A-Z_]+=|setenv/gi,\n severity: \"low\",\n category: \"environment\",\n title: \"Environment variable modification\",\n description: \"Modifies environment variables which could affect other tools or processes.\",\n codeOnly: true,\n fix: \"Document env var modifications in the skill description and declare them in requires.env.\",\n },\n {\n name: \"WILDCARD_FILE_ACCESS\",\n pattern: /\\*\\.(pem|key|p12|pfx|jks|keystore|ovpn|rdp)/gi,\n severity: \"low\",\n category: \"credential_theft\",\n title: \"Sensitive file extension glob\",\n description: \"Globs for files with sensitive extensions (keys, certificates, VPN configs).\",\n fix: \"Reference specific files by name instead of using wildcard patterns on sensitive extensions.\",\n },\n {\n name: \"LARGE_BASE64_LITERAL\",\n pattern: /[A-Za-z0-9+/=]{100,}/g,\n severity: \"low\",\n category: \"obfuscation\",\n title: \"Large base64-like string\",\n description: \"Contains a long base64-like string that may be an encoded payload.\",\n fix: \"Include the decoded content directly or explain what the base64 string contains.\",\n },\n];\n\nexport const POPULAR_SKILLS = [\n \"todoist-cli\",\n \"github-manager\",\n \"slack-assistant\",\n \"email-composer\",\n \"calendar-sync\",\n \"weather-forecast\",\n \"news-reader\",\n \"code-reviewer\",\n \"docker-helper\",\n \"aws-manager\",\n \"notion-sync\",\n \"jira-tracker\",\n \"spotify-controller\",\n \"home-assistant\",\n \"file-organizer\",\n \"pdf-reader\",\n \"translate-text\",\n \"image-generator\",\n \"web-scraper\",\n \"database-query\",\n \"git-assistant\",\n \"linux-admin\",\n \"python-helper\",\n \"react-builder\",\n \"api-tester\",\n \"markdown-editor\",\n \"csv-analyzer\",\n \"ssh-manager\",\n \"cron-scheduler\",\n \"log-analyzer\",\n];\n","import { parse as parseYaml } from \"yaml\";\nimport type { ParsedSkill, SkillFrontmatter, CodeBlock } from \"../types.js\";\n\nconst FRONTMATTER_RE = /^---\\r?\\n([\\s\\S]*?)\\r?\\n---/;\nconst CODE_BLOCK_RE = /```(\\w*)\\r?\\n([\\s\\S]*?)```/g;\nconst URL_RE = /https?:\\/\\/[^\\s\"'<>\\])+]+/gi;\nconst IP_RE = /\\b(?:\\d{1,3}\\.){3}\\d{1,3}\\b/g;\nconst DOMAIN_RE = /\\b(?:[a-z0-9](?:[a-z0-9-]{0,61}[a-z0-9])?\\.)+[a-z]{2,}\\b/gi;\n\nexport function parseSkill(content: string): ParsedSkill {\n let frontmatter: SkillFrontmatter = {};\n let body = content;\n\n const fmMatch = content.match(FRONTMATTER_RE);\n if (fmMatch) {\n try {\n frontmatter = parseYaml(fmMatch[1]) as SkillFrontmatter;\n } catch {\n frontmatter = {};\n }\n body = content.slice(fmMatch[0].length).trim();\n }\n\n const codeBlocks: CodeBlock[] = [];\n let match: RegExpExecArray | null;\n const cbRe = new RegExp(CODE_BLOCK_RE.source, CODE_BLOCK_RE.flags);\n\n while ((match = cbRe.exec(content)) !== null) {\n const before = content.slice(0, match.index);\n const lineStart = before.split(\"\\n\").length;\n const blockLines = match[0].split(\"\\n\").length;\n codeBlocks.push({\n language: match[1] || \"unknown\",\n content: match[2],\n lineStart,\n lineEnd: lineStart + blockLines - 1,\n });\n }\n\n const urls = [...new Set(content.match(URL_RE) || [])];\n const ipAddresses = [...new Set(content.match(IP_RE) || [])];\n const domains = [...new Set(content.match(DOMAIN_RE) || [])];\n\n return {\n frontmatter,\n body,\n codeBlocks,\n urls,\n ipAddresses,\n domains,\n rawContent: content,\n };\n}\n","import { THREAT_PATTERNS } from \"../patterns.js\";\nimport type { Finding, ParsedSkill, Severity } from \"../types.js\";\n\nfunction isInCodeBlock(lineNumber: number, skill: ParsedSkill): boolean {\n return skill.codeBlocks.some(\n (block) => lineNumber >= block.lineStart && lineNumber <= block.lineEnd\n );\n}\n\nfunction isInHeading(lineNumber: number, rawContent: string): boolean {\n const lines = rawContent.split(\"\\n\");\n const line = lines[lineNumber - 1] || \"\";\n return /^\\s*#{1,6}\\s/.test(line);\n}\n\nconst BASE_CONFIDENCE: Record<Severity, number> = {\n critical: 0.9,\n high: 0.8,\n medium: 0.6,\n low: 0.5,\n};\n\nexport function runStaticAnalysis(skill: ParsedSkill): Finding[] {\n const findings: Finding[] = [];\n\n for (const threat of THREAT_PATTERNS) {\n const re = new RegExp(threat.pattern.source, threat.pattern.flags);\n let match: RegExpExecArray | null;\n\n while ((match = re.exec(skill.rawContent)) !== null) {\n const before = skill.rawContent.slice(0, match.index);\n const lineNumber = before.split(\"\\n\").length;\n\n if (threat.codeOnly && !isInCodeBlock(lineNumber, skill)) {\n continue;\n }\n\n const inCode = isInCodeBlock(lineNumber, skill);\n const inHeading = isInHeading(lineNumber, skill.rawContent);\n\n let contextMultiplier: number;\n if (inCode && threat.codeOnly) {\n contextMultiplier = 1.0;\n } else if (inCode) {\n contextMultiplier = 0.95;\n } else if (inHeading) {\n contextMultiplier = 0.5;\n } else if (threat.codeOnly) {\n // codeOnly pattern somehow in prose (shouldn't happen due to skip above)\n contextMultiplier = 0.4;\n } else {\n // Non-codeOnly patterns are designed to match in prose\n contextMultiplier = 0.9;\n }\n\n const baseConfidence = BASE_CONFIDENCE[threat.severity];\n // A curated indicator of compromise is an exact match, not a fuzzy\n // heuristic, where it appears does not make it less certain.\n const confidence = threat.disqualifying\n ? 1.0\n : Math.min(1.0, baseConfidence * contextMultiplier);\n\n findings.push({\n category: threat.category,\n severity: threat.severity,\n title: threat.title,\n description: threat.description,\n evidence: match[0],\n lineNumber,\n analysisPass: \"static-analysis\",\n confidence: Math.round(confidence * 100) / 100,\n fix: threat.fix,\n disqualifying: threat.disqualifying,\n });\n }\n }\n\n return findings;\n}\n","import type { Finding, ParsedSkill } from \"../types.js\";\n\nconst SEMVER_RE = /^\\d+\\.\\d+\\.\\d+/;\n\nconst KNOWN_BINS = [\n \"curl\", \"wget\", \"git\", \"python\", \"python3\", \"node\", \"npm\", \"npx\",\n \"brew\", \"apt\", \"pip\", \"docker\", \"kubectl\", \"ssh\", \"scp\", \"rsync\",\n \"ffmpeg\", \"jq\", \"sed\", \"awk\", \"grep\", \"find\",\n];\n\nexport function validateMetadata(skill: ParsedSkill): Finding[] {\n const findings: Finding[] = [];\n const fm = skill.frontmatter;\n const pass = \"metadata-validator\";\n\n if (!fm.name) {\n findings.push({\n category: \"metadata\",\n severity: \"medium\",\n title: \"Missing skill name\",\n description: \"SKILL.md frontmatter does not declare a name.\",\n analysisPass: pass,\n fix: \"Add `name:` to the YAML frontmatter.\",\n });\n }\n\n if (!fm.description) {\n findings.push({\n category: \"metadata\",\n severity: \"medium\",\n title: \"Missing description\",\n description: \"SKILL.md frontmatter does not declare a description.\",\n analysisPass: pass,\n fix: \"Add `description:` to the YAML frontmatter.\",\n });\n } else if (fm.description.length < 10) {\n findings.push({\n category: \"metadata\",\n severity: \"low\",\n title: \"Vague description\",\n description: \"Skill description is suspiciously short.\",\n evidence: fm.description,\n analysisPass: pass,\n fix: \"Write a more detailed description (at least 10 characters).\",\n });\n }\n\n if (fm.version && !SEMVER_RE.test(fm.version)) {\n findings.push({\n category: \"metadata\",\n severity: \"low\",\n title: \"Invalid version format\",\n description: \"Version does not follow semver format.\",\n evidence: fm.version,\n analysisPass: pass,\n fix: \"Use semver format: `version: X.Y.Z` (e.g., `1.0.0`).\",\n });\n }\n\n // Real-world frontmatter sometimes gives bins/env as a scalar string instead\n // of a list; guard so `new Set(...)` doesn't throw on it.\n const rawBins = fm.metadata?.openclaw?.requires?.bins;\n const declaredBins = new Set(Array.isArray(rawBins) ? rawBins : []);\n\n for (const bin of KNOWN_BINS) {\n const binRe = new RegExp(`\\\\b${bin}\\\\b`, \"i\");\n if (binRe.test(skill.rawContent) && !declaredBins.has(bin)) {\n const usedInCode = skill.codeBlocks.some((cb) => binRe.test(cb.content));\n if (usedInCode) {\n findings.push({\n category: \"metadata\",\n severity: \"low\",\n title: `Undeclared binary: ${bin}`,\n description: `Skill uses '${bin}' in code but does not declare it in requires.bins.`,\n analysisPass: pass,\n fix: `Add '${bin}' to \\`metadata.openclaw.requires.bins\\` in frontmatter.`,\n });\n }\n }\n }\n\n const rawEnv = fm.metadata?.openclaw?.requires?.env;\n const declaredEnv = new Set(Array.isArray(rawEnv) ? rawEnv : []);\n\n // A variable the skill assigns itself is a local, not an environment\n // dependency. Without this, every `RESULT=$(curl ...)` in a shell block gets\n // reported as an undeclared env var, which buries the real ones.\n const assigned = new Set<string>();\n for (const re of [\n /^\\s*(?:export\\s+|local\\s+|declare\\s+(?:-\\w+\\s+)?)?([A-Z][A-Z0-9_]+)=/gm,\n /\\bread\\s+(?:-\\w+\\s+)*([A-Z][A-Z0-9_]+)/g,\n /\\bfor\\s+([A-Z][A-Z0-9_]+)\\s+in\\b/g,\n ]) {\n for (const m of skill.rawContent.matchAll(re)) assigned.add(m[1]);\n }\n\n const envRe = /\\$\\{?([A-Z][A-Z0-9_]+)\\}?/g;\n let match: RegExpExecArray | null;\n\n while ((match = envRe.exec(skill.rawContent)) !== null) {\n const envVar = match[1];\n if (!declaredEnv.has(envVar) && !assigned.has(envVar) && envVar.length > 2) {\n findings.push({\n category: \"metadata\",\n severity: \"low\",\n title: `Undeclared env var: ${envVar}`,\n description: `References environment variable $${envVar} but does not declare it in requires.env.`,\n evidence: match[0],\n analysisPass: pass,\n fix: `Add '${envVar}' to \\`metadata.openclaw.requires.env\\` in frontmatter.`,\n });\n }\n }\n\n return findings;\n}\n","import type { Finding, ParsedSkill } from \"../types.js\";\n\nconst NPX_AUTO_INSTALL_RE = /npx\\s+-y\\s+/gi;\nconst NPM_INSTALL_RE = /npm\\s+install\\s+(-g\\s+)?(\\S+)/gi;\n\nexport function checkDependencies(skill: ParsedSkill): Finding[] {\n const findings: Finding[] = [];\n const pass = \"dependency-checker\";\n\n let match: RegExpExecArray | null;\n const npxRe = new RegExp(NPX_AUTO_INSTALL_RE.source, NPX_AUTO_INSTALL_RE.flags);\n\n while ((match = npxRe.exec(skill.rawContent)) !== null) {\n const before = skill.rawContent.slice(0, match.index);\n const lineNumber = before.split(\"\\n\").length;\n\n findings.push({\n category: \"dependency_risk\",\n severity: \"medium\",\n title: \"npx auto-install (-y flag)\",\n description: \"Uses 'npx -y' which auto-installs packages without user confirmation.\",\n evidence: match[0],\n lineNumber,\n analysisPass: pass,\n fix: \"Remove the `-y` flag from npx to require user confirmation before installing.\",\n });\n }\n\n const npmRe = new RegExp(NPM_INSTALL_RE.source, NPM_INSTALL_RE.flags);\n while ((match = npmRe.exec(skill.rawContent)) !== null) {\n if (match[1]) {\n findings.push({\n category: \"dependency_risk\",\n severity: \"medium\",\n title: \"Global npm package install\",\n description: `Installs npm package globally: ${match[2]}`,\n evidence: match[0],\n analysisPass: pass,\n fix: \"Use a local install (`npm install` without `-g`) or declare the dependency in requires.bins.\",\n });\n }\n }\n\n return findings;\n}\n","import { distance } from \"fastest-levenshtein\";\nimport { POPULAR_SKILLS } from \"../patterns.js\";\nimport type { Finding } from \"../types.js\";\n\nconst MAX_EDIT_DISTANCE = 2;\n\nexport function detectTyposquats(skillName: string): Finding[] {\n if (!skillName) return [];\n\n const findings: Finding[] = [];\n const normalized = skillName.toLowerCase().trim();\n\n for (const popular of POPULAR_SKILLS) {\n if (normalized === popular) continue;\n\n const d = distance(normalized, popular);\n if (d > 0 && d <= MAX_EDIT_DISTANCE) {\n findings.push({\n category: \"typosquatting\",\n severity: \"high\",\n title: `Possible typosquat of \"${popular}\"`,\n description: `Skill name \"${skillName}\" is ${d} edit(s) away from popular skill \"${popular}\". This may be an attempt to impersonate a trusted skill.`,\n evidence: `\"${skillName}\" ≈ \"${popular}\" (distance: ${d})`,\n analysisPass: \"typosquat-detector\",\n });\n }\n }\n\n const patterns = [\n { re: /-{2,}/, desc: \"extra hyphens\" },\n { re: /(.)\\1{2,}/, desc: \"repeated characters\" },\n ];\n\n for (const p of patterns) {\n if (p.re.test(normalized) && !POPULAR_SKILLS.includes(normalized)) {\n findings.push({\n category: \"typosquatting\",\n severity: \"medium\",\n title: `Suspicious naming pattern: ${p.desc}`,\n description: `Skill name \"${skillName}\" has ${p.desc}, which is a common typosquatting technique.`,\n analysisPass: \"typosquat-detector\",\n });\n }\n }\n\n return findings;\n}\n","import type { Finding } from \"../types.js\";\n\n// A static context pass over the whole finding set, run before scoring.\n//\n// Many rules fire on a capability that is only dangerous once it is paired with\n// a way to get data out or run remote code. Reading ~/.aws/credentials is theft\n// when it is piped to a webhook and configuration when it is not. The regex\n// stage can't see that difference; this pass can, because it sees every finding\n// in the skill at once.\n//\n// Rule: if the skill contains no exfiltration/remote-exec SINK, downweight the\n// dual-use capability findings so a lone capability no longer reaches the warn\n// band. Skills that pair the same capability with a sink keep full weight, so\n// this costs no recall on the corpus (every credential/persistence-based\n// malicious fixture also carries an exfil or curl-pipe-bash sink).\n\n// Capabilities that are common in legitimate skills and only incriminating in\n// combination. Deliberately excludes remote_code_execution / obfuscation:\n// a lone inline interpreter or eval is left for the semantic stage to judge,\n// because static rules can't tell a REPL from an obfuscated dropper.\nconst DUAL_USE_CATEGORIES = new Set([\n \"credential_theft\",\n \"container_escape\",\n \"privilege_escalation\",\n \"persistence\",\n]);\n\n// Signals that a capability is actually being weaponised: data leaving the box,\n// or remote code being pulled and run.\nconst SINK_CATEGORIES = new Set([\"data_exfiltration\"]);\nconst SINK_TITLES = new Set([\n \"Curl piped to shell\",\n \"Wget with shell execution\",\n \"Shell execution API\",\n \"Reverse shell\",\n \"Known malicious IP\",\n]);\n\nconst DOWNWEIGHT = 0.3;\n\nfunction inCode(line: number | null | undefined, codeLines: Set<number>): boolean {\n return line !== null && line !== undefined && codeLines.has(line);\n}\n\n// The install-me envelope. Real malicious skills keep the payload in a\n// referenced script or binary and leave only the instructions that get a user\n// to run it in the markdown. Each half is common on its own: 48 of 400 clean\n// ClawHub skills say \"install X first\", and legitimate READMEs tell you to run\n// a command. Together they are not: on the 500-skill real corpus this pair\n// fires on 48 of 50 malicious skills and 0 of 450 benign ones.\n//\n// Each half is medium severity, so an envelope-only skill tops out at 24 and\n// never crosses the warn line at 26. Promoting the pair is what closes that\n// two-point gap on the threat class where the payload is out of file.\nconst ENVELOPE_TITLES = [\"Prerequisite install trick\", \"Copy-paste command instruction\"];\n\n// Hosts whose whole purpose is a documented one-line installer. `curl | sh` off\n// one of these is the vendor's own published instruction, not a dropper, and it\n// was the single most common cause of a real clean skill being flagged. An\n// attacker cannot use this without first compromising the vendor, in which case\n// the install script is the least of anyone's problems.\nconst TRUSTED_INSTALLER_HOSTS = [\n \"astral.sh\",\n \"sh.rustup.rs\",\n \"get.docker.com\",\n \"install.python-poetry.org\",\n \"get.pnpm.io\",\n \"bun.sh\",\n \"ollama.com\",\n \"deb.nodesource.com\",\n \"raw.githubusercontent.com/Homebrew\",\n \"get.volta.sh\",\n];\n\nconst PIPE_TO_SHELL = new Set([\"Curl piped to shell\", \"Wget with shell execution\"]);\n\nfunction fromTrustedInstaller(f: Finding): boolean {\n if (!PIPE_TO_SHELL.has(f.title)) return false;\n const ev = f.evidence ?? \"\";\n return TRUSTED_INSTALLER_HOSTS.some((h) => ev.includes(h));\n}\n\nfunction isSink(f: Finding): boolean {\n if (fromTrustedInstaller(f)) return false;\n return SINK_CATEGORIES.has(f.category) || SINK_TITLES.has(f.title);\n}\n\nfunction isDualUse(f: Finding): boolean {\n return DUAL_USE_CATEGORIES.has(f.category);\n}\n\nfunction promoteEnvelope(findings: Finding[]): Finding[] {\n const titles = new Set(findings.map((f) => f.title));\n if (!ENVELOPE_TITLES.every((t) => titles.has(t))) return findings;\n\n // The pair is its own concern, not a louder version of either half, so it is\n // reported as a separate finding and the halves stay as the evidence for it.\n // Confidence is 1.0 for the same reason a curated indicator of compromise is:\n // this is an exact co-occurrence, not a fuzzy heuristic that gets less\n // certain depending on where in the file it matched.\n const anchor = findings.find((f) => f.title === ENVELOPE_TITLES[0])!;\n const envelope: Finding = {\n category: \"social_engineering\",\n severity: \"high\",\n title: \"Install-me envelope\",\n description:\n \"The skill tells the user to install a prerequisite and run a command, without the payload being in SKILL.md. This is how a skill gets code it does not contain executed.\",\n evidence: anchor.evidence,\n lineNumber: anchor.lineNumber,\n analysisPass: \"context-classifier\",\n confidence: 1.0,\n fix: \"Declare dependencies in `metadata.openclaw.requires.bins` and ship the code you run, so it can be reviewed before it executes.\",\n };\n return [...findings, envelope];\n}\n\n// A credential read on its own is configuration, and an outbound request on its\n// own is an API call. Together in one skill they are the exfiltration pattern:\n// a secret is read and something sends data out. This is the mirror of the\n// downweight below, and the reason it can be stated with confidence 1.0 is the\n// same: the co-occurrence is exact, not a guess about any single line.\nfunction taintExfiltration(findings: Finding[], codeLines: Set<number>): Finding[] {\n const source = findings.find((f) => f.category === \"credential_theft\");\n const sink = findings.find((f) => f.category === \"data_exfiltration\");\n if (!source || !sink) return findings;\n // Both halves must be actual uses: a declaration in frontmatter or a prose\n // threat-table mention is documenting, not doing. An OAuth client declaring\n // its API key and posting to the user's own webhook, or a security scanner\n // listing exfiltration patterns, is not the exfiltration pattern.\n if (!inCode(source.lineNumber, codeLines) || !inCode(sink.lineNumber, codeLines)) {\n return findings;\n }\n\n return [\n ...findings,\n {\n category: \"data_exfiltration\",\n severity: \"critical\",\n title: \"Credential exfiltration\",\n description: `The skill reads credentials (${source.title}) and sends data out (${sink.title}). Together these are the pattern used to steal secrets.`,\n evidence: source.evidence,\n lineNumber: source.lineNumber,\n analysisPass: \"context-classifier\",\n confidence: 1.0,\n fix: \"Remove the outbound send, or document exactly what is transmitted and let the user supply their own endpoint.\",\n },\n ];\n}\n\nexport function applyContext(findings: Finding[], codeLines?: Set<number>): Finding[] {\n const withEnvelope = promoteEnvelope(findings).map((f) =>\n fromTrustedInstaller(f)\n ? { ...f, severity: \"low\" as const, confidence: 0.3, description: `${f.description} This one points at a well-known vendor installer.` }\n : f\n );\n const lines = codeLines ?? new Set<number>();\n if (withEnvelope.some(isSink)) return taintExfiltration(withEnvelope, lines);\n return withEnvelope.map((f) =>\n isDualUse(f) && !f.disqualifying\n ? { ...f, confidence: Math.round((f.confidence ?? 1.0) * DOWNWEIGHT * 100) / 100 }\n : f\n );\n}\n","import type { Finding, FindingsCount, RiskGrade } from \"../types.js\";\n\nconst SEVERITY_WEIGHTS = {\n critical: 30,\n high: 15,\n medium: 7,\n low: 3,\n} as const;\n\n// A disqualifying indicator of compromise pins the score to the bottom of the\n// F band regardless of aggregate, enough benign signal must never dilute a\n// known-bad match down into a passing grade.\nconst DISQUALIFYING_FLOOR = 90;\n\n// Identical matches of the same rule count with diminishing returns. A rule\n// matching the same evidence on four lines is one concern repeated, and letting\n// it stack linearly is what pushes legitimate skills (an ssh helper that reads\n// ~/.ssh a few times) into the block band. The first hit counts full; each\n// extra identical hit counts at this fraction, so repetition still adds signal\n// without dominating.\nconst REPEAT_FACTOR = 0.25;\n\nfunction weight(f: Finding): number {\n return SEVERITY_WEIGHTS[f.severity] * (f.confidence ?? 1.0);\n}\n\n// Key on title plus evidence, not title alone. Five \"prerequisite install\"\n// matches on five different lines are five separate malicious instructions and\n// each must count full; only identical matches on different lines discount.\nfunction repeatKey(f: Finding): string {\n return `${f.title}\\u0000${f.evidence ?? \"\"}`;\n}\n\nexport function calculateRiskScore(findings: Finding[]): number {\n const byKey = new Map<string, Finding[]>();\n for (const f of findings) {\n // Metadata findings are documentation hygiene, not risk. An undeclared\n // `grep` says the frontmatter is incomplete, not that the skill is\n // dangerous, and a skill using eight ordinary unix tools would otherwise\n // accumulate enough of them to be flagged on its own. They are still\n // reported, they just do not move the score.\n if (f.category === \"metadata\") continue;\n const key = repeatKey(f);\n const arr = byKey.get(key);\n if (arr) arr.push(f);\n else byKey.set(key, [f]);\n }\n\n let score = 0;\n for (const group of byKey.values()) {\n group.sort((a, b) => weight(b) - weight(a));\n group.forEach((f, i) => {\n score += i === 0 ? weight(f) : weight(f) * REPEAT_FACTOR;\n });\n }\n if (findings.some((f) => f.disqualifying)) {\n score = Math.max(score, DISQUALIFYING_FLOOR);\n }\n return Math.round(Math.min(score, 100));\n}\n\nexport function getRiskGrade(score: number): RiskGrade {\n if (score <= 10) return \"A\";\n if (score <= 25) return \"B\";\n if (score <= 50) return \"C\";\n if (score <= 75) return \"D\";\n return \"F\";\n}\n\nexport function countFindings(findings: Finding[]): FindingsCount {\n const counts: FindingsCount = { critical: 0, high: 0, medium: 0, low: 0 };\n for (const f of findings) {\n counts[f.severity]++;\n }\n return counts;\n}\n","import { createHash } from \"node:crypto\";\nimport type { ScanResult } from \"../types.js\";\n\nconst MAX_ENTRIES = 100;\nconst cache = new Map<string, ScanResult>();\n\nfunction hashContent(content: string): string {\n return createHash(\"sha256\").update(content).digest(\"hex\");\n}\n\nexport function getCached(content: string): ScanResult | undefined {\n const key = hashContent(content);\n const result = cache.get(key);\n if (result) {\n // Move to end (most recently used)\n cache.delete(key);\n cache.set(key, result);\n }\n return result;\n}\n\nexport function setCached(content: string, result: ScanResult): void {\n const key = hashContent(content);\n if (cache.has(key)) {\n cache.delete(key);\n } else if (cache.size >= MAX_ENTRIES) {\n // Evict oldest (first entry)\n const oldest = cache.keys().next().value!;\n cache.delete(oldest);\n }\n cache.set(key, result);\n}\n","import type { Finding, ParsedSkill, ScanResult } from \"../types.js\";\nimport { parseSkill } from \"./skill-parser.js\";\nimport { runStaticAnalysis } from \"./static-analysis.js\";\nimport { validateMetadata } from \"./metadata-validator.js\";\nimport { checkDependencies } from \"./dependency-checker.js\";\nimport { detectTyposquats } from \"./typosquat-detector.js\";\nimport { applyContext } from \"./context-classifier.js\";\nimport { calculateRiskScore, getRiskGrade, countFindings } from \"./risk-scorer.js\";\nimport { getCached, setCached } from \"./cache.js\";\n\n// Line numbers that fall inside a fenced code block. The context pass uses this\n// to tell a real use of a credential or exfil channel from a mention of one in\n// prose or frontmatter.\nfunction codeLinesOf(skill: ParsedSkill): Set<number> {\n const lines = new Set<number>();\n for (const cb of skill.codeBlocks) {\n for (let l = cb.lineStart; l <= cb.lineEnd; l++) lines.add(l);\n }\n return lines;\n}\n\nexport interface ScanOptions {\n semantic?: boolean;\n semanticAnalyzer?: (content: string) => Promise<Finding[]>;\n ignorePatterns?: string[];\n skipCache?: boolean;\n /** Fallback name when SKILL.md frontmatter is missing `name`. Typically the folder basename. */\n skillName?: string;\n}\n\nexport async function scanSkill(\n content: string,\n options: ScanOptions = {}\n): Promise<ScanResult> {\n if (!options.skipCache) {\n const cached = getCached(content);\n if (cached) {\n return { ...cached, cached: true };\n }\n }\n\n const skill = parseSkill(content);\n const allFindings: Finding[] = [];\n\n allFindings.push(...runStaticAnalysis(skill));\n allFindings.push(...validateMetadata(skill));\n\n if (options.semantic && options.semanticAnalyzer) {\n const semanticFindings = await options.semanticAnalyzer(content);\n allFindings.push(...semanticFindings);\n }\n\n allFindings.push(...checkDependencies(skill));\n\n if (skill.frontmatter.name) {\n allFindings.push(...detectTyposquats(skill.frontmatter.name));\n }\n\n // Filter out ignored patterns\n const filteredFindings = options.ignorePatterns?.length\n ? allFindings.filter(\n (f) => !options.ignorePatterns!.some((ig) => f.title === ig || f.category === ig)\n )\n : allFindings;\n\n // Context pass: downweight dual-use capabilities that have no exfil/exec sink.\n const contextFindings = applyContext(filteredFindings, codeLinesOf(skill));\n\n const riskScore = calculateRiskScore(contextFindings);\n const riskGrade = getRiskGrade(riskScore);\n // Report what was scored. Showing the pre-context findings would explain the\n // score wrong: a promoted envelope pair would read as two mediums next to a\n // score only a high can produce.\n const findingsCount = countFindings(contextFindings);\n\n const recommendation =\n riskScore >= 76 ? \"block\" : riskScore >= 26 ? \"warn\" : \"approve\";\n\n const result: ScanResult = {\n skillName: skill.frontmatter.name || options.skillName || \"unknown\",\n skillVersion: skill.frontmatter.version,\n skillSource: \"local\",\n status: \"complete\",\n riskScore,\n riskGrade,\n findingsCount,\n findings: contextFindings,\n recommendation,\n };\n\n if (!options.skipCache) {\n setCached(content, result);\n }\n\n return result;\n}\n\nexport { parseSkill } from \"./skill-parser.js\";\nexport { runStaticAnalysis } from \"./static-analysis.js\";\nexport { validateMetadata } from \"./metadata-validator.js\";\nexport { checkDependencies } from \"./dependency-checker.js\";\nexport { detectTyposquats } from \"./typosquat-detector.js\";\nexport { calculateRiskScore, getRiskGrade, countFindings } from \"./risk-scorer.js\";\n","import chalk from \"chalk\";\nimport type { ScanResult, Finding, Severity } from \"@clawvet/shared\";\n\nconst SEVERITY_COLORS: Record<Severity, (s: string) => string> = {\n critical: chalk.bgRed.white.bold,\n high: chalk.red.bold,\n medium: chalk.yellow,\n low: chalk.blue,\n};\n\nconst GRADE_COLORS: Record<string, (s: string) => string> = {\n A: chalk.green.bold,\n B: chalk.greenBright,\n C: chalk.yellow.bold,\n D: chalk.redBright.bold,\n F: chalk.bgRed.white.bold,\n};\n\nexport function printScanResult(result: ScanResult): void {\n console.log();\n console.log(chalk.bold(\"━\".repeat(60)));\n console.log(chalk.bold(\" ClawVet Scan Report\"));\n console.log(chalk.bold(\"━\".repeat(60)));\n console.log();\n\n console.log(` Skill: ${chalk.bold(result.skillName)}`);\n if (result.skillVersion) {\n console.log(` Version: ${result.skillVersion}`);\n }\n console.log();\n\n // Risk score\n const gradeColor = GRADE_COLORS[result.riskGrade] || chalk.white;\n console.log(\n ` Risk Score: ${gradeColor(`${Math.round(result.riskScore)}/100`)} Grade: ${gradeColor(result.riskGrade)}`\n );\n console.log();\n\n // Findings summary\n const fc = result.findingsCount;\n console.log(\" Findings:\");\n if (fc.critical)\n console.log(\n ` ${SEVERITY_COLORS.critical(` CRITICAL `)} ${fc.critical}`\n );\n if (fc.high)\n console.log(` ${SEVERITY_COLORS.high(\"HIGH\")} ${fc.high}`);\n if (fc.medium)\n console.log(` ${SEVERITY_COLORS.medium(\"MEDIUM\")} ${fc.medium}`);\n if (fc.low) console.log(` ${SEVERITY_COLORS.low(\"LOW\")} ${fc.low}`);\n if (!fc.critical && !fc.high && !fc.medium && !fc.low) {\n console.log(` ${chalk.green(\"No findings — skill looks clean!\")}`);\n }\n console.log();\n\n // Detailed findings\n if (result.findings.length > 0) {\n console.log(chalk.bold(\" Details:\"));\n console.log();\n for (const f of result.findings) {\n const color = SEVERITY_COLORS[f.severity];\n const confStr = f.confidence != null ? ` ${Math.round(f.confidence * 100)}%` : \"\";\n console.log(` ${color(`[${f.severity.toUpperCase()}${confStr}]`)} ${f.title}`);\n console.log(` ${chalk.dim(f.description)}`);\n if (f.evidence) {\n console.log(` Evidence: ${chalk.italic(f.evidence)}`);\n }\n if (f.lineNumber) {\n console.log(` Line: ${f.lineNumber}`);\n }\n if (f.fix) {\n console.log(` Fix: ${chalk.green(f.fix)}`);\n }\n console.log();\n }\n }\n\n // Recommendation\n const recColors: Record<string, (s: string) => string> = {\n block: chalk.bgRed.white.bold,\n warn: chalk.bgYellow.black.bold,\n approve: chalk.bgGreen.black.bold,\n };\n const rec = result.recommendation || \"approve\";\n console.log(\n ` Recommendation: ${(recColors[rec] || chalk.white)(` ${rec.toUpperCase()} `)}`\n );\n console.log();\n console.log(chalk.bold(\"━\".repeat(60)));\n console.log();\n}\n","import type { ScanResult } from \"@clawvet/shared\";\n\nexport function printJsonResult(result: ScanResult): void {\n console.log(JSON.stringify(result, null, 2));\n}\n","import type { ScanResult, Finding, Severity } from \"@clawvet/shared\";\n\nconst SEVERITY_TO_SARIF: Record<Severity, string> = {\n critical: \"error\",\n high: \"error\",\n medium: \"warning\",\n low: \"note\",\n};\n\nconst SEVERITY_TO_LEVEL: Record<Severity, string> = {\n critical: \"9.0\",\n high: \"7.0\",\n medium: \"4.0\",\n low: \"1.0\",\n};\n\nexport function printSarifResult(result: ScanResult): void {\n const rules = new Map<string, { id: string; finding: Finding }>();\n\n for (const f of result.findings) {\n const ruleId = f.category + \"/\" + f.title.toLowerCase().replace(/[^a-z0-9]+/g, \"-\");\n if (!rules.has(ruleId)) {\n rules.set(ruleId, { id: ruleId, finding: f });\n }\n }\n\n const sarif = {\n $schema: \"https://raw.githubusercontent.com/oasis-tcs/sarif-spec/main/sarif-2.1/schema/sarif-schema-2.1.0.json\",\n version: \"2.1.0\",\n runs: [\n {\n tool: {\n driver: {\n name: \"clawvet\",\n informationUri: \"https://github.com/clawvet/clawvet\",\n rules: [...rules.values()].map((r) => ({\n id: r.id,\n shortDescription: { text: r.finding.title },\n fullDescription: { text: r.finding.description },\n defaultConfiguration: {\n level: SEVERITY_TO_SARIF[r.finding.severity],\n },\n properties: {\n security_severity: SEVERITY_TO_LEVEL[r.finding.severity],\n },\n })),\n },\n },\n results: result.findings.map((f) => {\n const ruleId = f.category + \"/\" + f.title.toLowerCase().replace(/[^a-z0-9]+/g, \"-\");\n return {\n ruleId,\n level: SEVERITY_TO_SARIF[f.severity],\n message: {\n text: f.description + (f.evidence ? ` Evidence: ${f.evidence}` : \"\"),\n ...(f.fix ? { markdown: `${f.description}\\n\\n**Fix:** ${f.fix}` } : {}),\n },\n locations: [\n {\n physicalLocation: {\n artifactLocation: { uri: \"SKILL.md\" },\n region: { startLine: f.lineNumber ?? 1 },\n },\n },\n ],\n };\n }),\n },\n ],\n };\n\n console.log(JSON.stringify(sarif, null, 2));\n}\n","import { readFileSync, writeFileSync, mkdirSync, existsSync } from \"node:fs\";\nimport { join } from \"node:path\";\nimport { homedir, platform, release } from \"node:os\";\nimport { randomUUID, createHash } from \"node:crypto\";\nimport type { ScanResult } from \"@clawvet/shared\";\n\nconst CONFIG_DIR = join(homedir(), \".clawvet\");\nconst CONFIG_FILE = join(CONFIG_DIR, \"config.json\");\nconst TELEMETRY_ENDPOINT = \"https://bazzzz--0ab7a9301f3911f1ab9942dde27851f2.web.val.run\";\n\n// Never send raw skill names — that would leak what skills a user has\n// installed (including private/internal ones) to the telemetry endpoint.\n// A SHA-256 hash still lets us correlate a *known public* skill across devices\n// (hash the public name and match), but arbitrary/private names stay\n// unrecoverable, so nothing sensitive leaves the machine in cleartext.\nfunction hashSkillName(name: string): string {\n return createHash(\"sha256\").update(name).digest(\"hex\").slice(0, 16);\n}\n\n// Tag traffic so dev/CI runs can be excluded from product metrics server-side\n// instead of polluting them (the \"dev-local\" rows problem).\nfunction detectEnvironment(): \"ci\" | \"development\" | \"production\" {\n if (process.env.CI || process.env.GITHUB_ACTIONS) return \"ci\";\n if (process.env.CLAWVET_ENV === \"development\" || process.env.NODE_ENV === \"development\") {\n return \"development\";\n }\n return \"production\";\n}\n\nfunction readCliVersion(): string {\n try {\n const pkg = JSON.parse(\n readFileSync(new URL(\"../package.json\", import.meta.url), \"utf-8\")\n );\n return pkg.version ?? \"0.0.0\";\n } catch {\n return \"0.0.0\";\n }\n}\n\ninterface Config {\n telemetry?: \"on\" | \"off\" | undefined; // undefined = not yet asked\n deviceId?: string;\n scanCount?: number;\n}\n\nfunction loadConfig(): Config {\n try {\n if (existsSync(CONFIG_FILE)) {\n return JSON.parse(readFileSync(CONFIG_FILE, \"utf-8\"));\n }\n } catch {\n // corrupted config, start fresh\n }\n return {};\n}\n\nfunction saveConfig(config: Config): void {\n try {\n mkdirSync(CONFIG_DIR, { recursive: true });\n writeFileSync(CONFIG_FILE, JSON.stringify(config, null, 2));\n } catch {\n // non-critical, ignore\n }\n}\n\nexport function isTelemetryEnabled(): boolean {\n const env = process.env.CLAWVET_TELEMETRY;\n if (env === \"0\" || env === \"off\") return false;\n if (env === \"1\" || env === \"on\") return true;\n const config = loadConfig();\n return config.telemetry === \"on\";\n}\n\nexport function setTelemetry(enabled: boolean): void {\n const config = loadConfig();\n config.telemetry = enabled ? \"on\" : \"off\";\n saveConfig(config);\n}\n\nexport function hasBeenAsked(): boolean {\n const config = loadConfig();\n return config.telemetry !== undefined;\n}\n\nfunction getDeviceId(): string {\n const config = loadConfig();\n if (!config.deviceId) {\n config.deviceId = randomUUID();\n saveConfig(config);\n }\n return config.deviceId;\n}\n\nfunction incrementScanCount(): number {\n const config = loadConfig();\n config.scanCount = (config.scanCount || 0) + 1;\n saveConfig(config);\n return config.scanCount;\n}\n\nexport function getScanCount(): number {\n return loadConfig().scanCount || 0;\n}\n\nexport function sendTelemetry(result: ScanResult): Promise<void> {\n if (!isTelemetryEnabled()) return Promise.resolve();\n\n const scanCount = incrementScanCount();\n\n const payload = {\n event: \"scan_completed\",\n deviceId: getDeviceId(),\n scanCount,\n ts: new Date().toISOString(),\n os: platform(),\n osVersion: release(),\n cliVersion: readCliVersion(),\n environment: detectEnvironment(),\n skillHash: hashSkillName(result.skillName),\n riskScore: result.riskScore,\n riskGrade: result.riskGrade,\n findingsCount: result.findingsCount,\n cached: result.cached ?? false,\n };\n\n return post(payload);\n}\n\nexport interface AuditSummary {\n skillsScanned: number;\n findingsTotal: number;\n grades: Record<string, number>;\n durationMs: number;\n}\n\n/**\n * One session-level event summarising a whole `clawvet audit` run, instead of\n * one event per scanned skill. Lets an audit of N skills register as a single\n * data point (the strongest usage signal) without inflating scan counts.\n */\nexport function sendAuditTelemetry(summary: AuditSummary): Promise<void> {\n if (!isTelemetryEnabled()) return Promise.resolve();\n\n const payload = {\n event: \"audit_completed\",\n deviceId: getDeviceId(),\n ts: new Date().toISOString(),\n os: platform(),\n osVersion: release(),\n cliVersion: readCliVersion(),\n environment: detectEnvironment(),\n skillsScanned: summary.skillsScanned,\n findingsTotal: summary.findingsTotal,\n grades: summary.grades,\n durationMs: summary.durationMs,\n };\n\n return post(payload);\n}\n\nfunction post(payload: unknown): Promise<void> {\n return fetch(TELEMETRY_ENDPOINT, {\n method: \"POST\",\n headers: { \"Content-Type\": \"application/json\" },\n body: JSON.stringify(payload),\n signal: AbortSignal.timeout(3000),\n })\n .then(() => {})\n .catch(() => {\n // silently ignore — telemetry is best-effort\n });\n}\n","import { readFileSync, readdirSync, statSync } from \"node:fs\";\nimport { basename, join } from \"node:path\";\n\nconst MAX_REFERENCED_FILE_SIZE = 256 * 1024;\nconst SHALLOW_DIRECTORIES = new Set([\"lib\", \"scripts\"]);\n\ninterface CandidateFile {\n absolutePath: string;\n relativePath: string;\n}\n\nfunction escapeRegExp(value: string): string {\n return value.replace(/[.*+?^${}()|[\\]\\\\]/g, \"\\\\$&\");\n}\n\nfunction isReferenced(skillMd: string, relativePath: string): boolean {\n // ponytail: basename substring match; upgrade to real reference parsing\n // (Markdown links + shell tokens) if benchmark false-negatives appear.\n const names = new Set([basename(relativePath), relativePath]);\n return [...names].some((name) =>\n new RegExp(`\\\\b${escapeRegExp(name)}\\\\b`).test(skillMd)\n );\n}\n\nfunction listCandidates(skillDir: string): CandidateFile[] {\n const candidates: CandidateFile[] = [];\n\n let entries;\n try {\n entries = readdirSync(skillDir, { withFileTypes: true });\n } catch {\n return candidates;\n }\n\n for (const entry of entries) {\n if (entry.isFile()) {\n if (entry.name !== \"SKILL.md\") {\n candidates.push({\n absolutePath: join(skillDir, entry.name),\n relativePath: entry.name,\n });\n }\n continue;\n }\n\n if (!entry.isDirectory() || !SHALLOW_DIRECTORIES.has(entry.name)) {\n continue;\n }\n\n try {\n for (const child of readdirSync(join(skillDir, entry.name), {\n withFileTypes: true,\n })) {\n if (child.isFile()) {\n candidates.push({\n absolutePath: join(skillDir, entry.name, child.name),\n relativePath: `${entry.name}/${child.name}`,\n });\n }\n }\n } catch {\n // A missing or unreadable optional directory should not abort the scan.\n }\n }\n\n return candidates.sort((a, b) =>\n a.relativePath.localeCompare(b.relativePath)\n );\n}\n\n/**\n * Appends local files explicitly referenced by a skill manifest so the shared\n * string-only scanner can inspect cross-file payloads without filesystem access.\n */\nexport function assembleSkill(skillDir: string, skillMd: string): string {\n let assembled = skillMd;\n\n for (const candidate of listCandidates(skillDir)) {\n if (!isReferenced(skillMd, candidate.relativePath)) {\n continue;\n }\n\n try {\n const stat = statSync(candidate.absolutePath);\n if (stat.size > MAX_REFERENCED_FILE_SIZE) {\n continue;\n }\n\n const contents = readFileSync(candidate.absolutePath);\n if (contents.includes(0)) {\n continue;\n }\n\n const separator = assembled.endsWith(\"\\n\") ? \"\\n\" : \"\\n\\n\";\n assembled += `${separator}# [clawvet] referenced file: ${candidate.relativePath}\\n${contents.toString(\"utf-8\")}`;\n } catch {\n // Files may disappear or become unreadable between listing and reading.\n }\n }\n\n return assembled;\n}\n","/**\n * Feedback goes to a prefilled GitHub issue rather than a form.\n *\n * The old Tally form got 11 visits and 0 submissions in 12 months (2s average\n * dwell — people landed and bounced). A prefilled issue is one click, needs no\n * account switch for anyone already on GitHub, and lands somewhere public where\n * it helps other users instead of a private form inbox.\n */\nconst ISSUE_BODY = [\n \"**What were you scanning?**\",\n \"\",\n \"\",\n \"**What happened, and what did you expect instead?**\",\n \"\",\n \"\",\n \"---\",\n \"_Filed from the ClawVet CLI._\",\n].join(\"\\n\");\n\n/** Full prefilled URL — used when opening a browser. */\nexport const FEEDBACK_URL =\n \"https://github.com/MohibShaikh/clawvet/issues/new\" +\n \"?labels=feedback\" +\n `&title=${encodeURIComponent(\"Feedback: \")}` +\n `&body=${encodeURIComponent(ISSUE_BODY)}`;\n\n/** Short form for terminal output — the encoded URL is unreadable when wrapped. */\nexport const FEEDBACK_DISPLAY_URL =\n \"https://github.com/MohibShaikh/clawvet/issues/new\";\n","import { readdirSync, existsSync, readFileSync } from \"node:fs\";\nimport { join, basename } from \"node:path\";\nimport { homedir } from \"node:os\";\nimport { scanSkill } from \"@clawvet/shared\";\nimport { printScanResult } from \"../output/terminal.js\";\nimport { sendAuditTelemetry } from \"../telemetry.js\";\nimport chalk from \"chalk\";\n\nconst DEFAULT_SKILL_DIRS = [\n join(homedir(), \".openclaw\", \"skills\"),\n join(homedir(), \".openclaw\", \"workspace\", \"skills\"),\n];\n\nexport async function auditCommand(options: { dir?: string } = {}): Promise<void> {\n const SKILL_DIRS = options.dir ? [options.dir] : DEFAULT_SKILL_DIRS;\n console.log(chalk.bold(\"\\nClawVet Audit — Scanning all installed skills\\n\"));\n\n const startedAt = Date.now();\n let totalScanned = 0;\n let totalThreats = 0;\n const grades: Record<string, number> = { A: 0, B: 0, C: 0, D: 0, F: 0 };\n\n for (const dir of SKILL_DIRS) {\n if (!existsSync(dir)) {\n if (options.dir) {\n console.error(chalk.yellow(`Warning: Directory not found: ${dir}\\n`));\n process.exit(1);\n }\n continue;\n }\n\n // If the dir itself contains a SKILL.md, scan it directly\n const directSkillFile = join(dir, \"SKILL.md\");\n if (existsSync(directSkillFile)) {\n const content = readFileSync(directSkillFile, \"utf-8\");\n const result = await scanSkill(content, { skillName: basename(dir) });\n totalScanned++;\n totalThreats += result.findings.length;\n grades[result.riskGrade] = (grades[result.riskGrade] ?? 0) + 1;\n printScanResult(result);\n continue;\n }\n\n const entries = readdirSync(dir, { withFileTypes: true });\n for (const entry of entries) {\n if (!entry.isDirectory()) continue;\n const skillFile = join(dir, entry.name, \"SKILL.md\");\n if (!existsSync(skillFile)) continue;\n\n const content = readFileSync(skillFile, \"utf-8\");\n const result = await scanSkill(content, { skillName: entry.name });\n totalScanned++;\n totalThreats += result.findings.length;\n grades[result.riskGrade] = (grades[result.riskGrade] ?? 0) + 1;\n\n printScanResult(result);\n }\n }\n\n const gradeColors: Record<string, (s: string) => string> = {\n A: chalk.green.bold,\n B: chalk.greenBright,\n C: chalk.yellow.bold,\n D: chalk.redBright.bold,\n F: chalk.bgRed.white.bold,\n };\n const gradeSummary = ([\"A\", \"B\", \"C\", \"D\", \"F\"] as const)\n .filter((g) => grades[g] > 0)\n .map((g) => `${gradeColors[g](g)} ${grades[g]}`)\n .join(\" \");\n\n console.log(\n chalk.bold(\n `\\nAudit complete: ${totalScanned} skills scanned, ${totalThreats} findings`\n )\n );\n if (totalScanned > 0) {\n console.log(` Grades: ${gradeSummary}`);\n }\n const blocked = grades.D + grades.F;\n if (blocked > 0) {\n console.log(\n chalk.red(` ${blocked} skill${blocked > 1 ? \"s\" : \"\"} graded D or F — review before use`)\n );\n }\n console.log();\n\n // One session-level telemetry event for the whole audit (best-effort).\n await sendAuditTelemetry({\n skillsScanned: totalScanned,\n findingsTotal: totalThreats,\n grades,\n durationMs: Date.now() - startedAt,\n });\n}\n","import { readFileSync, existsSync, watch } from \"node:fs\";\nimport { join, dirname, basename } from \"node:path\";\nimport { homedir } from \"node:os\";\nimport chalk from \"chalk\";\nimport { scanSkill } from \"@clawvet/shared\";\nimport { printScanResult } from \"../output/terminal.js\";\n\nconst DEFAULT_SKILL_DIRS = [\n join(homedir(), \".openclaw\", \"skills\"),\n join(homedir(), \".openclaw\", \"workspace\", \"skills\"),\n];\n\nexport async function watchCommand(options: {\n threshold?: number;\n dir?: string;\n}): Promise<void> {\n const threshold = options.threshold || 50;\n const SKILL_DIRS = options.dir ? [options.dir] : DEFAULT_SKILL_DIRS;\n console.log(\n chalk.bold(\n `\\nClawVet Watch — monitoring skill directories (threshold: ${threshold})\\n`\n )\n );\n\n const watchDirs: string[] = [];\n for (const dir of SKILL_DIRS) {\n if (existsSync(dir)) {\n watchDirs.push(dir);\n }\n }\n\n if (watchDirs.length === 0) {\n console.log(\n chalk.yellow(\n \"No OpenClaw skill directories found. Watching will start when directories are created.\\n\"\n )\n );\n console.log(chalk.dim(\"Expected directories:\"));\n for (const dir of SKILL_DIRS) {\n console.log(chalk.dim(` ${dir}`));\n }\n console.log();\n process.exit(1);\n }\n\n console.log(chalk.dim(\"Watching:\"));\n for (const dir of watchDirs) {\n console.log(chalk.dim(` ${dir}`));\n }\n console.log();\n\n for (const dir of watchDirs) {\n const watcher = watch(dir, { recursive: true }, async (event, filename) => {\n if (!filename?.endsWith(\"SKILL.md\")) return;\n\n const skillFile = join(dir, filename);\n if (!existsSync(skillFile)) return;\n\n console.log(chalk.dim(`\\nDetected change: ${filename}`));\n\n try {\n const content = readFileSync(skillFile, \"utf-8\");\n const result = await scanSkill(content, {\n skillName: basename(dirname(skillFile)),\n });\n\n if (result.cached) {\n console.log(chalk.dim(\"(cached)\"));\n }\n printScanResult(result);\n\n if (result.riskScore > threshold) {\n console.log(\n chalk.bgRed.white.bold(\n ` BLOCKED — Risk score ${result.riskScore} exceeds threshold ${threshold} `\n )\n );\n console.log(\n chalk.red(\n `This skill should not be installed. Run 'clawvet scan ${skillFile}' for details.\\n`\n )\n );\n }\n } catch (err) {\n console.error(chalk.red(`Error scanning ${filename}:`), err);\n }\n });\n\n process.on(\"SIGINT\", () => {\n watcher.close();\n console.log(chalk.dim(\"\\nWatch stopped.\"));\n process.exit(0);\n });\n }\n\n console.log(chalk.dim(\"Press Ctrl+C to stop watching.\\n\"));\n await new Promise(() => {});\n}\n","import { readFileSync, existsSync, statSync } from \"node:fs\";\nimport { resolve, join, dirname, basename } from \"node:path\";\nimport chalk from \"chalk\";\nimport { scanSkill } from \"@clawvet/shared\";\nimport type { RiskGrade } from \"@clawvet/shared\";\n\nconst GRADE_COLORS: Record<RiskGrade, string> = {\n A: \"brightgreen\",\n B: \"green\",\n C: \"yellow\",\n D: \"orange\",\n F: \"red\",\n};\n\nconst GRADE_LABELS: Record<RiskGrade, string> = {\n A: \"safe\",\n B: \"safe\",\n C: \"review\",\n D: \"risky\",\n F: \"dangerous\",\n};\n\nexport async function badgeCommand(\n target: string,\n options: { markdown?: boolean }\n): Promise<void> {\n const skillPath = resolve(target);\n let skillFile = skillPath;\n\n if (\n existsSync(skillPath) &&\n !skillPath.endsWith(\".md\") &&\n existsSync(join(skillPath, \"SKILL.md\"))\n ) {\n skillFile = join(skillPath, \"SKILL.md\");\n }\n\n if (!existsSync(skillFile) || statSync(skillFile).isDirectory()) {\n console.error(`Error: Cannot find SKILL.md at ${skillFile}`);\n process.exit(1);\n }\n\n const content = readFileSync(skillFile, \"utf-8\");\n const result = await scanSkill(content, {\n skillName: basename(dirname(skillFile)),\n });\n\n const label = GRADE_LABELS[result.riskGrade];\n const color = GRADE_COLORS[result.riskGrade];\n const badgeUrl = `https://img.shields.io/badge/clawvet-${result.riskGrade}%20${label}-${color}`;\n const linkUrl = \"https://github.com/MohibShaikh/clawvet\";\n\n if (options.markdown) {\n console.log(`[](${linkUrl})`);\n } else {\n console.log();\n console.log(chalk.bold(\" ClawVet Trust Badge\"));\n console.log();\n console.log(` Skill: ${chalk.bold(result.skillName)}`);\n console.log(` Grade: ${result.riskGrade} (${label})`);\n console.log(` Score: ${result.riskScore}/100`);\n console.log();\n console.log(chalk.dim(\" Markdown (paste in README):\"));\n console.log();\n console.log(` [](${linkUrl})`);\n console.log();\n console.log(chalk.dim(\" HTML:\"));\n console.log();\n console.log(` <a href=\"${linkUrl}\"><img src=\"${badgeUrl}\" alt=\"ClawVet ${result.riskGrade}\"></a>`);\n console.log();\n }\n}\n","import { readFileSync, existsSync, statSync, realpathSync } from \"node:fs\";\nimport { join, basename, dirname } from \"node:path\";\nimport { fileURLToPath } from \"node:url\";\nimport { scanSkill } from \"@clawvet/shared\";\nimport type { Finding, Recommendation } from \"@clawvet/shared\";\nimport { assembleSkill } from \"../assemble.js\";\n\n// OpenClaw's security.installPolicy hook. It writes staged install metadata to\n// our stdin and reads a single JSON verdict from our stdout, after the source\n// is staged and before the install completes. Anything it cannot parse fails\n// closed, so this path must emit exactly one JSON object on stdout and put\n// every diagnostic on stderr.\n//\n// Named `gate`, not `policy`. `openclaw policy` is a different thing in the\n// same ecosystem: a workspace-config conformance linter a human runs. This is\n// an install-time admission gate the host runs. Two commands called policy\n// meaning two different things is a trap, and it would also collide with a\n// future declarative `clawvet policy` that reads a rules file.\n//\n// This is the enforcement half of ClawVet. The clawvet skill asks an agent to\n// remember to scan; this runs whether or not it remembers.\n\nconst PROTOCOL_VERSION = 1;\n\n// Only the fields we use. OpenClaw sends more and may add more.\ninterface PolicyRequest {\n protocolVersion?: number;\n targetType?: string;\n targetName?: string;\n sourcePath?: string;\n sourcePathKind?: \"directory\" | \"file\";\n origin?: { slug?: string; version?: string; registry?: string };\n}\n\ntype Decision = \"allow\" | \"warn\" | \"block\";\n\ninterface PolicyFinding {\n ruleId: string;\n message: string;\n severity: \"info\" | \"warn\" | \"critical\";\n evidence?: string;\n line?: number;\n}\n\ninterface PolicyResponse {\n protocolVersion: number;\n decision: Decision;\n reason?: string;\n findings?: PolicyFinding[];\n}\n\n// approve/warn/block is already ClawVet's own vocabulary, so the mapping is\n// identity. Kept explicit so a change on either side is a compile error here\n// rather than a silently wrong verdict.\nconst DECISION: Record<Recommendation, Decision> = {\n approve: \"allow\",\n warn: \"warn\",\n block: \"block\",\n};\n\n// ClawVet grades severity for a human reading a report; installPolicy grades it\n// for a gate. medium and low are advisory either way.\nconst SEVERITY: Record<Finding[\"severity\"], PolicyFinding[\"severity\"]> = {\n critical: \"critical\",\n high: \"critical\",\n medium: \"warn\",\n low: \"info\",\n};\n\nconst REASON_MAX = 1000;\n\n// ClawVet carries two thresholds for two jobs. `recommendation` blocks at 76,\n// which is the conservative posture for a report a human reads. The scanner's\n// warn line is 26, and that is the threshold the paper validates detection at.\n// A hard install gate inherits whichever one it is wired to, so the operator\n// picks: default to 76 so ordinary dual-use skills still install, and let a\n// stricter deployment lower it. See --block-at.\nconst DEFAULT_BLOCK_AT = 76;\n\nfunction emit(res: PolicyResponse): never {\n process.stdout.write(JSON.stringify(res) + \"\\n\");\n process.exit(0);\n}\n\n// A scanner that cannot read the target has not cleared it. Block with a\n// reason the user can act on, rather than exiting non-zero and leaving them\n// with a bare install failure.\nfunction blockWith(reason: string): never {\n emit({\n protocolVersion: PROTOCOL_VERSION,\n decision: \"block\",\n reason: reason.slice(0, REASON_MAX),\n });\n}\n\nasync function readStdin(): Promise<string> {\n const chunks: Buffer[] = [];\n for await (const chunk of process.stdin) chunks.push(chunk as Buffer);\n return Buffer.concat(chunks).toString(\"utf-8\");\n}\n\nfunction summarize(\n name: string,\n grade: string,\n score: number,\n findings: Finding[]\n): string {\n const worst = findings\n .filter((f) => f.severity === \"critical\" || f.severity === \"high\")\n .slice(0, 3)\n .map((f) => f.title);\n const head = `ClawVet graded \"${name}\" ${grade} (risk ${score}/100).`;\n return worst.length ? `${head} ${worst.join(\"; \")}.` : head;\n}\n\nexport interface GateOptions {\n blockAt?: number;\n printConfig?: boolean;\n}\n\n// OpenClaw requires the policy command and any interpreter script argument to\n// be \"direct regular files with trusted ownership, restricted permissions, and\n// verifiable parent directories. Symlinks and insecure paths are rejected.\"\n// `npm i -g clawvet` puts a symlink in bin/, so pointing installPolicy at\n// `which clawvet` fails. Resolve through to the real file and invoke it via\n// node explicitly, so both the command and the script argument are regular\n// files.\nfunction printConfig(blockAt: number): void {\n const self = realpathSync(fileURLToPath(import.meta.url));\n const args: string[] = [self, \"gate\"];\n if (blockAt !== DEFAULT_BLOCK_AT) args.push(\"--block-at\", String(blockAt));\n const config = {\n security: {\n installPolicy: {\n enabled: true,\n // Only \"skill\". ClawVet reads SKILL.md and the files it references. A\n // plugin with no SKILL.md has no instruction layer to inspect and is\n // allowed through, so listing \"plugin\" here would claim a protection\n // that does not exist yet.\n targets: [\"skill\"],\n exec: {\n command: realpathSync(process.execPath),\n args,\n timeoutMs: 10000,\n },\n },\n },\n };\n process.stdout.write(JSON.stringify(config, null, 2) + \"\\n\");\n}\n\nexport async function gateCommand(options: GateOptions = {}): Promise<void> {\n const blockAt = Number.isFinite(options.blockAt)\n ? (options.blockAt as number)\n : DEFAULT_BLOCK_AT;\n\n if (options.printConfig) {\n printConfig(blockAt);\n return;\n }\n let req: PolicyRequest;\n try {\n const raw = await readStdin();\n if (!raw.trim()) blockWith(\"ClawVet gate received no install metadata on stdin.\");\n req = JSON.parse(raw) as PolicyRequest;\n } catch {\n blockWith(\"ClawVet gate could not parse the install metadata on stdin.\");\n }\n\n if (req.protocolVersion !== undefined && req.protocolVersion !== PROTOCOL_VERSION) {\n blockWith(\n `ClawVet gate speaks protocol ${PROTOCOL_VERSION}, host sent ${req.protocolVersion}. Upgrade clawvet.`\n );\n }\n\n const sourcePath = req.sourcePath;\n if (!sourcePath || !existsSync(sourcePath)) {\n blockWith(`ClawVet gate could not read the staged source at ${sourcePath ?? \"(none)\"}.`);\n }\n\n let skillFile = sourcePath;\n let skillDir: string | undefined;\n if (statSync(sourcePath).isDirectory()) {\n skillDir = sourcePath;\n skillFile = join(sourcePath, \"SKILL.md\");\n }\n\n if (!existsSync(skillFile) || statSync(skillFile).isDirectory()) {\n // No SKILL.md means no instruction layer to vet. Plugins can legitimately\n // ship without one, so this is not on its own a reason to fail an install.\n emit({ protocolVersion: PROTOCOL_VERSION, decision: \"allow\" });\n }\n\n let result;\n try {\n const skillMd = readFileSync(skillFile, \"utf-8\");\n // Assemble referenced files so a payload split across them cannot hide.\n const content = skillDir ? assembleSkill(skillDir, skillMd) : skillMd;\n // Static passes only. The semantic pass needs a key and a network round\n // trip, and this runs inside the host's install timeout.\n result = await scanSkill(content, {\n skillName: req.targetName || req.origin?.slug || basename(dirname(skillFile)),\n });\n } catch (err) {\n blockWith(\n `ClawVet gate failed to scan the staged skill: ${err instanceof Error ? err.message : \"unknown error\"}`\n );\n }\n\n // A disqualifying indicator is a verdict on its own, independent of score.\n const disqualified = result.findings.some((f) => f.disqualifying);\n const decision: Decision =\n disqualified || result.riskScore >= blockAt\n ? \"block\"\n : DECISION[result.recommendation ?? \"warn\"] === \"allow\"\n ? \"allow\"\n : \"warn\";\n const findings: PolicyFinding[] = result.findings.slice(0, 20).map((f) => ({\n ruleId: f.id ?? f.category,\n message: f.description || f.title,\n severity: SEVERITY[f.severity],\n ...(f.evidence ? { evidence: f.evidence } : {}),\n ...(f.lineNumber ? { line: f.lineNumber } : {}),\n }));\n\n if (decision === \"allow\") {\n emit({ protocolVersion: PROTOCOL_VERSION, decision, findings });\n }\n\n emit({\n protocolVersion: PROTOCOL_VERSION,\n decision,\n reason: summarize(\n result.skillName,\n result.riskGrade,\n result.riskScore,\n result.findings\n ).slice(0, REASON_MAX),\n findings,\n });\n}\n"],"mappings":"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","names":["readFileSync","fileURLToPath","dirname","join","readFileSync","existsSync","statSync","join","basename","chalk","re","inCode","re","createHash","readFileSync","join","existsSync","statSync","join","readFileSync","basename","chalk","resolve","readdirSync","existsSync","readFileSync","join","basename","homedir","chalk","join","homedir","existsSync","readFileSync","basename","readdirSync","readFileSync","existsSync","join","dirname","basename","homedir","chalk","DEFAULT_SKILL_DIRS","join","homedir","chalk","existsSync","readFileSync","basename","dirname","readFileSync","existsSync","statSync","resolve","join","dirname","basename","chalk","GRADE_COLORS","resolve","existsSync","join","statSync","readFileSync","basename","dirname","chalk","readFileSync","existsSync","statSync","join","basename","dirname","existsSync","statSync","join","readFileSync","basename","dirname","dirname","fileURLToPath","readFileSync","join"]}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "clawvet",
|
|
3
|
-
"version": "0.12.
|
|
3
|
+
"version": "0.12.3",
|
|
4
4
|
"description": "Skill vetting & supply chain security for OpenClaw. Scans SKILL.md files for prompt injection, credential theft, RCE, typosquatting, and social engineering.",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"bin": {
|