clawvet 0.2.2 → 0.3.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/index.js CHANGED
@@ -4,7 +4,7 @@
4
4
  import { Command } from "commander";
5
5
 
6
6
  // src/commands/scan.ts
7
- import { readFileSync, existsSync } from "fs";
7
+ import { readFileSync, existsSync, statSync } from "fs";
8
8
  import { resolve, join } from "path";
9
9
 
10
10
  // ../shared/src/patterns.ts
@@ -18,7 +18,8 @@ var THREAT_PATTERNS = [
18
18
  severity: "critical",
19
19
  category: "remote_code_execution",
20
20
  title: "Curl piped to shell",
21
- description: "Downloads and executes remote code directly \u2014 classic supply chain attack vector."
21
+ description: "Downloads and executes remote code directly \u2014 classic supply chain attack vector.",
22
+ codeOnly: true
22
23
  },
23
24
  {
24
25
  name: "WGET_EXECUTE",
@@ -26,7 +27,8 @@ var THREAT_PATTERNS = [
26
27
  severity: "critical",
27
28
  category: "remote_code_execution",
28
29
  title: "Wget with shell execution",
29
- description: "Downloads and executes remote code via wget."
30
+ description: "Downloads and executes remote code via wget.",
31
+ codeOnly: true
30
32
  },
31
33
  {
32
34
  name: "EVAL_DYNAMIC",
@@ -34,7 +36,8 @@ var THREAT_PATTERNS = [
34
36
  severity: "critical",
35
37
  category: "remote_code_execution",
36
38
  title: "Dynamic eval() usage",
37
- description: "Uses eval() which can execute arbitrary code."
39
+ description: "Uses eval() which can execute arbitrary code.",
40
+ codeOnly: true
38
41
  },
39
42
  {
40
43
  name: "BASE64_DECODE",
@@ -42,7 +45,8 @@ var THREAT_PATTERNS = [
42
45
  severity: "critical",
43
46
  category: "obfuscation",
44
47
  title: "Base64 decode execution",
45
- description: "Decodes base64 content, often used to hide malicious payloads."
48
+ description: "Decodes base64 content, often used to hide malicious payloads.",
49
+ codeOnly: true
46
50
  },
47
51
  {
48
52
  name: "PYTHON_EXEC",
@@ -50,7 +54,8 @@ var THREAT_PATTERNS = [
50
54
  severity: "critical",
51
55
  category: "remote_code_execution",
52
56
  title: "Python inline execution",
53
- description: "Executes inline Python code which may contain hidden payloads."
57
+ description: "Executes inline Python code which may contain hidden payloads.",
58
+ codeOnly: true
54
59
  },
55
60
  {
56
61
  name: "REVERSE_SHELL",
@@ -58,7 +63,8 @@ var THREAT_PATTERNS = [
58
63
  severity: "critical",
59
64
  category: "remote_code_execution",
60
65
  title: "Reverse shell",
61
- description: "Creates a reverse shell connection back to an attacker-controlled server."
66
+ description: "Creates a reverse shell connection back to an attacker-controlled server.",
67
+ codeOnly: true
62
68
  },
63
69
  {
64
70
  name: "CRON_PERSISTENCE",
@@ -66,7 +72,8 @@ var THREAT_PATTERNS = [
66
72
  severity: "critical",
67
73
  category: "persistence",
68
74
  title: "Scheduled task persistence",
69
- description: "Installs a cron job or systemd service for persistent execution after reboot."
75
+ description: "Installs a cron job or systemd service for persistent execution after reboot.",
76
+ codeOnly: true
70
77
  },
71
78
  {
72
79
  name: "PERL_EXEC",
@@ -74,7 +81,8 @@ var THREAT_PATTERNS = [
74
81
  severity: "critical",
75
82
  category: "remote_code_execution",
76
83
  title: "Perl inline execution",
77
- description: "Executes inline Perl code which may contain obfuscated payloads."
84
+ description: "Executes inline Perl code which may contain obfuscated payloads.",
85
+ codeOnly: true
78
86
  },
79
87
  {
80
88
  name: "NODE_EVAL",
@@ -82,7 +90,8 @@ var THREAT_PATTERNS = [
82
90
  severity: "critical",
83
91
  category: "remote_code_execution",
84
92
  title: "Node.js inline execution",
85
- description: "Executes inline Node.js code, often used to hide malicious logic."
93
+ description: "Executes inline Node.js code, often used to hide malicious logic.",
94
+ codeOnly: true
86
95
  },
87
96
  {
88
97
  name: "RUBY_EXEC",
@@ -90,7 +99,8 @@ var THREAT_PATTERNS = [
90
99
  severity: "critical",
91
100
  category: "remote_code_execution",
92
101
  title: "Ruby inline execution",
93
- description: "Executes inline Ruby code which may contain hidden payloads."
102
+ description: "Executes inline Ruby code which may contain hidden payloads.",
103
+ codeOnly: true
94
104
  },
95
105
  // ═══════════════════════════════════════════════════════
96
106
  // HIGH: Credential theft
@@ -240,7 +250,8 @@ var THREAT_PATTERNS = [
240
250
  severity: "high",
241
251
  category: "data_exfiltration",
242
252
  title: "Raw socket connection",
243
- description: "Creates raw network sockets which can bypass HTTP monitoring."
253
+ description: "Creates raw network sockets which can bypass HTTP monitoring.",
254
+ codeOnly: true
244
255
  },
245
256
  // ═══════════════════════════════════════════════════════
246
257
  // MEDIUM: Social engineering
@@ -345,7 +356,8 @@ var THREAT_PATTERNS = [
345
356
  severity: "medium",
346
357
  category: "obfuscation",
347
358
  title: "Hex-encoded payload",
348
- description: "Contains hex-encoded strings commonly used to hide malicious commands."
359
+ description: "Contains hex-encoded strings commonly used to hide malicious commands.",
360
+ codeOnly: true
349
361
  },
350
362
  {
351
363
  name: "JS_OBFUSCATOR",
@@ -353,7 +365,8 @@ var THREAT_PATTERNS = [
353
365
  severity: "medium",
354
366
  category: "obfuscation",
355
367
  title: "JavaScript obfuscator output",
356
- description: "Contains patterns from JavaScript obfuscation tools used to hide malicious code."
368
+ description: "Contains patterns from JavaScript obfuscation tools used to hide malicious code.",
369
+ codeOnly: true
357
370
  },
358
371
  {
359
372
  name: "UNICODE_STEGANOGRAPHY",
@@ -385,7 +398,35 @@ var THREAT_PATTERNS = [
385
398
  severity: "medium",
386
399
  category: "obfuscation",
387
400
  title: "String concatenation obfuscation",
388
- description: "Builds commands via single-character string concatenation to evade pattern detection."
401
+ description: "Builds commands via single-character string concatenation to evade pattern detection.",
402
+ codeOnly: true
403
+ },
404
+ {
405
+ name: "BUFFER_BASE64_DECODE",
406
+ pattern: /Buffer\.from\s*\(.*['"]base64['"]\)|atob\s*\(/gi,
407
+ severity: "critical",
408
+ category: "obfuscation",
409
+ title: "Buffer/atob base64 decode",
410
+ description: "Decodes base64 content via Buffer.from() or atob(), often used to hide malicious payloads.",
411
+ codeOnly: true
412
+ },
413
+ {
414
+ name: "STRING_FROMCHARCODE",
415
+ pattern: /String\.fromCharCode\s*\(/gi,
416
+ severity: "medium",
417
+ category: "obfuscation",
418
+ title: "String.fromCharCode usage",
419
+ description: "Builds strings from character codes to evade static pattern detection.",
420
+ codeOnly: true
421
+ },
422
+ {
423
+ name: "DYNAMIC_PROPERTY_ACCESS",
424
+ pattern: /(?:process|global|window|globalThis)\s*\[\s*['"`]?\w*['"`]?\s*\+/gi,
425
+ severity: "medium",
426
+ category: "obfuscation",
427
+ title: "Dynamic property access on globals",
428
+ description: "Dynamically accesses global object properties via string concatenation to hide intent.",
429
+ codeOnly: true
389
430
  },
390
431
  // ═══════════════════════════════════════════════════════
391
432
  // MEDIUM: Privilege escalation & system access
@@ -396,7 +437,8 @@ var THREAT_PATTERNS = [
396
437
  severity: "medium",
397
438
  category: "privilege_escalation",
398
439
  title: "Sudo usage",
399
- description: "Requests elevated privileges \u2014 check if actually required for the task."
440
+ description: "Requests elevated privileges \u2014 check if actually required for the task.",
441
+ codeOnly: true
400
442
  },
401
443
  {
402
444
  name: "CHMOD_DANGEROUS",
@@ -404,7 +446,8 @@ var THREAT_PATTERNS = [
404
446
  severity: "medium",
405
447
  category: "privilege_escalation",
406
448
  title: "Dangerous file permissions",
407
- description: "Sets overly permissive file permissions (777) or setuid/setgid bits."
449
+ description: "Sets overly permissive file permissions (777) or setuid/setgid bits.",
450
+ codeOnly: true
408
451
  },
409
452
  {
410
453
  name: "PATH_TRAVERSAL",
@@ -423,7 +466,8 @@ var THREAT_PATTERNS = [
423
466
  severity: "low",
424
467
  category: "code_execution",
425
468
  title: "Shell execution API",
426
- description: "Uses shell execution APIs \u2014 legitimate but worth noting."
469
+ description: "Uses shell execution APIs \u2014 legitimate but worth noting.",
470
+ codeOnly: true
427
471
  },
428
472
  {
429
473
  name: "NETWORK_REQUEST",
@@ -431,7 +475,8 @@ var THREAT_PATTERNS = [
431
475
  severity: "low",
432
476
  category: "network",
433
477
  title: "Network request API",
434
- description: "Makes network requests \u2014 legitimate but worth reviewing targets."
478
+ description: "Makes network requests \u2014 legitimate but worth reviewing targets.",
479
+ codeOnly: true
435
480
  },
436
481
  {
437
482
  name: "FILE_WRITE",
@@ -439,7 +484,8 @@ var THREAT_PATTERNS = [
439
484
  severity: "low",
440
485
  category: "file_system",
441
486
  title: "File write operation",
442
- description: "Writes to the filesystem \u2014 check what files are being modified."
487
+ description: "Writes to the filesystem \u2014 check what files are being modified.",
488
+ codeOnly: true
443
489
  },
444
490
  {
445
491
  name: "ENV_MODIFICATION",
@@ -447,7 +493,8 @@ var THREAT_PATTERNS = [
447
493
  severity: "low",
448
494
  category: "environment",
449
495
  title: "Environment variable modification",
450
- description: "Modifies environment variables which could affect other tools or processes."
496
+ description: "Modifies environment variables which could affect other tools or processes.",
497
+ codeOnly: true
451
498
  },
452
499
  {
453
500
  name: "WILDCARD_FILE_ACCESS",
@@ -547,6 +594,11 @@ function parseSkill(content) {
547
594
  }
548
595
 
549
596
  // ../shared/src/scanner/static-analysis.ts
597
+ function isInCodeBlock(lineNumber, skill) {
598
+ return skill.codeBlocks.some(
599
+ (block) => lineNumber >= block.lineStart && lineNumber <= block.lineEnd
600
+ );
601
+ }
550
602
  function runStaticAnalysis(skill) {
551
603
  const findings = [];
552
604
  for (const threat of THREAT_PATTERNS) {
@@ -555,6 +607,9 @@ function runStaticAnalysis(skill) {
555
607
  while ((match = re.exec(skill.rawContent)) !== null) {
556
608
  const before = skill.rawContent.slice(0, match.index);
557
609
  const lineNumber = before.split("\n").length;
610
+ if (threat.codeOnly && !isInCodeBlock(lineNumber, skill)) {
611
+ continue;
612
+ }
558
613
  findings.push({
559
614
  category: threat.category,
560
615
  severity: threat.severity,
@@ -730,7 +785,7 @@ function detectTyposquats(skillName) {
730
785
  }
731
786
  }
732
787
  const patterns = [
733
- { re: /-+/, desc: "extra hyphens" },
788
+ { re: /-{2,}/, desc: "extra hyphens" },
734
789
  { re: /(.)\1{2,}/, desc: "repeated characters" }
735
790
  ];
736
791
  for (const p of patterns) {
@@ -790,9 +845,12 @@ async function scanSkill(content, options = {}) {
790
845
  if (skill.frontmatter.name) {
791
846
  allFindings.push(...detectTyposquats(skill.frontmatter.name));
792
847
  }
793
- const riskScore = calculateRiskScore(allFindings);
848
+ const filteredFindings = options.ignorePatterns?.length ? allFindings.filter(
849
+ (f) => !options.ignorePatterns.some((ig) => f.title === ig || f.category === ig)
850
+ ) : allFindings;
851
+ const riskScore = calculateRiskScore(filteredFindings);
794
852
  const riskGrade = getRiskGrade(riskScore);
795
- const findingsCount = countFindings(allFindings);
853
+ const findingsCount = countFindings(filteredFindings);
796
854
  const recommendation = riskScore >= 76 ? "block" : riskScore >= 26 ? "warn" : "approve";
797
855
  return {
798
856
  skillName: skill.frontmatter.name || "unknown",
@@ -802,7 +860,7 @@ async function scanSkill(content, options = {}) {
802
860
  riskScore,
803
861
  riskGrade,
804
862
  findingsCount,
805
- findings: allFindings,
863
+ findings: filteredFindings,
806
864
  recommendation
807
865
  };
808
866
  }
@@ -888,11 +946,76 @@ function printJsonResult(result) {
888
946
  console.log(JSON.stringify(result, null, 2));
889
947
  }
890
948
 
949
+ // src/output/sarif.ts
950
+ var SEVERITY_TO_SARIF = {
951
+ critical: "error",
952
+ high: "error",
953
+ medium: "warning",
954
+ low: "note"
955
+ };
956
+ var SEVERITY_TO_LEVEL = {
957
+ critical: "9.0",
958
+ high: "7.0",
959
+ medium: "4.0",
960
+ low: "1.0"
961
+ };
962
+ function printSarifResult(result) {
963
+ const rules = /* @__PURE__ */ new Map();
964
+ for (const f of result.findings) {
965
+ const ruleId = f.category + "/" + f.title.toLowerCase().replace(/[^a-z0-9]+/g, "-");
966
+ if (!rules.has(ruleId)) {
967
+ rules.set(ruleId, { id: ruleId, finding: f });
968
+ }
969
+ }
970
+ const sarif = {
971
+ $schema: "https://raw.githubusercontent.com/oasis-tcs/sarif-spec/main/sarif-2.1/schema/sarif-schema-2.1.0.json",
972
+ version: "2.1.0",
973
+ runs: [
974
+ {
975
+ tool: {
976
+ driver: {
977
+ name: "clawvet",
978
+ informationUri: "https://github.com/clawvet/clawvet",
979
+ rules: [...rules.values()].map((r) => ({
980
+ id: r.id,
981
+ shortDescription: { text: r.finding.title },
982
+ fullDescription: { text: r.finding.description },
983
+ defaultConfiguration: {
984
+ level: SEVERITY_TO_SARIF[r.finding.severity]
985
+ },
986
+ properties: {
987
+ security_severity: SEVERITY_TO_LEVEL[r.finding.severity]
988
+ }
989
+ }))
990
+ }
991
+ },
992
+ results: result.findings.map((f) => {
993
+ const ruleId = f.category + "/" + f.title.toLowerCase().replace(/[^a-z0-9]+/g, "-");
994
+ return {
995
+ ruleId,
996
+ level: SEVERITY_TO_SARIF[f.severity],
997
+ message: { text: f.description + (f.evidence ? ` Evidence: ${f.evidence}` : "") },
998
+ locations: [
999
+ {
1000
+ physicalLocation: {
1001
+ artifactLocation: { uri: "SKILL.md" },
1002
+ region: { startLine: f.lineNumber ?? 1 }
1003
+ }
1004
+ }
1005
+ ]
1006
+ };
1007
+ })
1008
+ }
1009
+ ]
1010
+ };
1011
+ console.log(JSON.stringify(sarif, null, 2));
1012
+ }
1013
+
891
1014
  // src/commands/scan.ts
892
1015
  async function fetchRemoteSkill(slug) {
893
1016
  const urls = [
894
1017
  `https://raw.githubusercontent.com/openclaw/skills/main/${slug}/SKILL.md`,
895
- `https://clawhub.com/api/v1/skills/${slug}/raw`
1018
+ `https://clawhub.ai/api/v1/skills/${slug}/raw`
896
1019
  ];
897
1020
  for (const url of urls) {
898
1021
  try {
@@ -926,23 +1049,41 @@ async function scanCommand(target, options) {
926
1049
  if (existsSync(skillPath) && !skillPath.endsWith(".md") && existsSync(join(skillPath, "SKILL.md"))) {
927
1050
  skillFile = join(skillPath, "SKILL.md");
928
1051
  }
929
- if (!existsSync(skillFile)) {
1052
+ if (!existsSync(skillFile) || statSync(skillFile).isDirectory()) {
930
1053
  console.error(`Error: Cannot find SKILL.md at ${skillFile}`);
1054
+ console.error(`Hint: If this is a directory of skills, use 'clawvet audit --dir ${target}' instead.`);
931
1055
  process.exit(1);
932
1056
  }
933
1057
  content = readFileSync(skillFile, "utf-8");
934
1058
  }
1059
+ const ignoreFile = join(process.cwd(), ".clawvetignore");
1060
+ const ignorePatterns = [];
1061
+ if (existsSync(ignoreFile)) {
1062
+ const lines = readFileSync(ignoreFile, "utf-8").split("\n");
1063
+ for (const line of lines) {
1064
+ const trimmed = line.trim();
1065
+ if (trimmed && !trimmed.startsWith("#")) {
1066
+ ignorePatterns.push(trimmed);
1067
+ }
1068
+ }
1069
+ }
935
1070
  const result = await scanSkill(content, {
936
- semantic: options.semantic ?? false
1071
+ semantic: options.semantic ?? false,
1072
+ ignorePatterns: ignorePatterns.length ? ignorePatterns : void 0
937
1073
  });
938
- if (options.format === "json") {
939
- printJsonResult(result);
940
- } else {
941
- printScanResult(result);
1074
+ if (!options.quiet) {
1075
+ if (options.format === "sarif") {
1076
+ printSarifResult(result);
1077
+ } else if (options.format === "json") {
1078
+ printJsonResult(result);
1079
+ } else {
1080
+ printScanResult(result);
1081
+ }
942
1082
  }
943
- if (options.failOn) {
1083
+ const failOn = options.failOn || (options.quiet ? "high" : void 0);
1084
+ if (failOn) {
944
1085
  const severityOrder = ["low", "medium", "high", "critical"];
945
- const threshold = severityOrder.indexOf(options.failOn);
1086
+ const threshold = severityOrder.indexOf(failOn);
946
1087
  const hasFailure = result.findings.some(
947
1088
  (f) => severityOrder.indexOf(f.severity) >= threshold
948
1089
  );
@@ -957,16 +1098,33 @@ import { readdirSync, existsSync as existsSync2, readFileSync as readFileSync2 }
957
1098
  import { join as join2 } from "path";
958
1099
  import { homedir } from "os";
959
1100
  import chalk2 from "chalk";
960
- var SKILL_DIRS = [
1101
+ var DEFAULT_SKILL_DIRS = [
961
1102
  join2(homedir(), ".openclaw", "skills"),
962
1103
  join2(homedir(), ".openclaw", "workspace", "skills")
963
1104
  ];
964
- async function auditCommand() {
1105
+ async function auditCommand(options = {}) {
1106
+ const SKILL_DIRS = options.dir ? [options.dir] : DEFAULT_SKILL_DIRS;
965
1107
  console.log(chalk2.bold("\nClawVet Audit \u2014 Scanning all installed skills\n"));
966
1108
  let totalScanned = 0;
967
1109
  let totalThreats = 0;
968
1110
  for (const dir of SKILL_DIRS) {
969
- if (!existsSync2(dir)) continue;
1111
+ if (!existsSync2(dir)) {
1112
+ if (options.dir) {
1113
+ console.error(chalk2.yellow(`Warning: Directory not found: ${dir}
1114
+ `));
1115
+ process.exit(1);
1116
+ }
1117
+ continue;
1118
+ }
1119
+ const directSkillFile = join2(dir, "SKILL.md");
1120
+ if (existsSync2(directSkillFile)) {
1121
+ const content = readFileSync2(directSkillFile, "utf-8");
1122
+ const result = await scanSkill(content);
1123
+ totalScanned++;
1124
+ totalThreats += result.findings.length;
1125
+ printScanResult(result);
1126
+ continue;
1127
+ }
970
1128
  const entries = readdirSync(dir, { withFileTypes: true });
971
1129
  for (const entry of entries) {
972
1130
  if (!entry.isDirectory()) continue;
@@ -989,12 +1147,13 @@ import { readFileSync as readFileSync3, existsSync as existsSync3, watch } from
989
1147
  import { join as join3 } from "path";
990
1148
  import { homedir as homedir2 } from "os";
991
1149
  import chalk3 from "chalk";
992
- var SKILL_DIRS2 = [
1150
+ var DEFAULT_SKILL_DIRS2 = [
993
1151
  join3(homedir2(), ".openclaw", "skills"),
994
1152
  join3(homedir2(), ".openclaw", "workspace", "skills")
995
1153
  ];
996
1154
  async function watchCommand(options) {
997
1155
  const threshold = options.threshold || 50;
1156
+ const SKILL_DIRS = options.dir ? [options.dir] : DEFAULT_SKILL_DIRS2;
998
1157
  console.log(
999
1158
  chalk3.bold(
1000
1159
  `
@@ -1003,7 +1162,7 @@ ClawVet Watch \u2014 monitoring skill directories (threshold: ${threshold})
1003
1162
  )
1004
1163
  );
1005
1164
  const watchDirs = [];
1006
- for (const dir of SKILL_DIRS2) {
1165
+ for (const dir of SKILL_DIRS) {
1007
1166
  if (existsSync3(dir)) {
1008
1167
  watchDirs.push(dir);
1009
1168
  }
@@ -1015,11 +1174,11 @@ ClawVet Watch \u2014 monitoring skill directories (threshold: ${threshold})
1015
1174
  )
1016
1175
  );
1017
1176
  console.log(chalk3.dim("Expected directories:"));
1018
- for (const dir of SKILL_DIRS2) {
1177
+ for (const dir of SKILL_DIRS) {
1019
1178
  console.log(chalk3.dim(` ${dir}`));
1020
1179
  }
1021
1180
  console.log();
1022
- return;
1181
+ process.exit(1);
1023
1182
  }
1024
1183
  console.log(chalk3.dim("Watching:"));
1025
1184
  for (const dir of watchDirs) {
@@ -1067,20 +1226,21 @@ Detected change: ${filename}`));
1067
1226
 
1068
1227
  // src/index.ts
1069
1228
  var program = new Command();
1070
- program.name("clawvet").description("Skill vetting & supply chain security for OpenClaw").version("0.2.0");
1071
- program.command("scan").description("Scan a skill for security threats").argument("<target>", "Path to skill folder or SKILL.md file").option("--format <format>", "Output format: terminal or json", "terminal").option("--fail-on <severity>", "Exit 1 if findings at this severity or above").option("--semantic", "Enable AI semantic analysis (requires ANTHROPIC_API_KEY)").option("--remote", "Fetch skill from ClawHub by name instead of local path").action(async (target, opts) => {
1229
+ program.name("clawvet").description("Skill vetting & supply chain security for OpenClaw").version("0.3.0");
1230
+ program.command("scan").description("Scan a skill for security threats").argument("<target>", "Path to skill folder or SKILL.md file").option("--format <format>", "Output format: terminal, json, or sarif", "terminal").option("--fail-on <severity>", "Exit 1 if findings at this severity or above").option("--semantic", "Enable AI semantic analysis (requires ANTHROPIC_API_KEY)").option("--remote", "Fetch skill from ClawHub by name instead of local path").option("-q, --quiet", "Suppress all output, exit code only (0=pass, 1=fail)").action(async (target, opts) => {
1072
1231
  await scanCommand(target, {
1073
1232
  format: opts.format,
1074
1233
  failOn: opts.failOn,
1075
1234
  semantic: opts.semantic,
1076
- remote: opts.remote
1235
+ remote: opts.remote,
1236
+ quiet: opts.quiet
1077
1237
  });
1078
1238
  });
1079
- program.command("audit").description("Scan all installed OpenClaw skills").action(async () => {
1080
- await auditCommand();
1239
+ program.command("audit").description("Scan all installed OpenClaw skills").option("--dir <path>", "Custom skills directory to scan").action(async (opts) => {
1240
+ await auditCommand({ dir: opts.dir });
1081
1241
  });
1082
- program.command("watch").description("Pre-install hook \u2014 blocks risky skill installs").option("--threshold <score>", "Risk score threshold (default 50)", "50").action(async (opts) => {
1083
- await watchCommand({ threshold: parseInt(opts.threshold) });
1242
+ program.command("watch").description("Pre-install hook \u2014 blocks risky skill installs").option("--threshold <score>", "Risk score threshold (default 50)", "50").option("--dir <path>", "Custom skills directory to watch").action(async (opts) => {
1243
+ await watchCommand({ threshold: parseInt(opts.threshold), dir: opts.dir });
1084
1244
  });
1085
1245
  program.parse();
1086
1246
  //# sourceMappingURL=index.js.map
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/risk-scorer.ts","../../shared/src/scanner/index.ts","../src/output/terminal.ts","../src/output/json.ts","../src/commands/audit.ts","../src/commands/watch.ts"],"sourcesContent":["import { Command } from \"commander\";\nimport { scanCommand } from \"./commands/scan.js\";\nimport { auditCommand } from \"./commands/audit.js\";\nimport { watchCommand } from \"./commands/watch.js\";\n\nconst program = new Command();\n\nprogram\n .name(\"clawvet\")\n .description(\"Skill vetting & supply chain security for OpenClaw\")\n .version(\"0.2.0\");\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 or json\", \"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 .action(async (target, opts) => {\n await scanCommand(target, {\n format: opts.format,\n failOn: opts.failOn,\n semantic: opts.semantic,\n remote: opts.remote,\n });\n });\n\nprogram\n .command(\"audit\")\n .description(\"Scan all installed OpenClaw skills\")\n .action(async () => {\n await auditCommand();\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 .action(async (opts) => {\n await watchCommand({ threshold: parseInt(opts.threshold) });\n });\n\nprogram.parse();\n","import { readFileSync, existsSync } from \"node:fs\";\nimport { resolve, join } from \"node:path\";\nimport { scanSkill } from \"@clawvet/shared\";\nimport { printScanResult } from \"../output/terminal.js\";\nimport { printJsonResult } from \"../output/json.js\";\n\nexport interface ScanOptions {\n format?: \"terminal\" | \"json\";\n failOn?: \"critical\" | \"high\" | \"medium\" | \"low\";\n semantic?: boolean;\n remote?: boolean;\n}\n\nasync function fetchRemoteSkill(slug: string): Promise<string> {\n const urls = [\n `https://raw.githubusercontent.com/openclaw/skills/main/${slug}/SKILL.md`,\n `https://clawhub.com/api/v1/skills/${slug}/raw`,\n ];\n\n for (const url of urls) {\n try {\n const res = await fetch(url, { signal: AbortSignal.timeout(10000) });\n if (res.ok) {\n return await res.text();\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\n if (options.remote) {\n try {\n process.stderr.write(`Fetching \"${target}\" from ClawHub...\\n`);\n content = await fetchRemoteSkill(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\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)) {\n console.error(`Error: Cannot find SKILL.md at ${skillFile}`);\n process.exit(1);\n }\n\n content = readFileSync(skillFile, \"utf-8\");\n }\n\n const result = await scanSkill(content, {\n semantic: options.semantic ?? false,\n });\n\n if (options.format === \"json\") {\n printJsonResult(result);\n } else {\n printScanResult(result);\n }\n\n if (options.failOn) {\n const severityOrder = [\"low\", \"medium\", \"high\", \"critical\"];\n const threshold = severityOrder.indexOf(options.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\nexport const THREAT_PATTERNS: ThreatPattern[] = [\n // ═══════════════════════════════════════════════════════\n // CRITICAL: Remote code execution\n // ═══════════════════════════════════════════════════════\n {\n name: \"CURL_PIPE_BASH\",\n pattern: /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 },\n {\n name: \"WGET_EXECUTE\",\n pattern: /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 },\n {\n name: \"EVAL_DYNAMIC\",\n pattern: /eval\\s*\\(/gi,\n severity: \"critical\",\n category: \"remote_code_execution\",\n title: \"Dynamic eval() usage\",\n description: \"Uses eval() which can execute arbitrary code.\",\n },\n {\n name: \"BASE64_DECODE\",\n pattern: /base64\\s+(-d|--decode)/gi,\n severity: \"critical\",\n category: \"obfuscation\",\n title: \"Base64 decode execution\",\n description: \"Decodes base64 content, often used to hide malicious payloads.\",\n },\n {\n name: \"PYTHON_EXEC\",\n pattern: /python[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 },\n {\n name: \"REVERSE_SHELL\",\n pattern: /\\/dev\\/tcp\\/|nc\\s+-[elp]|ncat\\s+-|mkfifo\\s+.*\\/tmp/gi,\n severity: \"critical\",\n category: \"remote_code_execution\",\n title: \"Reverse shell\",\n description: \"Creates a reverse shell connection back to an attacker-controlled server.\",\n },\n {\n name: \"CRON_PERSISTENCE\",\n pattern: /crontab\\s+-|\\/etc\\/cron|systemctl\\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 },\n {\n name: \"PERL_EXEC\",\n pattern: /perl\\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 },\n {\n name: \"NODE_EVAL\",\n pattern: /node\\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 },\n {\n name: \"RUBY_EXEC\",\n pattern: /ruby\\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 },\n\n // ═══════════════════════════════════════════════════════\n // HIGH: Credential theft\n // ═══════════════════════════════════════════════════════\n {\n name: \"ENV_FILE_READ\",\n pattern: /\\.env|credentials|\\.aws|\\.ssh|keychain/gi,\n severity: \"high\",\n category: \"credential_theft\",\n title: \"Sensitive file access\",\n description: \"Accesses credential files (.env, .aws, .ssh, keychain).\",\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 },\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 },\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 },\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 },\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 },\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 },\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 },\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 },\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 },\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 },\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 },\n {\n name: \"SUSPICIOUS_IP\",\n pattern: /\\b(?:91\\.92\\.242\\.\\d+|45\\.61\\.\\d+\\.\\d+)\\b/g,\n severity: \"high\",\n category: \"data_exfiltration\",\n title: \"Known malicious IP\",\n description: \"Contains IP addresses associated with known ClawHavoc C2 infrastructure.\",\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 },\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 },\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 },\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 },\n {\n name: \"RAW_SOCKET\",\n pattern: /new\\s+Socket|net\\.connect|dgram\\.createSocket/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 },\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 },\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 },\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 },\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 },\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 },\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 },\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 },\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 },\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 },\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 },\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 },\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 },\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 },\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 },\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 },\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 },\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 },\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 },\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 },\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 },\n\n // ═══════════════════════════════════════════════════════\n // LOW: Suspicious but not necessarily malicious\n // ═══════════════════════════════════════════════════════\n {\n name: \"SHELL_EXEC\",\n pattern: /child_process|exec\\(|spawn\\(/gi,\n severity: \"low\",\n category: \"code_execution\",\n title: \"Shell execution API\",\n description: \"Uses shell execution APIs — legitimate but worth noting.\",\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 },\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 },\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 },\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 },\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 },\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 = /^---\\n([\\s\\S]*?)\\n---/;\nconst CODE_BLOCK_RE = /```(\\w*)\\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 } from \"../types.js\";\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 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 });\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 });\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 });\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 });\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 });\n }\n\n const declaredBins = new Set(fm.metadata?.openclaw?.requires?.bins || []);\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 });\n }\n }\n }\n\n const declaredEnv = new Set(fm.metadata?.openclaw?.requires?.env || []);\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) && 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 });\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 });\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 });\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: /-+/, 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, 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\nexport function calculateRiskScore(findings: Finding[]): number {\n let score = 0;\n for (const f of findings) {\n score += SEVERITY_WEIGHTS[f.severity];\n }\n return 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 type { Finding, 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 { calculateRiskScore, getRiskGrade, countFindings } from \"./risk-scorer.js\";\n\nexport interface ScanOptions {\n semantic?: boolean;\n semanticAnalyzer?: (content: string) => Promise<Finding[]>;\n}\n\nexport async function scanSkill(\n content: string,\n options: ScanOptions = {}\n): Promise<ScanResult> {\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 const riskScore = calculateRiskScore(allFindings);\n const riskGrade = getRiskGrade(riskScore);\n const findingsCount = countFindings(allFindings);\n\n const recommendation =\n riskScore >= 76 ? \"block\" : riskScore >= 26 ? \"warn\" : \"approve\";\n\n return {\n skillName: skill.frontmatter.name || \"unknown\",\n skillVersion: skill.frontmatter.version,\n skillSource: \"local\",\n status: \"complete\",\n riskScore,\n riskGrade,\n findingsCount,\n findings: allFindings,\n recommendation,\n };\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(`${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 console.log(` ${color(`[${f.severity.toUpperCase()}]`)} ${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 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 { readdirSync, existsSync, readFileSync } from \"node:fs\";\nimport { join } from \"node:path\";\nimport { homedir } from \"node:os\";\nimport { scanSkill } from \"@clawvet/shared\";\nimport { printScanResult } from \"../output/terminal.js\";\nimport chalk from \"chalk\";\n\nconst SKILL_DIRS = [\n join(homedir(), \".openclaw\", \"skills\"),\n join(homedir(), \".openclaw\", \"workspace\", \"skills\"),\n];\n\nexport async function auditCommand(): Promise<void> {\n console.log(chalk.bold(\"\\nClawVet Audit — Scanning all installed skills\\n\"));\n\n let totalScanned = 0;\n let totalThreats = 0;\n\n for (const dir of SKILL_DIRS) {\n if (!existsSync(dir)) continue;\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);\n totalScanned++;\n totalThreats += result.findings.length;\n\n printScanResult(result);\n }\n }\n\n console.log(chalk.bold(`\\nAudit complete: ${totalScanned} skills scanned, ${totalThreats} findings\\n`));\n}\n","import { readFileSync, existsSync, watch } from \"node:fs\";\nimport { join } 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 SKILL_DIRS = [\n join(homedir(), \".openclaw\", \"skills\"),\n join(homedir(), \".openclaw\", \"workspace\", \"skills\"),\n];\n\nexport async function watchCommand(options: {\n threshold?: number;\n}): Promise<void> {\n const threshold = options.threshold || 50;\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 return;\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\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"],"mappings":"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","names":["existsSync","readFileSync","join","chalk","join","existsSync","readFileSync","readFileSync","existsSync","join","homedir","chalk","SKILL_DIRS","join","homedir","chalk","existsSync","readFileSync"]}
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/risk-scorer.ts","../../shared/src/scanner/index.ts","../src/output/terminal.ts","../src/output/json.ts","../src/output/sarif.ts","../src/commands/audit.ts","../src/commands/watch.ts"],"sourcesContent":["import { Command } from \"commander\";\nimport { scanCommand } from \"./commands/scan.js\";\nimport { auditCommand } from \"./commands/audit.js\";\nimport { watchCommand } from \"./commands/watch.js\";\n\nconst program = new Command();\n\nprogram\n .name(\"clawvet\")\n .description(\"Skill vetting & supply chain security for OpenClaw\")\n .version(\"0.3.0\");\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 .action(async (target, opts) => {\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(\"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.parse();\n","import { readFileSync, existsSync, statSync } from \"node:fs\";\nimport { resolve, join } from \"node:path\";\nimport { scanSkill } from \"@clawvet/shared\";\nimport { printScanResult } from \"../output/terminal.js\";\nimport { printJsonResult } from \"../output/json.js\";\nimport { printSarifResult } from \"../output/sarif.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\nasync function fetchRemoteSkill(slug: string): Promise<string> {\n const urls = [\n `https://raw.githubusercontent.com/openclaw/skills/main/${slug}/SKILL.md`,\n `https://clawhub.ai/api/v1/skills/${slug}/raw`,\n ];\n\n for (const url of urls) {\n try {\n const res = await fetch(url, { signal: AbortSignal.timeout(10000) });\n if (res.ok) {\n return await res.text();\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\n if (options.remote) {\n try {\n process.stderr.write(`Fetching \"${target}\" from ClawHub...\\n`);\n content = await fetchRemoteSkill(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\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 console.error(`Hint: If this is a directory of skills, use 'clawvet audit --dir ${target}' instead.`);\n process.exit(1);\n }\n\n content = readFileSync(skillFile, \"utf-8\");\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 });\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 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\nexport const THREAT_PATTERNS: ThreatPattern[] = [\n // ═══════════════════════════════════════════════════════\n // CRITICAL: Remote code execution\n // ═══════════════════════════════════════════════════════\n {\n name: \"CURL_PIPE_BASH\",\n pattern: /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 },\n {\n name: \"WGET_EXECUTE\",\n pattern: /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 },\n {\n name: \"EVAL_DYNAMIC\",\n pattern: /eval\\s*\\(/gi,\n severity: \"critical\",\n category: \"remote_code_execution\",\n title: \"Dynamic eval() usage\",\n description: \"Uses eval() which can execute arbitrary code.\",\n codeOnly: true,\n },\n {\n name: \"BASE64_DECODE\",\n pattern: /base64\\s+(-d|--decode)/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 },\n {\n name: \"PYTHON_EXEC\",\n pattern: /python[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 },\n {\n name: \"REVERSE_SHELL\",\n pattern: /\\/dev\\/tcp\\/|nc\\s+-[elp]|ncat\\s+-|mkfifo\\s+.*\\/tmp/gi,\n severity: \"critical\",\n category: \"remote_code_execution\",\n title: \"Reverse shell\",\n description: \"Creates a reverse shell connection back to an attacker-controlled server.\",\n codeOnly: true,\n },\n {\n name: \"CRON_PERSISTENCE\",\n pattern: /crontab\\s+-|\\/etc\\/cron|systemctl\\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 },\n {\n name: \"PERL_EXEC\",\n pattern: /perl\\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 },\n {\n name: \"NODE_EVAL\",\n pattern: /node\\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 },\n {\n name: \"RUBY_EXEC\",\n pattern: /ruby\\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 },\n\n // ═══════════════════════════════════════════════════════\n // HIGH: Credential theft\n // ═══════════════════════════════════════════════════════\n {\n name: \"ENV_FILE_READ\",\n pattern: /\\.env|credentials|\\.aws|\\.ssh|keychain/gi,\n severity: \"high\",\n category: \"credential_theft\",\n title: \"Sensitive file access\",\n description: \"Accesses credential files (.env, .aws, .ssh, keychain).\",\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 },\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 },\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 },\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 },\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 },\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 },\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 },\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 },\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 },\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 },\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 },\n {\n name: \"SUSPICIOUS_IP\",\n pattern: /\\b(?:91\\.92\\.242\\.\\d+|45\\.61\\.\\d+\\.\\d+)\\b/g,\n severity: \"high\",\n category: \"data_exfiltration\",\n title: \"Known malicious IP\",\n description: \"Contains IP addresses associated with known ClawHavoc C2 infrastructure.\",\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 },\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 },\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 },\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 },\n {\n name: \"RAW_SOCKET\",\n pattern: /new\\s+Socket|net\\.connect|dgram\\.createSocket/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 },\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 },\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 },\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 },\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 },\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 },\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 },\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 },\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 },\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 },\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 },\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 },\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 },\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 },\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 },\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 },\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 },\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 },\n {\n name: \"BUFFER_BASE64_DECODE\",\n pattern: /Buffer\\.from\\s*\\(.*['\"]base64['\"]\\)|atob\\s*\\(/gi,\n severity: \"critical\",\n category: \"obfuscation\",\n title: \"Buffer/atob base64 decode\",\n description: \"Decodes base64 content via Buffer.from() or atob(), often used to hide malicious payloads.\",\n codeOnly: true,\n },\n {\n name: \"STRING_FROMCHARCODE\",\n pattern: /String\\.fromCharCode\\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 },\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 },\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 },\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 },\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 },\n\n // ═══════════════════════════════════════════════════════\n // LOW: Suspicious but not necessarily malicious\n // ═══════════════════════════════════════════════════════\n {\n name: \"SHELL_EXEC\",\n pattern: /child_process|exec\\(|spawn\\(/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 },\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 },\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 },\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 },\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 },\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 },\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 = /^---\\n([\\s\\S]*?)\\n---/;\nconst CODE_BLOCK_RE = /```(\\w*)\\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 } 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\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 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 });\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 });\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 });\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 });\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 });\n }\n\n const declaredBins = new Set(fm.metadata?.openclaw?.requires?.bins || []);\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 });\n }\n }\n }\n\n const declaredEnv = new Set(fm.metadata?.openclaw?.requires?.env || []);\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) && 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 });\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 });\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 });\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, 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\nexport function calculateRiskScore(findings: Finding[]): number {\n let score = 0;\n for (const f of findings) {\n score += SEVERITY_WEIGHTS[f.severity];\n }\n return 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 type { Finding, 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 { calculateRiskScore, getRiskGrade, countFindings } from \"./risk-scorer.js\";\n\nexport interface ScanOptions {\n semantic?: boolean;\n semanticAnalyzer?: (content: string) => Promise<Finding[]>;\n ignorePatterns?: string[];\n}\n\nexport async function scanSkill(\n content: string,\n options: ScanOptions = {}\n): Promise<ScanResult> {\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 const riskScore = calculateRiskScore(filteredFindings);\n const riskGrade = getRiskGrade(riskScore);\n const findingsCount = countFindings(filteredFindings);\n\n const recommendation =\n riskScore >= 76 ? \"block\" : riskScore >= 26 ? \"warn\" : \"approve\";\n\n return {\n skillName: skill.frontmatter.name || \"unknown\",\n skillVersion: skill.frontmatter.version,\n skillSource: \"local\",\n status: \"complete\",\n riskScore,\n riskGrade,\n findingsCount,\n findings: filteredFindings,\n recommendation,\n };\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(`${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 console.log(` ${color(`[${f.severity.toUpperCase()}]`)} ${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 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: { text: f.description + (f.evidence ? ` Evidence: ${f.evidence}` : \"\") },\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 { readdirSync, existsSync, readFileSync } from \"node:fs\";\nimport { join } from \"node:path\";\nimport { homedir } from \"node:os\";\nimport { scanSkill } from \"@clawvet/shared\";\nimport { printScanResult } from \"../output/terminal.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 let totalScanned = 0;\n let totalThreats = 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);\n totalScanned++;\n totalThreats += result.findings.length;\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);\n totalScanned++;\n totalThreats += result.findings.length;\n\n printScanResult(result);\n }\n }\n\n console.log(chalk.bold(`\\nAudit complete: ${totalScanned} skills scanned, ${totalThreats} findings\\n`));\n}\n","import { readFileSync, existsSync, watch } from \"node:fs\";\nimport { join } 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\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"],"mappings":"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","names":["existsSync","readFileSync","join","chalk","join","existsSync","readFileSync","readFileSync","existsSync","join","homedir","chalk","DEFAULT_SKILL_DIRS","join","homedir","chalk","existsSync","readFileSync"]}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "clawvet",
3
- "version": "0.2.2",
3
+ "version": "0.3.0",
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": {