@solongate/proxy 0.82.47 → 0.82.49
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 +117 -24
- package/dist/lib.js +9 -2
- package/dist/policy-engine/defaults.d.ts +3 -2
- package/dist/tui/index.js +107 -21
- package/package.json +1 -1
package/dist/index.js
CHANGED
|
@@ -9222,6 +9222,7 @@ function PoliciesPanel({ focused }) {
|
|
|
9222
9222
|
const [editing, setEditing] = useState4(false);
|
|
9223
9223
|
const [editVal, setEditVal] = useState4("");
|
|
9224
9224
|
const [status, setStatus] = useState4(null);
|
|
9225
|
+
const [pendingDel, setPendingDel] = useState4(null);
|
|
9225
9226
|
const selected = policies[Math.min(pi, Math.max(0, policies.length - 1))];
|
|
9226
9227
|
const detail = useLoader(() => selected ? api.policies.get(selected.id) : Promise.resolve(null), [selected?.id]);
|
|
9227
9228
|
useEffect4(() => {
|
|
@@ -9244,6 +9245,11 @@ function PoliciesPanel({ focused }) {
|
|
|
9244
9245
|
};
|
|
9245
9246
|
const save = async () => {
|
|
9246
9247
|
if (!selected || !detail.data) return;
|
|
9248
|
+
const blanket = rules.findIndex(isBlanketRule);
|
|
9249
|
+
if (blanket !== -1) {
|
|
9250
|
+
setStatus(`\u2717 rule ${blanket + 1} matches EVERYTHING \u2014 pick a Constraint + Match value, or narrow Permissions`);
|
|
9251
|
+
return;
|
|
9252
|
+
}
|
|
9247
9253
|
setStatus("Saving\u2026");
|
|
9248
9254
|
try {
|
|
9249
9255
|
const res = await api.policies.update(selected.id, {
|
|
@@ -9292,9 +9298,13 @@ function PoliciesPanel({ focused }) {
|
|
|
9292
9298
|
return;
|
|
9293
9299
|
}
|
|
9294
9300
|
if (view === "list") {
|
|
9295
|
-
if (key.upArrow)
|
|
9296
|
-
|
|
9297
|
-
|
|
9301
|
+
if (key.upArrow) {
|
|
9302
|
+
setPendingDel(null);
|
|
9303
|
+
setPi((n) => Math.max(0, n - 1));
|
|
9304
|
+
} else if (key.downArrow) {
|
|
9305
|
+
setPendingDel(null);
|
|
9306
|
+
setPi((n) => Math.min(policies.length - 1, n + 1));
|
|
9307
|
+
} else if (key.return || key.rightArrow) {
|
|
9298
9308
|
setStatus(null);
|
|
9299
9309
|
setView("rules");
|
|
9300
9310
|
} else if (input === "a") {
|
|
@@ -9314,6 +9324,22 @@ function PoliciesPanel({ focused }) {
|
|
|
9314
9324
|
setStatus("\u2713 active policy: NONE \u2014 no rules enforced, w/b grants are refused");
|
|
9315
9325
|
activeQ.reload();
|
|
9316
9326
|
}).catch((e) => setStatus("\u2717 " + (e instanceof Error ? e.message : String(e))));
|
|
9327
|
+
} else if (input === "d") {
|
|
9328
|
+
if (!selected) return;
|
|
9329
|
+
if (pendingDel !== selected.id) {
|
|
9330
|
+
setPendingDel(selected.id);
|
|
9331
|
+
setStatus(`\u26A0 d again to DELETE policy "${selected.name}" \u2014 cannot be undone`);
|
|
9332
|
+
return;
|
|
9333
|
+
}
|
|
9334
|
+
setPendingDel(null);
|
|
9335
|
+
setStatus("Deleting\u2026");
|
|
9336
|
+
const name = selected.name;
|
|
9337
|
+
void api.policies.remove(selected.id).then(() => {
|
|
9338
|
+
setStatus(`\u2713 deleted policy "${name}"`);
|
|
9339
|
+
setPi(0);
|
|
9340
|
+
list5.reload();
|
|
9341
|
+
activeQ.reload();
|
|
9342
|
+
}).catch((e) => setStatus("\u2717 " + (e instanceof Error ? e.message : String(e))));
|
|
9317
9343
|
}
|
|
9318
9344
|
return;
|
|
9319
9345
|
}
|
|
@@ -9329,7 +9355,7 @@ function PoliciesPanel({ focused }) {
|
|
|
9329
9355
|
} else if (input === " ") {
|
|
9330
9356
|
if (rules[ri]) mutate(rules.map((r, i) => i === ri ? { ...r, enabled: !r.enabled } : r));
|
|
9331
9357
|
} else if (input === "e") {
|
|
9332
|
-
if (rules[ri]) mutate(rules.map((r, i) => i === ri ?
|
|
9358
|
+
if (rules[ri]) mutate(rules.map((r, i) => i === ri ? migrateEffect(r, r.effect === "ALLOW" ? "DENY" : "ALLOW") : r));
|
|
9333
9359
|
} else if (input === "d") {
|
|
9334
9360
|
if (rules[ri]) {
|
|
9335
9361
|
const next = rules.filter((_, i) => i !== ri);
|
|
@@ -9360,9 +9386,16 @@ function PoliciesPanel({ focused }) {
|
|
|
9360
9386
|
else if (key.upArrow) setFi((n) => Math.max(0, n - 1));
|
|
9361
9387
|
else if (key.downArrow) setFi((n) => Math.min(FIELDS.length - 1, n + 1));
|
|
9362
9388
|
else if (field.kind === "effect" && (input === " " || key.rightArrow)) {
|
|
9363
|
-
mutate(rules.map((r, i) => i === ri ?
|
|
9389
|
+
mutate(rules.map((r, i) => i === ri ? migrateEffect(r, r.effect === "ALLOW" ? "DENY" : "ALLOW") : r));
|
|
9364
9390
|
} else if (field.kind === "enabled" && (input === " " || key.rightArrow)) {
|
|
9365
9391
|
mutate(rules.map((r, i) => i === ri ? { ...r, enabled: !r.enabled } : r));
|
|
9392
|
+
} else if (field.kind === "ctype" && (input === " " || key.rightArrow || key.leftArrow)) {
|
|
9393
|
+
const dir = key.leftArrow ? -1 : 1;
|
|
9394
|
+
const nextT = CTYPES[(CTYPES.indexOf(currentCType(rule2)) + dir + CTYPES.length) % CTYPES.length];
|
|
9395
|
+
mutate(rules.map((r, i) => i === ri ? setCType(r, nextT) : r));
|
|
9396
|
+
} else if (field.kind === "perms" && (input === "r" || input === "w" || input === "e" || input === "n")) {
|
|
9397
|
+
const p = { r: "READ", w: "WRITE", e: "EXECUTE", n: "NETWORK" }[input];
|
|
9398
|
+
mutate(rules.map((rr, i) => i === ri ? togglePerm(rr, p) : rr));
|
|
9366
9399
|
} else if (field.kind === "priority" && (key.rightArrow || key.leftArrow)) {
|
|
9367
9400
|
const d = key.rightArrow ? 1 : -1;
|
|
9368
9401
|
mutate(rules.map((r, i) => i === ri ? { ...r, priority: Math.max(0, r.priority + d) } : r));
|
|
@@ -9392,7 +9425,7 @@ function PoliciesPanel({ focused }) {
|
|
|
9392
9425
|
/* @__PURE__ */ jsx4(Text4, { color: theme.dim, children: activeBy ? ` (${activeBy})` : "" })
|
|
9393
9426
|
] }) : /* @__PURE__ */ jsx4(Text4, { color: theme.bad, bold: true, children: "\u25CB NONE \u2014 nothing enforced, grants refused" })
|
|
9394
9427
|
] }),
|
|
9395
|
-
/* @__PURE__ */ jsx4(Text4, { color: theme.dim, wrap: "truncate", children: focused ? `\u2191\u2193 select \xB7 enter open \xB7 n new
|
|
9428
|
+
/* @__PURE__ */ jsx4(Text4, { color: theme.dim, wrap: "truncate", children: focused ? `\u2191\u2193 select \xB7 enter open \xB7 n new \xB7 a activate \xB7 x deactivate \xB7 d delete \xB7 ^R refresh${lAbove ? ` \xB7 \u25B2${lAbove}` : ""}${lBelow ? ` \xB7 \u25BC${lBelow}` : ""}` : "press \u2192 to browse" }),
|
|
9396
9429
|
creating !== null ? /* @__PURE__ */ jsxs4(Box4, { children: [
|
|
9397
9430
|
/* @__PURE__ */ jsx4(Text4, { color: theme.warn, children: "new policy name: " }),
|
|
9398
9431
|
/* @__PURE__ */ jsx4(TextInput3, { value: creating, onChange: setCreating, onSubmit: commitNewPolicy })
|
|
@@ -9467,7 +9500,7 @@ function PoliciesPanel({ focused }) {
|
|
|
9467
9500
|
/* @__PURE__ */ jsx4(Text4, { color: theme.dim, children: rule?.id ?? "" }),
|
|
9468
9501
|
dirtyTag
|
|
9469
9502
|
] }),
|
|
9470
|
-
/* @__PURE__ */ jsx4(Text4, { color: theme.dim, children: "\u2191\u2193 field \xB7 enter edit text \xB7 space/\u2192 toggle \xB7 s save \xB7 \u2190 back" }),
|
|
9503
|
+
/* @__PURE__ */ jsx4(Text4, { color: theme.dim, children: "\u2191\u2193 field \xB7 enter edit text \xB7 space/\u2192 toggle \xB7 r/w/e/n perms \xB7 s save \xB7 \u2190 back" }),
|
|
9471
9504
|
/* @__PURE__ */ jsx4(Box4, { marginTop: 1, flexDirection: "column", children: FIELDS.map((f, i) => {
|
|
9472
9505
|
const val = rule ? f.get(rule) : "";
|
|
9473
9506
|
const active2 = i === fi;
|
|
@@ -9496,7 +9529,7 @@ function PoliciesPanel({ focused }) {
|
|
|
9496
9529
|
status ? /* @__PURE__ */ jsx4(Text4, { color: status.startsWith("\u2717") ? theme.bad : theme.ok, children: status }) : null
|
|
9497
9530
|
] });
|
|
9498
9531
|
}
|
|
9499
|
-
var
|
|
9532
|
+
var CTYPES, CTYPE_GROUP, currentCType, clearConstraints, setCType, cValue, setCValue, migrateEffect, PERMS, permList, permIsAny, togglePerm, permDisplay, ruleHasValues, isBlanketRule, FIELDS;
|
|
9500
9533
|
var init_Policies = __esm({
|
|
9501
9534
|
"src/tui/panels/Policies.tsx"() {
|
|
9502
9535
|
"use strict";
|
|
@@ -9506,26 +9539,79 @@ var init_Policies = __esm({
|
|
|
9506
9539
|
init_components();
|
|
9507
9540
|
init_hooks();
|
|
9508
9541
|
init_theme();
|
|
9509
|
-
|
|
9510
|
-
|
|
9542
|
+
CTYPES = ["none", "command", "path", "filename", "url"];
|
|
9543
|
+
CTYPE_GROUP = {
|
|
9544
|
+
command: "commandConstraints",
|
|
9545
|
+
path: "pathConstraints",
|
|
9546
|
+
filename: "filenameConstraints",
|
|
9547
|
+
url: "urlConstraints"
|
|
9548
|
+
};
|
|
9549
|
+
currentCType = (r) => {
|
|
9550
|
+
if (r.filenameConstraints) return "filename";
|
|
9551
|
+
if (r.urlConstraints) return "url";
|
|
9552
|
+
if (r.commandConstraints) return "command";
|
|
9553
|
+
if (r.pathConstraints) return "path";
|
|
9554
|
+
return "none";
|
|
9555
|
+
};
|
|
9556
|
+
clearConstraints = (r) => ({
|
|
9557
|
+
...r,
|
|
9558
|
+
commandConstraints: void 0,
|
|
9559
|
+
pathConstraints: void 0,
|
|
9560
|
+
filenameConstraints: void 0,
|
|
9561
|
+
urlConstraints: void 0
|
|
9562
|
+
});
|
|
9563
|
+
setCType = (r, t) => {
|
|
9564
|
+
const base = clearConstraints(r);
|
|
9565
|
+
if (t === "none") return base;
|
|
9566
|
+
const listKey = r.effect === "ALLOW" ? "allowed" : "denied";
|
|
9567
|
+
return { ...base, [CTYPE_GROUP[t]]: { [listKey]: [] } };
|
|
9568
|
+
};
|
|
9569
|
+
cValue = (r) => {
|
|
9570
|
+
const t = currentCType(r);
|
|
9571
|
+
if (t === "none") return "";
|
|
9572
|
+
const c2 = r[CTYPE_GROUP[t]];
|
|
9573
|
+
return [...c2?.allowed ?? [], ...c2?.denied ?? []].join(", ");
|
|
9574
|
+
};
|
|
9575
|
+
setCValue = (r, val) => {
|
|
9576
|
+
const t = currentCType(r);
|
|
9577
|
+
if (t === "none") return r;
|
|
9511
9578
|
const arr = val.split(",").map((s) => s.trim()).filter(Boolean);
|
|
9512
|
-
const
|
|
9513
|
-
return { ...r, [
|
|
9579
|
+
const listKey = r.effect === "ALLOW" ? "allowed" : "denied";
|
|
9580
|
+
return { ...r, [CTYPE_GROUP[t]]: { [listKey]: arr } };
|
|
9581
|
+
};
|
|
9582
|
+
migrateEffect = (r, nextEffect) => {
|
|
9583
|
+
const flipped = { ...r, effect: nextEffect };
|
|
9584
|
+
const t = currentCType(r);
|
|
9585
|
+
if (t === "none") return flipped;
|
|
9586
|
+
const c2 = r[CTYPE_GROUP[t]];
|
|
9587
|
+
const items = [...c2?.allowed ?? [], ...c2?.denied ?? []];
|
|
9588
|
+
const listKey = nextEffect === "ALLOW" ? "allowed" : "denied";
|
|
9589
|
+
return { ...flipped, [CTYPE_GROUP[t]]: { [listKey]: items } };
|
|
9514
9590
|
};
|
|
9591
|
+
PERMS = ["READ", "WRITE", "EXECUTE", "NETWORK"];
|
|
9592
|
+
permList = (r) => !r.permission ? [...PERMS] : Array.isArray(r.permission) ? r.permission : [r.permission];
|
|
9593
|
+
permIsAny = (r) => PERMS.every((p) => permList(r).includes(p));
|
|
9594
|
+
togglePerm = (r, p) => {
|
|
9595
|
+
const cur = permList(r);
|
|
9596
|
+
let next = cur.includes(p) ? cur.filter((x) => x !== p) : [...cur, p];
|
|
9597
|
+
if (next.length === 0) next = [p];
|
|
9598
|
+
return { ...r, permission: PERMS.every((x) => next.includes(x)) ? void 0 : next };
|
|
9599
|
+
};
|
|
9600
|
+
permDisplay = (r) => permIsAny(r) ? "any (READ WRITE EXECUTE NETWORK)" : permList(r).join(" ");
|
|
9601
|
+
ruleHasValues = (r) => {
|
|
9602
|
+
const t = currentCType(r);
|
|
9603
|
+
if (t === "none") return false;
|
|
9604
|
+
const c2 = r[CTYPE_GROUP[t]];
|
|
9605
|
+
return (c2?.allowed?.length ?? 0) + (c2?.denied?.length ?? 0) > 0;
|
|
9606
|
+
};
|
|
9607
|
+
isBlanketRule = (r) => !ruleHasValues(r) && permIsAny(r);
|
|
9515
9608
|
FIELDS = [
|
|
9516
9609
|
{ label: "Effect", kind: "effect", get: (r) => r.effect },
|
|
9517
|
-
{ label: "
|
|
9610
|
+
{ label: "Constraint", kind: "ctype", get: (r) => currentCType(r) },
|
|
9611
|
+
{ label: "Permissions", kind: "perms", get: permDisplay },
|
|
9518
9612
|
{ label: "Priority", kind: "priority", get: (r) => String(r.priority) },
|
|
9519
|
-
{ label: "Tool pattern", kind: "text", get: (r) => r.toolPattern, set: (r, v) => ({ ...r, toolPattern: v || "*" }) },
|
|
9520
9613
|
{ label: "Description", kind: "text", get: (r) => r.description ?? "", set: (r, v) => ({ ...r, description: v }) },
|
|
9521
|
-
{ label: "
|
|
9522
|
-
{ label: "Cmd deny", kind: "text", get: (r) => constr(r, "commandConstraints", "denied"), set: (r, v) => setConstr(r, "commandConstraints", "denied", v) },
|
|
9523
|
-
{ label: "Path allow", kind: "text", get: (r) => constr(r, "pathConstraints", "allowed"), set: (r, v) => setConstr(r, "pathConstraints", "allowed", v) },
|
|
9524
|
-
{ label: "Path deny", kind: "text", get: (r) => constr(r, "pathConstraints", "denied"), set: (r, v) => setConstr(r, "pathConstraints", "denied", v) },
|
|
9525
|
-
{ label: "File allow", kind: "text", get: (r) => constr(r, "filenameConstraints", "allowed"), set: (r, v) => setConstr(r, "filenameConstraints", "allowed", v) },
|
|
9526
|
-
{ label: "File deny", kind: "text", get: (r) => constr(r, "filenameConstraints", "denied"), set: (r, v) => setConstr(r, "filenameConstraints", "denied", v) },
|
|
9527
|
-
{ label: "URL allow", kind: "text", get: (r) => constr(r, "urlConstraints", "allowed"), set: (r, v) => setConstr(r, "urlConstraints", "allowed", v) },
|
|
9528
|
-
{ label: "URL deny", kind: "text", get: (r) => constr(r, "urlConstraints", "denied"), set: (r, v) => setConstr(r, "urlConstraints", "denied", v) }
|
|
9614
|
+
{ label: "Match", kind: "text", get: cValue, set: setCValue }
|
|
9529
9615
|
];
|
|
9530
9616
|
}
|
|
9531
9617
|
});
|
|
@@ -13476,13 +13562,20 @@ function analyzeSecurityWarnings(policySet) {
|
|
|
13476
13562
|
const allowRules = policySet.rules.filter(
|
|
13477
13563
|
(r) => r.effect === "ALLOW" && r.enabled
|
|
13478
13564
|
);
|
|
13479
|
-
const
|
|
13565
|
+
const hasNoConstraint = (r) => {
|
|
13566
|
+
const groups = ["commandConstraints", "pathConstraints", "filenameConstraints", "urlConstraints"];
|
|
13567
|
+
return !groups.some((g) => {
|
|
13568
|
+
const c2 = r[g];
|
|
13569
|
+
return c2 ? (c2.allowed?.length ?? 0) + (c2.denied?.length ?? 0) > 0 : false;
|
|
13570
|
+
});
|
|
13571
|
+
};
|
|
13572
|
+
const wildcardAllows = allowRules.filter(hasNoConstraint);
|
|
13480
13573
|
if (wildcardAllows.length > 0) {
|
|
13481
13574
|
warnings.push({
|
|
13482
13575
|
level: "CRITICAL",
|
|
13483
13576
|
code: "WILDCARD_ALLOW",
|
|
13484
13577
|
message: UNSAFE_CONFIGURATION_WARNINGS.WILDCARD_ALLOW,
|
|
13485
|
-
recommendation: "
|
|
13578
|
+
recommendation: "Add command or path constraints so an ALLOW rule does not permit everything."
|
|
13486
13579
|
});
|
|
13487
13580
|
}
|
|
13488
13581
|
return warnings;
|
package/dist/lib.js
CHANGED
|
@@ -7019,13 +7019,20 @@ function analyzeSecurityWarnings(policySet) {
|
|
|
7019
7019
|
const allowRules = policySet.rules.filter(
|
|
7020
7020
|
(r) => r.effect === "ALLOW" && r.enabled
|
|
7021
7021
|
);
|
|
7022
|
-
const
|
|
7022
|
+
const hasNoConstraint = (r) => {
|
|
7023
|
+
const groups = ["commandConstraints", "pathConstraints", "filenameConstraints", "urlConstraints"];
|
|
7024
|
+
return !groups.some((g) => {
|
|
7025
|
+
const c = r[g];
|
|
7026
|
+
return c ? (c.allowed?.length ?? 0) + (c.denied?.length ?? 0) > 0 : false;
|
|
7027
|
+
});
|
|
7028
|
+
};
|
|
7029
|
+
const wildcardAllows = allowRules.filter(hasNoConstraint);
|
|
7023
7030
|
if (wildcardAllows.length > 0) {
|
|
7024
7031
|
warnings.push({
|
|
7025
7032
|
level: "CRITICAL",
|
|
7026
7033
|
code: "WILDCARD_ALLOW",
|
|
7027
7034
|
message: UNSAFE_CONFIGURATION_WARNINGS.WILDCARD_ALLOW,
|
|
7028
|
-
recommendation: "
|
|
7035
|
+
recommendation: "Add command or path constraints so an ALLOW rule does not permit everything."
|
|
7029
7036
|
});
|
|
7030
7037
|
}
|
|
7031
7038
|
return warnings;
|
|
@@ -11,8 +11,9 @@ export declare function createDefaultDenyPolicySet(): PolicySet;
|
|
|
11
11
|
*/
|
|
12
12
|
export declare function createPermissivePolicySet(): PolicySet;
|
|
13
13
|
/**
|
|
14
|
-
*
|
|
15
|
-
*
|
|
14
|
+
* Internal helper. Builds a read-only policy set scoped by an internal tool
|
|
15
|
+
* selector — fixed to "*" in the shipped product; no UI or CLI exposes it, so
|
|
16
|
+
* users can neither see nor change it. Allows reads for VERIFIED requests only.
|
|
16
17
|
*/
|
|
17
18
|
export declare function createReadOnlyPolicySet(toolPattern: string): PolicySet;
|
|
18
19
|
/**
|
package/dist/tui/index.js
CHANGED
|
@@ -2420,26 +2420,79 @@ function onSectionRequest(fn) {
|
|
|
2420
2420
|
|
|
2421
2421
|
// src/tui/panels/Policies.tsx
|
|
2422
2422
|
import { Fragment as Fragment3, jsx as jsx4, jsxs as jsxs4 } from "react/jsx-runtime";
|
|
2423
|
-
var
|
|
2424
|
-
var
|
|
2423
|
+
var CTYPES = ["none", "command", "path", "filename", "url"];
|
|
2424
|
+
var CTYPE_GROUP = {
|
|
2425
|
+
command: "commandConstraints",
|
|
2426
|
+
path: "pathConstraints",
|
|
2427
|
+
filename: "filenameConstraints",
|
|
2428
|
+
url: "urlConstraints"
|
|
2429
|
+
};
|
|
2430
|
+
var currentCType = (r) => {
|
|
2431
|
+
if (r.filenameConstraints) return "filename";
|
|
2432
|
+
if (r.urlConstraints) return "url";
|
|
2433
|
+
if (r.commandConstraints) return "command";
|
|
2434
|
+
if (r.pathConstraints) return "path";
|
|
2435
|
+
return "none";
|
|
2436
|
+
};
|
|
2437
|
+
var clearConstraints = (r) => ({
|
|
2438
|
+
...r,
|
|
2439
|
+
commandConstraints: void 0,
|
|
2440
|
+
pathConstraints: void 0,
|
|
2441
|
+
filenameConstraints: void 0,
|
|
2442
|
+
urlConstraints: void 0
|
|
2443
|
+
});
|
|
2444
|
+
var setCType = (r, t) => {
|
|
2445
|
+
const base = clearConstraints(r);
|
|
2446
|
+
if (t === "none") return base;
|
|
2447
|
+
const listKey = r.effect === "ALLOW" ? "allowed" : "denied";
|
|
2448
|
+
return { ...base, [CTYPE_GROUP[t]]: { [listKey]: [] } };
|
|
2449
|
+
};
|
|
2450
|
+
var cValue = (r) => {
|
|
2451
|
+
const t = currentCType(r);
|
|
2452
|
+
if (t === "none") return "";
|
|
2453
|
+
const c2 = r[CTYPE_GROUP[t]];
|
|
2454
|
+
return [...c2?.allowed ?? [], ...c2?.denied ?? []].join(", ");
|
|
2455
|
+
};
|
|
2456
|
+
var setCValue = (r, val) => {
|
|
2457
|
+
const t = currentCType(r);
|
|
2458
|
+
if (t === "none") return r;
|
|
2425
2459
|
const arr = val.split(",").map((s) => s.trim()).filter(Boolean);
|
|
2426
|
-
const
|
|
2427
|
-
return { ...r, [
|
|
2460
|
+
const listKey = r.effect === "ALLOW" ? "allowed" : "denied";
|
|
2461
|
+
return { ...r, [CTYPE_GROUP[t]]: { [listKey]: arr } };
|
|
2462
|
+
};
|
|
2463
|
+
var migrateEffect = (r, nextEffect) => {
|
|
2464
|
+
const flipped = { ...r, effect: nextEffect };
|
|
2465
|
+
const t = currentCType(r);
|
|
2466
|
+
if (t === "none") return flipped;
|
|
2467
|
+
const c2 = r[CTYPE_GROUP[t]];
|
|
2468
|
+
const items = [...c2?.allowed ?? [], ...c2?.denied ?? []];
|
|
2469
|
+
const listKey = nextEffect === "ALLOW" ? "allowed" : "denied";
|
|
2470
|
+
return { ...flipped, [CTYPE_GROUP[t]]: { [listKey]: items } };
|
|
2471
|
+
};
|
|
2472
|
+
var PERMS = ["READ", "WRITE", "EXECUTE", "NETWORK"];
|
|
2473
|
+
var permList = (r) => !r.permission ? [...PERMS] : Array.isArray(r.permission) ? r.permission : [r.permission];
|
|
2474
|
+
var permIsAny = (r) => PERMS.every((p) => permList(r).includes(p));
|
|
2475
|
+
var togglePerm = (r, p) => {
|
|
2476
|
+
const cur = permList(r);
|
|
2477
|
+
let next = cur.includes(p) ? cur.filter((x) => x !== p) : [...cur, p];
|
|
2478
|
+
if (next.length === 0) next = [p];
|
|
2479
|
+
return { ...r, permission: PERMS.every((x) => next.includes(x)) ? void 0 : next };
|
|
2480
|
+
};
|
|
2481
|
+
var permDisplay = (r) => permIsAny(r) ? "any (READ WRITE EXECUTE NETWORK)" : permList(r).join(" ");
|
|
2482
|
+
var ruleHasValues = (r) => {
|
|
2483
|
+
const t = currentCType(r);
|
|
2484
|
+
if (t === "none") return false;
|
|
2485
|
+
const c2 = r[CTYPE_GROUP[t]];
|
|
2486
|
+
return (c2?.allowed?.length ?? 0) + (c2?.denied?.length ?? 0) > 0;
|
|
2428
2487
|
};
|
|
2488
|
+
var isBlanketRule = (r) => !ruleHasValues(r) && permIsAny(r);
|
|
2429
2489
|
var FIELDS = [
|
|
2430
2490
|
{ label: "Effect", kind: "effect", get: (r) => r.effect },
|
|
2431
|
-
{ label: "
|
|
2491
|
+
{ label: "Constraint", kind: "ctype", get: (r) => currentCType(r) },
|
|
2492
|
+
{ label: "Permissions", kind: "perms", get: permDisplay },
|
|
2432
2493
|
{ label: "Priority", kind: "priority", get: (r) => String(r.priority) },
|
|
2433
|
-
{ label: "Tool pattern", kind: "text", get: (r) => r.toolPattern, set: (r, v) => ({ ...r, toolPattern: v || "*" }) },
|
|
2434
2494
|
{ label: "Description", kind: "text", get: (r) => r.description ?? "", set: (r, v) => ({ ...r, description: v }) },
|
|
2435
|
-
{ label: "
|
|
2436
|
-
{ label: "Cmd deny", kind: "text", get: (r) => constr(r, "commandConstraints", "denied"), set: (r, v) => setConstr(r, "commandConstraints", "denied", v) },
|
|
2437
|
-
{ label: "Path allow", kind: "text", get: (r) => constr(r, "pathConstraints", "allowed"), set: (r, v) => setConstr(r, "pathConstraints", "allowed", v) },
|
|
2438
|
-
{ label: "Path deny", kind: "text", get: (r) => constr(r, "pathConstraints", "denied"), set: (r, v) => setConstr(r, "pathConstraints", "denied", v) },
|
|
2439
|
-
{ label: "File allow", kind: "text", get: (r) => constr(r, "filenameConstraints", "allowed"), set: (r, v) => setConstr(r, "filenameConstraints", "allowed", v) },
|
|
2440
|
-
{ label: "File deny", kind: "text", get: (r) => constr(r, "filenameConstraints", "denied"), set: (r, v) => setConstr(r, "filenameConstraints", "denied", v) },
|
|
2441
|
-
{ label: "URL allow", kind: "text", get: (r) => constr(r, "urlConstraints", "allowed"), set: (r, v) => setConstr(r, "urlConstraints", "allowed", v) },
|
|
2442
|
-
{ label: "URL deny", kind: "text", get: (r) => constr(r, "urlConstraints", "denied"), set: (r, v) => setConstr(r, "urlConstraints", "denied", v) }
|
|
2495
|
+
{ label: "Match", kind: "text", get: cValue, set: setCValue }
|
|
2443
2496
|
];
|
|
2444
2497
|
function ruleSummary(r) {
|
|
2445
2498
|
const bits = [];
|
|
@@ -2467,6 +2520,7 @@ function PoliciesPanel({ focused }) {
|
|
|
2467
2520
|
const [editing, setEditing] = useState4(false);
|
|
2468
2521
|
const [editVal, setEditVal] = useState4("");
|
|
2469
2522
|
const [status, setStatus] = useState4(null);
|
|
2523
|
+
const [pendingDel, setPendingDel] = useState4(null);
|
|
2470
2524
|
const selected = policies[Math.min(pi, Math.max(0, policies.length - 1))];
|
|
2471
2525
|
const detail = useLoader(() => selected ? api.policies.get(selected.id) : Promise.resolve(null), [selected?.id]);
|
|
2472
2526
|
useEffect4(() => {
|
|
@@ -2489,6 +2543,11 @@ function PoliciesPanel({ focused }) {
|
|
|
2489
2543
|
};
|
|
2490
2544
|
const save = async () => {
|
|
2491
2545
|
if (!selected || !detail.data) return;
|
|
2546
|
+
const blanket = rules.findIndex(isBlanketRule);
|
|
2547
|
+
if (blanket !== -1) {
|
|
2548
|
+
setStatus(`\u2717 rule ${blanket + 1} matches EVERYTHING \u2014 pick a Constraint + Match value, or narrow Permissions`);
|
|
2549
|
+
return;
|
|
2550
|
+
}
|
|
2492
2551
|
setStatus("Saving\u2026");
|
|
2493
2552
|
try {
|
|
2494
2553
|
const res = await api.policies.update(selected.id, {
|
|
@@ -2537,9 +2596,13 @@ function PoliciesPanel({ focused }) {
|
|
|
2537
2596
|
return;
|
|
2538
2597
|
}
|
|
2539
2598
|
if (view === "list") {
|
|
2540
|
-
if (key.upArrow)
|
|
2541
|
-
|
|
2542
|
-
|
|
2599
|
+
if (key.upArrow) {
|
|
2600
|
+
setPendingDel(null);
|
|
2601
|
+
setPi((n) => Math.max(0, n - 1));
|
|
2602
|
+
} else if (key.downArrow) {
|
|
2603
|
+
setPendingDel(null);
|
|
2604
|
+
setPi((n) => Math.min(policies.length - 1, n + 1));
|
|
2605
|
+
} else if (key.return || key.rightArrow) {
|
|
2543
2606
|
setStatus(null);
|
|
2544
2607
|
setView("rules");
|
|
2545
2608
|
} else if (input === "a") {
|
|
@@ -2559,6 +2622,22 @@ function PoliciesPanel({ focused }) {
|
|
|
2559
2622
|
setStatus("\u2713 active policy: NONE \u2014 no rules enforced, w/b grants are refused");
|
|
2560
2623
|
activeQ.reload();
|
|
2561
2624
|
}).catch((e) => setStatus("\u2717 " + (e instanceof Error ? e.message : String(e))));
|
|
2625
|
+
} else if (input === "d") {
|
|
2626
|
+
if (!selected) return;
|
|
2627
|
+
if (pendingDel !== selected.id) {
|
|
2628
|
+
setPendingDel(selected.id);
|
|
2629
|
+
setStatus(`\u26A0 d again to DELETE policy "${selected.name}" \u2014 cannot be undone`);
|
|
2630
|
+
return;
|
|
2631
|
+
}
|
|
2632
|
+
setPendingDel(null);
|
|
2633
|
+
setStatus("Deleting\u2026");
|
|
2634
|
+
const name = selected.name;
|
|
2635
|
+
void api.policies.remove(selected.id).then(() => {
|
|
2636
|
+
setStatus(`\u2713 deleted policy "${name}"`);
|
|
2637
|
+
setPi(0);
|
|
2638
|
+
list5.reload();
|
|
2639
|
+
activeQ.reload();
|
|
2640
|
+
}).catch((e) => setStatus("\u2717 " + (e instanceof Error ? e.message : String(e))));
|
|
2562
2641
|
}
|
|
2563
2642
|
return;
|
|
2564
2643
|
}
|
|
@@ -2574,7 +2653,7 @@ function PoliciesPanel({ focused }) {
|
|
|
2574
2653
|
} else if (input === " ") {
|
|
2575
2654
|
if (rules[ri]) mutate(rules.map((r, i) => i === ri ? { ...r, enabled: !r.enabled } : r));
|
|
2576
2655
|
} else if (input === "e") {
|
|
2577
|
-
if (rules[ri]) mutate(rules.map((r, i) => i === ri ?
|
|
2656
|
+
if (rules[ri]) mutate(rules.map((r, i) => i === ri ? migrateEffect(r, r.effect === "ALLOW" ? "DENY" : "ALLOW") : r));
|
|
2578
2657
|
} else if (input === "d") {
|
|
2579
2658
|
if (rules[ri]) {
|
|
2580
2659
|
const next = rules.filter((_, i) => i !== ri);
|
|
@@ -2605,9 +2684,16 @@ function PoliciesPanel({ focused }) {
|
|
|
2605
2684
|
else if (key.upArrow) setFi((n) => Math.max(0, n - 1));
|
|
2606
2685
|
else if (key.downArrow) setFi((n) => Math.min(FIELDS.length - 1, n + 1));
|
|
2607
2686
|
else if (field.kind === "effect" && (input === " " || key.rightArrow)) {
|
|
2608
|
-
mutate(rules.map((r, i) => i === ri ?
|
|
2687
|
+
mutate(rules.map((r, i) => i === ri ? migrateEffect(r, r.effect === "ALLOW" ? "DENY" : "ALLOW") : r));
|
|
2609
2688
|
} else if (field.kind === "enabled" && (input === " " || key.rightArrow)) {
|
|
2610
2689
|
mutate(rules.map((r, i) => i === ri ? { ...r, enabled: !r.enabled } : r));
|
|
2690
|
+
} else if (field.kind === "ctype" && (input === " " || key.rightArrow || key.leftArrow)) {
|
|
2691
|
+
const dir = key.leftArrow ? -1 : 1;
|
|
2692
|
+
const nextT = CTYPES[(CTYPES.indexOf(currentCType(rule2)) + dir + CTYPES.length) % CTYPES.length];
|
|
2693
|
+
mutate(rules.map((r, i) => i === ri ? setCType(r, nextT) : r));
|
|
2694
|
+
} else if (field.kind === "perms" && (input === "r" || input === "w" || input === "e" || input === "n")) {
|
|
2695
|
+
const p = { r: "READ", w: "WRITE", e: "EXECUTE", n: "NETWORK" }[input];
|
|
2696
|
+
mutate(rules.map((rr, i) => i === ri ? togglePerm(rr, p) : rr));
|
|
2611
2697
|
} else if (field.kind === "priority" && (key.rightArrow || key.leftArrow)) {
|
|
2612
2698
|
const d = key.rightArrow ? 1 : -1;
|
|
2613
2699
|
mutate(rules.map((r, i) => i === ri ? { ...r, priority: Math.max(0, r.priority + d) } : r));
|
|
@@ -2637,7 +2723,7 @@ function PoliciesPanel({ focused }) {
|
|
|
2637
2723
|
/* @__PURE__ */ jsx4(Text4, { color: theme.dim, children: activeBy ? ` (${activeBy})` : "" })
|
|
2638
2724
|
] }) : /* @__PURE__ */ jsx4(Text4, { color: theme.bad, bold: true, children: "\u25CB NONE \u2014 nothing enforced, grants refused" })
|
|
2639
2725
|
] }),
|
|
2640
|
-
/* @__PURE__ */ jsx4(Text4, { color: theme.dim, wrap: "truncate", children: focused ? `\u2191\u2193 select \xB7 enter open \xB7 n new
|
|
2726
|
+
/* @__PURE__ */ jsx4(Text4, { color: theme.dim, wrap: "truncate", children: focused ? `\u2191\u2193 select \xB7 enter open \xB7 n new \xB7 a activate \xB7 x deactivate \xB7 d delete \xB7 ^R refresh${lAbove ? ` \xB7 \u25B2${lAbove}` : ""}${lBelow ? ` \xB7 \u25BC${lBelow}` : ""}` : "press \u2192 to browse" }),
|
|
2641
2727
|
creating !== null ? /* @__PURE__ */ jsxs4(Box4, { children: [
|
|
2642
2728
|
/* @__PURE__ */ jsx4(Text4, { color: theme.warn, children: "new policy name: " }),
|
|
2643
2729
|
/* @__PURE__ */ jsx4(TextInput3, { value: creating, onChange: setCreating, onSubmit: commitNewPolicy })
|
|
@@ -2712,7 +2798,7 @@ function PoliciesPanel({ focused }) {
|
|
|
2712
2798
|
/* @__PURE__ */ jsx4(Text4, { color: theme.dim, children: rule?.id ?? "" }),
|
|
2713
2799
|
dirtyTag
|
|
2714
2800
|
] }),
|
|
2715
|
-
/* @__PURE__ */ jsx4(Text4, { color: theme.dim, children: "\u2191\u2193 field \xB7 enter edit text \xB7 space/\u2192 toggle \xB7 s save \xB7 \u2190 back" }),
|
|
2801
|
+
/* @__PURE__ */ jsx4(Text4, { color: theme.dim, children: "\u2191\u2193 field \xB7 enter edit text \xB7 space/\u2192 toggle \xB7 r/w/e/n perms \xB7 s save \xB7 \u2190 back" }),
|
|
2716
2802
|
/* @__PURE__ */ jsx4(Box4, { marginTop: 1, flexDirection: "column", children: FIELDS.map((f, i) => {
|
|
2717
2803
|
const val = rule ? f.get(rule) : "";
|
|
2718
2804
|
const active2 = i === fi;
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@solongate/proxy",
|
|
3
|
-
"version": "0.82.
|
|
3
|
+
"version": "0.82.49",
|
|
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": {
|