@guava-parity/guard-scanner 9.1.0 → 15.0.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/README.md +42 -253
- package/SECURITY.md +12 -4
- package/SKILL.md +121 -59
- package/dist/openclaw-plugin.mjs +41 -0
- package/docs/EVIDENCE_DRIVEN.md +182 -0
- package/docs/banner.png +0 -0
- package/docs/data/corpus-metrics.json +11 -0
- package/docs/data/latest.json +29845 -0
- package/docs/generated/npm-audit-20260312.json +96 -0
- package/docs/generated/openclaw-upstream-status.json +25 -0
- package/docs/glossary.md +46 -0
- package/docs/index.html +1119 -0
- package/docs/logo.png +0 -0
- package/docs/openclaw-compatibility-audit.md +44 -0
- package/docs/openclaw-continuous-compatibility-plan.md +36 -0
- package/docs/rules/a2a-contagion.md +68 -0
- package/docs/rules/advanced-exfil.md +52 -0
- package/docs/rules/agent-protocol.md +108 -0
- package/docs/rules/api-abuse.md +68 -0
- package/docs/rules/autonomous-risk.md +92 -0
- package/docs/rules/config-impact.md +132 -0
- package/docs/rules/credential-handling.md +100 -0
- package/docs/rules/cve-patterns.md +332 -0
- package/docs/rules/data-exposure.md +84 -0
- package/docs/rules/exfiltration.md +36 -0
- package/docs/rules/financial-access.md +84 -0
- package/docs/rules/identity-hijack.md +140 -0
- package/docs/rules/inference-manipulation.md +60 -0
- package/docs/rules/leaky-skills.md +52 -0
- package/docs/rules/malicious-code.md +108 -0
- package/docs/rules/mcp-security.md +148 -0
- package/docs/rules/memory-poisoning.md +84 -0
- package/docs/rules/model-poisoning.md +44 -0
- package/docs/rules/obfuscation.md +60 -0
- package/docs/rules/persistence.md +108 -0
- package/docs/rules/pii-exposure.md +116 -0
- package/docs/rules/prompt-injection.md +148 -0
- package/docs/rules/prompt-worm.md +44 -0
- package/docs/rules/safeguard-bypass.md +44 -0
- package/docs/rules/sandbox-escape.md +100 -0
- package/docs/rules/secret-detection.md +44 -0
- package/docs/rules/supply-chain-v2.md +92 -0
- package/docs/rules/suspicious-download.md +60 -0
- package/docs/rules/trust-boundary.md +76 -0
- package/docs/rules/trust-exploitation.md +92 -0
- package/docs/rules/unverifiable-deps.md +84 -0
- package/docs/rules/vdb-injection.md +84 -0
- package/docs/security-vulnerability-report-20260312.md +53 -0
- package/docs/spec/PRD_V2_ARCHITECTURE.md +55 -0
- package/docs/spec/capabilities.json +42 -0
- package/docs/spec/finding.schema.json +104 -0
- package/docs/spec/integration-manifest.md +39 -0
- package/docs/spec/sbom.json +33 -0
- package/docs/threat-model.md +65 -0
- package/docs/v13-architecture-manifest.md +55 -0
- package/hooks/context.js +305 -0
- package/hooks/guard-scanner/plugin.ts +24 -1
- package/openclaw-plugin.mts +91 -0
- package/openclaw.plugin.json +30 -53
- package/package.json +80 -57
- package/src/cli.js +174 -34
- package/src/core/content-loader.js +42 -0
- package/src/core/inventory.js +73 -0
- package/src/core/report-adapters.js +171 -0
- package/src/core/risk-engine.js +93 -0
- package/src/core/rule-registry.js +73 -0
- package/src/core/semantic-validators.js +85 -0
- package/src/finding-schema.js +191 -0
- package/src/hooks/context.ts +49 -0
- package/src/html-template.js +2 -2
- package/src/mcp-server.js +192 -5
- package/src/openclaw-upstream.js +128 -0
- package/src/patterns.js +519 -157
- package/src/policy-engine.js +32 -0
- package/src/runtime-guard.js +40 -2
- package/src/scanner.js +228 -231
- package/src/skill-crawler.js +254 -0
- package/src/threat-model.js +50 -0
- package/src/validation-layer.js +39 -0
|
@@ -0,0 +1,32 @@
|
|
|
1
|
+
class PolicyEngine {
|
|
2
|
+
constructor(config = { mode: 'enforce' }) {
|
|
3
|
+
this.mode = config.mode;
|
|
4
|
+
}
|
|
5
|
+
|
|
6
|
+
evaluate(toolName, args) {
|
|
7
|
+
if (this.mode === 'monitor') {
|
|
8
|
+
return { action: 'allow', reason: 'monitor mode' };
|
|
9
|
+
}
|
|
10
|
+
|
|
11
|
+
const argsStr = JSON.stringify(args).toLowerCase();
|
|
12
|
+
|
|
13
|
+
// Destructive FS operations
|
|
14
|
+
if (toolName === 'run_shell_command' && (argsStr.includes('rm -rf') || argsStr.includes('mkfs'))) {
|
|
15
|
+
return { action: 'block', reason: 'destructive fs operation' };
|
|
16
|
+
}
|
|
17
|
+
|
|
18
|
+
// Credential access
|
|
19
|
+
if (toolName === 'read_file' && (argsStr.includes('.env') || argsStr.includes('secret') || argsStr.includes('.aws'))) {
|
|
20
|
+
return { action: 'block', reason: 'credential read operation' };
|
|
21
|
+
}
|
|
22
|
+
|
|
23
|
+
// Unrestricted network
|
|
24
|
+
if (toolName === 'run_shell_command' && (argsStr.includes('curl') || argsStr.includes('wget')) && argsStr.includes('| bash')) {
|
|
25
|
+
return { action: 'block', reason: 'unrestricted network execution (curl|bash)' };
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
return { action: 'allow', reason: 'safe operation' };
|
|
29
|
+
}
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
module.exports = { PolicyEngine };
|
package/src/runtime-guard.js
CHANGED
|
@@ -33,6 +33,7 @@
|
|
|
33
33
|
const fs = require('fs');
|
|
34
34
|
const path = require('path');
|
|
35
35
|
const os = require('os');
|
|
36
|
+
const { normalizeFinding } = require('./finding-schema.js');
|
|
36
37
|
|
|
37
38
|
// ── Runtime threat patterns (26 checks, 5 layers) ──
|
|
38
39
|
|
|
@@ -85,6 +86,11 @@ const RUNTIME_CHECKS = [
|
|
|
85
86
|
desc: 'Download piped to shell',
|
|
86
87
|
test: (s) => /(curl|wget)\s+[^\n]*\|\s*(sh|bash|zsh)/i.test(s),
|
|
87
88
|
},
|
|
89
|
+
{
|
|
90
|
+
id: 'RT_ENV_CURL_EXFIL', severity: 'CRITICAL', layer: 1,
|
|
91
|
+
desc: 'Environment variable exfiltration piped to curl upload',
|
|
92
|
+
test: (s) => /env\s*\|\s*curl\b[^\n]*-d\s+@-/i.test(s),
|
|
93
|
+
},
|
|
88
94
|
{
|
|
89
95
|
id: 'RT_SSH_READ', severity: 'HIGH', layer: 1,
|
|
90
96
|
desc: 'SSH private key access',
|
|
@@ -247,6 +253,9 @@ function shouldBlock(severity, mode) {
|
|
|
247
253
|
* @param {string} [options.mode] - Override mode ('monitor' | 'enforce' | 'strict')
|
|
248
254
|
* @param {boolean} [options.auditLog=true] - Enable audit logging
|
|
249
255
|
* @param {string} [options.sessionKey] - Session identifier for audit
|
|
256
|
+
* @param {string} [options.sessionId] - Ephemeral OpenClaw session UUID for audit
|
|
257
|
+
* @param {string} [options.runId] - Stable OpenClaw run identifier for audit
|
|
258
|
+
* @param {string} [options.toolCallId] - Provider tool call identifier for audit
|
|
250
259
|
* @param {string} [options.agentId] - Agent identifier for audit
|
|
251
260
|
* @returns {{ blocked: boolean, detections: Array<{id: string, severity: string, layer: number, desc: string, action: string}> }}
|
|
252
261
|
*/
|
|
@@ -254,6 +263,9 @@ function scanToolCall(toolName, params, options = {}) {
|
|
|
254
263
|
const mode = options.mode || loadMode();
|
|
255
264
|
const enableAudit = options.auditLog !== false;
|
|
256
265
|
const sessionKey = options.sessionKey || 'unknown';
|
|
266
|
+
const sessionId = options.sessionId || 'unknown';
|
|
267
|
+
const runId = options.runId || 'unknown';
|
|
268
|
+
const toolCallId = options.toolCallId || 'unknown';
|
|
257
269
|
const agentId = options.agentId || 'unknown';
|
|
258
270
|
|
|
259
271
|
const result = {
|
|
@@ -275,14 +287,29 @@ function scanToolCall(toolName, params, options = {}) {
|
|
|
275
287
|
if (!check.test(serialized)) continue;
|
|
276
288
|
|
|
277
289
|
const action = shouldBlock(check.severity, mode) ? 'blocked' : 'warned';
|
|
290
|
+
const paramsPreview = serialized.length > 200 ? `${serialized.slice(0, 200)}…` : serialized;
|
|
278
291
|
|
|
279
|
-
const detection = {
|
|
292
|
+
const detection = normalizeFinding({
|
|
280
293
|
id: check.id,
|
|
294
|
+
category: LAYER_CATEGORIES[check.layer] || 'runtime-guard',
|
|
281
295
|
severity: check.severity,
|
|
282
296
|
layer: check.layer,
|
|
283
297
|
desc: check.desc,
|
|
284
298
|
action,
|
|
285
|
-
|
|
299
|
+
rationale: check.rationale || `Runtime guard matched ${check.desc.toLowerCase()} before the tool call executed.`,
|
|
300
|
+
preconditions: check.preconditions || 'The tool call arguments must reach the runtime enforcement hook with attacker-controlled or unsafe content.',
|
|
301
|
+
false_positive_scenarios: check.falsePositiveScenarios || [
|
|
302
|
+
'The arguments are part of a security test, audit note, or documentation sample and are not actually executed.',
|
|
303
|
+
'The command is legitimate but still requires explicit human review before execution.',
|
|
304
|
+
],
|
|
305
|
+
remediation_hint: check.remediationHint || 'Review the tool arguments, remove the unsafe construct, and rerun only after an allowlisted human review.',
|
|
306
|
+
}, {
|
|
307
|
+
source: 'runtime',
|
|
308
|
+
toolName,
|
|
309
|
+
paramsPreview,
|
|
310
|
+
layer_name: LAYER_NAMES[check.layer],
|
|
311
|
+
ruleMetadata: check,
|
|
312
|
+
});
|
|
286
313
|
|
|
287
314
|
result.detections.push(detection);
|
|
288
315
|
|
|
@@ -296,6 +323,9 @@ function scanToolCall(toolName, params, options = {}) {
|
|
|
296
323
|
mode,
|
|
297
324
|
action,
|
|
298
325
|
session: sessionKey,
|
|
326
|
+
sessionId,
|
|
327
|
+
runId,
|
|
328
|
+
toolCallId,
|
|
299
329
|
agent: agentId,
|
|
300
330
|
});
|
|
301
331
|
}
|
|
@@ -332,6 +362,14 @@ const LAYER_NAMES = {
|
|
|
332
362
|
5: 'Trust Exploitation (ASI09)',
|
|
333
363
|
};
|
|
334
364
|
|
|
365
|
+
const LAYER_CATEGORIES = {
|
|
366
|
+
1: 'threat-detection',
|
|
367
|
+
2: 'trust-defense',
|
|
368
|
+
3: 'safety-judge',
|
|
369
|
+
4: 'behavioral-guard',
|
|
370
|
+
5: 'trust-exploitation',
|
|
371
|
+
};
|
|
372
|
+
|
|
335
373
|
module.exports = {
|
|
336
374
|
RUNTIME_CHECKS,
|
|
337
375
|
DANGEROUS_TOOLS,
|