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 +208 -48
- package/dist/index.js.map +1 -1
- package/package.json +1 -1
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:
|
|
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
|
|
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(
|
|
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:
|
|
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.
|
|
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.
|
|
939
|
-
|
|
940
|
-
|
|
941
|
-
|
|
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
|
-
|
|
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(
|
|
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
|
|
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))
|
|
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
|
|
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
|
|
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
|
|
1177
|
+
for (const dir of SKILL_DIRS) {
|
|
1019
1178
|
console.log(chalk3.dim(` ${dir}`));
|
|
1020
1179
|
}
|
|
1021
1180
|
console.log();
|
|
1022
|
-
|
|
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.
|
|
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
|
|
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.
|
|
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": {
|