@solongate/proxy 0.38.0 → 0.40.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 +45 -5
- package/dist/init.js +24 -5
- package/dist/lib.js +21 -0
- package/hooks/guard.mjs +26 -0
- package/package.json +1 -1
package/dist/index.js
CHANGED
|
@@ -809,7 +809,7 @@ function installGeminiConfig(clientDir, guardCmd, auditCmd, _stopCmd, wrappedMcp
|
|
|
809
809
|
console.log(` ${existingStr ? "Updated" : "Created"} ${settingsPath}`);
|
|
810
810
|
return "installed";
|
|
811
811
|
}
|
|
812
|
-
function installOpenClawConfig(
|
|
812
|
+
function installOpenClawConfig(_clientDir) {
|
|
813
813
|
const yamlPath = resolve3("openclaw.yaml");
|
|
814
814
|
const pluginLine = "@solongate/openclaw-plugin";
|
|
815
815
|
if (existsSync4(yamlPath)) {
|
|
@@ -817,11 +817,30 @@ function installOpenClawConfig(clientDir) {
|
|
|
817
817
|
if (content.includes(pluginLine)) {
|
|
818
818
|
return "skipped";
|
|
819
819
|
}
|
|
820
|
+
if (content.includes("plugins:")) {
|
|
821
|
+
const updated = content.replace(
|
|
822
|
+
/^(plugins:\s*\n)/m,
|
|
823
|
+
`$1 - "${pluginLine}"
|
|
824
|
+
`
|
|
825
|
+
);
|
|
826
|
+
writeFileSync2(yamlPath, updated);
|
|
827
|
+
} else {
|
|
828
|
+
const separator = content.endsWith("\n") ? "" : "\n";
|
|
829
|
+
writeFileSync2(yamlPath, content + `${separator}
|
|
830
|
+
plugins:
|
|
831
|
+
- "${pluginLine}"
|
|
832
|
+
`);
|
|
833
|
+
}
|
|
834
|
+
} else {
|
|
835
|
+
writeFileSync2(yamlPath, `# OpenClaw configuration
|
|
836
|
+
# SolonGate security plugin \u2014 manage policies at https://dashboard.solongate.com
|
|
837
|
+
|
|
838
|
+
plugins:
|
|
839
|
+
- "${pluginLine}"
|
|
840
|
+
`);
|
|
820
841
|
}
|
|
821
|
-
console.log(`
|
|
822
|
-
console.log(`
|
|
823
|
-
console.log(` - "${pluginLine}"`);
|
|
824
|
-
console.log(` Then: npm install ${pluginLine}`);
|
|
842
|
+
console.log(` Updated openclaw.yaml with SolonGate plugin`);
|
|
843
|
+
console.log(` \u2192 Run: npm install ${pluginLine}`);
|
|
825
844
|
return "installed";
|
|
826
845
|
}
|
|
827
846
|
function ensureEnvFile() {
|
|
@@ -5707,6 +5726,27 @@ var AiJudge = class _AiJudge {
|
|
|
5707
5726
|
}
|
|
5708
5727
|
this.consecutiveFailures = 0;
|
|
5709
5728
|
}
|
|
5729
|
+
const argStr = JSON.stringify(args).toLowerCase();
|
|
5730
|
+
const hasShellTricks = /\$[\({]|`|<\(|>\(|\beval\b|\bexec\b|\bsource\b|\bxargs\b/.test(argStr);
|
|
5731
|
+
const hasWildcard = /[*?]/.test(argStr);
|
|
5732
|
+
let couldMatchProtected = hasShellTricks || hasWildcard;
|
|
5733
|
+
if (!couldMatchProtected) {
|
|
5734
|
+
const allProtected = [...this.protectedFiles, ...this.protectedPaths];
|
|
5735
|
+
for (const p of allProtected) {
|
|
5736
|
+
const core = p.replace(/[*?[\]{}]/g, "").replace(/^\.+/, "").toLowerCase();
|
|
5737
|
+
if (core && core.length >= 2 && argStr.includes(core)) {
|
|
5738
|
+
couldMatchProtected = true;
|
|
5739
|
+
break;
|
|
5740
|
+
}
|
|
5741
|
+
}
|
|
5742
|
+
}
|
|
5743
|
+
if (!couldMatchProtected) {
|
|
5744
|
+
return {
|
|
5745
|
+
decision: "ALLOW",
|
|
5746
|
+
reason: "No protected file/path referenced in arguments",
|
|
5747
|
+
confidence: 1
|
|
5748
|
+
};
|
|
5749
|
+
}
|
|
5710
5750
|
const sanitizedArgs = this.sanitizeArgs(args);
|
|
5711
5751
|
const userMessage = JSON.stringify({
|
|
5712
5752
|
tool: toolName,
|
package/dist/init.js
CHANGED
|
@@ -392,7 +392,7 @@ function installGeminiConfig(clientDir, guardCmd, auditCmd, _stopCmd, wrappedMcp
|
|
|
392
392
|
console.log(` ${existingStr ? "Updated" : "Created"} ${settingsPath}`);
|
|
393
393
|
return "installed";
|
|
394
394
|
}
|
|
395
|
-
function installOpenClawConfig(
|
|
395
|
+
function installOpenClawConfig(_clientDir) {
|
|
396
396
|
const yamlPath = resolve("openclaw.yaml");
|
|
397
397
|
const pluginLine = "@solongate/openclaw-plugin";
|
|
398
398
|
if (existsSync(yamlPath)) {
|
|
@@ -400,11 +400,30 @@ function installOpenClawConfig(clientDir) {
|
|
|
400
400
|
if (content.includes(pluginLine)) {
|
|
401
401
|
return "skipped";
|
|
402
402
|
}
|
|
403
|
+
if (content.includes("plugins:")) {
|
|
404
|
+
const updated = content.replace(
|
|
405
|
+
/^(plugins:\s*\n)/m,
|
|
406
|
+
`$1 - "${pluginLine}"
|
|
407
|
+
`
|
|
408
|
+
);
|
|
409
|
+
writeFileSync(yamlPath, updated);
|
|
410
|
+
} else {
|
|
411
|
+
const separator = content.endsWith("\n") ? "" : "\n";
|
|
412
|
+
writeFileSync(yamlPath, content + `${separator}
|
|
413
|
+
plugins:
|
|
414
|
+
- "${pluginLine}"
|
|
415
|
+
`);
|
|
416
|
+
}
|
|
417
|
+
} else {
|
|
418
|
+
writeFileSync(yamlPath, `# OpenClaw configuration
|
|
419
|
+
# SolonGate security plugin \u2014 manage policies at https://dashboard.solongate.com
|
|
420
|
+
|
|
421
|
+
plugins:
|
|
422
|
+
- "${pluginLine}"
|
|
423
|
+
`);
|
|
403
424
|
}
|
|
404
|
-
console.log(`
|
|
405
|
-
console.log(`
|
|
406
|
-
console.log(` - "${pluginLine}"`);
|
|
407
|
-
console.log(` Then: npm install ${pluginLine}`);
|
|
425
|
+
console.log(` Updated openclaw.yaml with SolonGate plugin`);
|
|
426
|
+
console.log(` \u2192 Run: npm install ${pluginLine}`);
|
|
408
427
|
return "installed";
|
|
409
428
|
}
|
|
410
429
|
function ensureEnvFile() {
|
package/dist/lib.js
CHANGED
|
@@ -4006,6 +4006,27 @@ var AiJudge = class _AiJudge {
|
|
|
4006
4006
|
}
|
|
4007
4007
|
this.consecutiveFailures = 0;
|
|
4008
4008
|
}
|
|
4009
|
+
const argStr = JSON.stringify(args).toLowerCase();
|
|
4010
|
+
const hasShellTricks = /\$[\({]|`|<\(|>\(|\beval\b|\bexec\b|\bsource\b|\bxargs\b/.test(argStr);
|
|
4011
|
+
const hasWildcard = /[*?]/.test(argStr);
|
|
4012
|
+
let couldMatchProtected = hasShellTricks || hasWildcard;
|
|
4013
|
+
if (!couldMatchProtected) {
|
|
4014
|
+
const allProtected = [...this.protectedFiles, ...this.protectedPaths];
|
|
4015
|
+
for (const p of allProtected) {
|
|
4016
|
+
const core = p.replace(/[*?[\]{}]/g, "").replace(/^\.+/, "").toLowerCase();
|
|
4017
|
+
if (core && core.length >= 2 && argStr.includes(core)) {
|
|
4018
|
+
couldMatchProtected = true;
|
|
4019
|
+
break;
|
|
4020
|
+
}
|
|
4021
|
+
}
|
|
4022
|
+
}
|
|
4023
|
+
if (!couldMatchProtected) {
|
|
4024
|
+
return {
|
|
4025
|
+
decision: "ALLOW",
|
|
4026
|
+
reason: "No protected file/path referenced in arguments",
|
|
4027
|
+
confidence: 1
|
|
4028
|
+
};
|
|
4029
|
+
}
|
|
4009
4030
|
const sanitizedArgs = this.sanitizeArgs(args);
|
|
4010
4031
|
const userMessage = JSON.stringify({
|
|
4011
4032
|
tool: toolName,
|
package/hooks/guard.mjs
CHANGED
|
@@ -1194,6 +1194,30 @@ process.stdin.on('end', async () => {
|
|
|
1194
1194
|
}
|
|
1195
1195
|
}
|
|
1196
1196
|
|
|
1197
|
+
// Pre-filter: skip AI Judge if tool args clearly don't touch any protected file/path.
|
|
1198
|
+
// Only call LLM when there's a potential match or obfuscation attempt.
|
|
1199
|
+
const argStr = JSON.stringify(args).toLowerCase();
|
|
1200
|
+
const hasShellTricks = /\$[\({]|`|<\(|>\(|\beval\b|\bexec\b|\bsource\b|\bxargs\b/.test(argStr);
|
|
1201
|
+
const hasWildcard = /[*?]/.test(argStr);
|
|
1202
|
+
let couldMatchProtected = hasShellTricks || hasWildcard;
|
|
1203
|
+
if (!couldMatchProtected) {
|
|
1204
|
+
// Check if any protected file/path name (without glob chars) appears in args
|
|
1205
|
+
const allProtected = [...protectedFiles, ...protectedPathsList];
|
|
1206
|
+
for (const p of allProtected) {
|
|
1207
|
+
// Strip glob chars to get the core name: "*.env*" → "env", ".solongate" → "solongate"
|
|
1208
|
+
const core = p.replace(/[*?[\]{}]/g, '').replace(/^\.+/, '').toLowerCase();
|
|
1209
|
+
if (core && core.length >= 2 && argStr.includes(core)) {
|
|
1210
|
+
couldMatchProtected = true;
|
|
1211
|
+
break;
|
|
1212
|
+
}
|
|
1213
|
+
}
|
|
1214
|
+
}
|
|
1215
|
+
|
|
1216
|
+
// If args clearly don't reference any protected pattern, skip LLM — instant ALLOW
|
|
1217
|
+
if (!couldMatchProtected) {
|
|
1218
|
+
// No need to call AI Judge — nothing to protect here
|
|
1219
|
+
} else {
|
|
1220
|
+
|
|
1197
1221
|
const judgePayload = JSON.stringify({
|
|
1198
1222
|
tool: toolName,
|
|
1199
1223
|
arguments: args,
|
|
@@ -1262,6 +1286,8 @@ Respond with ONLY valid JSON: {"decision": "ALLOW" or "DENY", "reason": "brief e
|
|
|
1262
1286
|
}
|
|
1263
1287
|
// Auth/config errors (401, 403) → skip AI Judge, don't DENY
|
|
1264
1288
|
// Other LLM errors → skip too (policy engine already evaluated)
|
|
1289
|
+
|
|
1290
|
+
} // end else (couldMatchProtected)
|
|
1265
1291
|
} catch {
|
|
1266
1292
|
// Timeout or parse error → skip AI Judge (policy engine already passed)
|
|
1267
1293
|
}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@solongate/proxy",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.40.0",
|
|
4
4
|
"description": "AI tool security proxy — protect any AI tool server with customizable policies, path/command constraints, rate limiting, and audit logging. Zero code changes required.",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"bin": {
|