@solongate/proxy 0.83.43 → 0.83.45

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.
@@ -11,7 +11,7 @@ export declare function remove(id: string): Promise<{
11
11
  }>;
12
12
  export interface RuleSpec {
13
13
  toolPattern?: string;
14
- kind?: 'command' | 'path' | 'url' | 'tool';
14
+ kind?: 'command' | 'path' | 'filename' | 'url' | 'tool';
15
15
  value?: string;
16
16
  effect?: 'ALLOW' | 'DENY';
17
17
  /** Scopes the rule to a class of call. Absent means every class. */
@@ -693,10 +693,12 @@ var USAGE = usage("solongate policy", "manage cloud policies", [
693
693
  ["policy create <name>", "create a new empty policy"],
694
694
  ["policy delete <id>", "delete a policy"],
695
695
  ["policy show <id>", "show one policy (rules, mode)"],
696
- ["policy allow <id> [--command|--path|--url <val>]", "append an ALLOW rule"],
697
- ["policy deny <id> [--command|--path|--url <val>]", "append a DENY rule"],
696
+ ["policy allow <id> [--command|--path|--filename|--url <val>]", "append an ALLOW rule"],
697
+ ["policy deny <id> [--command|--path|--filename|--url <val>]", "append a DENY rule"],
698
698
  ["policy revoke <id> <ruleId>", "remove a rule"],
699
699
  ["policy active", "show the resolved active policy"],
700
+ ["policy mode <id> <denylist|whitelist>", "switch deny-by-default / allow-by-default"],
701
+ ["policy rule <id> <ruleId> <enable|disable>", "turn one rule on or off without deleting it"],
700
702
  ["policy activate <id> | --off", "pin the active policy, or enforce nothing"],
701
703
  ["policy dry-run <id|file.json> [--limit N] [--mode denylist|whitelist]", "replay recent traffic against rules"]
702
704
  ], "Add --json to any read command for machine-readable output.");
@@ -761,11 +763,11 @@ async function run(argv) {
761
763
  case "deny": {
762
764
  const effect = sub === "allow" ? "ALLOW" : "DENY";
763
765
  const id = positionals[1];
764
- if (!id) return err(` Usage: policy ${sub} <id> [--command|--path|--url <val>]`), 1;
766
+ if (!id) return err(` Usage: policy ${sub} <id> [--command|--path|--filename|--url <val>]`), 1;
765
767
  const toolPattern = "*";
766
768
  let kind = "tool";
767
769
  let value;
768
- for (const k of ["command", "path", "url"]) {
770
+ for (const k of ["command", "path", "filename", "url"]) {
769
771
  const v = flagStr(flags, k);
770
772
  if (v !== void 0) {
771
773
  kind = k;
@@ -792,6 +794,45 @@ async function run(argv) {
792
794
  else err(green(` \u2713 ${effect} rule added`) + dim(` (${res.rule?.id})`));
793
795
  return 0;
794
796
  }
797
+ case "mode": {
798
+ const id = positionals[1];
799
+ const mode = (positionals[2] ?? "").toLowerCase();
800
+ if (!id || mode !== "denylist" && mode !== "whitelist") {
801
+ return err(" Usage: policy mode <id> <denylist|whitelist>"), 1;
802
+ }
803
+ const p = await api.policies.get(id);
804
+ const saved = await api.policies.update(id, { ...p, mode });
805
+ if (json) return printJson(saved), 0;
806
+ err(green(` \u2713 ${saved.id} is now a ${mode} policy`));
807
+ if (mode === "whitelist" && (saved.rules?.length ?? 0) === 0) {
808
+ err(dim(" It has no ALLOW rules, so it currently blocks everything."));
809
+ }
810
+ return 0;
811
+ }
812
+ case "rule": {
813
+ const id = positionals[1];
814
+ const ruleId = positionals[2];
815
+ const action = (positionals[3] ?? "").toLowerCase();
816
+ if (!id || !ruleId || action !== "enable" && action !== "disable") {
817
+ return err(" Usage: policy rule <id> <ruleId> <enable|disable>"), 1;
818
+ }
819
+ const p = await api.policies.get(id);
820
+ const rules = p.rules ?? [];
821
+ const target = rules.find((r) => r.id === ruleId);
822
+ if (!target) {
823
+ err(` No rule "${ruleId}" in ${id}`);
824
+ if (rules.length) {
825
+ err(dim(" Rules in this policy:"));
826
+ for (const r of rules) err(` ${dim("\u2022")} ${String(r.id)}`);
827
+ }
828
+ return 1;
829
+ }
830
+ const next = rules.map((r) => r.id === ruleId ? { ...r, enabled: action === "enable" } : r);
831
+ const saved = await api.policies.update(id, { ...p, rules: next });
832
+ if (json) return printJson(saved), 0;
833
+ err(green(` \u2713 Rule ${ruleId} ${action}d`));
834
+ return 0;
835
+ }
795
836
  case "revoke": {
796
837
  const id = positionals[1];
797
838
  const ruleId = positionals[2];
package/dist/index.js CHANGED
@@ -13055,11 +13055,11 @@ async function run2(argv) {
13055
13055
  case "deny": {
13056
13056
  const effect = sub === "allow" ? "ALLOW" : "DENY";
13057
13057
  const id = positionals[1];
13058
- if (!id) return err(` Usage: policy ${sub} <id> [--command|--path|--url <val>]`), 1;
13058
+ if (!id) return err(` Usage: policy ${sub} <id> [--command|--path|--filename|--url <val>]`), 1;
13059
13059
  const toolPattern = "*";
13060
13060
  let kind = "tool";
13061
13061
  let value;
13062
- for (const k of ["command", "path", "url"]) {
13062
+ for (const k of ["command", "path", "filename", "url"]) {
13063
13063
  const v = flagStr(flags, k);
13064
13064
  if (v !== void 0) {
13065
13065
  kind = k;
@@ -13086,6 +13086,45 @@ async function run2(argv) {
13086
13086
  else err(green(` \u2713 ${effect} rule added`) + dim(` (${res.rule?.id})`));
13087
13087
  return 0;
13088
13088
  }
13089
+ case "mode": {
13090
+ const id = positionals[1];
13091
+ const mode = (positionals[2] ?? "").toLowerCase();
13092
+ if (!id || mode !== "denylist" && mode !== "whitelist") {
13093
+ return err(" Usage: policy mode <id> <denylist|whitelist>"), 1;
13094
+ }
13095
+ const p = await api.policies.get(id);
13096
+ const saved = await api.policies.update(id, { ...p, mode });
13097
+ if (json) return printJson(saved), 0;
13098
+ err(green(` \u2713 ${saved.id} is now a ${mode} policy`));
13099
+ if (mode === "whitelist" && (saved.rules?.length ?? 0) === 0) {
13100
+ err(dim(" It has no ALLOW rules, so it currently blocks everything."));
13101
+ }
13102
+ return 0;
13103
+ }
13104
+ case "rule": {
13105
+ const id = positionals[1];
13106
+ const ruleId = positionals[2];
13107
+ const action = (positionals[3] ?? "").toLowerCase();
13108
+ if (!id || !ruleId || action !== "enable" && action !== "disable") {
13109
+ return err(" Usage: policy rule <id> <ruleId> <enable|disable>"), 1;
13110
+ }
13111
+ const p = await api.policies.get(id);
13112
+ const rules = p.rules ?? [];
13113
+ const target = rules.find((r) => r.id === ruleId);
13114
+ if (!target) {
13115
+ err(` No rule "${ruleId}" in ${id}`);
13116
+ if (rules.length) {
13117
+ err(dim(" Rules in this policy:"));
13118
+ for (const r of rules) err(` ${dim("\u2022")} ${String(r.id)}`);
13119
+ }
13120
+ return 1;
13121
+ }
13122
+ const next = rules.map((r) => r.id === ruleId ? { ...r, enabled: action === "enable" } : r);
13123
+ const saved = await api.policies.update(id, { ...p, rules: next });
13124
+ if (json) return printJson(saved), 0;
13125
+ err(green(` \u2713 Rule ${ruleId} ${action}d`));
13126
+ return 0;
13127
+ }
13089
13128
  case "revoke": {
13090
13129
  const id = positionals[1];
13091
13130
  const ruleId = positionals[2];
@@ -13181,10 +13220,12 @@ var init_policy = __esm({
13181
13220
  ["policy create <name>", "create a new empty policy"],
13182
13221
  ["policy delete <id>", "delete a policy"],
13183
13222
  ["policy show <id>", "show one policy (rules, mode)"],
13184
- ["policy allow <id> [--command|--path|--url <val>]", "append an ALLOW rule"],
13185
- ["policy deny <id> [--command|--path|--url <val>]", "append a DENY rule"],
13223
+ ["policy allow <id> [--command|--path|--filename|--url <val>]", "append an ALLOW rule"],
13224
+ ["policy deny <id> [--command|--path|--filename|--url <val>]", "append a DENY rule"],
13186
13225
  ["policy revoke <id> <ruleId>", "remove a rule"],
13187
13226
  ["policy active", "show the resolved active policy"],
13227
+ ["policy mode <id> <denylist|whitelist>", "switch deny-by-default / allow-by-default"],
13228
+ ["policy rule <id> <ruleId> <enable|disable>", "turn one rule on or off without deleting it"],
13188
13229
  ["policy activate <id> | --off", "pin the active policy, or enforce nothing"],
13189
13230
  ["policy dry-run <id|file.json> [--limit N] [--mode denylist|whitelist]", "replay recent traffic against rules"]
13190
13231
  ], "Add --json to any read command for machine-readable output.");
@@ -17739,9 +17780,11 @@ function printHelp() {
17739
17780
  cmd("policy create <name>", "create a new empty policy");
17740
17781
  cmd("policy delete <id>", "delete a policy");
17741
17782
  cmd("policy show <id>", "show one policy (rules, mode)");
17742
- cmd("policy allow <id> [--command|--path|--url <val>]", "add an ALLOW rule");
17743
- cmd("policy deny <id> [--command|--path|--url <val>]", "add a DENY rule");
17783
+ cmd("policy allow <id> [--command|--path|--filename|--url <val>]", "add an ALLOW rule");
17784
+ cmd("policy deny <id> [--command|--path|--filename|--url <val>]", "add a DENY rule");
17744
17785
  cmd(" --permission READ,WRITE,EXECUTE,NETWORK", "scope an allow/deny to a class of call");
17786
+ cmd("policy mode <id> <denylist|whitelist>", "switch deny-by-default / allow-by-default");
17787
+ cmd("policy rule <id> <ruleId> <enable|disable>", "turn one rule on or off without deleting it");
17745
17788
  cmd("policy revoke <id> <ruleId>", "remove a rule");
17746
17789
  cmd("policy activate <id> | --off", "pin the active policy, or enforce nothing");
17747
17790
  cmd("policy active", "show the resolved active policy");
@@ -6579,7 +6579,7 @@ function sweepLegacyFlagDir() {
6579
6579
  } catch {
6580
6580
  }
6581
6581
  }
6582
- var HOOK_VERSION = 82;
6582
+ var HOOK_VERSION = 83;
6583
6583
  var SG_REFRESH_ARG = process.argv.includes("--sg-refresh-policy");
6584
6584
  var SG_STDIN = SG_REFRESH_ARG ? "" : (() => {
6585
6585
  try {
@@ -6798,7 +6798,7 @@ function guessPermission(toolName) {
6798
6798
  return "EXECUTE";
6799
6799
  if (name.includes("fetch") || name.includes("http") || name.includes("request") || name.includes("curl") || name.includes("network") || name.includes("download") || name.includes("upload") || name === "websearch")
6800
6800
  return "NETWORK";
6801
- if (name.includes("write") || name.includes("create") || name.includes("delete") || name.includes("update") || name.includes("set") || name.includes("edit") || name.includes("remove") || name.includes("insert"))
6801
+ if (name.includes("write") || name.includes("create") || name.includes("delete") || name.includes("update") || name.includes("set") || name.includes("edit") || name.includes("remove") || name.includes("insert") || name.includes("replace") || name.includes("patch") || name.includes("modify") || name.includes("append") || name.includes("overwrite") || name.includes("rename") || name.includes("move") || name.includes("mkdir") || name.includes("touch"))
6802
6802
  return "WRITE";
6803
6803
  return "READ";
6804
6804
  }
package/hooks/guard.mjs CHANGED
@@ -82,7 +82,7 @@ import { createHash } from 'node:crypto';
82
82
  // the installed hook self-updates when the cloud version is higher (see
83
83
  // maybeSelfUpdate). This is what makes guard fixes propagate without a manual
84
84
  // reinstall — the same trust model as the OPA WASM this hook already runs.
85
- const HOOK_VERSION = 82;
85
+ const HOOK_VERSION = 83;
86
86
 
87
87
  // ── The Go guard, when there is one and it is the right one ──────────────────
88
88
  //
@@ -443,7 +443,15 @@ function guessPermission(toolName) {
443
443
  if (name === 'apply_patch' || name === 'applypatch') return 'WRITE';
444
444
  if (name.includes('exec') || name.includes('shell') || name.includes('run') || name.includes('eval') || name === 'bash') return 'EXECUTE';
445
445
  if (name.includes('fetch') || name.includes('http') || name.includes('request') || name.includes('curl') || name.includes('network') || name.includes('download') || name.includes('upload') || name === 'websearch') return 'NETWORK';
446
- if (name.includes('write') || name.includes('create') || name.includes('delete') || name.includes('update') || name.includes('set') || name.includes('edit') || name.includes('remove') || name.includes('insert')) return 'WRITE';
446
+ // `replace`, `patch` and `modify` are here because Antigravity's edit tool is
447
+ // `replace_file_content`, which matched none of the words above and fell
448
+ // through to READ. A WRITE-scoped rule therefore missed the tool the agent
449
+ // actually edits with: "deny writes under config/" left it free to rewrite
450
+ // config/ all day. The list is words a tool NAME uses for changing something,
451
+ // not an enumeration of known tools, and it errs wide because the name that
452
+ // classifies wrong is silently unguarded rather than loudly broken.
453
+ if (name.includes('write') || name.includes('create') || name.includes('delete') || name.includes('update') || name.includes('set') || name.includes('edit') || name.includes('remove') || name.includes('insert') ||
454
+ name.includes('replace') || name.includes('patch') || name.includes('modify') || name.includes('append') || name.includes('overwrite') || name.includes('rename') || name.includes('move') || name.includes('mkdir') || name.includes('touch')) return 'WRITE';
447
455
  return 'READ';
448
456
  }
449
457
 
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@solongate/proxy",
3
- "version": "0.83.43",
3
+ "version": "0.83.45",
4
4
  "description": "AI tool security proxy: protect any AI tool server with customizable policies, path/command constraints, rate limiting, and audit logging. No code changes required.",
5
5
  "type": "module",
6
6
  "bin": {
@@ -61,12 +61,12 @@
61
61
  "node": ">=20.0.0"
62
62
  },
63
63
  "optionalDependencies": {
64
- "@solongate/guard-linux-x64": "0.83.43",
65
- "@solongate/guard-linux-arm64": "0.83.43",
66
- "@solongate/guard-darwin-x64": "0.83.43",
67
- "@solongate/guard-darwin-arm64": "0.83.43",
68
- "@solongate/guard-win32-x64": "0.83.43",
69
- "@solongate/guard-win32-arm64": "0.83.43"
64
+ "@solongate/guard-linux-x64": "0.83.45",
65
+ "@solongate/guard-linux-arm64": "0.83.45",
66
+ "@solongate/guard-darwin-x64": "0.83.45",
67
+ "@solongate/guard-darwin-arm64": "0.83.45",
68
+ "@solongate/guard-win32-x64": "0.83.45",
69
+ "@solongate/guard-win32-arm64": "0.83.45"
70
70
  },
71
71
  "dependencies": {
72
72
  "@modelcontextprotocol/sdk": "^1.26.0",