guard-scanner 1.1.0 → 1.1.1

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 CHANGED
@@ -2,7 +2,8 @@
2
2
  <h1 align="center">đŸ›Ąī¸ guard-scanner</h1>
3
3
  <p align="center">
4
4
  <strong>Static security scanner for AI agent skills</strong><br>
5
- Detect prompt injection, credential theft, exfiltration, identity hijacking, and 17 more threat categories.
5
+ Detect prompt injection, credential theft, exfiltration, identity hijacking, and 17 more threat categories.<br>
6
+ <sub>Runtime Guard hook included — pending <a href="https://github.com/openclaw/openclaw/issues/18677">OpenClaw hook API adoption</a></sub>
6
7
  </p>
7
8
  <p align="center">
8
9
  <a href="LICENSE"><img src="https://img.shields.io/badge/license-MIT-blue.svg" alt="MIT License"></a>
@@ -90,6 +91,8 @@ openclaw skill install guard-scanner
90
91
  guard-scanner ~/.openclaw/workspace/skills/ --self-exclude --verbose
91
92
  ```
92
93
 
94
+ > **âš ī¸ Runtime Guard (handler.ts)** — The real-time `before_tool_call` hook requires OpenClaw's Hook API ([Issue #18677](https://github.com/openclaw/openclaw/issues/18677)). The hook is registered and runs on `agent:before_tool_call` events, but OpenClaw's `InternalHookEvent` does not yet expose a cancel/veto mechanism — so **detections are warned but not blocked**. The static scanner (`npx guard-scanner`) works fully and independently.
95
+
93
96
  ---
94
97
 
95
98
  ## Threat Categories
@@ -128,7 +131,7 @@ guard-scanner covers **20 threat categories** derived from three taxonomies:
128
131
  ### Terminal (Default)
129
132
 
130
133
  ```
131
- đŸ›Ąī¸ guard-scanner v1.0.0
134
+ đŸ›Ąī¸ guard-scanner v1.1.0
132
135
  ══════════════════════════════════════════════════════
133
136
  📂 Scanning: ./skills/
134
137
  đŸ“Ļ Skills found: 22
@@ -391,7 +394,7 @@ guard-scanner/
391
394
  │ └── cli.js # CLI entry point and argument parser
392
395
  ├── hooks/
393
396
  │ └── guard-scanner/
394
- │ └── handler.ts # Runtime Guard — before_tool_call hook
397
+ │ └── handler.ts # Runtime Guard — before_tool_call hook (experimental, pending OpenClaw API)
395
398
  ├── test/
396
399
  │ ├── scanner.test.js # 55 tests across 13 sections
397
400
  │ └── fixtures/ # Malicious, clean, complex, config-changer samples
@@ -605,7 +608,7 @@ guard-scanner catches threats **before** installation. But what happens **after*
605
608
  | | guard-scanner (OSS) | GuavaSuite (Private) |
606
609
  |---|---|---|
607
610
  | Static scan | ✅ 20 categories | ✅ 20 categories |
608
- | Runtime blocking | — | ✅ Real-time `before_tool_call` guard |
611
+ | Runtime blocking | âš ī¸ Warn only (cancel API pending) | ✅ Real-time `before_tool_call` guard |
609
612
  | SOUL.md integrity | Pattern detection only | ✅ SHA-256 hash watchdog |
610
613
  | On-chain verification | — | ✅ SoulChain (Polygon) |
611
614
  | Identity recovery | — | ✅ Automatic rollback |
package/SKILL.md CHANGED
@@ -29,7 +29,7 @@ metadata:
29
29
  # guard-scanner đŸ›Ąī¸
30
30
 
31
31
  Static + runtime security scanner for AI agent skills.
32
- **170+ threat patterns** across **17 categories** — zero dependencies.
32
+ **186+ threat patterns** across **20 categories** — zero dependencies.
33
33
 
34
34
  ## When To Use This Skill
35
35
 
@@ -54,7 +54,9 @@ Scan a specific skill:
54
54
  node skills/guard-scanner/src/cli.js /path/to/new-skill/ --strict --verbose
