@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.
- package/hooks/guard.mjs +53 -4
- 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.
|
|
479
|
+
// 7b. Pipe-to-interpreter — TOTAL 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
|
-
//
|
|
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
|
-
//
|
|
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
|
-
//
|
|
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.
|
|
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": {
|