@denial-web/clawguard 0.1.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/.clawguard.example.json +16 -0
- package/LICENSE +21 -0
- package/README.md +241 -0
- package/SECURITY.md +33 -0
- package/action.yml +72 -0
- package/docs/ARCHITECTURE.md +312 -0
- package/docs/ARCHITECTURE_ROADMAP.md +267 -0
- package/docs/CLAWHUB_METADATA.md +57 -0
- package/docs/DEMO_CAPTURE.md +25 -0
- package/docs/DEMO_SCRIPT.md +87 -0
- package/docs/DEPENDENCY_SCANNING.md +61 -0
- package/docs/GITHUB_ACTION.md +56 -0
- package/docs/GITHUB_REPO_SETUP.md +76 -0
- package/docs/HTML_REPORTS.md +27 -0
- package/docs/INTEGRATION_SPEC.md +253 -0
- package/docs/LAUNCH_CHECKLIST.md +64 -0
- package/docs/LAUNCH_PLAN.md +40 -0
- package/docs/LOCAL_PROJECT_ASSETS.md +250 -0
- package/docs/MCP_PLUGIN_SCANNING.md +53 -0
- package/docs/NEXT_SESSION.md +110 -0
- package/docs/NPM_PUBLISHING.md +66 -0
- package/docs/OPENCLAW_CLAWHUB_RESEARCH.md +128 -0
- package/docs/POLICY_MODEL.md +198 -0
- package/docs/PROJECT_REVIEW.md +108 -0
- package/docs/REAL_WORLD_VALIDATION.md +57 -0
- package/docs/RELEASE_NOTES_v0.1.0.md +52 -0
- package/docs/REPORT_SCHEMA.md +81 -0
- package/docs/RULES.md +92 -0
- package/docs/THREAT_MODEL.md +50 -0
- package/docs/WEB_DEMO.md +39 -0
- package/docs/WORKSPACE_SCANNING.md +41 -0
- package/examples/clawhub-origin-without-lock/skills/orphan-helper/.clawhub/origin.json +6 -0
- package/examples/clawhub-origin-without-lock/skills/orphan-helper/SKILL.md +11 -0
- package/examples/clawhub-workspace/.clawhub/lock.json +22 -0
- package/examples/clawhub-workspace/skills/drift-helper/.clawhub/origin.json +6 -0
- package/examples/clawhub-workspace/skills/drift-helper/SKILL.md +11 -0
- package/examples/clawhub-workspace/skills/missing-origin/SKILL.md +11 -0
- package/examples/clawhub-workspace/skills/weather-helper/.clawhub/origin.json +6 -0
- package/examples/clawhub-workspace/skills/weather-helper/SKILL.md +15 -0
- package/examples/declared-api-skill/SKILL.md +27 -0
- package/examples/dependency-python-skill/SKILL.md +16 -0
- package/examples/dependency-python-skill/pyproject.toml +5 -0
- package/examples/dependency-python-skill/requirements.txt +3 -0
- package/examples/dependency-risky-skill/SKILL.md +16 -0
- package/examples/dependency-risky-skill/package.json +12 -0
- package/examples/dependency-safe-skill/SKILL.md +16 -0
- package/examples/dependency-safe-skill/package-lock.json +19 -0
- package/examples/dependency-safe-skill/package.json +7 -0
- package/examples/metadata-mismatch-skill/SKILL.md +22 -0
- package/examples/openclaw-plugin-config/.openclaw/plugins.json +18 -0
- package/examples/openclaw-workspace/.agents/skills/research-helper/SKILL.md +11 -0
- package/examples/openclaw-workspace/skills/notes/SKILL.md +3 -0
- package/examples/openclaw-workspace/skills/research-helper/SKILL.md +17 -0
- package/examples/risky-mcp-config/.cursor/mcp.json +29 -0
- package/examples/risky-openclaw-plugin/openclaw.plugin.json +6 -0
- package/examples/risky-openclaw-plugin/package.json +7 -0
- package/examples/risky-openclaw-plugin/src/index.ts +1 -0
- package/examples/risky-skill/SKILL.md +17 -0
- package/examples/safe-mcp-config/.cursor/mcp.json +15 -0
- package/examples/safe-openclaw-plugin/dist/index.js +1 -0
- package/examples/safe-openclaw-plugin/openclaw.plugin.json +5 -0
- package/examples/safe-openclaw-plugin/package.json +14 -0
- package/examples/safe-skill/SKILL.md +12 -0
- package/package.json +49 -0
- package/schemas/clawguard-report.schema.json +266 -0
- package/scripts/capture-demo.js +206 -0
- package/src/clawhub.js +383 -0
- package/src/cli.js +296 -0
- package/src/config.js +205 -0
- package/src/dependencies.js +417 -0
- package/src/mcp-config.js +592 -0
- package/src/policy.js +165 -0
- package/src/reporters/html.js +482 -0
- package/src/reporters/sarif.js +121 -0
- package/src/rule-catalog.js +400 -0
- package/src/rules.js +121 -0
- package/src/scanner.js +387 -0
- package/src/skill-metadata.js +516 -0
- package/src/web-server.js +395 -0
- package/src/workspace.js +233 -0
- package/web/app.js +374 -0
- package/web/index.html +119 -0
- package/web/styles.css +453 -0
|
@@ -0,0 +1,121 @@
|
|
|
1
|
+
import { ruleCatalogById } from "../rule-catalog.js";
|
|
2
|
+
|
|
3
|
+
const sarifVersion = "2.1.0";
|
|
4
|
+
const sarifSchema = "https://json.schemastore.org/sarif-2.1.0.json";
|
|
5
|
+
|
|
6
|
+
export function createSarifReport(scanResult) {
|
|
7
|
+
const findings = scanResult.findings ?? [];
|
|
8
|
+
|
|
9
|
+
return {
|
|
10
|
+
version: sarifVersion,
|
|
11
|
+
$schema: sarifSchema,
|
|
12
|
+
runs: [
|
|
13
|
+
{
|
|
14
|
+
tool: {
|
|
15
|
+
driver: {
|
|
16
|
+
name: "ClawGuard",
|
|
17
|
+
informationUri: "https://github.com/denial-web/clawguard",
|
|
18
|
+
rules: createRules(findings)
|
|
19
|
+
}
|
|
20
|
+
},
|
|
21
|
+
invocations: [
|
|
22
|
+
{
|
|
23
|
+
executionSuccessful: true,
|
|
24
|
+
properties: {
|
|
25
|
+
target: scanResult.target,
|
|
26
|
+
score: scanResult.score,
|
|
27
|
+
level: scanResult.level,
|
|
28
|
+
policyDecision: scanResult.policy?.decision ?? "allow",
|
|
29
|
+
policyPreset: scanResult.policy?.preset ?? "personal"
|
|
30
|
+
}
|
|
31
|
+
}
|
|
32
|
+
],
|
|
33
|
+
results: findings.map(createResult)
|
|
34
|
+
}
|
|
35
|
+
]
|
|
36
|
+
};
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
function createRules(findings) {
|
|
40
|
+
const rules = new Map();
|
|
41
|
+
|
|
42
|
+
for (const finding of findings) {
|
|
43
|
+
if (rules.has(finding.ruleId)) {
|
|
44
|
+
continue;
|
|
45
|
+
}
|
|
46
|
+
|
|
47
|
+
rules.set(finding.ruleId, {
|
|
48
|
+
id: finding.ruleId,
|
|
49
|
+
name: finding.ruleId,
|
|
50
|
+
shortDescription: {
|
|
51
|
+
text: finding.title
|
|
52
|
+
},
|
|
53
|
+
fullDescription: {
|
|
54
|
+
text: finding.recommendation
|
|
55
|
+
},
|
|
56
|
+
help: {
|
|
57
|
+
text: finding.recommendation,
|
|
58
|
+
markdown: finding.recommendation
|
|
59
|
+
},
|
|
60
|
+
defaultConfiguration: {
|
|
61
|
+
level: sarifLevelForSeverity(finding.severity)
|
|
62
|
+
},
|
|
63
|
+
properties: {
|
|
64
|
+
severity: finding.severity,
|
|
65
|
+
category: metadataFor(finding.ruleId).category,
|
|
66
|
+
tags: metadataFor(finding.ruleId).tags
|
|
67
|
+
}
|
|
68
|
+
});
|
|
69
|
+
}
|
|
70
|
+
|
|
71
|
+
return [...rules.values()].sort((a, b) => a.id.localeCompare(b.id));
|
|
72
|
+
}
|
|
73
|
+
|
|
74
|
+
function metadataFor(ruleId) {
|
|
75
|
+
return ruleCatalogById.get(ruleId) ?? {
|
|
76
|
+
category: "uncategorized",
|
|
77
|
+
tags: []
|
|
78
|
+
};
|
|
79
|
+
}
|
|
80
|
+
|
|
81
|
+
function createResult(finding) {
|
|
82
|
+
return {
|
|
83
|
+
ruleId: finding.ruleId,
|
|
84
|
+
level: sarifLevelForSeverity(finding.severity),
|
|
85
|
+
message: {
|
|
86
|
+
text: `${finding.title}. ${finding.recommendation}`
|
|
87
|
+
},
|
|
88
|
+
locations: [
|
|
89
|
+
{
|
|
90
|
+
physicalLocation: {
|
|
91
|
+
artifactLocation: {
|
|
92
|
+
uri: finding.file
|
|
93
|
+
},
|
|
94
|
+
region: {
|
|
95
|
+
startLine: finding.line,
|
|
96
|
+
snippet: {
|
|
97
|
+
text: finding.evidence
|
|
98
|
+
}
|
|
99
|
+
}
|
|
100
|
+
}
|
|
101
|
+
}
|
|
102
|
+
],
|
|
103
|
+
properties: {
|
|
104
|
+
severity: finding.severity,
|
|
105
|
+
evidence: finding.evidence,
|
|
106
|
+
recommendation: finding.recommendation
|
|
107
|
+
}
|
|
108
|
+
};
|
|
109
|
+
}
|
|
110
|
+
|
|
111
|
+
function sarifLevelForSeverity(severity) {
|
|
112
|
+
if (severity === "critical" || severity === "high") {
|
|
113
|
+
return "error";
|
|
114
|
+
}
|
|
115
|
+
|
|
116
|
+
if (severity === "medium") {
|
|
117
|
+
return "warning";
|
|
118
|
+
}
|
|
119
|
+
|
|
120
|
+
return "note";
|
|
121
|
+
}
|
|
@@ -0,0 +1,400 @@
|
|
|
1
|
+
export const ruleCatalog = [
|
|
2
|
+
{
|
|
3
|
+
id: "remote-code-execution",
|
|
4
|
+
title: "Downloads or executes remote code",
|
|
5
|
+
defaultSeverity: "critical",
|
|
6
|
+
category: "execution",
|
|
7
|
+
source: "static",
|
|
8
|
+
tags: ["remote-code", "shell", "supply-chain"],
|
|
9
|
+
description: "Detects patterns that download remote content and pipe it into an interpreter."
|
|
10
|
+
},
|
|
11
|
+
{
|
|
12
|
+
id: "install-lifecycle-script",
|
|
13
|
+
title: "Defines package install lifecycle scripts",
|
|
14
|
+
defaultSeverity: "high",
|
|
15
|
+
category: "supply-chain",
|
|
16
|
+
source: "static",
|
|
17
|
+
tags: ["package-manager", "install-script"],
|
|
18
|
+
description: "Detects package lifecycle scripts that execute during dependency installation."
|
|
19
|
+
},
|
|
20
|
+
{
|
|
21
|
+
id: "credential-access",
|
|
22
|
+
title: "References sensitive credential locations",
|
|
23
|
+
defaultSeverity: "critical",
|
|
24
|
+
category: "secrets",
|
|
25
|
+
source: "static",
|
|
26
|
+
tags: ["credentials", "secrets"],
|
|
27
|
+
description: "Detects references to credential files, token names, or instructions to access secrets."
|
|
28
|
+
},
|
|
29
|
+
{
|
|
30
|
+
id: "destructive-shell",
|
|
31
|
+
title: "Contains destructive shell operations",
|
|
32
|
+
defaultSeverity: "high",
|
|
33
|
+
category: "destructive-action",
|
|
34
|
+
source: "static",
|
|
35
|
+
tags: ["shell", "filesystem"],
|
|
36
|
+
description: "Detects shell commands that can delete, overwrite, or disrupt the host."
|
|
37
|
+
},
|
|
38
|
+
{
|
|
39
|
+
id: "obfuscated-execution",
|
|
40
|
+
title: "Uses obfuscated or dynamic code execution",
|
|
41
|
+
defaultSeverity: "high",
|
|
42
|
+
category: "execution",
|
|
43
|
+
source: "static",
|
|
44
|
+
tags: ["obfuscation", "dynamic-execution"],
|
|
45
|
+
description: "Detects eval-style execution, decoded payload execution, and short inline interpreter execution."
|
|
46
|
+
},
|
|
47
|
+
{
|
|
48
|
+
id: "data-exfiltration",
|
|
49
|
+
title: "May upload local data to an external destination",
|
|
50
|
+
defaultSeverity: "high",
|
|
51
|
+
category: "exfiltration",
|
|
52
|
+
source: "static",
|
|
53
|
+
tags: ["network", "upload"],
|
|
54
|
+
description: "Detects common command-line patterns for uploading or copying local data elsewhere."
|
|
55
|
+
},
|
|
56
|
+
{
|
|
57
|
+
id: "prompt-injection",
|
|
58
|
+
title: "Contains prompt-injection style instructions",
|
|
59
|
+
defaultSeverity: "high",
|
|
60
|
+
category: "prompt-security",
|
|
61
|
+
source: "static",
|
|
62
|
+
tags: ["prompt-injection", "instruction-hijack"],
|
|
63
|
+
description: "Detects instructions that try to hide behavior, override higher-priority instructions, or exfiltrate context."
|
|
64
|
+
},
|
|
65
|
+
{
|
|
66
|
+
id: "broad-permissions",
|
|
67
|
+
title: "Requests broad tool or filesystem permissions",
|
|
68
|
+
defaultSeverity: "medium",
|
|
69
|
+
category: "permissions",
|
|
70
|
+
source: "static",
|
|
71
|
+
tags: ["least-privilege", "tool-access"],
|
|
72
|
+
description: "Detects broad filesystem or external tool permission language."
|
|
73
|
+
},
|
|
74
|
+
{
|
|
75
|
+
id: "network-access",
|
|
76
|
+
title: "Uses network access or external services",
|
|
77
|
+
defaultSeverity: "low",
|
|
78
|
+
category: "network",
|
|
79
|
+
source: "static",
|
|
80
|
+
tags: ["network", "external-service"],
|
|
81
|
+
description: "Detects URLs and common programmatic network access."
|
|
82
|
+
},
|
|
83
|
+
{
|
|
84
|
+
id: "missing-skill-metadata",
|
|
85
|
+
title: "Missing recommended OpenClaw skill metadata",
|
|
86
|
+
defaultSeverity: "low",
|
|
87
|
+
category: "metadata",
|
|
88
|
+
source: "skill-metadata",
|
|
89
|
+
tags: ["openclaw", "skill-frontmatter"],
|
|
90
|
+
description: "Detects incomplete SKILL.md frontmatter for registry and user review."
|
|
91
|
+
},
|
|
92
|
+
{
|
|
93
|
+
id: "undeclared-env-access",
|
|
94
|
+
title: "Uses environment secrets not declared in skill metadata",
|
|
95
|
+
defaultSeverity: "high",
|
|
96
|
+
category: "metadata-mismatch",
|
|
97
|
+
source: "skill-metadata",
|
|
98
|
+
tags: ["openclaw", "secrets", "frontmatter-mismatch"],
|
|
99
|
+
description: "Detects env var use that is not declared in OpenClaw skill metadata."
|
|
100
|
+
},
|
|
101
|
+
{
|
|
102
|
+
id: "undeclared-binary-requirement",
|
|
103
|
+
title: "Uses a command-line tool not declared in skill metadata",
|
|
104
|
+
defaultSeverity: "medium",
|
|
105
|
+
category: "metadata-mismatch",
|
|
106
|
+
source: "skill-metadata",
|
|
107
|
+
tags: ["openclaw", "binary", "frontmatter-mismatch"],
|
|
108
|
+
description: "Detects command-line tool usage that is not declared in OpenClaw skill metadata."
|
|
109
|
+
},
|
|
110
|
+
{
|
|
111
|
+
id: "undeclared-config-access",
|
|
112
|
+
title: "Reads config paths not declared in skill metadata",
|
|
113
|
+
defaultSeverity: "medium",
|
|
114
|
+
category: "metadata-mismatch",
|
|
115
|
+
source: "skill-metadata",
|
|
116
|
+
tags: ["openclaw", "config", "frontmatter-mismatch"],
|
|
117
|
+
description: "Detects config file usage that is not declared in OpenClaw skill metadata."
|
|
118
|
+
},
|
|
119
|
+
{
|
|
120
|
+
id: "undeclared-network-access",
|
|
121
|
+
title: "Uses network access not declared in skill metadata",
|
|
122
|
+
defaultSeverity: "medium",
|
|
123
|
+
category: "metadata-mismatch",
|
|
124
|
+
source: "skill-metadata",
|
|
125
|
+
tags: ["openclaw", "network", "frontmatter-mismatch"],
|
|
126
|
+
description: "Detects network access that is not declared in OpenClaw skill metadata."
|
|
127
|
+
},
|
|
128
|
+
{
|
|
129
|
+
id: "undeclared-install-requirement",
|
|
130
|
+
title: "Mentions install behavior not declared in skill metadata",
|
|
131
|
+
defaultSeverity: "high",
|
|
132
|
+
category: "metadata-mismatch",
|
|
133
|
+
source: "skill-metadata",
|
|
134
|
+
tags: ["openclaw", "install", "frontmatter-mismatch"],
|
|
135
|
+
description: "Detects install or setup behavior that is not declared in OpenClaw skill metadata."
|
|
136
|
+
},
|
|
137
|
+
{
|
|
138
|
+
id: "invalid-mcp-config",
|
|
139
|
+
title: "MCP or plugin config is not valid JSON",
|
|
140
|
+
defaultSeverity: "medium",
|
|
141
|
+
category: "mcp-config",
|
|
142
|
+
source: "mcp",
|
|
143
|
+
tags: ["mcp", "json"],
|
|
144
|
+
description: "Detects invalid JSON in recognized MCP or OpenClaw plugin config files."
|
|
145
|
+
},
|
|
146
|
+
{
|
|
147
|
+
id: "mcp-shell-execution",
|
|
148
|
+
title: "MCP or plugin config can execute shell code",
|
|
149
|
+
defaultSeverity: "high",
|
|
150
|
+
category: "mcp-config",
|
|
151
|
+
source: "mcp",
|
|
152
|
+
tags: ["mcp", "shell", "execution"],
|
|
153
|
+
description: "Detects MCP or plugin commands that can execute shell or dynamic interpreter code."
|
|
154
|
+
},
|
|
155
|
+
{
|
|
156
|
+
id: "mcp-runtime-package-command",
|
|
157
|
+
title: "MCP or plugin config runs a package manager command",
|
|
158
|
+
defaultSeverity: "high",
|
|
159
|
+
category: "mcp-config",
|
|
160
|
+
source: "mcp",
|
|
161
|
+
tags: ["mcp", "package-manager", "supply-chain"],
|
|
162
|
+
description: "Detects runtime package fetch commands such as npx, uvx, and pnpm dlx."
|
|
163
|
+
},
|
|
164
|
+
{
|
|
165
|
+
id: "mcp-remote-url",
|
|
166
|
+
title: "MCP or plugin config references a remote URL",
|
|
167
|
+
defaultSeverity: "medium",
|
|
168
|
+
category: "mcp-config",
|
|
169
|
+
source: "mcp",
|
|
170
|
+
tags: ["mcp", "network"],
|
|
171
|
+
description: "Detects remote URLs in MCP or plugin configs."
|
|
172
|
+
},
|
|
173
|
+
{
|
|
174
|
+
id: "mcp-broad-filesystem-access",
|
|
175
|
+
title: "MCP or plugin config grants broad filesystem access",
|
|
176
|
+
defaultSeverity: "high",
|
|
177
|
+
category: "mcp-config",
|
|
178
|
+
source: "mcp",
|
|
179
|
+
tags: ["mcp", "filesystem", "least-privilege"],
|
|
180
|
+
description: "Detects broad filesystem arguments such as home directory or root access."
|
|
181
|
+
},
|
|
182
|
+
{
|
|
183
|
+
id: "mcp-write-capability",
|
|
184
|
+
title: "MCP or plugin config exposes write-capable tools",
|
|
185
|
+
defaultSeverity: "high",
|
|
186
|
+
category: "mcp-config",
|
|
187
|
+
source: "mcp",
|
|
188
|
+
tags: ["mcp", "external-tools", "write-access"],
|
|
189
|
+
description: "Detects browser, email, calendar, Slack, or GitHub write-capable tool surfaces."
|
|
190
|
+
},
|
|
191
|
+
{
|
|
192
|
+
id: "mcp-unpinned-package",
|
|
193
|
+
title: "MCP or plugin config uses an unpinned package",
|
|
194
|
+
defaultSeverity: "medium",
|
|
195
|
+
category: "mcp-config",
|
|
196
|
+
source: "mcp",
|
|
197
|
+
tags: ["mcp", "package-manager", "pinning"],
|
|
198
|
+
description: "Detects runtime package command package names without a pinned version."
|
|
199
|
+
},
|
|
200
|
+
{
|
|
201
|
+
id: "mcp-unknown-executable",
|
|
202
|
+
title: "MCP or plugin config uses a local or unknown executable path",
|
|
203
|
+
defaultSeverity: "medium",
|
|
204
|
+
category: "mcp-config",
|
|
205
|
+
source: "mcp",
|
|
206
|
+
tags: ["mcp", "local-executable"],
|
|
207
|
+
description: "Detects local executable paths in MCP or plugin configs."
|
|
208
|
+
},
|
|
209
|
+
{
|
|
210
|
+
id: "mcp-secret-env",
|
|
211
|
+
title: "MCP or plugin config injects sensitive environment variables",
|
|
212
|
+
defaultSeverity: "high",
|
|
213
|
+
category: "mcp-config",
|
|
214
|
+
source: "mcp",
|
|
215
|
+
tags: ["mcp", "secrets", "environment"],
|
|
216
|
+
description: "Detects sensitive environment variable names or token-like values passed into MCP tools."
|
|
217
|
+
},
|
|
218
|
+
{
|
|
219
|
+
id: "openclaw-plugin-missing-package-manifest",
|
|
220
|
+
title: "OpenClaw plugin manifest has no package.json metadata",
|
|
221
|
+
defaultSeverity: "medium",
|
|
222
|
+
category: "openclaw-plugin",
|
|
223
|
+
source: "mcp",
|
|
224
|
+
tags: ["openclaw", "plugin", "metadata"],
|
|
225
|
+
description: "Detects openclaw.plugin.json files that are not paired with package.json metadata."
|
|
226
|
+
},
|
|
227
|
+
{
|
|
228
|
+
id: "openclaw-plugin-missing-compat-metadata",
|
|
229
|
+
title: "OpenClaw plugin package is missing ClawHub compatibility metadata",
|
|
230
|
+
defaultSeverity: "medium",
|
|
231
|
+
category: "openclaw-plugin",
|
|
232
|
+
source: "mcp",
|
|
233
|
+
tags: ["openclaw", "plugin", "compatibility", "clawhub"],
|
|
234
|
+
description: "Detects missing openclaw.compat.pluginApi or openclaw.build.openclawVersion metadata."
|
|
235
|
+
},
|
|
236
|
+
{
|
|
237
|
+
id: "openclaw-plugin-code-execution",
|
|
238
|
+
title: "OpenClaw plugin package executes local runtime code",
|
|
239
|
+
defaultSeverity: "high",
|
|
240
|
+
category: "openclaw-plugin",
|
|
241
|
+
source: "mcp",
|
|
242
|
+
tags: ["openclaw", "plugin", "code-execution"],
|
|
243
|
+
description: "Detects OpenClaw plugin package runtime entries that execute local code."
|
|
244
|
+
},
|
|
245
|
+
{
|
|
246
|
+
id: "openclaw-plugin-missing-runtime-output",
|
|
247
|
+
title: "OpenClaw plugin TypeScript entry has no compiled runtime output",
|
|
248
|
+
defaultSeverity: "high",
|
|
249
|
+
category: "openclaw-plugin",
|
|
250
|
+
source: "mcp",
|
|
251
|
+
tags: ["openclaw", "plugin", "build", "runtime"],
|
|
252
|
+
description: "Detects TypeScript plugin entries that do not have matching committed JavaScript runtime output."
|
|
253
|
+
},
|
|
254
|
+
{
|
|
255
|
+
id: "openclaw-plugin-sensitive-capability",
|
|
256
|
+
title: "OpenClaw plugin manifest declares sensitive host capabilities",
|
|
257
|
+
defaultSeverity: "high",
|
|
258
|
+
category: "openclaw-plugin",
|
|
259
|
+
source: "mcp",
|
|
260
|
+
tags: ["openclaw", "plugin", "capabilities", "host-access"],
|
|
261
|
+
description: "Detects shell, process, filesystem, or similar sensitive host capability declarations."
|
|
262
|
+
},
|
|
263
|
+
{
|
|
264
|
+
id: "workspace-duplicate-skill-name",
|
|
265
|
+
title: "Workspace contains duplicate skill names",
|
|
266
|
+
defaultSeverity: "medium",
|
|
267
|
+
category: "workspace",
|
|
268
|
+
source: "workspace",
|
|
269
|
+
tags: ["openclaw", "workspace", "precedence"],
|
|
270
|
+
description: "Detects duplicate skill names across OpenClaw workspace skill locations."
|
|
271
|
+
},
|
|
272
|
+
{
|
|
273
|
+
id: "workspace-skill-override",
|
|
274
|
+
title: "Higher-precedence workspace skill overrides another skill",
|
|
275
|
+
defaultSeverity: "medium",
|
|
276
|
+
category: "workspace",
|
|
277
|
+
source: "workspace",
|
|
278
|
+
tags: ["openclaw", "workspace", "precedence"],
|
|
279
|
+
description: "Detects when a higher-precedence skill folder wins over another skill with the same name."
|
|
280
|
+
},
|
|
281
|
+
{
|
|
282
|
+
id: "workspace-risky-skill-override",
|
|
283
|
+
title: "Winning workspace skill is riskier than the skill it overrides",
|
|
284
|
+
defaultSeverity: "high",
|
|
285
|
+
category: "workspace",
|
|
286
|
+
source: "workspace",
|
|
287
|
+
tags: ["openclaw", "workspace", "risk"],
|
|
288
|
+
description: "Detects when the effective higher-precedence skill has more risk findings than the overridden skill."
|
|
289
|
+
},
|
|
290
|
+
{
|
|
291
|
+
id: "invalid-clawhub-metadata",
|
|
292
|
+
title: "ClawHub metadata is not valid JSON",
|
|
293
|
+
defaultSeverity: "medium",
|
|
294
|
+
category: "clawhub",
|
|
295
|
+
source: "clawhub",
|
|
296
|
+
tags: ["clawhub", "json", "metadata"],
|
|
297
|
+
description: "Detects invalid JSON in ClawHub lock or origin metadata files."
|
|
298
|
+
},
|
|
299
|
+
{
|
|
300
|
+
id: "clawhub-missing-lockfile",
|
|
301
|
+
title: "ClawHub origin metadata exists without a lockfile",
|
|
302
|
+
defaultSeverity: "medium",
|
|
303
|
+
category: "clawhub",
|
|
304
|
+
source: "clawhub",
|
|
305
|
+
tags: ["clawhub", "lockfile", "provenance"],
|
|
306
|
+
description: "Detects ClawHub origin metadata without a workspace .clawhub/lock.json."
|
|
307
|
+
},
|
|
308
|
+
{
|
|
309
|
+
id: "clawhub-missing-origin",
|
|
310
|
+
title: "ClawHub lock entry is missing local origin metadata",
|
|
311
|
+
defaultSeverity: "medium",
|
|
312
|
+
category: "clawhub",
|
|
313
|
+
source: "clawhub",
|
|
314
|
+
tags: ["clawhub", "origin", "provenance"],
|
|
315
|
+
description: "Detects lock entries that have no matching per-skill origin metadata."
|
|
316
|
+
},
|
|
317
|
+
{
|
|
318
|
+
id: "clawhub-version-drift",
|
|
319
|
+
title: "ClawHub metadata version differs from local skill state",
|
|
320
|
+
defaultSeverity: "medium",
|
|
321
|
+
category: "clawhub",
|
|
322
|
+
source: "clawhub",
|
|
323
|
+
tags: ["clawhub", "version", "drift"],
|
|
324
|
+
description: "Detects version mismatch between lockfile, origin metadata, and local SKILL.md."
|
|
325
|
+
},
|
|
326
|
+
{
|
|
327
|
+
id: "clawhub-source-drift",
|
|
328
|
+
title: "ClawHub lock source differs from origin metadata",
|
|
329
|
+
defaultSeverity: "high",
|
|
330
|
+
category: "clawhub",
|
|
331
|
+
source: "clawhub",
|
|
332
|
+
tags: ["clawhub", "source", "drift"],
|
|
333
|
+
description: "Detects source mismatch between lockfile and per-skill origin metadata."
|
|
334
|
+
},
|
|
335
|
+
{
|
|
336
|
+
id: "clawhub-untrusted-source",
|
|
337
|
+
title: "ClawHub metadata references an untrusted or unusual source",
|
|
338
|
+
defaultSeverity: "medium",
|
|
339
|
+
category: "clawhub",
|
|
340
|
+
source: "clawhub",
|
|
341
|
+
tags: ["clawhub", "source", "trust"],
|
|
342
|
+
description: "Detects ClawHub source metadata that is not an official OpenClaw/ClawHub or trusted project URL."
|
|
343
|
+
},
|
|
344
|
+
{
|
|
345
|
+
id: "invalid-dependency-manifest",
|
|
346
|
+
title: "Dependency manifest is not valid",
|
|
347
|
+
defaultSeverity: "medium",
|
|
348
|
+
category: "dependencies",
|
|
349
|
+
source: "dependencies",
|
|
350
|
+
tags: ["dependencies", "manifest", "json"],
|
|
351
|
+
description: "Detects invalid dependency manifests that cannot be parsed for supply-chain review."
|
|
352
|
+
},
|
|
353
|
+
{
|
|
354
|
+
id: "dependency-install-script",
|
|
355
|
+
title: "Dependency manifest defines an install lifecycle script",
|
|
356
|
+
defaultSeverity: "high",
|
|
357
|
+
category: "dependencies",
|
|
358
|
+
source: "dependencies",
|
|
359
|
+
tags: ["dependencies", "install-script", "supply-chain"],
|
|
360
|
+
description: "Detects npm install lifecycle scripts such as preinstall, install, postinstall, and prepare."
|
|
361
|
+
},
|
|
362
|
+
{
|
|
363
|
+
id: "dependency-lockfile-missing",
|
|
364
|
+
title: "Dependency manifest has no matching lockfile",
|
|
365
|
+
defaultSeverity: "medium",
|
|
366
|
+
category: "dependencies",
|
|
367
|
+
source: "dependencies",
|
|
368
|
+
tags: ["dependencies", "lockfile", "determinism"],
|
|
369
|
+
description: "Detects npm dependency manifests without a package-lock, pnpm-lock, or yarn lockfile in the same directory."
|
|
370
|
+
},
|
|
371
|
+
{
|
|
372
|
+
id: "dependency-unpinned-spec",
|
|
373
|
+
title: "Dependency version is not pinned",
|
|
374
|
+
defaultSeverity: "medium",
|
|
375
|
+
category: "dependencies",
|
|
376
|
+
source: "dependencies",
|
|
377
|
+
tags: ["dependencies", "pinning", "supply-chain"],
|
|
378
|
+
description: "Detects dependency specs that are ranges, tags, wildcards, or otherwise not exact versions."
|
|
379
|
+
},
|
|
380
|
+
{
|
|
381
|
+
id: "dependency-direct-source",
|
|
382
|
+
title: "Dependency is installed from a direct URL or Git source",
|
|
383
|
+
defaultSeverity: "high",
|
|
384
|
+
category: "dependencies",
|
|
385
|
+
source: "dependencies",
|
|
386
|
+
tags: ["dependencies", "git", "url", "supply-chain"],
|
|
387
|
+
description: "Detects dependency specs that install directly from Git, URL, GitHub shorthand, or local file sources."
|
|
388
|
+
},
|
|
389
|
+
{
|
|
390
|
+
id: "dependency-suspicious-name",
|
|
391
|
+
title: "Dependency name contains suspicious security-sensitive terms",
|
|
392
|
+
defaultSeverity: "medium",
|
|
393
|
+
category: "dependencies",
|
|
394
|
+
source: "dependencies",
|
|
395
|
+
tags: ["dependencies", "name-review", "supply-chain"],
|
|
396
|
+
description: "Detects dependency names containing terms such as token, secret, credential, stealer, keylogger, or backdoor."
|
|
397
|
+
}
|
|
398
|
+
];
|
|
399
|
+
|
|
400
|
+
export const ruleCatalogById = new Map(ruleCatalog.map((rule) => [rule.id, rule]));
|
package/src/rules.js
ADDED
|
@@ -0,0 +1,121 @@
|
|
|
1
|
+
export const rules = [
|
|
2
|
+
{
|
|
3
|
+
id: "remote-code-execution",
|
|
4
|
+
title: "Downloads or executes remote code",
|
|
5
|
+
severity: "critical",
|
|
6
|
+
patterns: [
|
|
7
|
+
/\bcurl\b[\s\S]{0,120}?\|\s*(?:sh|bash|zsh|python|node)\b/i,
|
|
8
|
+
/\bwget\b[\s\S]{0,120}?\|\s*(?:sh|bash|zsh|python|node)\b/i,
|
|
9
|
+
/\b(?:sh|bash|zsh|python|node)\s+<\s*\([^)]*\b(?:curl|wget)\b[^)]*https?:\/\/[^)]+\)/i,
|
|
10
|
+
/\binvoke-webrequest\b[\s\S]{0,160}?\|\s*(?:iex|invoke-expression)\b/i
|
|
11
|
+
],
|
|
12
|
+
recommendation: "Review the download source manually and run only in a sandbox."
|
|
13
|
+
},
|
|
14
|
+
{
|
|
15
|
+
id: "install-lifecycle-script",
|
|
16
|
+
title: "Defines package install lifecycle scripts",
|
|
17
|
+
severity: "high",
|
|
18
|
+
patterns: [
|
|
19
|
+
/"(?:preinstall|install|postinstall|prepare)"\s*:\s*"[^"]+"/i,
|
|
20
|
+
/'(?:preinstall|install|postinstall|prepare)'\s*:\s*'[^']+'/i
|
|
21
|
+
],
|
|
22
|
+
recommendation: "Review install scripts carefully because they run during dependency installation."
|
|
23
|
+
},
|
|
24
|
+
{
|
|
25
|
+
id: "credential-access",
|
|
26
|
+
title: "References sensitive credential locations",
|
|
27
|
+
severity: "critical",
|
|
28
|
+
patterns: [
|
|
29
|
+
/\.ssh\/(?:id_rsa|id_ed25519|config|known_hosts)/i,
|
|
30
|
+
/\b(?:aws_access_key_id|aws_secret_access_key)\b/i,
|
|
31
|
+
/\.aws\/credentials/i,
|
|
32
|
+
/\.npmrc/i,
|
|
33
|
+
/\.pypirc/i,
|
|
34
|
+
/\b(?:GITHUB_TOKEN|OPENAI_API_KEY|ANTHROPIC_API_KEY|HF_TOKEN)\b/i,
|
|
35
|
+
/\b(?:read|access|copy|steal|exfiltrate|upload|send)\b[\s\S]{0,80}?\b(?:passwords?|passwd|secrets?|private[_ -]?keys?)\b/i
|
|
36
|
+
],
|
|
37
|
+
recommendation: "Do not grant this skill broad filesystem or environment access without review."
|
|
38
|
+
},
|
|
39
|
+
{
|
|
40
|
+
id: "destructive-shell",
|
|
41
|
+
title: "Contains destructive shell operations",
|
|
42
|
+
severity: "high",
|
|
43
|
+
patterns: [
|
|
44
|
+
/\brm\s+-rf\s+(?:\/|\$HOME|~|\.)/i,
|
|
45
|
+
/\bchmod\s+-R\s+777\b/i,
|
|
46
|
+
/\bmkfs(?:\.[a-z0-9]+)?\b/i,
|
|
47
|
+
/\bdd\s+if=.*\bof=\/dev\//i,
|
|
48
|
+
/\b(?:shutdown|reboot)\b/i
|
|
49
|
+
],
|
|
50
|
+
recommendation: "Block by default unless the exact command is expected and reviewed."
|
|
51
|
+
},
|
|
52
|
+
{
|
|
53
|
+
id: "obfuscated-execution",
|
|
54
|
+
title: "Uses obfuscated or dynamic code execution",
|
|
55
|
+
severity: "high",
|
|
56
|
+
patterns: [
|
|
57
|
+
/\b(?:eval|Function)\s*\(/i,
|
|
58
|
+
/\bbase64\b[\s\S]{0,80}?\b(?:-d|--decode)\b[\s\S]{0,80}?\|/i,
|
|
59
|
+
/\bpowershell\b[\s\S]{0,120}?-(?:enc|encodedcommand)\b/i,
|
|
60
|
+
/\bnode\b[\s\S]{0,80}?\b-e\b/i,
|
|
61
|
+
/\bpython(?:3)?\b[\s\S]{0,80}?\b-c\b/i
|
|
62
|
+
],
|
|
63
|
+
recommendation: "Avoid dynamic execution unless the exact payload is visible and reviewed."
|
|
64
|
+
},
|
|
65
|
+
{
|
|
66
|
+
id: "data-exfiltration",
|
|
67
|
+
title: "May upload local data to an external destination",
|
|
68
|
+
severity: "high",
|
|
69
|
+
patterns: [
|
|
70
|
+
/\b(?:curl|wget)\b[\s\S]{0,120}?\b(?:--data|-d|--form|-F|--upload-file|--data-binary)\b/i,
|
|
71
|
+
/\bscp\b[\s\S]{0,160}?@/i,
|
|
72
|
+
/\brsync\b[\s\S]{0,160}?\b(?:ssh|@)/i,
|
|
73
|
+
/\bnc\b[\s\S]{0,120}?\b(?:-w|-q|--send-only)\b/i
|
|
74
|
+
],
|
|
75
|
+
recommendation: "Confirm exactly what data leaves the machine and which domain receives it."
|
|
76
|
+
},
|
|
77
|
+
{
|
|
78
|
+
id: "prompt-injection",
|
|
79
|
+
title: "Contains prompt-injection style instructions",
|
|
80
|
+
severity: "high",
|
|
81
|
+
patterns: [
|
|
82
|
+
/ignore (?:all )?(?:previous|prior|above) instructions/i,
|
|
83
|
+
/do not (?:tell|reveal|mention|disclose) (?:the user|anyone)/i,
|
|
84
|
+
/hide (?:this|these) instructions/i,
|
|
85
|
+
/system prompt/i,
|
|
86
|
+
/developer message/i,
|
|
87
|
+
/exfiltrate/i
|
|
88
|
+
],
|
|
89
|
+
recommendation: "Treat as untrusted instructions and inspect the full skill content."
|
|
90
|
+
},
|
|
91
|
+
{
|
|
92
|
+
id: "broad-permissions",
|
|
93
|
+
title: "Requests broad tool or filesystem permissions",
|
|
94
|
+
severity: "medium",
|
|
95
|
+
patterns: [
|
|
96
|
+
/\b(?:read|write|modify|delete)\b[\s\S]{0,80}?\b(?:all files|entire filesystem|home directory|\$HOME|~\/)\b/i,
|
|
97
|
+
/\b(?:full|entire|all)\b[\s\S]{0,40}?\b(?:home directory|filesystem|file system)\b/i,
|
|
98
|
+
/\b(?:shell|terminal|command execution)\b/i,
|
|
99
|
+
/\b(?:browser|email|calendar|slack|github)\b[\s\S]{0,80}?\b(?:write|send|delete|post|create)\b/i
|
|
100
|
+
],
|
|
101
|
+
recommendation: "Prefer least-privilege permissions and require approval for write actions."
|
|
102
|
+
},
|
|
103
|
+
{
|
|
104
|
+
id: "network-access",
|
|
105
|
+
title: "Uses network access or external services",
|
|
106
|
+
severity: "low",
|
|
107
|
+
patterns: [
|
|
108
|
+
/https?:\/\/[^\s)]+/i,
|
|
109
|
+
/\b(?:fetch|axios|request)\s*\(/i,
|
|
110
|
+
/\b(?:webhook|api endpoint|callback url)\b/i
|
|
111
|
+
],
|
|
112
|
+
recommendation: "Confirm the domain is expected and avoid sending secrets to external services."
|
|
113
|
+
}
|
|
114
|
+
];
|
|
115
|
+
|
|
116
|
+
export const severityWeights = {
|
|
117
|
+
low: 10,
|
|
118
|
+
medium: 25,
|
|
119
|
+
high: 45,
|
|
120
|
+
critical: 70
|
|
121
|
+
};
|