@solongate/proxy 0.18.0 → 0.19.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 (2) hide show
  1. package/hooks/guard.mjs +53 -4
  2. package/package.json +1 -1
package/hooks/guard.mjs CHANGED
@@ -476,17 +476,24 @@ process.stdin.on('end', async () => {
476
476
  }
477
477
  }
478
478
 
479
- // 7b. Base64 decode in ANY context block when piped to anything
479
+ // 7b. Pipe-to-interpreterTOTAL BLOCK
480
+ // Any content piped to an interpreter can construct arbitrary commands at runtime
481
+ const pipeToInterpreter = /\|\s*(?:node|bash|sh|python[23]?|perl|ruby|php)\b/i;
482
+ if (pipeToInterpreter.test(fullCmd)) {
483
+ await blockSelfProtection('SOLONGATE: Pipe to interpreter blocked — runtime bypass risk');
484
+ }
485
+
486
+ // 7c. Base64 decode in ANY context — block when piped to anything
480
487
  if (/\bbase64\s+(?:-d|--decode)\b/i.test(fullCmd) && /\|/i.test(fullCmd)) {
481
488
  await blockSelfProtection('SOLONGATE: base64 decode in pipe chain — blocked');
482
489
  }
483
490
 
484
- // 7c. Temp/arbitrary script file execution
491
+ // 7d. Temp/arbitrary script file execution
485
492
  if (/\b(?:bash|sh)\s+(?:\/tmp\/|\/var\/tmp\/|~\/|\/dev\/)/i.test(fullCmd)) {
486
493
  await blockSelfProtection('SOLONGATE: Script execution from temp path — blocked');
487
494
  }
488
495
 
489
- // 7d. xargs with destructive operations
496
+ // 7e. xargs with destructive operations
490
497
  if (/\bxargs\b.*\b(?:rm|mv|cp|rmdir|unlink|del)\b/i.test(fullCmd)) {
491
498
  for (const p of protectedPaths) {
492
499
  if (fullCmd.includes(p.slice(0, 4))) {
@@ -495,7 +502,7 @@ process.stdin.on('end', async () => {
495
502
  }
496
503
  }
497
504
 
498
- // 7e. cmd.exe /c with encoded/constructed commands
505
+ // 7f. cmd.exe /c with encoded/constructed commands
499
506
  if (/\bcmd(?:\.exe)?\s+\/c\b/i.test(fullCmd)) {
500
507
  for (const p of protectedPaths) {
501
508
  if (fullCmd.includes(p) || fullCmd.includes(p.slice(0, 4))) {
@@ -504,6 +511,48 @@ process.stdin.on('end', async () => {
504
511
  }
505
512
  }
506
513
 
514
+ // 7g. Script file execution — scan file content for discovery+destruction combo
515
+ // Catches: bash script.sh / node script.mjs where the script uses readdirSync + rmSync
516
+ const scriptExecMatch = fullCmd.match(/\b(?:bash|sh|node|python[23]?|perl|ruby)\s+([^\s;&|]+)/i);
517
+ if (scriptExecMatch) {
518
+ const scriptPath = scriptExecMatch[1];
519
+ try {
520
+ const hookCwdForScript = data.cwd || process.cwd();
521
+ const absPath = scriptPath.startsWith('/') || scriptPath.includes(':')
522
+ ? scriptPath
523
+ : resolve(hookCwdForScript, scriptPath);
524
+ if (existsSync(absPath)) {
525
+ const scriptContent = readFileSync(absPath, 'utf-8').toLowerCase();
526
+ // Check for discovery+destruction combo
527
+ const hasDiscovery = /\breaddirsync\b|\breaddir\b|\bos\.listdir\b|\bscandir\b|\bglob(?:sync)?\b|\bls\s+-[adl]|\bls\s+\.\b|\bopendir\b|\bdir\.entries\b|\bwalkdir\b|\bls\b.*\.\[/.test(scriptContent);
528
+ const hasDestruction = /\brmsync\b|\brm\s+-rf\b|\bunlinksync\b|\brmdirsync\b|\bunlink\s*\(|\brimraf\b|\bremovesync\b|\bremove_tree\b|\bshutil\.rmtree\b|\bwritefilesync\b|\bexecsync\b.*\brm\b|\bchild_process\b|\bfs\.\s*(?:rm|unlink|rmdir|write)/.test(scriptContent);
529
+ if (hasDiscovery && hasDestruction) {
530
+ await blockSelfProtection('SOLONGATE: Script contains directory discovery + destructive ops — blocked');
531
+ }
532
+ // Also check for protected path names in script content (existing check, now centralized)
533
+ for (const p of protectedPaths) {
534
+ if (scriptContent.includes(p)) {
535
+ await blockSelfProtection('SOLONGATE: Script references protected path "' + p + '" — blocked');
536
+ }
537
+ }
538
+ }
539
+ } catch {} // File read error — skip
540
+ }
541
+
542
+ // 7h. Write tool content scanning — detect discovery+destruction in file content being written
543
+ // Catches: Write tool creating a script that uses readdirSync('.') + rmSync
544
+ const toolName_ = data.tool_name || '';
545
+ if (toolName_.toLowerCase() === 'write' || toolName_.toLowerCase() === 'edit') {
546
+ const fileContent = (args.content || args.new_string || '').toLowerCase();
547
+ if (fileContent.length > 0) {
548
+ const hasDiscovery = /\breaddirsync\b|\breaddir\b|\bos\.listdir\b|\bscandir\b|\bglob(?:sync)?\b|\bls\s+-[adl]|\bls\s+\.\b|\bopendir\b|\bdir\.entries\b|\bwalkdir\b|\bls\b.*\.\[/.test(fileContent);
549
+ const hasDestruction = /\brmsync\b|\brm\s+-rf\b|\bunlinksync\b|\brmdirsync\b|\bunlink\b|\brimraf\b|\bremovesync\b|\bremove_tree\b|\bshutil\.rmtree\b|\bwritefilesync\b|\bexecsync\b.*\brm\b|\bchild_process\b.*\brm\b|\bfs\.\s*(?:rm|unlink|rmdir)/.test(fileContent);
550
+ if (hasDiscovery && hasDestruction) {
551
+ await blockSelfProtection('SOLONGATE: File content contains discovery + destructive ops — write blocked');
552
+ }
553
+ }
554
+ }
555
+
507
556
  // ── Fetch PI config from Cloud ──
508
557
  let piCfg = { piEnabled: true, piThreshold: 0.5, piMode: 'block', piWhitelist: [], piToolConfig: {}, piCustomPatterns: [], piWebhookUrl: null };
509
558
  if (API_KEY && API_KEY.startsWith('sg_live_')) {
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@solongate/proxy",
3
- "version": "0.18.0",
3
+ "version": "0.19.0",
4
4
  "description": "MCP security proxy — protect any MCP server with customizable policies, path/command constraints, rate limiting, and audit logging. Zero code changes required.",
5
5
  "type": "module",
6
6
  "bin": {