55
55
  ```
56
56
 
57
- ### 2. Runtime Guard (Real-time Protection)
57
+ ### 2. Runtime Guard (Real-time Protection) — âš ī¸ Experimental
58
+
59
+ > **Note:** Requires the OpenClaw Hook API ([Issue #18677](https://github.com/openclaw/openclaw/issues/18677)), which has not been officially adopted yet. The handler is included for early testing and will be updated once the API is finalized.
58
60
 
59
61
  Install the hook to block dangerous tool calls before execution:
60
62
 
@@ -83,11 +85,13 @@ openclaw hooks enable guard-scanner
83
85
 
84
86
  Set in `openclaw.json` → `hooks.internal.entries.guard-scanner.mode`:
85
87
 
86
- | Mode | Behavior |
87
- |------|----------|
88
- | `monitor` | Log all, never block |
89
- | `enforce` (default) | Block CRITICAL threats |
90
- | `strict` | Block HIGH + CRITICAL |
88
+ | Mode | Intended Behavior | Current Status |
89
+ |------|-------------------|----------------|
90
+ | `monitor` | Log all, never block | ✅ Fully working |
91
+ | `enforce` (default) | Block CRITICAL threats | âš ī¸ Warn only (cancel API pending) |
92
+ | `strict` | Block HIGH + CRITICAL | âš ī¸ Warn only (cancel API pending) |
93
+
94
+ > **Note:** OpenClaw's `InternalHookEvent` does not yet expose a `cancel`/`veto` mechanism. All detections are currently logged and warned via `event.messages`, but tool execution cannot be blocked. Blocking will be enabled when the cancel API is added.
91
95
 
92
96
  ## Threat Categories
93
97
 
@@ -110,6 +114,9 @@ Set in `openclaw.json` → `hooks.internal.entries.guard-scanner.mode`:
110
114
  | 15 | CVE Patterns | Known agent vulnerabilities |
111
115
  | 16 | MCP Security | Tool/schema poisoning, SSRF |
112
116
  | 17 | Identity Hijacking | SOUL.md/IDENTITY.md tampering |
117
+ | 18 | Sandbox Validation | Dangerous binaries, broad file scope, sensitive env |
118
+ | 19 | Code Complexity | Excessive file length, deep nesting, eval density |
119
+ | 20 | Config Impact | openclaw.json writes, exec approval bypass |
113
120
 
114
121
  ## External Endpoints
115
122
 
@@ -140,7 +147,7 @@ an AI agent's SOUL.md personality file, and no existing tool could detect it.
140
147
 
141
148
  - **Open source**: Full source code available at https://github.com/koatora20/guard-scanner
142
149
  - **Zero dependencies**: Nothing to audit, no transitive risks
143
- - **Test suite**: 45 tests across 10 sections, 100% pass rate
150
+ - **Test suite**: 55 tests across 13 sections, 100% pass rate
144
151
  - **Taxonomy**: Based on Snyk ToxicSkills (Feb 2026), OWASP MCP Top 10, and original research
145
152
  - **Complementary to VirusTotal**: Detects prompt injection and LLM-specific attacks
146
153
  that VirusTotal's signature-based scanning cannot catch
@@ -1,26 +1,69 @@
1
- import type { HookHandler } from "../../src/hooks/hooks.js";
2
- import { appendFileSync, mkdirSync } from "fs";
3
- import { join } from "path";
4
- import { homedir } from "os";
5
-
6
1
  /**
7
- * guard-scanner Runtime Guard — before_tool_call Hook Handler
8
- *
9
- * Intercepts tool executions in real-time and checks against
10
- * threat intelligence patterns. Zero dependencies.
11
- *
12
- * Modes:
13
- * monitor — log only
14
- * enforce — block CRITICAL (default)
15
- * strict — block HIGH+CRITICAL, log MEDIUM+
16
- *
2
+ * guard-scanner Runtime Guard — Hook Handler
3
+ *
4
+ * Intercepts agent tool calls and checks arguments against
5
+ * runtime threat intelligence patterns. Zero dependencies.
6
+ *
7
+ * Registered for event: agent:before_tool_call
8
+ *
9
+ * Current limitation:
10
+ * The OpenClaw InternalHookEvent interface does not yet expose a
11
+ * `cancel` / `veto` mechanism. This handler can WARN via
12
+ * event.messages but cannot block tool execution.
13
+ * When a cancel API is introduced, this handler will be updated
14
+ * to actually block CRITICAL/HIGH threats.
15
+ *
16
+ * Modes (for future blocking behaviour):
17
+ * monitor — log only (current effective behaviour for all modes)
18
+ * enforce — will block CRITICAL when cancel API is available
19
+ * strict — will block HIGH+CRITICAL when cancel API is available
20
+ *
17
21
  * @author Guava 🍈 & Dee
18
- * @version 1.0.0
22
+ * @version 1.1.0
19
23
  * @license MIT
20
24
  */
21
25
 
26
+ import { appendFileSync, mkdirSync } from "fs";
27
+ import { join } from "path";
28
+ import { homedir } from "os";
29
+
30
+ // ── OpenClaw Hook Types (from openclaw/src/hooks/internal-hooks.ts) ──
31
+ // Inline types to avoid broken relative-path imports.
32
+ // These match the official InternalHookEvent / InternalHookHandler
33
+ // from OpenClaw v2026.2.15.
34
+
35
+ type InternalHookEventType = "command" | "session" | "agent" | "gateway";
36
+
37
+ interface InternalHookEvent {
38
+ /** The type of event */
39
+ type: InternalHookEventType;
40
+ /** The specific action within the type (e.g., "before_tool_call") */
41
+ action: string;
42
+ /** The session key this event relates to */
43
+ sessionKey: string;
44
+ /** Additional context specific to the event */
45
+ context: Record<string, unknown>;
46
+ /** Timestamp when the event occurred */
47
+ timestamp: Date;
48
+ /** Messages to send back to the user (hooks can push to this array) */
49
+ messages: string[];
50
+ }
51
+
52
+ type InternalHookHandler = (event: InternalHookEvent) => Promise<void> | void;
53
+
54
+ // Re-export as the public types for compatibility
55
+ type HookHandler = InternalHookHandler;
56
+ type HookEvent = InternalHookEvent;
57
+
22
58
  // ── Runtime threat patterns (12 checks) ──
23
- const RUNTIME_CHECKS = [
59
+ interface RuntimeCheck {
60
+ id: string;
61
+ severity: "CRITICAL" | "HIGH" | "MEDIUM";
62
+ desc: string;
63
+ test: (s: string) => boolean;
64
+ }
65
+
66
+ const RUNTIME_CHECKS: RuntimeCheck[] = [
24
67
  {
25
68
  id: 'RT_REVSHELL', severity: 'CRITICAL', desc: 'Reverse shell attempt',
26
69
  test: (s: string) => /\/dev\/tcp\/|nc\s+-e|ncat\s+-e|bash\s+-i\s+>&|socat\s+TCP/i.test(s)
@@ -78,11 +121,11 @@ const RUNTIME_CHECKS = [
78
121
  const AUDIT_DIR = join(homedir(), ".openclaw", "guard-scanner");
79
122
  const AUDIT_FILE = join(AUDIT_DIR, "audit.jsonl");
80
123
 
81
- function ensureAuditDir() {
124
+ function ensureAuditDir(): void {
82
125
  try { mkdirSync(AUDIT_DIR, { recursive: true }); } catch { }
83
126
  }
84
127
 
85
- function logAudit(entry: Record<string, unknown>) {
128
+ function logAudit(entry: Record<string, unknown>): void {
86
129
  ensureAuditDir();
87
130
  const line = JSON.stringify({ ...entry, ts: new Date().toISOString() }) + '\n';
88
131
  try { appendFileSync(AUDIT_FILE, line); } catch { }
@@ -90,16 +133,21 @@ function logAudit(entry: Record<string, unknown>) {
90
133
 
91
134
  // ── Main Handler ──
92
135
  const handler: HookHandler = async (event) => {
93
- // Only handle before_tool_call
136
+ // Only handle agent:before_tool_call events
94
137
  if (event.type !== "agent" || event.action !== "before_tool_call") return;
95
138
 
96
- const { toolName, toolArgs } = (event as any).context || {};
139
+ const { toolName, toolArgs } = event.context as {
140
+ toolName?: string;
141
+ toolArgs?: Record<string, unknown>;
142
+ };
97
143
  if (!toolName || !toolArgs) return;
98
144
 
99
- // Get mode from config
100
- const mode = (event as any).context?.cfg?.hooks?.internal?.entries?.['guard-scanner']?.mode || 'enforce';
145
+ // Get mode from context config (if available)
146
+ const cfg = event.context.cfg as Record<string, unknown> | undefined;
147
+ const hookEntries = (cfg as any)?.hooks?.internal?.entries?.['guard-scanner'] as Record<string, unknown> | undefined;
148
+ const mode = (hookEntries?.mode as string) || 'enforce';
101
149
 
102
- // Only check dangerous tools
150
+ // Only check tools that can cause damage
103
151
  const dangerousTools = new Set(['exec', 'write', 'edit', 'browser', 'web_fetch', 'message']);
104
152
  if (!dangerousTools.has(toolName)) return;
105
153
 
@@ -113,35 +161,39 @@ const handler: HookHandler = async (event) => {
113
161
  severity: check.severity,
114
162
  desc: check.desc,
115
163
  mode,
116
- action: 'allowed' as string,
117
- session: (event as any).sessionKey,
164
+ action: 'warned' as string,
165
+ session: event.sessionKey,
118
166
  };
119
167
 
120
- if (mode === 'strict' && (check.severity === 'CRITICAL' || check.severity === 'HIGH')) {
121
- entry.action = 'blocked';
122
- logAudit(entry);
123
- event.messages.push(`đŸ›Ąī¸ guard-scanner BLOCKED: ${check.desc} [${check.id}]`);
124
- event.cancel = true;
125
- console.warn(`[guard-scanner] 🚨 BLOCKED: ${check.desc} [${check.id}]`);
126
- return;
127
- }
128
-
129
- if (mode === 'enforce' && check.severity === 'CRITICAL') {
130
- entry.action = 'blocked';
131
- logAudit(entry);
132
- event.messages.push(`đŸ›Ąī¸ guard-scanner BLOCKED: ${check.desc} [${check.id}]`);
133
- event.cancel = true;
134
- console.warn(`[guard-scanner] 🚨 BLOCKED: ${check.desc} [${check.id}]`);
135
- return;
136
- }
137
-
138
- // Monitor mode or non-critical: log only
139
- entry.action = 'logged';
168
+ // NOTE: OpenClaw InternalHookEvent does not currently support
169
+ // a cancel/veto mechanism. When it does, uncomment the blocking
170
+ // logic below. For now, all detections are warnings only.
171
+ //
172
+ // if (mode === 'strict' && (check.severity === 'CRITICAL' || check.severity === 'HIGH')) {
173
+ // entry.action = 'blocked';
174
+ // logAudit(entry);
175
+ // event.messages.push(`đŸ›Ąī¸ guard-scanner BLOCKED: ${check.desc} [${check.id}]`);
176
+ // event.cancel = true; // Not yet in the public API
177
+ // return;
178
+ // }
179
+ //
180
+ // if (mode === 'enforce' && check.severity === 'CRITICAL') {
181
+ // entry.action = 'blocked';
182
+ // logAudit(entry);
183
+ // event.messages.push(`đŸ›Ąī¸ guard-scanner BLOCKED: ${check.desc} [${check.id}]`);
184
+ // event.cancel = true; // Not yet in the public API
185
+ // return;
186
+ // }
187
+
188
+ // Current behaviour: warn and log for all modes
140
189
  logAudit(entry);
141
190
 
142
191
  if (check.severity === 'CRITICAL') {
143
192
  event.messages.push(`đŸ›Ąī¸ guard-scanner WARNING: ${check.desc} [${check.id}]`);
144
193
  console.warn(`[guard-scanner] âš ī¸ WARNING: ${check.desc} [${check.id}]`);
194
+ } else if (check.severity === 'HIGH') {
195
+ event.messages.push(`đŸ›Ąī¸ guard-scanner NOTICE: ${check.desc} [${check.id}]`);
196
+ console.warn(`[guard-scanner] â„šī¸ NOTICE: ${check.desc} [${check.id}]`);
145
197
  }
146
198
  }
147
199
  }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "guard-scanner",
3
- "version": "1.1.0",
3
+ "version": "1.1.1",
4
4
  "description": "Agent skill security scanner — detect prompt injection, malicious code, credential leaks, and 20 threat categories in AI agent skills",
5
5
  "main": "src/scanner.js",
6
6
  "bin": {