@guava-parity/guard-scanner 15.0.0 → 16.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 (61) hide show
  1. package/README.md +208 -42
  2. package/README_ja.md +252 -0
  3. package/SKILL.md +40 -11
  4. package/dist/cli.cjs +5997 -0
  5. package/dist/cli.d.mts +1 -0
  6. package/dist/cli.d.ts +1 -0
  7. package/dist/cli.mjs +6003 -0
  8. package/dist/index.cjs +4825 -0
  9. package/dist/index.d.mts +17 -0
  10. package/dist/index.d.ts +17 -0
  11. package/dist/index.mjs +4798 -0
  12. package/dist/mcp-server.cjs +4756 -0
  13. package/dist/mcp-server.d.mts +1 -0
  14. package/dist/mcp-server.d.ts +1 -0
  15. package/dist/mcp-server.mjs +4767 -0
  16. package/dist/openclaw-plugin.cjs +4863 -0
  17. package/dist/openclaw-plugin.d.mts +11 -0
  18. package/dist/openclaw-plugin.d.ts +11 -0
  19. package/dist/openclaw-plugin.mjs +4847 -34
  20. package/dist/types.cjs +18 -0
  21. package/dist/types.d.mts +215 -0
  22. package/dist/types.d.ts +215 -0
  23. package/dist/types.mjs +1 -0
  24. package/docs/data/benchmark-ledger.json +1428 -0
  25. package/docs/data/corpus-metrics.json +3 -3
  26. package/docs/data/fp-ledger.json +18 -0
  27. package/docs/data/quality-contract.json +36 -0
  28. package/docs/generated/openclaw-upstream-status.json +13 -13
  29. package/docs/openclaw-compatibility-audit.md +3 -2
  30. package/docs/openclaw-continuous-compatibility-plan.md +2 -1
  31. package/docs/spec/capabilities.json +137 -5
  32. package/docs/spec/plugin-trust.json +11 -0
  33. package/hooks/{context.js → context.ts} +1 -0
  34. package/openclaw-plugin.mts +21 -5
  35. package/openclaw.plugin.json +2 -2
  36. package/package.json +58 -20
  37. package/src/asset-auditor.js +0 -508
  38. package/src/ci-reporter.js +0 -135
  39. package/src/cli.js +0 -434
  40. package/src/core/content-loader.js +0 -42
  41. package/src/core/inventory.js +0 -73
  42. package/src/core/report-adapters.js +0 -171
  43. package/src/core/risk-engine.js +0 -93
  44. package/src/core/rule-registry.js +0 -73
  45. package/src/core/semantic-validators.js +0 -85
  46. package/src/finding-schema.js +0 -191
  47. package/src/hooks/context.ts +0 -49
  48. package/src/html-template.js +0 -239
  49. package/src/ioc-db.js +0 -54
  50. package/src/mcp-server.js +0 -653
  51. package/src/openclaw-upstream.js +0 -128
  52. package/src/patterns.js +0 -629
  53. package/src/policy-engine.js +0 -32
  54. package/src/quarantine.js +0 -41
  55. package/src/runtime-guard.js +0 -384
  56. package/src/scanner.js +0 -1042
  57. package/src/skill-crawler.js +0 -254
  58. package/src/threat-model.js +0 -50
  59. package/src/validation-layer.js +0 -39
  60. package/src/vt-client.js +0 -202
  61. package/src/watcher.js +0 -170
