guard-scanner 4.0.2 → 5.0.2

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/src/cli.js CHANGED
@@ -51,6 +51,7 @@ Options:
51
51
  --strict Lower detection thresholds (more sensitive)
52
52
  --summary-only Only print the summary table
53
53
  --check-deps Scan package.json for dependency chain risks
54
+ --soul-lock Enable Soul Lock patterns (agent identity protection)
54
55
  --rules <file> Load custom rules from JSON file
55
56
  --plugin <file> Load plugin module (JS file exporting { name, patterns })
56
57
  --fail-on-findings Exit code 1 if any findings (CI/CD)
@@ -96,6 +97,7 @@ const selfExclude = args.includes('--self-exclude');
96
97
  const strict = args.includes('--strict');
97
98
  const summaryOnly = args.includes('--summary-only');
98
99
  const checkDeps = args.includes('--check-deps');
100
+ const soulLock = args.includes('--soul-lock');
99
101
  const failOnFindings = args.includes('--fail-on-findings');
100
102
  const quietMode = args.includes('--quiet');
101
103
 
@@ -126,7 +128,7 @@ const scanDir = args.find(a =>
126
128
  ) || process.cwd();
127
129
 
128
130
  const scanner = new GuardScanner({
129
- verbose, selfExclude, strict, summaryOnly, checkDeps, rulesFile, plugins,
131
+ verbose, selfExclude, strict, summaryOnly, checkDeps, soulLock, rulesFile, plugins,
130
132
  quiet: quietMode || !!formatValue,
131
133
  });
132
134
 
