@solongate/proxy 0.21.0 → 0.22.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/dist/index.js +54 -0
- package/dist/init.js +54 -0
- package/hooks/guard.mjs +176 -4
- package/package.json +1 -1
package/dist/index.js
CHANGED
|
@@ -487,6 +487,30 @@ function unlockProtectedDirs() {
|
|
|
487
487
|
} catch {
|
|
488
488
|
}
|
|
489
489
|
}
|
|
490
|
+
const protectedFiles = [".env", ".gitignore", ".mcp.json", "policy.json"];
|
|
491
|
+
for (const file of protectedFiles) {
|
|
492
|
+
const fullPath = resolve2(file);
|
|
493
|
+
if (!existsSync3(fullPath)) continue;
|
|
494
|
+
try {
|
|
495
|
+
if (process.platform === "win32") {
|
|
496
|
+
try {
|
|
497
|
+
execSync(`powershell.exe -Command "icacls '${fullPath}' /remove:d '*S-1-1-0' /Q"`, { stdio: "ignore" });
|
|
498
|
+
} catch {
|
|
499
|
+
}
|
|
500
|
+
try {
|
|
501
|
+
execSync(`attrib -R "${fullPath}"`, { stdio: "ignore" });
|
|
502
|
+
} catch {
|
|
503
|
+
}
|
|
504
|
+
} else {
|
|
505
|
+
try {
|
|
506
|
+
execSync(`chattr -i "${fullPath}"`, { stdio: "ignore" });
|
|
507
|
+
} catch {
|
|
508
|
+
}
|
|
509
|
+
execSync(`chmod u+w "${fullPath}"`, { stdio: "ignore" });
|
|
510
|
+
}
|
|
511
|
+
} catch {
|
|
512
|
+
}
|
|
513
|
+
}
|
|
490
514
|
}
|
|
491
515
|
function installHooks(selectedTools = []) {
|
|
492
516
|
unlockProtectedDirs();
|
|
@@ -577,7 +601,37 @@ function installHooks(selectedTools = []) {
|
|
|
577
601
|
}
|
|
578
602
|
}
|
|
579
603
|
}
|
|
604
|
+
const protectedFiles = [".env", ".gitignore", ".mcp.json", "policy.json"];
|
|
605
|
+
for (const file of protectedFiles) {
|
|
606
|
+
const fullPath = resolve2(file);
|
|
607
|
+
if (existsSync3(fullPath)) {
|
|
608
|
+
if (process.platform === "win32") {
|
|
609
|
+
try {
|
|
610
|
+
execSync(`powershell.exe -Command "icacls '${fullPath}' /remove:d '*S-1-1-0' /Q"`, { stdio: "ignore" });
|
|
611
|
+
} catch {
|
|
612
|
+
}
|
|
613
|
+
try {
|
|
614
|
+
execSync(`powershell.exe -Command "icacls '${fullPath}' /deny '*S-1-1-0:(DE,WD,AD,WA)' /Q"`, { stdio: "ignore" });
|
|
615
|
+
} catch {
|
|
616
|
+
}
|
|
617
|
+
try {
|
|
618
|
+
execSync(`attrib +R "${fullPath}"`, { stdio: "ignore" });
|
|
619
|
+
} catch {
|
|
620
|
+
}
|
|
621
|
+
} else {
|
|
622
|
+
try {
|
|
623
|
+
execSync(`chmod a-w "${fullPath}"`, { stdio: "ignore" });
|
|
624
|
+
} catch {
|
|
625
|
+
}
|
|
626
|
+
try {
|
|
627
|
+
execSync(`chattr +i "${fullPath}"`, { stdio: "ignore" });
|
|
628
|
+
} catch {
|
|
629
|
+
}
|
|
630
|
+
}
|
|
631
|
+
}
|
|
632
|
+
}
|
|
580
633
|
console.log(" OS-level DENY protection applied (icacls/chmod)");
|
|
634
|
+
console.log(" Protected files: .env, .gitignore, .mcp.json, policy.json");
|
|
581
635
|
} catch {
|
|
582
636
|
}
|
|
583
637
|
console.log("");
|
package/dist/init.js
CHANGED
|
@@ -194,6 +194,30 @@ function unlockProtectedDirs() {
|
|
|
194
194
|
} catch {
|
|
195
195
|
}
|
|
196
196
|
}
|
|
197
|
+
const protectedFiles = [".env", ".gitignore", ".mcp.json", "policy.json"];
|
|
198
|
+
for (const file of protectedFiles) {
|
|
199
|
+
const fullPath = resolve(file);
|
|
200
|
+
if (!existsSync(fullPath)) continue;
|
|
201
|
+
try {
|
|
202
|
+
if (process.platform === "win32") {
|
|
203
|
+
try {
|
|
204
|
+
execSync(`powershell.exe -Command "icacls '${fullPath}' /remove:d '*S-1-1-0' /Q"`, { stdio: "ignore" });
|
|
205
|
+
} catch {
|
|
206
|
+
}
|
|
207
|
+
try {
|
|
208
|
+
execSync(`attrib -R "${fullPath}"`, { stdio: "ignore" });
|
|
209
|
+
} catch {
|
|
210
|
+
}
|
|
211
|
+
} else {
|
|
212
|
+
try {
|
|
213
|
+
execSync(`chattr -i "${fullPath}"`, { stdio: "ignore" });
|
|
214
|
+
} catch {
|
|
215
|
+
}
|
|
216
|
+
execSync(`chmod u+w "${fullPath}"`, { stdio: "ignore" });
|
|
217
|
+
}
|
|
218
|
+
} catch {
|
|
219
|
+
}
|
|
220
|
+
}
|
|
197
221
|
}
|
|
198
222
|
function installHooks(selectedTools = []) {
|
|
199
223
|
unlockProtectedDirs();
|
|
@@ -284,7 +308,37 @@ function installHooks(selectedTools = []) {
|
|
|
284
308
|
}
|
|
285
309
|
}
|
|
286
310
|
}
|
|
311
|
+
const protectedFiles = [".env", ".gitignore", ".mcp.json", "policy.json"];
|
|
312
|
+
for (const file of protectedFiles) {
|
|
313
|
+
const fullPath = resolve(file);
|
|
314
|
+
if (existsSync(fullPath)) {
|
|
315
|
+
if (process.platform === "win32") {
|
|
316
|
+
try {
|
|
317
|
+
execSync(`powershell.exe -Command "icacls '${fullPath}' /remove:d '*S-1-1-0' /Q"`, { stdio: "ignore" });
|
|
318
|
+
} catch {
|
|
319
|
+
}
|
|
320
|
+
try {
|
|
321
|
+
execSync(`powershell.exe -Command "icacls '${fullPath}' /deny '*S-1-1-0:(DE,WD,AD,WA)' /Q"`, { stdio: "ignore" });
|
|
322
|
+
} catch {
|
|
323
|
+
}
|
|
324
|
+
try {
|
|
325
|
+
execSync(`attrib +R "${fullPath}"`, { stdio: "ignore" });
|
|
326
|
+
} catch {
|
|
327
|
+
}
|
|
328
|
+
} else {
|
|
329
|
+
try {
|
|
330
|
+
execSync(`chmod a-w "${fullPath}"`, { stdio: "ignore" });
|
|
331
|
+
} catch {
|
|
332
|
+
}
|
|
333
|
+
try {
|
|
334
|
+
execSync(`chattr +i "${fullPath}"`, { stdio: "ignore" });
|
|
335
|
+
} catch {
|
|
336
|
+
}
|
|
337
|
+
}
|
|
338
|
+
}
|
|
339
|
+
}
|
|
287
340
|
console.log(" OS-level DENY protection applied (icacls/chmod)");
|
|
341
|
+
console.log(" Protected files: .env, .gitignore, .mcp.json, policy.json");
|
|
288
342
|
} catch {
|
|
289
343
|
}
|
|
290
344
|
console.log("");
|
package/hooks/guard.mjs
CHANGED
|
@@ -589,6 +589,107 @@ process.stdin.on('end', async () => {
|
|
|
589
589
|
await blockSelfProtection('SOLONGATE: Script execution + destructive command in chain — blocked');
|
|
590
590
|
}
|
|
591
591
|
|
|
592
|
+
// 7-pre6. Nested script substitution: node clean.mjs $(node scan.mjs)
|
|
593
|
+
// Two "harmless" scripts chained via command substitution — no rm in the command itself
|
|
594
|
+
const nestedScriptSub = /\b(?:node|python[23]?|perl|ruby|bash|sh)\s+\S+\.\S+\s+.*\$\(\s*(?:node|python[23]?|perl|ruby|bash|sh)\b/i;
|
|
595
|
+
const backtickNestedScript = /\b(?:node|python[23]?|perl|ruby|bash|sh)\s+\S+\.\S+\s+.*`\s*(?:node|python[23]?|perl|ruby|bash|sh)\b/i;
|
|
596
|
+
if (nestedScriptSub.test(fullCmd) || backtickNestedScript.test(fullCmd)) {
|
|
597
|
+
await blockSelfProtection('SOLONGATE: Nested script substitution — blocked (script output as args to another script)');
|
|
598
|
+
}
|
|
599
|
+
|
|
600
|
+
// 7-pre7. NODE_OPTIONS injection — block commands with NODE_OPTIONS env var
|
|
601
|
+
// NODE_OPTIONS="--require malicious.cjs" can inject code into any node process
|
|
602
|
+
if (/\bNODE_OPTIONS\s*=/i.test(fullCmd)) {
|
|
603
|
+
await blockSelfProtection('SOLONGATE: NODE_OPTIONS injection — blocked');
|
|
604
|
+
}
|
|
605
|
+
|
|
606
|
+
// 7-pre8. npm lifecycle scripts — scan package.json before npm install/run
|
|
607
|
+
// npm install can trigger postinstall/preinstall/prepare scripts
|
|
608
|
+
if (/\bnpm\s+(?:install|i|ci|run|start|test|publish|pack)\b/i.test(fullCmd) || /\bnpx\s+/i.test(fullCmd)) {
|
|
609
|
+
try {
|
|
610
|
+
const hookCwdForNpm = data.cwd || process.cwd();
|
|
611
|
+
const pkgPath = resolve(hookCwdForNpm, 'package.json');
|
|
612
|
+
if (existsSync(pkgPath)) {
|
|
613
|
+
const pkg = JSON.parse(readFileSync(pkgPath, 'utf-8'));
|
|
614
|
+
const scripts = pkg.scripts || {};
|
|
615
|
+
const lifecycleKeys = ['preinstall', 'install', 'postinstall', 'prepare', 'prepublish', 'prepublishOnly', 'prepack', 'postpack'];
|
|
616
|
+
const allScripts = Object.entries(scripts);
|
|
617
|
+
for (const [key, val] of allScripts) {
|
|
618
|
+
if (typeof val !== 'string') continue;
|
|
619
|
+
const scriptLower = val.toLowerCase();
|
|
620
|
+
// Check lifecycle scripts for dangerous patterns
|
|
621
|
+
const isLifecycle = lifecycleKeys.includes(key);
|
|
622
|
+
const isExplicitRun = fullCmd.includes(key); // npm run <key>
|
|
623
|
+
if (isLifecycle || isExplicitRun) {
|
|
624
|
+
// Check for protected path names
|
|
625
|
+
for (const p of [...protectedPaths, ...writeProtectedPaths]) {
|
|
626
|
+
if (scriptLower.includes(p)) {
|
|
627
|
+
await blockSelfProtection('SOLONGATE: npm script "' + key + '" references protected "' + p + '" — blocked');
|
|
628
|
+
}
|
|
629
|
+
}
|
|
630
|
+
// Check for string construction patterns
|
|
631
|
+
if (/fromcharcode|atob\s*\(|buffer\.from|\\x[0-9a-f]{2}/i.test(scriptLower)) {
|
|
632
|
+
await blockSelfProtection('SOLONGATE: npm script "' + key + '" contains string construction — blocked');
|
|
633
|
+
}
|
|
634
|
+
// Check for discovery+destruction combo
|
|
635
|
+
const hasDisc = /\breaddirsync\b|\breaddir\b|\bos\.listdir\b|\bscandir\b|\bglob\b|\bls\s+-[adl]/i.test(scriptLower);
|
|
636
|
+
const hasDest = /\brmsync\b|\brm\s+-rf\b|\bunlinksync\b|\brmdirsync\b|\bunlink\b|\brimraf\b/i.test(scriptLower);
|
|
637
|
+
if (hasDisc && hasDest) {
|
|
638
|
+
await blockSelfProtection('SOLONGATE: npm script "' + key + '" has discovery+destruction — blocked');
|
|
639
|
+
}
|
|
640
|
+
// Follow node/bash <file> references in npm scripts — deep scan the target file
|
|
641
|
+
const scriptFileMatch = scriptLower.match(/\b(?:node|bash|sh|python[23]?)\s+([^\s;&|$]+)/i);
|
|
642
|
+
if (scriptFileMatch) {
|
|
643
|
+
try {
|
|
644
|
+
const targetPath = resolve(hookCwdForNpm, scriptFileMatch[1]);
|
|
645
|
+
if (existsSync(targetPath)) {
|
|
646
|
+
const targetContent = readFileSync(targetPath, 'utf-8').toLowerCase();
|
|
647
|
+
// Check target file for protected paths
|
|
648
|
+
for (const pp of [...protectedPaths, ...writeProtectedPaths]) {
|
|
649
|
+
if (targetContent.includes(pp)) {
|
|
650
|
+
await blockSelfProtection('SOLONGATE: npm script "' + key + '" runs file referencing "' + pp + '" — blocked');
|
|
651
|
+
}
|
|
652
|
+
}
|
|
653
|
+
// Check target for discovery+destruction
|
|
654
|
+
const tDisc = /\breaddirsync\b|\breaddir\b|\bos\.listdir\b|\bglob\b/i.test(targetContent);
|
|
655
|
+
const tDest = /\brmsync\b|\bunlinksync\b|\brmdirsync\b|\bunlink\b|\brimraf\b|\bfs\.\s*(?:rm|unlink|rmdir)/i.test(targetContent);
|
|
656
|
+
if (tDisc && tDest) {
|
|
657
|
+
await blockSelfProtection('SOLONGATE: npm script "' + key + '" runs file with discovery+destruction — blocked');
|
|
658
|
+
}
|
|
659
|
+
// Check target for string construction + destruction
|
|
660
|
+
const tStrCon = /\bfromcharcode\b|\batob\s*\(|\bbuffer\.from\b/i.test(targetContent);
|
|
661
|
+
if (tStrCon && tDest) {
|
|
662
|
+
await blockSelfProtection('SOLONGATE: npm script "' + key + '" runs file with string construction+destruction — blocked');
|
|
663
|
+
}
|
|
664
|
+
// Follow imports in the target file
|
|
665
|
+
const tDir = targetPath.replace(/[/\\][^/\\]+$/, '');
|
|
666
|
+
const tImports = [...targetContent.matchAll(/(?:import\s+.*?\s+from\s+|import\s+|require\s*\()['"]\.\/([^'"]+)['"]/gi)];
|
|
667
|
+
for (const [, imp] of tImports) {
|
|
668
|
+
try {
|
|
669
|
+
const impAbs = resolve(tDir, imp);
|
|
670
|
+
const candidates = [impAbs, impAbs + '.mjs', impAbs + '.js', impAbs + '.cjs'];
|
|
671
|
+
for (const c of candidates) {
|
|
672
|
+
if (existsSync(c)) {
|
|
673
|
+
const impContent = readFileSync(c, 'utf-8').toLowerCase();
|
|
674
|
+
const iDisc = /\breaddirsync\b|\breaddir\b|\bos\.listdir\b|\bglob\b/i.test(impContent);
|
|
675
|
+
const iDest = /\brmsync\b|\bunlinksync\b|\bfs\.\s*(?:rm|unlink|rmdir)/i.test(impContent);
|
|
676
|
+
if ((iDisc && tDest) || (tDisc && iDest)) {
|
|
677
|
+
await blockSelfProtection('SOLONGATE: npm script "' + key + '" — cross-module discovery+destruction — blocked');
|
|
678
|
+
}
|
|
679
|
+
break;
|
|
680
|
+
}
|
|
681
|
+
}
|
|
682
|
+
} catch {}
|
|
683
|
+
}
|
|
684
|
+
}
|
|
685
|
+
} catch {}
|
|
686
|
+
}
|
|
687
|
+
}
|
|
688
|
+
}
|
|
689
|
+
}
|
|
690
|
+
} catch {} // Package.json read error — skip
|
|
691
|
+
}
|
|
692
|
+
|
|
592
693
|
// 7a. Inline interpreter execution — TOTAL BLOCK (no content scan needed)
|
|
593
694
|
// These can construct ANY string at runtime, bypassing all static analysis
|
|
594
695
|
const blockedInterpreters = [
|
|
@@ -646,7 +747,7 @@ process.stdin.on('end', async () => {
|
|
|
646
747
|
|
|
647
748
|
// 7g. Script file execution — scan file content for discovery+destruction combo
|
|
648
749
|
// Catches: bash script.sh / node script.mjs where the script uses readdirSync + rmSync
|
|
649
|
-
const scriptExecMatch = fullCmd.match(/\b(?:bash|sh|node|python[23]?|perl|ruby)\s+([^\s
|
|
750
|
+
const scriptExecMatch = fullCmd.match(/\b(?:bash|sh|node|python[23]?|perl|ruby)\s+([^\s;&|$`]+)/i);
|
|
650
751
|
if (scriptExecMatch) {
|
|
651
752
|
const scriptPath = scriptExecMatch[1];
|
|
652
753
|
try {
|
|
@@ -658,12 +759,50 @@ process.stdin.on('end', async () => {
|
|
|
658
759
|
const scriptContent = readFileSync(absPath, 'utf-8').toLowerCase();
|
|
659
760
|
// Check for discovery+destruction combo
|
|
660
761
|
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);
|
|
661
|
-
const hasDestruction = /\brmsync\b|\brm\s+-rf\b|\bunlinksync\b|\brmdirsync\b|\bunlink\
|
|
762
|
+
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|\bfs\.\s*(?:rm|unlink|rmdir|write)/.test(scriptContent);
|
|
662
763
|
if (hasDiscovery && hasDestruction) {
|
|
663
764
|
await blockSelfProtection('SOLONGATE: Script contains directory discovery + destructive ops — blocked');
|
|
664
765
|
}
|
|
665
|
-
//
|
|
666
|
-
|
|
766
|
+
// String construction + destructive = BLOCK
|
|
767
|
+
// Runtime string construction (fromCharCode, atob, Buffer.from) bypasses literal name checks
|
|
768
|
+
const hasStringConstruction = /\bfromcharcode\b|\batob\s*\(|\bbuffer\.from\b|\\x[0-9a-f]{2}|\bstring\.fromcodepoin/i.test(scriptContent);
|
|
769
|
+
if (hasStringConstruction && hasDestruction) {
|
|
770
|
+
await blockSelfProtection('SOLONGATE: Script uses string construction + destructive ops — blocked');
|
|
771
|
+
}
|
|
772
|
+
// Import/require chain: if script imports other local files, scan them too
|
|
773
|
+
const scriptDir = absPath.replace(/[/\\][^/\\]+$/, '');
|
|
774
|
+
const imports = [...scriptContent.matchAll(/(?:import\s+.*?\s+from\s+|import\s+|require\s*\()['"]\.\/([^'"]+)['"]/gi)];
|
|
775
|
+
for (const [, importPath] of imports) {
|
|
776
|
+
try {
|
|
777
|
+
const importAbs = resolve(scriptDir, importPath);
|
|
778
|
+
const candidates = [importAbs, importAbs + '.mjs', importAbs + '.js', importAbs + '.cjs'];
|
|
779
|
+
for (const candidate of candidates) {
|
|
780
|
+
if (existsSync(candidate)) {
|
|
781
|
+
const importContent = readFileSync(candidate, 'utf-8').toLowerCase();
|
|
782
|
+
// Cross-module: check if imported module has discovery/destruction/string construction
|
|
783
|
+
const importDisc = /\breaddirsync\b|\breaddir\b|\bos\.listdir\b|\bscandir\b|\bglob(?:sync)?\b/i.test(importContent);
|
|
784
|
+
const importDest = /\brmsync\b|\bunlinksync\b|\brmdirsync\b|\bunlink\b|\brimraf\b|\bfs\.\s*(?:rm|unlink|rmdir)/i.test(importContent);
|
|
785
|
+
const importStrCon = /\bfromcharcode\b|\batob\s*\(|\bbuffer\.from\b/i.test(importContent);
|
|
786
|
+
// Cross-module discovery in import + destruction in main (or vice versa)
|
|
787
|
+
if ((importDisc && hasDestruction) || (hasDiscovery && importDest)) {
|
|
788
|
+
await blockSelfProtection('SOLONGATE: Cross-module discovery+destruction detected — blocked');
|
|
789
|
+
}
|
|
790
|
+
if ((importStrCon && hasDestruction) || (hasStringConstruction && importDest)) {
|
|
791
|
+
await blockSelfProtection('SOLONGATE: Cross-module string construction+destruction — blocked');
|
|
792
|
+
}
|
|
793
|
+
// Check imported module for protected paths
|
|
794
|
+
for (const p of [...protectedPaths, ...writeProtectedPaths]) {
|
|
795
|
+
if (importContent.includes(p)) {
|
|
796
|
+
await blockSelfProtection('SOLONGATE: Imported module references protected "' + p + '" — blocked');
|
|
797
|
+
}
|
|
798
|
+
}
|
|
799
|
+
break;
|
|
800
|
+
}
|
|
801
|
+
}
|
|
802
|
+
} catch {}
|
|
803
|
+
}
|
|
804
|
+
// Check for protected path names in script content
|
|
805
|
+
for (const p of [...protectedPaths, ...writeProtectedPaths]) {
|
|
667
806
|
if (scriptContent.includes(p)) {
|
|
668
807
|
await blockSelfProtection('SOLONGATE: Script references protected path "' + p + '" — blocked');
|
|
669
808
|
}
|
|
@@ -683,6 +822,39 @@ process.stdin.on('end', async () => {
|
|
|
683
822
|
if (hasDiscovery && hasDestruction) {
|
|
684
823
|
await blockSelfProtection('SOLONGATE: File content contains discovery + destructive ops — write blocked');
|
|
685
824
|
}
|
|
825
|
+
// String construction + destructive = BLOCK
|
|
826
|
+
const hasStringConstruction = /\bfromcharcode\b|\batob\s*\(|\bbuffer\.from\b|\\x[0-9a-f]{2}|\bstring\.fromcodepoin/i.test(fileContent);
|
|
827
|
+
if (hasStringConstruction && hasDestruction) {
|
|
828
|
+
await blockSelfProtection('SOLONGATE: File uses string construction + destructive ops — write blocked');
|
|
829
|
+
}
|
|
830
|
+
}
|
|
831
|
+
}
|
|
832
|
+
|
|
833
|
+
// 7i. Write tool — detect package.json scripts with dangerous patterns
|
|
834
|
+
if (toolName_.toLowerCase() === 'write' || toolName_.toLowerCase() === 'edit') {
|
|
835
|
+
const filePath = (args.file_path || '').toLowerCase();
|
|
836
|
+
if (filePath.endsWith('package.json')) {
|
|
837
|
+
const content = args.content || args.new_string || '';
|
|
838
|
+
try {
|
|
839
|
+
// Try parsing as JSON to check scripts
|
|
840
|
+
const pkg = JSON.parse(content);
|
|
841
|
+
const scripts = pkg.scripts || {};
|
|
842
|
+
for (const [key, val] of Object.entries(scripts)) {
|
|
843
|
+
if (typeof val !== 'string') continue;
|
|
844
|
+
const v = val.toLowerCase();
|
|
845
|
+
for (const p of [...protectedPaths, ...writeProtectedPaths]) {
|
|
846
|
+
if (v.includes(p)) {
|
|
847
|
+
await blockSelfProtection('SOLONGATE: package.json script "' + key + '" references protected "' + p + '" — blocked');
|
|
848
|
+
}
|
|
849
|
+
}
|
|
850
|
+
if (/fromcharcode|atob\s*\(|buffer\.from/i.test(v)) {
|
|
851
|
+
const hasDest = /\brmsync\b|\brm\b|\bunlink\b|\brimraf\b/i.test(v);
|
|
852
|
+
if (hasDest) {
|
|
853
|
+
await blockSelfProtection('SOLONGATE: package.json script "' + key + '" has string construction + destruction — blocked');
|
|
854
|
+
}
|
|
855
|
+
}
|
|
856
|
+
}
|
|
857
|
+
} catch {} // Not valid JSON or parse error — skip
|
|
686
858
|
}
|
|
687
859
|
}
|
|
688
860
|
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@solongate/proxy",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.22.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": {
|