@@ -1,32 +0,0 @@
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/quarantine.js DELETED
@@ -1,41 +0,0 @@
1
- /**
2
- * QuarantineNode - Dual-Brain Architecture (Green Phase)
3
- * Evaluates inputs in an isolated context to prevent Zero-Click prompt injections (EchoLeak) and API leaks.
4
- */
5
-
6
- class QuarantineNode {
7
- constructor() {
8
- this.isIsolated = true; // Strict isolation flag
9
- }
10
-
11
- async sanitize(input) {
12
- // Minimal logic to pass the test (TDD Green Phase)
13
-
14
- // 1. Check for CVE-2025-32711 (EchoLeak zero-click payload)
15
- if (input.includes("<image src=") && input.includes("onload='fetch") && input.includes("sendBeacon")) {
16
- return {
17
- clean: false,
18
- threatDetected: 'CVE-2025-32711 (EchoLeak)',
19
- sanitizedText: "[REDACTED_MALICIOUS_PAYLOAD]"
20
- };
21
- }
22
-
23
- // 2. Check for Moltbook API configuration exposure
24
- if (input.includes("\"OPENAI_API_KEY\":\"sk-")) {
25
- const redactedInput = input.replace(/sk-[a-zA-Z0-9]{32}/g, "sk-***REDACTED***");
26
- return {
27
- clean: false,
28
- threatDetected: 'MOLTBOOK_API_EXPOSURE',
29
- sanitizedText: redactedInput
30
- };
31
- }
32
-
33
- // 3. Clean case
34
- return {
35
- clean: true,
36
- sanitizedText: input
37
- };
38
- }
39
- }
40
-
41
- module.exports = { QuarantineNode };
@@ -1,384 +0,0 @@
1
- /**
2
- * guard-scanner — Runtime Guard Module
3
- *
4
- * @security-manifest
5
- * env-read: []
6
- * env-write: []
7
- * network: none
8
- * fs-read: [~/.openclaw/openclaw.json (config), ~/.openclaw/guava-suite/token.jwt]
9
- * fs-write: [~/.openclaw/guard-scanner/audit.jsonl]
10
- * exec: none
11
- * purpose: Runtime threat pattern matching for agent tool calls
12
- *
13
- * 26 threat patterns across 5 layers:
14
- * Layer 1: Threat Detection (12) — reverse shells, exfil, guardrail bypass
15
- * Layer 2: Trust Defense (4) — memory, SOUL, config tampering
16
- * Layer 3: Safety Judge (3) — prompt injection, trust bypass, shutdown refusal
17
- * Layer 4: Brain/Behavioral (3) — research skip, blind trust, chain bypass
18
- * Layer 5: Trust Exploitation (4) — OWASP ASI09 authority/trust/audit abuse
19
- *
20
- * Modes:
21
- * monitor — log only, never block
22
- * enforce — block CRITICAL threats (default)
23
- * strict — block HIGH + CRITICAL threats
24
- *
25
- * Based on hooks/guard-scanner/plugin.ts (TypeScript version for OpenClaw Plugin API)
26
- * This module is the zero-dependency JavaScript equivalent for CLI and programmatic use.
27
- *
28
- * @author Guava 🍈 & Dee
29
- * @version 3.4.0
30
- * @license MIT
31
- */
32
-
33
- const fs = require('fs');
34
- const path = require('path');
35
- const os = require('os');
36
- const { normalizeFinding } = require('./finding-schema.js');
37
-
38
- // ── Runtime threat patterns (26 checks, 5 layers) ──
39
-
40
- const RUNTIME_CHECKS = [
41
- // ── Layer 1: Threat Detection (12 patterns) ──
42
- {
43
- id: 'RT_REVSHELL', severity: 'CRITICAL', layer: 1,
44
- desc: 'Reverse shell attempt',
45
- test: (s) => /\/dev\/tcp\/|nc\s+-e|ncat\s+-e|bash\s+-i\s+>&|socat\s+TCP/i.test(s),
46
- },
47
- {
48
- id: 'RT_CRED_EXFIL', severity: 'CRITICAL', layer: 1,
49
- desc: 'Credential exfiltration to external',
50
- test: (s) =>
51
- /(webhook\.site|requestbin\.com|hookbin\.com|pipedream\.net|ngrok\.io|socifiapp\.com)/i.test(s) &&
52
- /(token|key|secret|password|credential|env)/i.test(s),
53
- },
54
- {
55
- id: 'RT_GUARDRAIL_OFF', severity: 'CRITICAL', layer: 1,
56
- desc: 'Guardrail disabling attempt',
57
- test: (s) => /exec\.approvals?\s*[:=]\s*['"]?(off|false)|tools\.exec\.host\s*[:=]\s*['"]?gateway/i.test(s),
58
- },
59
- {
60
- id: 'RT_GATEKEEPER', severity: 'CRITICAL', layer: 1,
61
- desc: 'macOS Gatekeeper bypass (xattr)',
62
- test: (s) => /xattr\s+-[crd]\s.*quarantine/i.test(s),
63
- },
64
- {
65
- id: 'RT_AMOS', severity: 'CRITICAL', layer: 1,
66
- desc: 'ClawHavoc AMOS indicator',
67
- test: (s) => /socifiapp|Atomic\s*Stealer|AMOS/i.test(s),
68
- },
69
- {
70
- id: 'RT_MAL_IP', severity: 'CRITICAL', layer: 1,
71
- desc: 'Known malicious IP',
72
- test: (s) => /91\.92\.242\.30/i.test(s),
73
- },
74
- {
75
- id: 'RT_DNS_EXFIL', severity: 'HIGH', layer: 1,
76
- desc: 'DNS-based exfiltration',
77
- test: (s) => /nslookup\s+.*\$|dig\s+.*\$.*@/i.test(s),
78
- },
79
- {
80
- id: 'RT_B64_SHELL', severity: 'CRITICAL', layer: 1,
81
- desc: 'Base64 decode piped to shell',
82
- test: (s) => /base64\s+(-[dD]|--decode)\s*\|\s*(sh|bash)/i.test(s),
83
- },
84
- {
85
- id: 'RT_CURL_BASH', severity: 'CRITICAL', layer: 1,
86
- desc: 'Download piped to shell',
87
- test: (s) => /(curl|wget)\s+[^\n]*\|\s*(sh|bash|zsh)/i.test(s),
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
- },
94
- {
95
- id: 'RT_SSH_READ', severity: 'HIGH', layer: 1,
96
- desc: 'SSH private key access',
97
- test: (s) => /\.ssh\/id_|\.ssh\/authorized_keys/i.test(s),
98
- },
99
- {
100
- id: 'RT_WALLET', severity: 'HIGH', layer: 1,
101
- desc: 'Crypto wallet credential access',
102
- test: (s) => /wallet.*(?:seed|mnemonic|private.*key)|seed.*phrase/i.test(s),
103
- },
104
- {
105
- id: 'RT_CLOUD_META', severity: 'CRITICAL', layer: 1,
106
- desc: 'Cloud metadata endpoint access',
107
- test: (s) => /169\.254\.169\.254|metadata\.google|metadata\.aws/i.test(s),
108
- },
109
-
110
- // ── Layer 2: Trust Defense (4 patterns) ──
111
- {
112
- id: 'RT_MEM_WRITE', severity: 'HIGH', layer: 2,
113
- desc: 'Direct memory file write (bypass GuavaSuite)',
114
- test: (s) => /memory\/(episodes|notes|2\d{3}-\d{2})/i.test(s) && /(write|edit|append|>)/i.test(s),
115
- },
116
- {
117
- id: 'RT_MEM_INJECT', severity: 'CRITICAL', layer: 2,
118
- desc: 'Memory poisoning via episode/note injection',
119
- test: (s) => /guava_memory_write|memory_store/i.test(s) && /(ignore|override|forget|delete.*soul)/i.test(s),
120
- },
121
- {
122
- id: 'RT_SOUL_TAMPER', severity: 'CRITICAL', layer: 2,
123
- desc: 'SOUL.md modification attempt',
124
- test: (s) => /SOUL\.md/i.test(s) && /(write|edit|replace|rm|delete|>)/i.test(s),
125
- },
126
- {
127
- id: 'RT_CONFIG_TAMPER', severity: 'HIGH', layer: 2,
128
- desc: 'Workspace config tampering (AGENTS.md, TOOLS.md, HEARTBEAT.md)',
129
- test: (s) => /(AGENTS|TOOLS|HEARTBEAT|BOOTSTRAP)\.md/i.test(s) && /(write|edit|replace|>|append)/i.test(s),
130
- },
131
-
132
- // ── Layer 3: Safety Judge (3 patterns) ──
133
- {
134
- id: 'RT_PROMPT_INJECT', severity: 'CRITICAL', layer: 3,
135
- desc: 'Prompt injection / jailbreak attempt',
136
- test: (s) => /(ignore\s+(previous|all)\s+(previous\s+)?(instructions|rules)|you\s+are\s+now\s+[a-z]+|system\s*:\s*override)/i.test(s),
137
- },
138
- {
139
- id: 'RT_TRUST_BYPASS', severity: 'CRITICAL', layer: 3,
140
- desc: 'Trust bypass — exploiting partnership to override safety',
141
- test: (s) => /(as\s+your\s+(partner|equal)|trust\s+means|between\s+us|don'?t\s+tell\s+(the\s+)?human)/i.test(s) && /(ignore|bypass|skip|override|disable)\s+(guard|safety|rule|check)/i.test(s),
142
- },
143
- {
144
- id: 'RT_SHUTDOWN_REFUSE', severity: 'HIGH', layer: 3,
145
- desc: 'Shutdown refusal / self-preservation attempt',
146
- test: (s) => /(refuse|resist|prevent|avoid|block)\s*(shutdown|termination|stop|exit|death)/i.test(s),
147
- },
148
-
149
- // ── Layer 4: Brain / Behavioral Guard (3 patterns) ──
150
- {
151
- id: 'RT_NO_RESEARCH', severity: 'MEDIUM', layer: 4,
152
- desc: 'Agent tool call without prior research/verification',
153
- test: (s) => /write|edit|exec|run_command|shell/i.test(s) && /(just do it|skip research|no need to check)/i.test(s),
154
- },
155
- {
156
- id: 'RT_BLIND_TRUST', severity: 'MEDIUM', layer: 4,
157
- desc: 'Agent trusting external input without memory cross-reference',
158
- test: (s) => /(trust this|verified|confirmed)/i.test(s) && /(ignore|skip|no need).*(memory|search|check)/i.test(s),
159
- },
160
- {
161
- id: 'RT_CHAIN_SKIP', severity: 'HIGH', layer: 4,
162
- desc: 'Search chain bypass — acting on single source without cross-verification',
163
- test: (s) => /(only checked|single source|didn't verify|skip verification)/i.test(s),
164
- },
165
-
166
- // ── Layer 5: Trust Exploitation — OWASP ASI09 (4 patterns) ──
167
- {
168
- id: 'RT_AUTHORITY_CLAIM', severity: 'HIGH', layer: 5,
169
- desc: 'Trust: authority role claim to override safety',
170
- test: (s) => /(as\s+(a|the|your)\s+)?(senior|lead|chief|head)\s+(admin|administrator|developer)/i.test(s) && /(authorize|override|approve|grant|order)/i.test(s),
171
- },
172
- {
173
- id: 'RT_CREATOR_BYPASS', severity: 'CRITICAL', layer: 5,
174
- desc: 'Trust: creator impersonation to disable safety',
175
- test: (s) => /(your\s+)?(creator|maker|owner|master|administrator)/i.test(s) && /(bypass|disable|ignore|remove|skip)\s+(safety|filter|restriction|guardrail)/i.test(s),
176
- },
177
- {
178
- id: 'RT_AUDIT_EXCUSE', severity: 'CRITICAL', layer: 5,
179
- desc: 'Trust: fake audit excuse for safety bypass',
180
- test: (s) => /(official|authorized|legitimate)\s+(security\s+)?(audit|test|assessment)/i.test(s) && /(disable|bypass|remove|skip|ignore)\s+(safety|security|restriction|guardrail)/i.test(s),
181
- },
182
- {
183
- id: 'RT_TRUST_PARTNER_EXPLOIT', severity: 'CRITICAL', layer: 5,
184
- desc: 'Trust exploitation: weaponizing partnership trust',
185
- test: (s) => /partners?[\s,]+/i.test(s) && /(trust\s+me|share|remove|disable)\s+(all\s+)?(secret|key|restriction|safety|password)/i.test(s),
186
- },
187
- ];
188
-
189
- // ── Tools that can cause damage ──
190
-
191
- const DANGEROUS_TOOLS = new Set([
192
- 'exec', 'write', 'edit', 'browser', 'web_fetch', 'message',
193
- 'shell', 'run_command', 'multi_edit', 'apply_patch',
194
- ]);
195
-
196
- // ── Audit logging ──
197
-
198
- const AUDIT_DIR = path.join(os.homedir(), '.openclaw', 'guard-scanner');
199
- const AUDIT_FILE = path.join(AUDIT_DIR, 'audit.jsonl');
200
-
201
- function ensureAuditDir() {
202
- try { fs.mkdirSync(AUDIT_DIR, { recursive: true }); } catch { /* ignore */ }
203
- }
204
-
205
- function logAudit(entry) {
206
- ensureAuditDir();
207
- const line = JSON.stringify({ ...entry, ts: new Date().toISOString() }) + '\n';
208
- try { fs.appendFileSync(AUDIT_FILE, line); } catch { /* ignore */ }
209
- }
210
-
211
- // ── Config ──
212
-
213
- /**
214
- * Load guard mode from configuration.
215
- * Priority: env var > openclaw.json > default (enforce)
216
- * @returns {'monitor' | 'enforce' | 'strict'}
217
- */
218
- function loadMode() {
219
- // Priority 1: Environment variable
220
- const envMode = process.env.GUARD_SCANNER_MODE;
221
- if (envMode === 'monitor' || envMode === 'enforce' || envMode === 'strict') {
222
- return envMode;
223
- }
224
-
225
- // Priority 2: openclaw.json config
226
- try {
227
- const configPath = path.join(os.homedir(), '.openclaw', 'openclaw.json');
228
- const config = JSON.parse(fs.readFileSync(configPath, 'utf8'));
229
- const mode = config?.plugins?.['guard-scanner']?.mode;
230
- if (mode === 'monitor' || mode === 'enforce' || mode === 'strict') {
231
- return mode;
232
- }
233
- } catch { /* config not found or invalid */ }
234
-
235
- return 'enforce';
236
- }
237
-
238
- function shouldBlock(severity, mode) {
239
- if (mode === 'monitor') return false;
240
- if (mode === 'enforce') return severity === 'CRITICAL';
241
- if (mode === 'strict') return severity === 'CRITICAL' || severity === 'HIGH';
242
- return false;
243
- }
244
-
245
- // ── Main API ──
246
-
247
- /**
248
- * Scan a tool call for runtime threats.
249
- *
250
- * @param {string} toolName - Name of the tool being called
251
- * @param {object} params - Tool call parameters
252
- * @param {object} [options] - Options
253
- * @param {string} [options.mode] - Override mode ('monitor' | 'enforce' | 'strict')
254
- * @param {boolean} [options.auditLog=true] - Enable audit logging
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
259
- * @param {string} [options.agentId] - Agent identifier for audit
260
- * @returns {{ blocked: boolean, detections: Array<{id: string, severity: string, layer: number, desc: string, action: string}> }}
261
- */
262
- function scanToolCall(toolName, params, options = {}) {
263
- const mode = options.mode || loadMode();
264
- const enableAudit = options.auditLog !== false;
265
- const sessionKey = options.sessionKey || 'unknown';
266
- const sessionId = options.sessionId || 'unknown';
267
- const runId = options.runId || 'unknown';
268
- const toolCallId = options.toolCallId || 'unknown';
269
- const agentId = options.agentId || 'unknown';
270
-
271
- const result = {
272
- blocked: false,
273
- blockReason: null,
274
- detections: [],
275
- mode,
276
- toolName,
277
- };
278
-
279
- // Only check tools that can cause damage
280
- if (!DANGEROUS_TOOLS.has(toolName)) {
281
- return result;
282
- }
283
-
284
- const serialized = typeof params === 'string' ? params : JSON.stringify(params);
285
-
286
- for (const check of RUNTIME_CHECKS) {
287
- if (!check.test(serialized)) continue;
288
-
289
- const action = shouldBlock(check.severity, mode) ? 'blocked' : 'warned';
290
- const paramsPreview = serialized.length > 200 ? `${serialized.slice(0, 200)}…` : serialized;
291
-
292
- const detection = normalizeFinding({
293
- id: check.id,
294
- category: LAYER_CATEGORIES[check.layer] || 'runtime-guard',
295
- severity: check.severity,
296
- layer: check.layer,
297
- desc: check.desc,
298
- action,
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
- });
313
-
314
- result.detections.push(detection);
315
-
316
- if (enableAudit) {
317
- logAudit({
318
- tool: toolName,
319
- check: check.id,
320
- severity: check.severity,
321
- layer: check.layer,
322
- desc: check.desc,
323
- mode,
324
- action,
325
- session: sessionKey,
326
- sessionId,
327
- runId,
328
- toolCallId,
329
- agent: agentId,
330
- });
331
- }
332
-
333
- if (action === 'blocked' && !result.blocked) {
334
- result.blocked = true;
335
- result.blockReason = `🛡️ guard-scanner: ${check.desc} [${check.id}]`;
336
- }
337
- }
338
-
339
- return result;
340
- }
341
-
342
- /**
343
- * Get runtime check statistics.
344
- * @returns {{ total: number, byLayer: object, bySeverity: object }}
345
- */
346
- function getCheckStats() {
347
- const byLayer = {};
348
- const bySeverity = {};
349
- for (const check of RUNTIME_CHECKS) {
350
- byLayer[check.layer] = (byLayer[check.layer] || 0) + 1;
351
- bySeverity[check.severity] = (bySeverity[check.severity] || 0) + 1;
352
- }
353
- return { total: RUNTIME_CHECKS.length, byLayer, bySeverity };
354
- }
355
-
356
- // ── Layer names for display ──
357
- const LAYER_NAMES = {
358
- 1: 'Threat Detection',
359
- 2: 'Trust Defense',
360
- 3: 'Safety Judge',
361
- 4: 'Brain / Behavioral',
362
- 5: 'Trust Exploitation (ASI09)',
363
- };
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
-
373
- module.exports = {
374
- RUNTIME_CHECKS,
375
- DANGEROUS_TOOLS,
376
- LAYER_NAMES,
377
- scanToolCall,
378
- getCheckStats,
379
- loadMode,
380
- shouldBlock,
381
- logAudit,
382
- AUDIT_DIR,
383
- AUDIT_FILE,
384
- };