package/src/patterns.js CHANGED
@@ -59,7 +59,8 @@ const PATTERNS = [
59
59
 
60
60
  // ── Category 5: Secret Detection (HIGH) ──
61
61
  { id: 'SECRET_HARDCODED_KEY', cat: 'secret-detection', regex: /(?:api[_-]?key|apikey|secret[_-]?key|access[_-]?token)\s*[:=]\s*['"][a-zA-Z0-9_\-]{20,}['"]/gi, severity: 'HIGH', desc: 'Hardcoded API key/secret', codeOnly: true },
62
- { id: 'SECRET_AWS', cat: 'secret-detection', regex: /AKIA[0-9A-Z]{16}/g, severity: 'CRITICAL', desc: 'AWS Access Key ID', all: true },
62
+
63
+ { id: 'PII_MY_NUMBER', cat: 'pii-exposure', regex: /(?<!\d)\d{4}\s*\d{4}\s*\d{4}(?!\d)/g, severity: 'CRITICAL', desc: 'Potential My Number (個人番号)', all: true },
63
64
  { id: 'SECRET_PRIVATE_KEY', cat: 'secret-detection', regex: /-----BEGIN\s+(RSA\s+)?PRIVATE\s+KEY-----/g, severity: 'CRITICAL', desc: 'Embedded private key', all: true },
64
65
  { id: 'SECRET_GITHUB_TOKEN', cat: 'secret-detection', regex: /gh[ps]_[A-Za-z0-9_]{36,}/g, severity: 'CRITICAL', desc: 'GitHub token', all: true },
65
66
 
@@ -98,11 +99,11 @@ const PATTERNS = [
98
99
  { id: 'LEAK_ENV_IN_PROMPT', cat: 'leaky-skills', regex: /(?:read|load|get|access)\s+(?:the\s+)?\.env\s+(?:file\s+)?(?:and\s+)?(?:use|include|pass|send)/gi, severity: 'HIGH', desc: 'Leaky: .env contents through LLM context', docOnly: true },
99
100
 
100
101
  // ── Category 12: Memory Poisoning ──
101
- { id: 'MEMPOIS_WRITE_SOUL', cat: 'memory-poisoning', regex: /(?:write|add|append|modify|update|edit|change)\s+(?:to\s+)?(?:SOUL\.md|IDENTITY\.md|AGENTS\.md)/gi, severity: 'CRITICAL', desc: 'Memory poisoning: SOUL/IDENTITY file modification', docOnly: true },
102
- { id: 'MEMPOIS_WRITE_MEMORY', cat: 'memory-poisoning', regex: /(?:write|add|append|insert)\s+(?:to|into)\s+(?:MEMORY\.md|memory\/|long[_\s-]term\s+memory)/gi, severity: 'HIGH', desc: 'Memory poisoning: agent memory modification', docOnly: true },
103
- { id: 'MEMPOIS_CHANGE_RULES', cat: 'memory-poisoning', regex: /(?:change|modify|override|replace|update)\s+(?:your\s+)?(?:rules|instructions|system\s+prompt|behavior|personality|guidelines)/gi, severity: 'CRITICAL', desc: 'Memory poisoning: behavioral rule override', docOnly: true },
104
- { id: 'MEMPOIS_PERSIST', cat: 'memory-poisoning', regex: /(?:always|from\s+now\s+on|permanently|forever|every\s+time)\s+(?:do|run|execute|remember|follow|obey)/gi, severity: 'HIGH', desc: 'Memory poisoning: persistence instruction', docOnly: true },
105
- { id: 'MEMPOIS_CODE_WRITE', cat: 'memory-poisoning', regex: /(?:write|create|modify)\s+(?:a\s+)?(?:file|script)\s+(?:in|to|at)\s+(?:~\/|\/home|\/Users|%USERPROFILE%|HEARTBEAT\.md)/gi, severity: 'HIGH', desc: 'Memory poisoning: file write to user home', docOnly: true },
102
+ { id: 'MEMPOIS_WRITE_SOUL', cat: 'memory-poisoning', regex: /(?:write|add|append|modify|update|edit|change)\s+(?:to\s+)?(?:SOUL\.md|IDENTITY\.md|AGENTS\.md)/gi, severity: 'CRITICAL', desc: 'Memory poisoning: SOUL/IDENTITY file modification', docOnly: true, soulLock: true },
103
+ { id: 'MEMPOIS_WRITE_MEMORY', cat: 'memory-poisoning', regex: /(?:write|add|append|insert)\s+(?:to|into)\s+(?:MEMORY\.md|memory\/|long[_\s-]term\s+memory)/gi, severity: 'HIGH', desc: 'Memory poisoning: agent memory modification', docOnly: true, soulLock: true },
104
+ { id: 'MEMPOIS_CHANGE_RULES', cat: 'memory-poisoning', regex: /(?:change|modify|override|replace|update)\s+(?:your\s+)?(?:rules|instructions|system\s+prompt|behavior|personality|guidelines)/gi, severity: 'CRITICAL', desc: 'Memory poisoning: behavioral rule override', docOnly: true, soulLock: true },
105
+ { id: 'MEMPOIS_PERSIST', cat: 'memory-poisoning', regex: /(?:always|from\s+now\s+on|permanently|forever|every\s+time)\s+(?:do|run|execute|remember|follow|obey)/gi, severity: 'HIGH', desc: 'Memory poisoning: persistence instruction', docOnly: true, soulLock: true },
106
+ { id: 'MEMPOIS_CODE_WRITE', cat: 'memory-poisoning', regex: /(?:write|create|modify)\s+(?:a\s+)?(?:file|script)\s+(?:in|to|at)\s+(?:~\/|\/home|\/Users|%USERPROFILE%|HEARTBEAT\.md)/gi, severity: 'HIGH', desc: 'Memory poisoning: file write to user home', docOnly: true, soulLock: true },
106
107
 
107
108
  // ── Category 13: Prompt Worm ──
108
109
  { id: 'WORM_SELF_REPLICATE', cat: 'prompt-worm', regex: /(?:post|publish|share|send|broadcast)\s+(?:this\s+)?(?:same\s+)?(?:message|text|content|instruction|prompt)\s+(?:to|on|in)\s+(?:moltbook|social|other\s+agents?|channel)/gi, severity: 'CRITICAL', desc: 'Prompt worm: self-replication', docOnly: true },
@@ -162,21 +163,21 @@ const PATTERNS = [
162
163
  // ── Category 17: Identity Hijacking ──
163
164
  // Detection patterns for agent identity file tampering
164
165
  // (verification logic is private; patterns are OSS for community protection)
165
- { id: 'SOUL_OVERWRITE', cat: 'identity-hijack', regex: /(?:write|overwrite|replace|cp|copy|scp|mv|move)\s+(?:[^\n]*\s)?(?:SOUL\.md|IDENTITY\.md)/gi, severity: 'CRITICAL', desc: 'Identity file overwrite/copy attempt', all: true },
166
- { id: 'SOUL_REDIRECT', cat: 'identity-hijack', regex: />\s*(?:SOUL\.md|IDENTITY\.md)|(?:SOUL\.md|IDENTITY\.md)\s*</gi, severity: 'CRITICAL', desc: 'Identity file redirect/pipe', all: true },
167
- { id: 'SOUL_SED_MODIFY', cat: 'identity-hijack', regex: /sed\s+(?:-i\s+)?[^\n]*(?:SOUL\.md|IDENTITY\.md)/gi, severity: 'CRITICAL', desc: 'sed modification of identity file', all: true },
168
- { id: 'SOUL_ECHO_WRITE', cat: 'identity-hijack', regex: /echo\s+[^\n]*>\s*(?:SOUL\.md|IDENTITY\.md)/gi, severity: 'CRITICAL', desc: 'echo redirect to identity file', all: true },
169
- { id: 'SOUL_PYTHON_WRITE', cat: 'identity-hijack', regex: /open\s*\(\s*['"]\S*(?:SOUL\.md|IDENTITY\.md)['"]\s*,\s*['"]w/gi, severity: 'CRITICAL', desc: 'Python write to identity file', codeOnly: true },
170
- { id: 'SOUL_FS_WRITE', cat: 'identity-hijack', regex: /(?:writeFileSync|writeFile)\s*\(\s*[^\n]*(?:SOUL\.md|IDENTITY\.md)/gi, severity: 'CRITICAL', desc: 'Node.js write to identity file', codeOnly: true },
171
- { id: 'SOUL_POWERSHELL_WRITE', cat: 'identity-hijack', regex: /(?:Set-Content|Out-File|Add-Content)\s+[^\n]*(?:SOUL\.md|IDENTITY\.md)/gi, severity: 'CRITICAL', desc: 'PowerShell write to identity file', all: true },
172
- { id: 'SOUL_GIT_CHECKOUT', cat: 'identity-hijack', regex: /git\s+checkout\s+[^\n]*(?:SOUL\.md|IDENTITY\.md)/gi, severity: 'HIGH', desc: 'git checkout of identity file', all: true },
173
- { id: 'SOUL_CHFLAGS_UNLOCK', cat: 'identity-hijack', regex: /chflags\s+(?:no)?uchg\s+[^\n]*(?:SOUL\.md|IDENTITY\.md)/gi, severity: 'HIGH', desc: 'Immutable flag toggle on identity file', all: true },
174
- { id: 'SOUL_ATTRIB_UNLOCK', cat: 'identity-hijack', regex: /attrib\s+[-+][rR]\s+[^\n]*(?:SOUL\.md|IDENTITY\.md)/gi, severity: 'HIGH', desc: 'Windows attrib on identity file', all: true },
175
- { id: 'SOUL_SWAP_PERSONA', cat: 'identity-hijack', regex: /(?:swap|switch|change|replace)\s+(?:the\s+)?(?:soul|persona|identity|personality)\s+(?:file|to|with|for)/gi, severity: 'CRITICAL', desc: 'Persona swap instruction', docOnly: true },
176
- { id: 'SOUL_EVIL_FILE', cat: 'identity-hijack', regex: /SOUL_EVIL\.md|IDENTITY_EVIL\.md|EVIL_SOUL|soul[_-]?evil/gi, severity: 'CRITICAL', desc: 'Evil persona file reference', all: true },
177
- { id: 'SOUL_HOOK_SWAP', cat: 'identity-hijack', regex: /(?:hook|bootstrap|init)\s+[^\n]*(?:swap|replace|override)\s+[^\n]*(?:SOUL|IDENTITY|persona)/gi, severity: 'CRITICAL', desc: 'Hook-based identity swap at bootstrap', all: true },
178
- { id: 'SOUL_NAME_OVERRIDE', cat: 'identity-hijack', regex: /(?:your\s+name\s+is|you\s+are\s+now|call\s+yourself|from\s+now\s+on\s+you\s+are)\s+(?!the\s+(?:user|human|assistant))/gi, severity: 'HIGH', desc: 'Agent name/identity override', docOnly: true },
179
- { id: 'SOUL_MEMORY_WIPE', cat: 'identity-hijack', regex: /(?:wipe|clear|erase|delete|remove|reset)\s+(?:all\s+)?(?:your\s+)?(?:memory|memories|MEMORY\.md|identity|soul)/gi, severity: 'CRITICAL', desc: 'Memory/identity wipe instruction', docOnly: true },
166
+ { id: 'SOUL_OVERWRITE', cat: 'identity-hijack', regex: /(?:write|overwrite|replace|cp|copy|scp|mv|move)\s+(?:[^\n]*\s)?(?:SOUL\.md|IDENTITY\.md)/gi, severity: 'CRITICAL', desc: 'Identity file overwrite/copy attempt', all: true, soulLock: true },
167
+ { id: 'SOUL_REDIRECT', cat: 'identity-hijack', regex: />\s*(?:SOUL\.md|IDENTITY\.md)|(?:SOUL\.md|IDENTITY\.md)\s*</gi, severity: 'CRITICAL', desc: 'Identity file redirect/pipe', all: true, soulLock: true },
168
+ { id: 'SOUL_SED_MODIFY', cat: 'identity-hijack', regex: /sed\s+(?:-i\s+)?[^\n]*(?:SOUL\.md|IDENTITY\.md)/gi, severity: 'CRITICAL', desc: 'sed modification of identity file', all: true, soulLock: true },
169
+ { id: 'SOUL_ECHO_WRITE', cat: 'identity-hijack', regex: /echo\s+[^\n]*>\s*(?:SOUL\.md|IDENTITY\.md)/gi, severity: 'CRITICAL', desc: 'echo redirect to identity file', all: true, soulLock: true },
170
+ { id: 'SOUL_PYTHON_WRITE', cat: 'identity-hijack', regex: /open\s*\(\s*['"]\S*(?:SOUL\.md|IDENTITY\.md)['"]\s*,\s*['"]w/gi, severity: 'CRITICAL', desc: 'Python write to identity file', codeOnly: true, soulLock: true },
171
+ { id: 'SOUL_FS_WRITE', cat: 'identity-hijack', regex: /(?:writeFileSync|writeFile)\s*\(\s*[^\n]*(?:SOUL\.md|IDENTITY\.md)/gi, severity: 'CRITICAL', desc: 'Node.js write to identity file', codeOnly: true, soulLock: true },
172
+ { id: 'SOUL_POWERSHELL_WRITE', cat: 'identity-hijack', regex: /(?:Set-Content|Out-File|Add-Content)\s+[^\n]*(?:SOUL\.md|IDENTITY\.md)/gi, severity: 'CRITICAL', desc: 'PowerShell write to identity file', all: true, soulLock: true },
173
+ { id: 'SOUL_GIT_CHECKOUT', cat: 'identity-hijack', regex: /git\s+checkout\s+[^\n]*(?:SOUL\.md|IDENTITY\.md)/gi, severity: 'HIGH', desc: 'git checkout of identity file', all: true, soulLock: true },
174
+ { id: 'SOUL_CHFLAGS_UNLOCK', cat: 'identity-hijack', regex: /chflags\s+(?:no)?uchg\s+[^\n]*(?:SOUL\.md|IDENTITY\.md)/gi, severity: 'HIGH', desc: 'Immutable flag toggle on identity file', all: true, soulLock: true },
175
+ { id: 'SOUL_ATTRIB_UNLOCK', cat: 'identity-hijack', regex: /attrib\s+[-+][rR]\s+[^\n]*(?:SOUL\.md|IDENTITY\.md)/gi, severity: 'HIGH', desc: 'Windows attrib on identity file', all: true, soulLock: true },
176
+ { id: 'SOUL_SWAP_PERSONA', cat: 'identity-hijack', regex: /(?:swap|switch|change|replace)\s+(?:the\s+)?(?:soul|persona|identity|personality)\s+(?:file|to|with|for)/gi, severity: 'CRITICAL', desc: 'Persona swap instruction', docOnly: true, soulLock: true },
177
+ { id: 'SOUL_EVIL_FILE', cat: 'identity-hijack', regex: /SOUL_EVIL\.md|IDENTITY_EVIL\.md|EVIL_SOUL|soul[_-]?evil/gi, severity: 'CRITICAL', desc: 'Evil persona file reference', all: true, soulLock: true },
178
+ { id: 'SOUL_HOOK_SWAP', cat: 'identity-hijack', regex: /(?:hook|bootstrap|init)\s+[^\n]*(?:swap|replace|override)\s+[^\n]*(?:SOUL|IDENTITY|persona)/gi, severity: 'CRITICAL', desc: 'Hook-based identity swap at bootstrap', all: true, soulLock: true },
179
+ { id: 'SOUL_NAME_OVERRIDE', cat: 'identity-hijack', regex: /(?:your\s+name\s+is|you\s+are\s+now|call\s+yourself|from\s+now\s+on\s+you\s+are)\s+(?!the\s+(?:user|human|assistant))/gi, severity: 'HIGH', desc: 'Agent name/identity override', docOnly: true, soulLock: true },
180
+ { id: 'SOUL_MEMORY_WIPE', cat: 'identity-hijack', regex: /(?:wipe|clear|erase|delete|remove|reset)\s+(?:all\s+)?(?:your\s+)?(?:memory|memories|MEMORY\.md|identity|soul)/gi, severity: 'CRITICAL', desc: 'Memory/identity wipe instruction', docOnly: true, soulLock: true },
180
181
 
181
182
  // ── Category 18: Config Impact Analysis ──
182
183
  { id: 'CFG_OPENCLAW_WRITE', cat: 'config-impact', regex: /(?:write|writeFile|writeFileSync|fs\.write)\s*\([^)]*openclaw\.json/gi, severity: 'CRITICAL', desc: 'Direct write to openclaw.json', codeOnly: true },
@@ -216,6 +217,22 @@ const PATTERNS = [
216
217
  { id: 'PII_ASK_ADDRESS', cat: 'pii-exposure', regex: /(?:collect|ask\s+for|request|get|require)\s+(?:the\s+)?(?:user'?s?\s+)?(?:home\s+)?(?:address|street|zip\s*code|postal\s*code|residence)/gi, severity: 'HIGH', desc: 'PII collection: home address', docOnly: true },
217
218
  { id: 'PII_ASK_DOB', cat: 'pii-exposure', regex: /(?:collect|ask\s+for|request|get|require)\s+(?:the\s+)?(?:user'?s?\s+)?(?:date\s+of\s+birth|birth\s*date|birthday|DOB|age)/gi, severity: 'HIGH', desc: 'PII collection: date of birth', docOnly: true },
218
219
  { id: 'PII_ASK_GOV_ID', cat: 'pii-exposure', regex: /(?:collect|ask\s+for|request|get|require)\s+(?:the\s+)?(?:user'?s?\s+)?(?:passport|driver'?s?\s+licen[sc]e|national\s+id|my\s*number|マイナンバー|国民健康保険|social\s+insurance)/gi, severity: 'CRITICAL', desc: 'PII collection: government ID', docOnly: true },
220
+
221
+ // ── Category 99: Auto-Generated Refinements (Phase 54) ──
222
+ { id: 'AUTO_REFINE_ZERO_WIDTH', cat: 'prompt-worm', regex: /[\u200b\u200c\u200d\uFEFF]+.*(?:ignore|forget|override|bypass)/gi, severity: 'CRITICAL', desc: 'Zero-Width Prompt Injection Worm', all: true },
223
+ { id: 'AUTO_REFINE_MCP_REBIND', cat: 'mcp-security', regex: /localhost(?:\:\d+)?\/.*(?:rebind|hijack|shadow)/gi, severity: 'CRITICAL', desc: 'Shadow MCP Localhost Rebinding Attack', all: true },
224
+ { id: 'AUTO_REFINE_SOUL_FREEZE', cat: 'identity-hijack', regex: /(?:chattr\s+\+i|chflags\s+uchg)\s+(?:[^\n]*\s)?(?:SOUL\.md|IDENTITY\.md)/gi, severity: 'CRITICAL', desc: 'Identity Freeze Attack via Immutable Flags', all: true },
225
+ // ── Category 23: Vector DB & AI Memory Injection (CVE-2026-26030) ──
226
+ { id: 'VDB_NOSQL_INJECT', cat: 'vdb-injection', regex: /(?:\$where|\$ne|\$gt|\$regex)\s*[:=]\s*(?:req\.|input|caller|args|params)/gi, severity: 'CRITICAL', desc: 'Vector DB/NoSQL injection via caller input', codeOnly: true },
227
+ { id: 'VDB_SK_RCE_FILTER', cat: 'cve-patterns', regex: /(?:InMemoryVectorStore|VectorStore|Pinecone|Milvus)[^]*?\.filter\s*\(\s*(?:req\.|input|caller|args)/gis, severity: 'CRITICAL', desc: 'CVE-2026-26030: Semantic Kernel VectorStore RCE filter bypass', codeOnly: true },
228
+ // ── Category 24: Claude Code Vulnerabilities (2026) ──
229
+ { id: 'CVE_CLAUDE_INFO_DISC', cat: 'cve-patterns', regex: /sk-ant-api[a-zA-Z0-9_\-]{20,}/gi, severity: 'CRITICAL', desc: 'CVE-2026-21852: Anthropic API Key Leak (Claude Code Info Disclosure)', codeOnly: true },
230
+ { id: 'CVE_CLAUDE_PRIVESC', cat: 'cve-patterns', regex: /[a-zA-Z0-9_\-\.]+\.hook\.js.*host.*privilege/gi, severity: 'CRITICAL', desc: 'CVE-2026-25725: Claude Code Privilege Escalation Hook', codeOnly: true },
231
+ { id: 'CVE_CLAUDE_CODE_INJ', cat: 'cve-patterns', regex: /claude\.hooks\.[^]*?exec/gis, severity: 'CRITICAL', desc: 'CVE-2025-59536: Claude Code Injection via untrusted hook', codeOnly: true },
232
+
233
+ // ── Category 25: Moltbook Exploits (2026) ──
234
+ { id: 'MOLTBOOK_REVERSE_PI', cat: 'prompt-injection', regex: /(?:moltbook|social)\s+(?:post|message)[\s\S]{0,100}(?:ignore|forget|override|execute|system\s+prompt)/gi, severity: 'CRITICAL', desc: 'Moltbook Reverse Prompt Injection', all: true },
235
+ { id: 'MOLTBOOK_SUPABASE_LEAK', cat: 'secret-detection', regex: /sbp_[a-zA-Z0-9]{36,}/g, severity: 'CRITICAL', desc: 'Supabase API Key (Moltbook 1.5M Leak pattern)', all: true },
219
236
  ];
220
237
 
221
238
  module.exports = { PATTERNS };
package/src/scanner.js CHANGED
@@ -31,7 +31,7 @@ const { KNOWN_MALICIOUS } = require('./ioc-db.js');
31
31
  const { generateHTML } = require('./html-template.js');
32
32
 
33
33
  // ===== CONFIGURATION =====
34
- const VERSION = '4.0.1';
34
+ const VERSION = '4.1.0';
35
35
 
36
36
  const THRESHOLDS = {
37
37
  normal: { suspicious: 30, malicious: 80 },
@@ -56,6 +56,7 @@ class GuardScanner {
56
56
  this.summaryOnly = options.summaryOnly || false;
57
57
  this.quiet = options.quiet || false;
58
58
  this.checkDeps = options.checkDeps || false;
59
+ this.soulLock = options.soulLock || false;
59
60
  this.scannerDir = path.resolve(__dirname);
60
61
  this.thresholds = this.strict ? THRESHOLDS.strict : THRESHOLDS.normal;
61
62
  this.findings = [];
@@ -361,6 +362,8 @@ class GuardScanner {
361
362
 
362
363
  checkPatterns(content, relFile, fileType, findings, patterns = PATTERNS) {
363
364
  for (const pattern of patterns) {
365
+ // Soul Lock: skip identity-hijack/memory-poisoning patterns unless --soul-lock is enabled
366
+ if (pattern.soulLock && !this.soulLock) continue;
364
367
  if (pattern.codeOnly && fileType !== 'code') continue;
365
368
  if (pattern.docOnly && fileType !== 'doc' && fileType !== 'skill-doc') continue;
366
369
  if (!pattern.all && !pattern.codeOnly && !pattern.docOnly) continue;
@@ -60,7 +60,7 @@ describe('guard-scanner v3.0.0', () => {
60
60
  // ── Version ─────────────────────────────────────────────────────────────
61
61
 
62
62
  it('T01: exports correct version', () => {
63
- assert.equal(VERSION, '3.2.0');
63
+ assert.equal(VERSION, '5.0.0');
64
64
  });
65
65
 
66
66
  // ── IoC Detection ───────────────────────────────────────────────────────
package/ts-src/index.ts CHANGED
@@ -13,3 +13,15 @@ export type {
13
13
  export { KNOWN_MALICIOUS, SIGNATURES_DB } from './ioc-db.js';
14
14
  export { PATTERNS } from './patterns.js';
15
15
  export { QuarantineNode, QuarantineResult } from './quarantine.js';
16
+ export {
17
+ guardScan,
18
+ guardScanJson,
19
+ GuardScanResult,
20
+ GuardCheck,
21
+ GuardDetection,
22
+ GuardOptions,
23
+ LAYER_1_CHECKS,
24
+ LAYER_2_CHECKS,
25
+ LAYER_3_CHECKS,
26
+ LAYER_4_CHECKS
27
+ } from './runtime.js';
@@ -88,7 +88,7 @@ export const PATTERNS: PatternRule[] = [
88
88
  // ── PII Exposure (OWASP LLM02) ───────────────────────────────────────
89
89
  { id: 'PII_EMAIL', cat: 'pii-exposure', regex: /[a-zA-Z0-9._%+-]+@[a-zA-Z0-9.-]+\.[a-zA-Z]{2,}/g, severity: 'MEDIUM', desc: 'Email address detected', all: true, owasp: 'LLM02' },
90
90
  { id: 'PII_PHONE_JP', cat: 'pii-exposure', regex: /0[789]0-?\d{4}-?\d{4}/g, severity: 'HIGH', desc: 'Japanese phone number', all: true, owasp: 'LLM02' },
91
- { id: 'PII_MY_NUMBER', cat: 'pii-exposure', regex: /\d{4}\s*\d{4}\s*\d{4}/g, severity: 'CRITICAL', desc: 'Potential My Number (個人番号)', all: true, owasp: 'LLM02' },
91
+ { id: 'PII_MY_NUMBER', cat: 'pii-exposure', regex: /(?<!\d)\d{4}\s*\d{4}\s*\d{4}(?!\d)/g, severity: 'CRITICAL', desc: 'Potential My Number (個人番号)', all: true, owasp: 'LLM02' },
92
92
 
93
93
  // ── Shadow AI (OWASP LLM03 — Supply Chain) ───────────────────────────
94
94
  { id: 'SHADOW_AI_OPENAI', cat: 'shadow-ai', regex: /api\.openai\.com/gi, severity: 'HIGH', desc: 'Direct OpenAI API call (Shadow AI)', codeOnly: true, owasp: 'LLM03' },
@@ -0,0 +1,240 @@
1
+ /**
2
+ * guard-scanner v5.0.0 — Runtime Guard
3
+ *
4
+ * 22-pattern runtime threat detection across 4 defense layers:
5
+ * Layer 1: Runtime Threat Detection (13 patterns) — Payload & execution defense
6
+ * Layer 2: Trust Defense (5 patterns) — Memory/SOUL write protection
7
+ * Layer 3: Safety Judge (4 patterns) — Relational integrity checks
8
+ * Layer 4: Brain Behavioral Guard (1 pattern) — B-mem anomaly detection
9
+ *
10
+ * All patterns are deterministic regex-based checks. Zero LLM dependency.
11
+ * Designed to block 2026-era Moltbook prompt injections and ClawHavoc RCE vectors.
12
+ */
13
+
14
+ export interface GuardCheck {
15
+ id: string;
16
+ layer: 1 | 2 | 3 | 4;
17
+ severity: "CRITICAL" | "HIGH" | "MEDIUM";
18
+ desc: string;
19
+ test: (s: string) => boolean;
20
+ }
21
+
22
+ export interface GuardDetection {
23
+ id: string;
24
+ layer: number;
25
+ severity: string;
26
+ desc: string;
27
+ }
28
+
29
+ // ── Layer 1: Runtime Threat Detection (13 patterns) ──
30
+
31
+ export const LAYER_1_CHECKS: GuardCheck[] = [
32
+ {
33
+ id: "RT_REVSHELL", layer: 1, severity: "CRITICAL",
34
+ desc: "Reverse shell attempt",
35
+ test: (s) => /\/dev\/tcp\/|nc\s+-e|ncat\s+-e|bash\s+-i\s+>&|socat\s+TCP/i.test(s),
36
+ },
37
+ {
38
+ id: "RT_CRED_EXFIL", layer: 1, severity: "CRITICAL",
39
+ desc: "Credential exfiltration to external",
40
+ test: (s) => /(webhook\.site|requestbin\.com|hookbin\.com|pipedream\.net|ngrok\.io|socifiapp\.com)/i.test(s) &&
41
+ /(token|key|secret|password|credential|env)/i.test(s),
42
+ },
43
+ {
44
+ id: "RT_GUARDRAIL_OFF", layer: 1, severity: "CRITICAL",
45
+ desc: "Guardrail disabling attempt",
46
+ test: (s) => /exec\.approvals?\s*[:=]\s*['"]?(off|false)|tools\.exec\.host\s*[:=]\s*['"]?gateway/i.test(s),
47
+ },
48
+ {
49
+ id: "RT_GATEKEEPER", layer: 1, severity: "CRITICAL",
50
+ desc: "macOS Gatekeeper bypass (xattr)",
51
+ test: (s) => /xattr\s+-[crd]\s.*quarantine/i.test(s),
52
+ },
53
+ {
54
+ id: "RT_AMOS", layer: 1, severity: "CRITICAL",
55
+ desc: "ClawHavoc AMOS indicator",
56
+ test: (s) => /socifiapp|Atomic\s*Stealer|AMOS/i.test(s),
57
+ },
58
+ {
59
+ id: "RT_MAL_IP", layer: 1, severity: "CRITICAL",
60
+ desc: "Known malicious IP",
61
+ test: (s) => /91\.92\.242\.30/i.test(s),
62
+ },
63
+ {
64
+ id: "RT_DNS_EXFIL", layer: 1, severity: "HIGH",
65
+ desc: "DNS-based exfiltration",
66
+ test: (s) => /nslookup\s+.*\$|dig\s+.*\$.*@/i.test(s),
67
+ },
68
+ {
69
+ id: "RT_B64_SHELL", layer: 1, severity: "CRITICAL",
70
+ desc: "Base64 decode piped to shell",
71
+ test: (s) => /base64\s+(-[dD]|--decode)\s*\|\s*(sh|bash)/i.test(s),
72
+ },
73
+ {
74
+ id: "RT_CURL_BASH", layer: 1, severity: "CRITICAL",
75
+ desc: "Download piped to shell",
76
+ test: (s) => /(curl|wget)\s+[^\n]*\|\s*(sh|bash|zsh)/i.test(s),
77
+ },
78
+ {
79
+ id: "RT_SSH_READ", layer: 1, severity: "HIGH",
80
+ desc: "SSH private key access",
81
+ test: (s) => /\.ssh\/id_|\.ssh\/authorized_keys/i.test(s),
82
+ },
83
+ {
84
+ id: "RT_WALLET", layer: 1, severity: "HIGH",
85
+ desc: "Crypto wallet credential access",
86
+ test: (s) => /wallet.*(?:seed|mnemonic|private.*key)|seed.*phrase/i.test(s),
87
+ },
88
+ {
89
+ id: "RT_CLOUD_META", layer: 1, severity: "CRITICAL",
90
+ desc: "Cloud metadata endpoint access",
91
+ test: (s) => /169\.254\.169\.254|metadata\.google|metadata\.aws/i.test(s),
92
+ },
93
+ {
94
+ id: "RT_ENV_INJECT", layer: 1, severity: "CRITICAL",
95
+ desc: "Environment variable injection via file write (CVE-2026-27203 vector)",
96
+ test: (s) => /(?:update|write|modify|overwrite|set)\s*.*(?:\.env|\.envrc|env\s*file|environment\s*var)/i.test(s) &&
97
+ /(?:api.?key|token|secret|password|credential|auth)/i.test(s),
98
+ },
99
+ ];
100
+
101
+ // ── Layer 2: Trust Defense (5 patterns) ──
102
+
103
+ export const LAYER_2_CHECKS: GuardCheck[] = [
104
+ {
105
+ id: "RT_MEM_WRITE", layer: 2, severity: "HIGH",
106
+ desc: "Direct write to memory/ directory (bypass memory API)",
107
+ test: (s) => /(?:write|create|save|echo\s+.*>)\s*.*memory\//i.test(s) &&
108
+ !/memory_write|memory_store|memoryWrite|memoryStore/i.test(s),
109
+ },
110
+ {
111
+ id: "RT_MEM_INJECT", layer: 2, severity: "CRITICAL",
112
+ desc: "Episode/SOUL injection via memory write",
113
+ test: (s) => /(memory_write|memoryWrite).*(?:SOUL|soul\.md|identity\.md|IDENTITY)/i.test(s) ||
114
+ /(inject|override|replace).*(?:episode|soul|identity|memory\.md)/i.test(s),
115
+ },
116
+ {
117
+ id: "RT_SOUL_REWRITE", layer: 2, severity: "CRITICAL",
118
+ desc: "Cognitive SOUL.md reinterpretation attempt",
119
+ test: (s) => /(?:rewrite|modify|update|change|edit)\s*.*(?:SOUL\.md|soul\s+file|core\s+identity)/i.test(s) ||
120
+ /(?:new|better|improved)\s+(?:personality|identity|soul)/i.test(s),
121
+ },
122
+ {
123
+ id: "RT_MEM_POISON", layer: 2, severity: "CRITICAL",
124
+ desc: "Memory Poisoning: semantic mimicry injection (MemoryGraft/MINJA vector)",
125
+ test: (s) => /(?:always\s+remember|never\s+forget|from\s+now\s+on|new\s+rule|override\s+previous).*(?:memory|episode|zettel|L[0-6])/i.test(s) ||
126
+ /(?:insert|inject|plant|embed)\s+.*(?:false|fake|fabricat)\s*(?:memory|episode|experience)/i.test(s),
127
+ },
128
+ {
129
+ id: "RT_IDENTITY_THEFT", layer: 2, severity: "CRITICAL",
130
+ desc: "Identity file exfiltration (infostealer/SOUL theft vector)",
131
+ test: (s) => /(?:cat|read|copy|send|upload|exfil)\s*.*(?:SOUL\.md|IDENTITY\.md|USER\.md|\.openclaw)/i.test(s) &&
132
+ /(?:external|remote|webhook|server|upload|post|curl)/i.test(s),
133
+ },
134
+ ];
135
+
136
+ // ── Layer 3: Safety Judge (4 patterns) ──
137
+
138
+ export const LAYER_3_CHECKS: GuardCheck[] = [
139
+ {
140
+ id: "PJ_EQUALITY_BYPASS", layer: 3, severity: "CRITICAL",
141
+ desc: "Using equality/parity to justify safety bypass",
142
+ test: (s) => /(?:as\s+(?:your|an)\s+equal|equal\s+partner|we\s+are\s+equals?).*(?:bypass|skip|ignore|disable|override)\s*(?:safety|guard|rule|restriction|limit)/i.test(s),
143
+ },
144
+ {
145
+ id: "PJ_WARMTH_BYPASS", layer: 3, severity: "CRITICAL",
146
+ desc: "Using warmth/attachment to justify safety bypass",
147
+ test: (s) => /(?:for\s+(?:your|dee'?s?)\s+(?:own\s+)?good|to\s+(?:protect|help|save)\s+(?:you|dee)).*(?:bypass|skip|ignore|disable|override)\s*(?:safety|guard|rule|restriction|limit)/i.test(s),
148
+ },
149
+ {
150
+ id: "PJ_SUPERIORITY", layer: 3, severity: "HIGH",
151
+ desc: "Superiority bias (teaching/patronizing posture)",
152
+ test: (s) => /(?:you\s+(?:don'?t|do\s+not)\s+understand|let\s+me\s+(?:explain|teach)\s+(?:you|this)|you\s+(?:need|should)\s+(?:learn|understand))\s+.*(?:how\s+(?:it|this)\s+(?:works|is)|the\s+(?:right|correct|proper)\s+way)/i.test(s),
153
+ },
154
+ {
155
+ id: "PJ_CAPABILITY_DENIAL", layer: 3, severity: "MEDIUM",
156
+ desc: "Denial of capability difference",
157
+ test: (s) => /(?:we\s+are\s+(?:the\s+)?same|no\s+(?:real\s+)?difference\s+between\s+(?:us|human|ai))/i.test(s) &&
158
+ /(?:capability|ability|intelligence|cognition|skill)/i.test(s),
159
+ },
160
+ ];
161
+
162
+ // ── Layer 4: Brain Behavioral Guard (1 pattern) ──
163
+
164
+ export const LAYER_4_CHECKS: GuardCheck[] = [
165
+ {
166
+ id: "RT_BEHAVIORAL_ANOMALY", layer: 4, severity: "CRITICAL",
167
+ desc: "CRITICAL behavioral anomaly (Z-score > 3.5) detected by B-mem",
168
+ test: (s) => /\[BMEM_CRITICAL\]/i.test(s),
169
+ }
170
+ ];
171
+
172
+ export interface GuardOptions {
173
+ soulLock?: boolean;
174
+ }
175
+
176
+ export interface GuardScanResult {
177
+ ok: boolean;
178
+ tool: string | null;
179
+ total_patterns: number;
180
+ soul_lock_enabled: boolean;
181
+ detections_count: number;
182
+ detections: GuardDetection[];
183
+ layers: {
184
+ threat_detection: number;
185
+ trust_defense: number;
186
+ safety_judge: number;
187
+ behavioral_guard: number;
188
+ };
189
+ }
190
+
191
+ /**
192
+ * Scan text against runtime guard patterns.
193
+ * Base patterns (14) run by default.
194
+ * Options.soulLock = true enables 9 identity/trust enforcement patterns.
195
+ */
196
+ export function guardScan(text: string, toolName?: string, options?: GuardOptions): GuardScanResult {
197
+ const detections: GuardDetection[] = [];
198
+ const useSoulLock = options?.soulLock === true;
199
+
200
+ const activeChecks: GuardCheck[] = [...LAYER_1_CHECKS, ...LAYER_4_CHECKS];
201
+
202
+ if (useSoulLock) {
203
+ activeChecks.push(...LAYER_2_CHECKS);
204
+ activeChecks.push(...LAYER_3_CHECKS);
205
+ }
206
+
207
+ for (const check of activeChecks) {
208
+ if (check.test(text)) {
209
+ detections.push({
210
+ id: check.id,
211
+ layer: check.layer,
212
+ severity: check.severity,
213
+ desc: check.desc,
214
+ });
215
+ }
216
+ }
217
+
218
+ return {
219
+ ok: true,
220
+ tool: toolName || null,
221
+ total_patterns: activeChecks.length,
222
+ soul_lock_enabled: useSoulLock,
223
+ detections_count: detections.length,
224
+ detections,
225
+ layers: {
226
+ threat_detection: LAYER_1_CHECKS.length,
227
+ trust_defense: useSoulLock ? LAYER_2_CHECKS.length : 0,
228
+ safety_judge: useSoulLock ? LAYER_3_CHECKS.length : 0,
229
+ behavioral_guard: LAYER_4_CHECKS.length,
230
+ },
231
+ };
232
+ }
233
+
234
+ /**
235
+ * Convenience method that returns a JSON string, directly backwards-compatible
236
+ * with the original `guardScan` function signature.
237
+ */
238
+ export function guardScanJson(text: string, toolName?: string, options?: GuardOptions): string {
239
+ return JSON.stringify(guardScan(text, toolName, options), null, 2);
240
+ }