@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 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(clientDir) {
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(` OpenClaw uses plugins, not hooks. Add to your openclaw.yaml:`);
822
- console.log(` plugins:`);
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(clientDir) {
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(` OpenClaw uses plugins, not hooks. Add to your openclaw.yaml:`);
405
- console.log(` plugins:`);
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.38.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": {