@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.
Files changed (79) hide show
  1. package/README.md +42 -253
  2. package/SECURITY.md +12 -4
  3. package/SKILL.md +121 -59
  4. package/dist/openclaw-plugin.mjs +41 -0
  5. package/docs/EVIDENCE_DRIVEN.md +182 -0
  6. package/docs/banner.png +0 -0
  7. package/docs/data/corpus-metrics.json +11 -0
  8. package/docs/data/latest.json +29845 -0
  9. package/docs/generated/npm-audit-20260312.json +96 -0
  10. package/docs/generated/openclaw-upstream-status.json +25 -0
  11. package/docs/glossary.md +46 -0
  12. package/docs/index.html +1119 -0
  13. package/docs/logo.png +0 -0
  14. package/docs/openclaw-compatibility-audit.md +44 -0
  15. package/docs/openclaw-continuous-compatibility-plan.md +36 -0
  16. package/docs/rules/a2a-contagion.md +68 -0
  17. package/docs/rules/advanced-exfil.md +52 -0
  18. package/docs/rules/agent-protocol.md +108 -0
  19. package/docs/rules/api-abuse.md +68 -0
  20. package/docs/rules/autonomous-risk.md +92 -0
  21. package/docs/rules/config-impact.md +132 -0
  22. package/docs/rules/credential-handling.md +100 -0
  23. package/docs/rules/cve-patterns.md +332 -0
  24. package/docs/rules/data-exposure.md +84 -0
  25. package/docs/rules/exfiltration.md +36 -0
  26. package/docs/rules/financial-access.md +84 -0
  27. package/docs/rules/identity-hijack.md +140 -0
  28. package/docs/rules/inference-manipulation.md +60 -0
  29. package/docs/rules/leaky-skills.md +52 -0
  30. package/docs/rules/malicious-code.md +108 -0
  31. package/docs/rules/mcp-security.md +148 -0
  32. package/docs/rules/memory-poisoning.md +84 -0
  33. package/docs/rules/model-poisoning.md +44 -0
  34. package/docs/rules/obfuscation.md +60 -0
  35. package/docs/rules/persistence.md +108 -0
  36. package/docs/rules/pii-exposure.md +116 -0
  37. package/docs/rules/prompt-injection.md +148 -0
  38. package/docs/rules/prompt-worm.md +44 -0
  39. package/docs/rules/safeguard-bypass.md +44 -0
  40. package/docs/rules/sandbox-escape.md +100 -0
  41. package/docs/rules/secret-detection.md +44 -0
  42. package/docs/rules/supply-chain-v2.md +92 -0
  43. package/docs/rules/suspicious-download.md +60 -0
  44. package/docs/rules/trust-boundary.md +76 -0
  45. package/docs/rules/trust-exploitation.md +92 -0
  46. package/docs/rules/unverifiable-deps.md +84 -0
  47. package/docs/rules/vdb-injection.md +84 -0
  48. package/docs/security-vulnerability-report-20260312.md +53 -0
  49. package/docs/spec/PRD_V2_ARCHITECTURE.md +55 -0
  50. package/docs/spec/capabilities.json +42 -0
  51. package/docs/spec/finding.schema.json +104 -0
  52. package/docs/spec/integration-manifest.md +39 -0
  53. package/docs/spec/sbom.json +33 -0
  54. package/docs/threat-model.md +65 -0
  55. package/docs/v13-architecture-manifest.md +55 -0
  56. package/hooks/context.js +305 -0
  57. package/hooks/guard-scanner/plugin.ts +24 -1
  58. package/openclaw-plugin.mts +91 -0
  59. package/openclaw.plugin.json +30 -53
  60. package/package.json +80 -57
  61. package/src/cli.js +174 -34
  62. package/src/core/content-loader.js +42 -0
  63. package/src/core/inventory.js +73 -0
  64. package/src/core/report-adapters.js +171 -0
  65. package/src/core/risk-engine.js +93 -0
  66. package/src/core/rule-registry.js +73 -0
  67. package/src/core/semantic-validators.js +85 -0
  68. package/src/finding-schema.js +191 -0
  69. package/src/hooks/context.ts +49 -0
  70. package/src/html-template.js +2 -2
  71. package/src/mcp-server.js +192 -5
  72. package/src/openclaw-upstream.js +128 -0
  73. package/src/patterns.js +519 -157
  74. package/src/policy-engine.js +32 -0
  75. package/src/runtime-guard.js +40 -2
  76. package/src/scanner.js +228 -231
  77. package/src/skill-crawler.js +254 -0
  78. package/src/threat-model.js +50 -0
  79. 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 };
@@ -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,