@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.
- package/dist/api-client/policies.d.ts +1 -1
- package/dist/commands/index.js +45 -4
- package/dist/index.js +49 -6
- package/hooks/guard.bundled.mjs +2 -2
- package/hooks/guard.mjs +10 -2
- package/package.json +7 -7
|
@@ -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. */
|
package/dist/commands/index.js
CHANGED
|
@@ -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");
|
package/hooks/guard.bundled.mjs
CHANGED
|
@@ -6579,7 +6579,7 @@ function sweepLegacyFlagDir() {
|
|
|
6579
6579
|
} catch {
|
|
6580
6580
|
}
|
|
6581
6581
|
}
|
|
6582
|
-
var HOOK_VERSION =
|
|
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 =
|
|
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
|
-
|
|
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.
|
|
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.
|
|
65
|
-
"@solongate/guard-linux-arm64": "0.83.
|
|
66
|
-
"@solongate/guard-darwin-x64": "0.83.
|
|
67
|
-
"@solongate/guard-darwin-arm64": "0.83.
|
|
68
|
-
"@solongate/guard-win32-x64": "0.83.
|
|
69
|
-
"@solongate/guard-win32-arm64": "0.83.
|
|
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",
|