@solongate/proxy 0.82.48 → 0.82.50
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 +183 -25
- package/dist/tui/index.js +182 -24
- package/package.json +1 -1
package/dist/index.js
CHANGED
|
@@ -9222,6 +9222,8 @@ 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);
|
|
9226
|
+
const [permCursor, setPermCursor] = useState4(0);
|
|
9225
9227
|
const selected = policies[Math.min(pi, Math.max(0, policies.length - 1))];
|
|
9226
9228
|
const detail = useLoader(() => selected ? api.policies.get(selected.id) : Promise.resolve(null), [selected?.id]);
|
|
9227
9229
|
useEffect4(() => {
|
|
@@ -9244,6 +9246,11 @@ function PoliciesPanel({ focused }) {
|
|
|
9244
9246
|
};
|
|
9245
9247
|
const save = async () => {
|
|
9246
9248
|
if (!selected || !detail.data) return;
|
|
9249
|
+
const blanket = rules.findIndex(isBlanketRule);
|
|
9250
|
+
if (blanket !== -1) {
|
|
9251
|
+
setStatus(`\u2717 rule ${blanket + 1} matches EVERYTHING \u2014 pick a Constraint + Match value, or narrow Permissions`);
|
|
9252
|
+
return;
|
|
9253
|
+
}
|
|
9247
9254
|
setStatus("Saving\u2026");
|
|
9248
9255
|
try {
|
|
9249
9256
|
const res = await api.policies.update(selected.id, {
|
|
@@ -9292,9 +9299,13 @@ function PoliciesPanel({ focused }) {
|
|
|
9292
9299
|
return;
|
|
9293
9300
|
}
|
|
9294
9301
|
if (view === "list") {
|
|
9295
|
-
if (key.upArrow)
|
|
9296
|
-
|
|
9297
|
-
|
|
9302
|
+
if (key.upArrow) {
|
|
9303
|
+
setPendingDel(null);
|
|
9304
|
+
setPi((n) => Math.max(0, n - 1));
|
|
9305
|
+
} else if (key.downArrow) {
|
|
9306
|
+
setPendingDel(null);
|
|
9307
|
+
setPi((n) => Math.min(policies.length - 1, n + 1));
|
|
9308
|
+
} else if (key.return || key.rightArrow) {
|
|
9298
9309
|
setStatus(null);
|
|
9299
9310
|
setView("rules");
|
|
9300
9311
|
} else if (input === "a") {
|
|
@@ -9314,6 +9325,22 @@ function PoliciesPanel({ focused }) {
|
|
|
9314
9325
|
setStatus("\u2713 active policy: NONE \u2014 no rules enforced, w/b grants are refused");
|
|
9315
9326
|
activeQ.reload();
|
|
9316
9327
|
}).catch((e) => setStatus("\u2717 " + (e instanceof Error ? e.message : String(e))));
|
|
9328
|
+
} else if (input === "d") {
|
|
9329
|
+
if (!selected) return;
|
|
9330
|
+
if (pendingDel !== selected.id) {
|
|
9331
|
+
setPendingDel(selected.id);
|
|
9332
|
+
setStatus(`\u26A0 d again to DELETE policy "${selected.name}" \u2014 cannot be undone`);
|
|
9333
|
+
return;
|
|
9334
|
+
}
|
|
9335
|
+
setPendingDel(null);
|
|
9336
|
+
setStatus("Deleting\u2026");
|
|
9337
|
+
const name = selected.name;
|
|
9338
|
+
void api.policies.remove(selected.id).then(() => {
|
|
9339
|
+
setStatus(`\u2713 deleted policy "${name}"`);
|
|
9340
|
+
setPi(0);
|
|
9341
|
+
list5.reload();
|
|
9342
|
+
activeQ.reload();
|
|
9343
|
+
}).catch((e) => setStatus("\u2717 " + (e instanceof Error ? e.message : String(e))));
|
|
9317
9344
|
}
|
|
9318
9345
|
return;
|
|
9319
9346
|
}
|
|
@@ -9329,7 +9356,7 @@ function PoliciesPanel({ focused }) {
|
|
|
9329
9356
|
} else if (input === " ") {
|
|
9330
9357
|
if (rules[ri]) mutate(rules.map((r, i) => i === ri ? { ...r, enabled: !r.enabled } : r));
|
|
9331
9358
|
} else if (input === "e") {
|
|
9332
|
-
if (rules[ri]) mutate(rules.map((r, i) => i === ri ?
|
|
9359
|
+
if (rules[ri]) mutate(rules.map((r, i) => i === ri ? migrateEffect(r, r.effect === "ALLOW" ? "DENY" : "ALLOW") : r));
|
|
9333
9360
|
} else if (input === "d") {
|
|
9334
9361
|
if (rules[ri]) {
|
|
9335
9362
|
const next = rules.filter((_, i) => i !== ri);
|
|
@@ -9339,7 +9366,7 @@ function PoliciesPanel({ focused }) {
|
|
|
9339
9366
|
} else if (input === "m") {
|
|
9340
9367
|
mutate(rules, mode === "denylist" ? "whitelist" : "denylist");
|
|
9341
9368
|
} else if (input === "n") {
|
|
9342
|
-
const nr = { id: `rule-${Date.now()}`, description: "", effect: "DENY", priority: 100, toolPattern: "*", minimumTrustLevel: "UNTRUSTED", enabled: true };
|
|
9369
|
+
const nr = { id: `rule-${Date.now()}`, description: "", effect: "DENY", priority: 100, toolPattern: "*", minimumTrustLevel: "UNTRUSTED", enabled: true, commandConstraints: { denied: [] } };
|
|
9343
9370
|
mutate([nr, ...rules]);
|
|
9344
9371
|
setRi(0);
|
|
9345
9372
|
setFi(0);
|
|
@@ -9360,13 +9387,28 @@ function PoliciesPanel({ focused }) {
|
|
|
9360
9387
|
else if (key.upArrow) setFi((n) => Math.max(0, n - 1));
|
|
9361
9388
|
else if (key.downArrow) setFi((n) => Math.min(FIELDS.length - 1, n + 1));
|
|
9362
9389
|
else if (field.kind === "effect" && (input === " " || key.rightArrow)) {
|
|
9363
|
-
mutate(rules.map((r, i) => i === ri ?
|
|
9390
|
+
mutate(rules.map((r, i) => i === ri ? migrateEffect(r, r.effect === "ALLOW" ? "DENY" : "ALLOW") : r));
|
|
9364
9391
|
} else if (field.kind === "enabled" && (input === " " || key.rightArrow)) {
|
|
9365
9392
|
mutate(rules.map((r, i) => i === ri ? { ...r, enabled: !r.enabled } : r));
|
|
9393
|
+
} else if (field.kind === "ctype" && (input === " " || key.rightArrow || key.leftArrow)) {
|
|
9394
|
+
const dir = key.leftArrow ? -1 : 1;
|
|
9395
|
+
const nextT = CTYPES[(CTYPES.indexOf(currentCType(rule2)) + dir + CTYPES.length) % CTYPES.length];
|
|
9396
|
+
mutate(rules.map((r, i) => i === ri ? setCType(r, nextT) : r));
|
|
9397
|
+
} else if (field.kind === "perms" && key.leftArrow) {
|
|
9398
|
+
setPermCursor((n) => (n + PERMS.length - 1) % PERMS.length);
|
|
9399
|
+
} else if (field.kind === "perms" && key.rightArrow) {
|
|
9400
|
+
setPermCursor((n) => (n + 1) % PERMS.length);
|
|
9401
|
+
} else if (field.kind === "perms" && input === " ") {
|
|
9402
|
+
const p = PERMS[permCursor % PERMS.length];
|
|
9403
|
+
mutate(rules.map((rr, i) => i === ri ? togglePerm(rr, p) : rr));
|
|
9404
|
+
} else if (field.kind === "match" && input === "[") {
|
|
9405
|
+
mutate(rules.map((rr, i) => i === ri ? toggleStar(rr, "left") : rr));
|
|
9406
|
+
} else if (field.kind === "match" && input === "]") {
|
|
9407
|
+
mutate(rules.map((rr, i) => i === ri ? toggleStar(rr, "right") : rr));
|
|
9366
9408
|
} else if (field.kind === "priority" && (key.rightArrow || key.leftArrow)) {
|
|
9367
9409
|
const d = key.rightArrow ? 1 : -1;
|
|
9368
9410
|
mutate(rules.map((r, i) => i === ri ? { ...r, priority: Math.max(0, r.priority + d) } : r));
|
|
9369
|
-
} else if (field.kind === "text" && key.return) {
|
|
9411
|
+
} else if ((field.kind === "text" || field.kind === "match") && key.return) {
|
|
9370
9412
|
setEditVal(field.get(rule2));
|
|
9371
9413
|
setEditing(true);
|
|
9372
9414
|
} else if (input === "s") void save();
|
|
@@ -9392,7 +9434,7 @@ function PoliciesPanel({ focused }) {
|
|
|
9392
9434
|
/* @__PURE__ */ jsx4(Text4, { color: theme.dim, children: activeBy ? ` (${activeBy})` : "" })
|
|
9393
9435
|
] }) : /* @__PURE__ */ jsx4(Text4, { color: theme.bad, bold: true, children: "\u25CB NONE \u2014 nothing enforced, grants refused" })
|
|
9394
9436
|
] }),
|
|
9395
|
-
/* @__PURE__ */ jsx4(Text4, { color: theme.dim, wrap: "truncate", children: focused ? `\u2191\u2193 select \xB7 enter open \xB7 n new
|
|
9437
|
+
/* @__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
9438
|
creating !== null ? /* @__PURE__ */ jsxs4(Box4, { children: [
|
|
9397
9439
|
/* @__PURE__ */ jsx4(Text4, { color: theme.warn, children: "new policy name: " }),
|
|
9398
9440
|
/* @__PURE__ */ jsx4(TextInput3, { value: creating, onChange: setCreating, onSubmit: commitNewPolicy })
|
|
@@ -9467,12 +9509,53 @@ function PoliciesPanel({ focused }) {
|
|
|
9467
9509
|
/* @__PURE__ */ jsx4(Text4, { color: theme.dim, children: rule?.id ?? "" }),
|
|
9468
9510
|
dirtyTag
|
|
9469
9511
|
] }),
|
|
9470
|
-
/* @__PURE__ */ jsx4(Text4, { color: theme.dim, children: "\u2191\u2193 field \xB7 enter edit
|
|
9512
|
+
/* @__PURE__ */ jsx4(Text4, { color: theme.dim, children: "\u2191\u2193 field \xB7 enter edit \xB7 space toggle \xB7 \u2190\u2192 move/pick \xB7 [ ] wildcards \xB7 s save \xB7 \u2190 back" }),
|
|
9471
9513
|
/* @__PURE__ */ jsx4(Box4, { marginTop: 1, flexDirection: "column", children: FIELDS.map((f, i) => {
|
|
9472
|
-
const val = rule ? f.get(rule) : "";
|
|
9473
9514
|
const active2 = i === fi;
|
|
9515
|
+
const label = /* @__PURE__ */ jsx4(Text4, { color: active2 ? theme.accentBright : void 0, children: (active2 ? "\u25B8 " : " ") + f.label.padEnd(13) });
|
|
9516
|
+
if (f.kind === "perms" && rule) {
|
|
9517
|
+
return /* @__PURE__ */ jsxs4(Box4, { children: [
|
|
9518
|
+
label,
|
|
9519
|
+
PERMS.map((p, pi2) => {
|
|
9520
|
+
const on = permList(rule).includes(p);
|
|
9521
|
+
const cur = active2 && pi2 === permCursor % PERMS.length;
|
|
9522
|
+
return /* @__PURE__ */ jsxs4(Text4, { color: on ? theme.ok : theme.bad, inverse: cur, children: [
|
|
9523
|
+
p,
|
|
9524
|
+
pi2 < PERMS.length - 1 ? " " : ""
|
|
9525
|
+
] }, p);
|
|
9526
|
+
})
|
|
9527
|
+
] }, f.label);
|
|
9528
|
+
}
|
|
9529
|
+
if (f.kind === "match" && rule) {
|
|
9530
|
+
if (active2 && editing) {
|
|
9531
|
+
return /* @__PURE__ */ jsxs4(Box4, { children: [
|
|
9532
|
+
label,
|
|
9533
|
+
/* @__PURE__ */ jsx4(
|
|
9534
|
+
TextInput3,
|
|
9535
|
+
{
|
|
9536
|
+
value: editVal,
|
|
9537
|
+
onChange: setEditVal,
|
|
9538
|
+
onSubmit: (v2) => {
|
|
9539
|
+
if (f.set) mutate(rules.map((r, idx) => idx === ri ? f.set(r, v2) : r));
|
|
9540
|
+
setEditing(false);
|
|
9541
|
+
}
|
|
9542
|
+
}
|
|
9543
|
+
)
|
|
9544
|
+
] }, f.label);
|
|
9545
|
+
}
|
|
9546
|
+
const v = f.get(rule);
|
|
9547
|
+
const lOn = starActive(rule, "left");
|
|
9548
|
+
const rOn = starActive(rule, "right");
|
|
9549
|
+
return /* @__PURE__ */ jsxs4(Box4, { children: [
|
|
9550
|
+
label,
|
|
9551
|
+
/* @__PURE__ */ jsx4(Text4, { color: lOn ? theme.ok : theme.dim, children: lOn ? "\u2731 " : "\xB7 " }),
|
|
9552
|
+
/* @__PURE__ */ jsx4(Text4, { dimColor: !v, children: v || "\u2014" }),
|
|
9553
|
+
/* @__PURE__ */ jsx4(Text4, { color: rOn ? theme.ok : theme.dim, children: rOn ? " \u2731" : " \xB7" })
|
|
9554
|
+
] }, f.label);
|
|
9555
|
+
}
|
|
9556
|
+
const val = rule ? f.get(rule) : "";
|
|
9474
9557
|
return /* @__PURE__ */ jsxs4(Box4, { children: [
|
|
9475
|
-
|
|
9558
|
+
label,
|
|
9476
9559
|
active2 && editing && f.kind === "text" ? /* @__PURE__ */ jsx4(
|
|
9477
9560
|
TextInput3,
|
|
9478
9561
|
{
|
|
@@ -9493,10 +9576,11 @@ function PoliciesPanel({ focused }) {
|
|
|
9493
9576
|
)
|
|
9494
9577
|
] }, f.label);
|
|
9495
9578
|
}) }),
|
|
9579
|
+
/* @__PURE__ */ jsx4(Text4, { color: theme.dim, children: " * = wildcard (matches any run of chars). On Match: [ toggles a leading *, ] a trailing * \xB7 e.g. *ping* matches any command containing ping" }),
|
|
9496
9580
|
status ? /* @__PURE__ */ jsx4(Text4, { color: status.startsWith("\u2717") ? theme.bad : theme.ok, children: status }) : null
|
|
9497
9581
|
] });
|
|
9498
9582
|
}
|
|
9499
|
-
var
|
|
9583
|
+
var CTYPES, CTYPE_GROUP, currentCType, clearConstraints, setCType, cValue, setCValue, matchItems, starActive, toggleStar, migrateEffect, PERMS, permList, permIsAny, togglePerm, permDisplay, ruleHasValues, isBlanketRule, FIELDS;
|
|
9500
9584
|
var init_Policies = __esm({
|
|
9501
9585
|
"src/tui/panels/Policies.tsx"() {
|
|
9502
9586
|
"use strict";
|
|
@@ -9506,25 +9590,99 @@ var init_Policies = __esm({
|
|
|
9506
9590
|
init_components();
|
|
9507
9591
|
init_hooks();
|
|
9508
9592
|
init_theme();
|
|
9509
|
-
|
|
9510
|
-
|
|
9593
|
+
CTYPES = ["none", "command", "path", "filename", "url"];
|
|
9594
|
+
CTYPE_GROUP = {
|
|
9595
|
+
command: "commandConstraints",
|
|
9596
|
+
path: "pathConstraints",
|
|
9597
|
+
filename: "filenameConstraints",
|
|
9598
|
+
url: "urlConstraints"
|
|
9599
|
+
};
|
|
9600
|
+
currentCType = (r) => {
|
|
9601
|
+
if (r.filenameConstraints) return "filename";
|
|
9602
|
+
if (r.urlConstraints) return "url";
|
|
9603
|
+
if (r.commandConstraints) return "command";
|
|
9604
|
+
if (r.pathConstraints) return "path";
|
|
9605
|
+
return "none";
|
|
9606
|
+
};
|
|
9607
|
+
clearConstraints = (r) => ({
|
|
9608
|
+
...r,
|
|
9609
|
+
commandConstraints: void 0,
|
|
9610
|
+
pathConstraints: void 0,
|
|
9611
|
+
filenameConstraints: void 0,
|
|
9612
|
+
urlConstraints: void 0
|
|
9613
|
+
});
|
|
9614
|
+
setCType = (r, t) => {
|
|
9615
|
+
const base = clearConstraints(r);
|
|
9616
|
+
if (t === "none") return base;
|
|
9617
|
+
const listKey = r.effect === "ALLOW" ? "allowed" : "denied";
|
|
9618
|
+
return { ...base, [CTYPE_GROUP[t]]: { [listKey]: [] } };
|
|
9619
|
+
};
|
|
9620
|
+
cValue = (r) => {
|
|
9621
|
+
const t = currentCType(r);
|
|
9622
|
+
if (t === "none") return "";
|
|
9623
|
+
const c2 = r[CTYPE_GROUP[t]];
|
|
9624
|
+
return [...c2?.allowed ?? [], ...c2?.denied ?? []].join(", ");
|
|
9625
|
+
};
|
|
9626
|
+
setCValue = (r, val) => {
|
|
9627
|
+
const t = currentCType(r);
|
|
9628
|
+
if (t === "none") return r;
|
|
9511
9629
|
const arr = val.split(",").map((s) => s.trim()).filter(Boolean);
|
|
9512
|
-
const
|
|
9513
|
-
return { ...r, [
|
|
9630
|
+
const listKey = r.effect === "ALLOW" ? "allowed" : "denied";
|
|
9631
|
+
return { ...r, [CTYPE_GROUP[t]]: { [listKey]: arr } };
|
|
9632
|
+
};
|
|
9633
|
+
matchItems = (r) => {
|
|
9634
|
+
const t = currentCType(r);
|
|
9635
|
+
if (t === "none") return [];
|
|
9636
|
+
const c2 = r[CTYPE_GROUP[t]];
|
|
9637
|
+
return [...c2?.allowed ?? [], ...c2?.denied ?? []];
|
|
9638
|
+
};
|
|
9639
|
+
starActive = (r, side) => {
|
|
9640
|
+
const items = matchItems(r);
|
|
9641
|
+
return items.length > 0 && items.every((v) => side === "left" ? v.startsWith("*") : v.endsWith("*"));
|
|
9642
|
+
};
|
|
9643
|
+
toggleStar = (r, side) => {
|
|
9644
|
+
const t = currentCType(r);
|
|
9645
|
+
if (t === "none") return r;
|
|
9646
|
+
const on = starActive(r, side);
|
|
9647
|
+
const listKey = r.effect === "ALLOW" ? "allowed" : "denied";
|
|
9648
|
+
const items = matchItems(r).map(
|
|
9649
|
+
(v) => side === "left" ? on ? v.replace(/^\*+/, "") : "*" + v : on ? v.replace(/\*+$/, "") : v + "*"
|
|
9650
|
+
);
|
|
9651
|
+
return { ...r, [CTYPE_GROUP[t]]: { [listKey]: items } };
|
|
9652
|
+
};
|
|
9653
|
+
migrateEffect = (r, nextEffect) => {
|
|
9654
|
+
const flipped = { ...r, effect: nextEffect };
|
|
9655
|
+
const t = currentCType(r);
|
|
9656
|
+
if (t === "none") return flipped;
|
|
9657
|
+
const c2 = r[CTYPE_GROUP[t]];
|
|
9658
|
+
const items = [...c2?.allowed ?? [], ...c2?.denied ?? []];
|
|
9659
|
+
const listKey = nextEffect === "ALLOW" ? "allowed" : "denied";
|
|
9660
|
+
return { ...flipped, [CTYPE_GROUP[t]]: { [listKey]: items } };
|
|
9661
|
+
};
|
|
9662
|
+
PERMS = ["READ", "WRITE", "EXECUTE", "NETWORK"];
|
|
9663
|
+
permList = (r) => !r.permission ? [...PERMS] : Array.isArray(r.permission) ? r.permission : [r.permission];
|
|
9664
|
+
permIsAny = (r) => PERMS.every((p) => permList(r).includes(p));
|
|
9665
|
+
togglePerm = (r, p) => {
|
|
9666
|
+
const cur = permList(r);
|
|
9667
|
+
let next = cur.includes(p) ? cur.filter((x) => x !== p) : [...cur, p];
|
|
9668
|
+
if (next.length === 0) next = [p];
|
|
9669
|
+
return { ...r, permission: PERMS.every((x) => next.includes(x)) ? void 0 : next };
|
|
9670
|
+
};
|
|
9671
|
+
permDisplay = (r) => permIsAny(r) ? "any (READ WRITE EXECUTE NETWORK)" : permList(r).join(" ");
|
|
9672
|
+
ruleHasValues = (r) => {
|
|
9673
|
+
const t = currentCType(r);
|
|
9674
|
+
if (t === "none") return false;
|
|
9675
|
+
const c2 = r[CTYPE_GROUP[t]];
|
|
9676
|
+
return (c2?.allowed?.length ?? 0) + (c2?.denied?.length ?? 0) > 0;
|
|
9514
9677
|
};
|
|
9678
|
+
isBlanketRule = (r) => !ruleHasValues(r) && permIsAny(r);
|
|
9515
9679
|
FIELDS = [
|
|
9516
9680
|
{ label: "Effect", kind: "effect", get: (r) => r.effect },
|
|
9517
|
-
{ label: "
|
|
9681
|
+
{ label: "Constraint", kind: "ctype", get: (r) => currentCType(r) },
|
|
9682
|
+
{ label: "Permissions", kind: "perms", get: permDisplay },
|
|
9518
9683
|
{ label: "Priority", kind: "priority", get: (r) => String(r.priority) },
|
|
9519
9684
|
{ label: "Description", kind: "text", get: (r) => r.description ?? "", set: (r, v) => ({ ...r, description: v }) },
|
|
9520
|
-
{ label: "
|
|
9521
|
-
{ label: "Cmd deny", kind: "text", get: (r) => constr(r, "commandConstraints", "denied"), set: (r, v) => setConstr(r, "commandConstraints", "denied", v) },
|
|
9522
|
-
{ label: "Path allow", kind: "text", get: (r) => constr(r, "pathConstraints", "allowed"), set: (r, v) => setConstr(r, "pathConstraints", "allowed", v) },
|
|
9523
|
-
{ label: "Path deny", kind: "text", get: (r) => constr(r, "pathConstraints", "denied"), set: (r, v) => setConstr(r, "pathConstraints", "denied", v) },
|
|
9524
|
-
{ label: "File allow", kind: "text", get: (r) => constr(r, "filenameConstraints", "allowed"), set: (r, v) => setConstr(r, "filenameConstraints", "allowed", v) },
|
|
9525
|
-
{ label: "File deny", kind: "text", get: (r) => constr(r, "filenameConstraints", "denied"), set: (r, v) => setConstr(r, "filenameConstraints", "denied", v) },
|
|
9526
|
-
{ label: "URL allow", kind: "text", get: (r) => constr(r, "urlConstraints", "allowed"), set: (r, v) => setConstr(r, "urlConstraints", "allowed", v) },
|
|
9527
|
-
{ label: "URL deny", kind: "text", get: (r) => constr(r, "urlConstraints", "denied"), set: (r, v) => setConstr(r, "urlConstraints", "denied", v) }
|
|
9685
|
+
{ label: "Match", kind: "match", get: cValue, set: setCValue }
|
|
9528
9686
|
];
|
|
9529
9687
|
}
|
|
9530
9688
|
});
|
package/dist/tui/index.js
CHANGED
|
@@ -2420,25 +2420,99 @@ 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 matchItems = (r) => {
|
|
2464
|
+
const t = currentCType(r);
|
|
2465
|
+
if (t === "none") return [];
|
|
2466
|
+
const c2 = r[CTYPE_GROUP[t]];
|
|
2467
|
+
return [...c2?.allowed ?? [], ...c2?.denied ?? []];
|
|
2468
|
+
};
|
|
2469
|
+
var starActive = (r, side) => {
|
|
2470
|
+
const items = matchItems(r);
|
|
2471
|
+
return items.length > 0 && items.every((v) => side === "left" ? v.startsWith("*") : v.endsWith("*"));
|
|
2428
2472
|
};
|
|
2473
|
+
var toggleStar = (r, side) => {
|
|
2474
|
+
const t = currentCType(r);
|
|
2475
|
+
if (t === "none") return r;
|
|
2476
|
+
const on = starActive(r, side);
|
|
2477
|
+
const listKey = r.effect === "ALLOW" ? "allowed" : "denied";
|
|
2478
|
+
const items = matchItems(r).map(
|
|
2479
|
+
(v) => side === "left" ? on ? v.replace(/^\*+/, "") : "*" + v : on ? v.replace(/\*+$/, "") : v + "*"
|
|
2480
|
+
);
|
|
2481
|
+
return { ...r, [CTYPE_GROUP[t]]: { [listKey]: items } };
|
|
2482
|
+
};
|
|
2483
|
+
var migrateEffect = (r, nextEffect) => {
|
|
2484
|
+
const flipped = { ...r, effect: nextEffect };
|
|
2485
|
+
const t = currentCType(r);
|
|
2486
|
+
if (t === "none") return flipped;
|
|
2487
|
+
const c2 = r[CTYPE_GROUP[t]];
|
|
2488
|
+
const items = [...c2?.allowed ?? [], ...c2?.denied ?? []];
|
|
2489
|
+
const listKey = nextEffect === "ALLOW" ? "allowed" : "denied";
|
|
2490
|
+
return { ...flipped, [CTYPE_GROUP[t]]: { [listKey]: items } };
|
|
2491
|
+
};
|
|
2492
|
+
var PERMS = ["READ", "WRITE", "EXECUTE", "NETWORK"];
|
|
2493
|
+
var permList = (r) => !r.permission ? [...PERMS] : Array.isArray(r.permission) ? r.permission : [r.permission];
|
|
2494
|
+
var permIsAny = (r) => PERMS.every((p) => permList(r).includes(p));
|
|
2495
|
+
var togglePerm = (r, p) => {
|
|
2496
|
+
const cur = permList(r);
|
|
2497
|
+
let next = cur.includes(p) ? cur.filter((x) => x !== p) : [...cur, p];
|
|
2498
|
+
if (next.length === 0) next = [p];
|
|
2499
|
+
return { ...r, permission: PERMS.every((x) => next.includes(x)) ? void 0 : next };
|
|
2500
|
+
};
|
|
2501
|
+
var permDisplay = (r) => permIsAny(r) ? "any (READ WRITE EXECUTE NETWORK)" : permList(r).join(" ");
|
|
2502
|
+
var ruleHasValues = (r) => {
|
|
2503
|
+
const t = currentCType(r);
|
|
2504
|
+
if (t === "none") return false;
|
|
2505
|
+
const c2 = r[CTYPE_GROUP[t]];
|
|
2506
|
+
return (c2?.allowed?.length ?? 0) + (c2?.denied?.length ?? 0) > 0;
|
|
2507
|
+
};
|
|
2508
|
+
var isBlanketRule = (r) => !ruleHasValues(r) && permIsAny(r);
|
|
2429
2509
|
var FIELDS = [
|
|
2430
2510
|
{ label: "Effect", kind: "effect", get: (r) => r.effect },
|
|
2431
|
-
{ label: "
|
|
2511
|
+
{ label: "Constraint", kind: "ctype", get: (r) => currentCType(r) },
|
|
2512
|
+
{ label: "Permissions", kind: "perms", get: permDisplay },
|
|
2432
2513
|
{ label: "Priority", kind: "priority", get: (r) => String(r.priority) },
|
|
2433
2514
|
{ label: "Description", kind: "text", get: (r) => r.description ?? "", set: (r, v) => ({ ...r, description: v }) },
|
|
2434
|
-
{ label: "
|
|
2435
|
-
{ label: "Cmd deny", kind: "text", get: (r) => constr(r, "commandConstraints", "denied"), set: (r, v) => setConstr(r, "commandConstraints", "denied", v) },
|
|
2436
|
-
{ label: "Path allow", kind: "text", get: (r) => constr(r, "pathConstraints", "allowed"), set: (r, v) => setConstr(r, "pathConstraints", "allowed", v) },
|
|
2437
|
-
{ label: "Path deny", kind: "text", get: (r) => constr(r, "pathConstraints", "denied"), set: (r, v) => setConstr(r, "pathConstraints", "denied", v) },
|
|
2438
|
-
{ label: "File allow", kind: "text", get: (r) => constr(r, "filenameConstraints", "allowed"), set: (r, v) => setConstr(r, "filenameConstraints", "allowed", v) },
|
|
2439
|
-
{ label: "File deny", kind: "text", get: (r) => constr(r, "filenameConstraints", "denied"), set: (r, v) => setConstr(r, "filenameConstraints", "denied", v) },
|
|
2440
|
-
{ label: "URL allow", kind: "text", get: (r) => constr(r, "urlConstraints", "allowed"), set: (r, v) => setConstr(r, "urlConstraints", "allowed", v) },
|
|
2441
|
-
{ label: "URL deny", kind: "text", get: (r) => constr(r, "urlConstraints", "denied"), set: (r, v) => setConstr(r, "urlConstraints", "denied", v) }
|
|
2515
|
+
{ label: "Match", kind: "match", get: cValue, set: setCValue }
|
|
2442
2516
|
];
|
|
2443
2517
|
function ruleSummary(r) {
|
|
2444
2518
|
const bits = [];
|
|
@@ -2466,6 +2540,8 @@ function PoliciesPanel({ focused }) {
|
|
|
2466
2540
|
const [editing, setEditing] = useState4(false);
|
|
2467
2541
|
const [editVal, setEditVal] = useState4("");
|
|
2468
2542
|
const [status, setStatus] = useState4(null);
|
|
2543
|
+
const [pendingDel, setPendingDel] = useState4(null);
|
|
2544
|
+
const [permCursor, setPermCursor] = useState4(0);
|
|
2469
2545
|
const selected = policies[Math.min(pi, Math.max(0, policies.length - 1))];
|
|
2470
2546
|
const detail = useLoader(() => selected ? api.policies.get(selected.id) : Promise.resolve(null), [selected?.id]);
|
|
2471
2547
|
useEffect4(() => {
|
|
@@ -2488,6 +2564,11 @@ function PoliciesPanel({ focused }) {
|
|
|
2488
2564
|
};
|
|
2489
2565
|
const save = async () => {
|
|
2490
2566
|
if (!selected || !detail.data) return;
|
|
2567
|
+
const blanket = rules.findIndex(isBlanketRule);
|
|
2568
|
+
if (blanket !== -1) {
|
|
2569
|
+
setStatus(`\u2717 rule ${blanket + 1} matches EVERYTHING \u2014 pick a Constraint + Match value, or narrow Permissions`);
|
|
2570
|
+
return;
|
|
2571
|
+
}
|
|
2491
2572
|
setStatus("Saving\u2026");
|
|
2492
2573
|
try {
|
|
2493
2574
|
const res = await api.policies.update(selected.id, {
|
|
@@ -2536,9 +2617,13 @@ function PoliciesPanel({ focused }) {
|
|
|
2536
2617
|
return;
|
|
2537
2618
|
}
|
|
2538
2619
|
if (view === "list") {
|
|
2539
|
-
if (key.upArrow)
|
|
2540
|
-
|
|
2541
|
-
|
|
2620
|
+
if (key.upArrow) {
|
|
2621
|
+
setPendingDel(null);
|
|
2622
|
+
setPi((n) => Math.max(0, n - 1));
|
|
2623
|
+
} else if (key.downArrow) {
|
|
2624
|
+
setPendingDel(null);
|
|
2625
|
+
setPi((n) => Math.min(policies.length - 1, n + 1));
|
|
2626
|
+
} else if (key.return || key.rightArrow) {
|
|
2542
2627
|
setStatus(null);
|
|
2543
2628
|
setView("rules");
|
|
2544
2629
|
} else if (input === "a") {
|
|
@@ -2558,6 +2643,22 @@ function PoliciesPanel({ focused }) {
|
|
|
2558
2643
|
setStatus("\u2713 active policy: NONE \u2014 no rules enforced, w/b grants are refused");
|
|
2559
2644
|
activeQ.reload();
|
|
2560
2645
|
}).catch((e) => setStatus("\u2717 " + (e instanceof Error ? e.message : String(e))));
|
|
2646
|
+
} else if (input === "d") {
|
|
2647
|
+
if (!selected) return;
|
|
2648
|
+
if (pendingDel !== selected.id) {
|
|
2649
|
+
setPendingDel(selected.id);
|
|
2650
|
+
setStatus(`\u26A0 d again to DELETE policy "${selected.name}" \u2014 cannot be undone`);
|
|
2651
|
+
return;
|
|
2652
|
+
}
|
|
2653
|
+
setPendingDel(null);
|
|
2654
|
+
setStatus("Deleting\u2026");
|
|
2655
|
+
const name = selected.name;
|
|
2656
|
+
void api.policies.remove(selected.id).then(() => {
|
|
2657
|
+
setStatus(`\u2713 deleted policy "${name}"`);
|
|
2658
|
+
setPi(0);
|
|
2659
|
+
list5.reload();
|
|
2660
|
+
activeQ.reload();
|
|
2661
|
+
}).catch((e) => setStatus("\u2717 " + (e instanceof Error ? e.message : String(e))));
|
|
2561
2662
|
}
|
|
2562
2663
|
return;
|
|
2563
2664
|
}
|
|
@@ -2573,7 +2674,7 @@ function PoliciesPanel({ focused }) {
|
|
|
2573
2674
|
} else if (input === " ") {
|
|
2574
2675
|
if (rules[ri]) mutate(rules.map((r, i) => i === ri ? { ...r, enabled: !r.enabled } : r));
|
|
2575
2676
|
} else if (input === "e") {
|
|
2576
|
-
if (rules[ri]) mutate(rules.map((r, i) => i === ri ?
|
|
2677
|
+
if (rules[ri]) mutate(rules.map((r, i) => i === ri ? migrateEffect(r, r.effect === "ALLOW" ? "DENY" : "ALLOW") : r));
|
|
2577
2678
|
} else if (input === "d") {
|
|
2578
2679
|
if (rules[ri]) {
|
|
2579
2680
|
const next = rules.filter((_, i) => i !== ri);
|
|
@@ -2583,7 +2684,7 @@ function PoliciesPanel({ focused }) {
|
|
|
2583
2684
|
} else if (input === "m") {
|
|
2584
2685
|
mutate(rules, mode === "denylist" ? "whitelist" : "denylist");
|
|
2585
2686
|
} else if (input === "n") {
|
|
2586
|
-
const nr = { id: `rule-${Date.now()}`, description: "", effect: "DENY", priority: 100, toolPattern: "*", minimumTrustLevel: "UNTRUSTED", enabled: true };
|
|
2687
|
+
const nr = { id: `rule-${Date.now()}`, description: "", effect: "DENY", priority: 100, toolPattern: "*", minimumTrustLevel: "UNTRUSTED", enabled: true, commandConstraints: { denied: [] } };
|
|
2587
2688
|
mutate([nr, ...rules]);
|
|
2588
2689
|
setRi(0);
|
|
2589
2690
|
setFi(0);
|
|
@@ -2604,13 +2705,28 @@ function PoliciesPanel({ focused }) {
|
|
|
2604
2705
|
else if (key.upArrow) setFi((n) => Math.max(0, n - 1));
|
|
2605
2706
|
else if (key.downArrow) setFi((n) => Math.min(FIELDS.length - 1, n + 1));
|
|
2606
2707
|
else if (field.kind === "effect" && (input === " " || key.rightArrow)) {
|
|
2607
|
-
mutate(rules.map((r, i) => i === ri ?
|
|
2708
|
+
mutate(rules.map((r, i) => i === ri ? migrateEffect(r, r.effect === "ALLOW" ? "DENY" : "ALLOW") : r));
|
|
2608
2709
|
} else if (field.kind === "enabled" && (input === " " || key.rightArrow)) {
|
|
2609
2710
|
mutate(rules.map((r, i) => i === ri ? { ...r, enabled: !r.enabled } : r));
|
|
2711
|
+
} else if (field.kind === "ctype" && (input === " " || key.rightArrow || key.leftArrow)) {
|
|
2712
|
+
const dir = key.leftArrow ? -1 : 1;
|
|
2713
|
+
const nextT = CTYPES[(CTYPES.indexOf(currentCType(rule2)) + dir + CTYPES.length) % CTYPES.length];
|
|
2714
|
+
mutate(rules.map((r, i) => i === ri ? setCType(r, nextT) : r));
|
|
2715
|
+
} else if (field.kind === "perms" && key.leftArrow) {
|
|
2716
|
+
setPermCursor((n) => (n + PERMS.length - 1) % PERMS.length);
|
|
2717
|
+
} else if (field.kind === "perms" && key.rightArrow) {
|
|
2718
|
+
setPermCursor((n) => (n + 1) % PERMS.length);
|
|
2719
|
+
} else if (field.kind === "perms" && input === " ") {
|
|
2720
|
+
const p = PERMS[permCursor % PERMS.length];
|
|
2721
|
+
mutate(rules.map((rr, i) => i === ri ? togglePerm(rr, p) : rr));
|
|
2722
|
+
} else if (field.kind === "match" && input === "[") {
|
|
2723
|
+
mutate(rules.map((rr, i) => i === ri ? toggleStar(rr, "left") : rr));
|
|
2724
|
+
} else if (field.kind === "match" && input === "]") {
|
|
2725
|
+
mutate(rules.map((rr, i) => i === ri ? toggleStar(rr, "right") : rr));
|
|
2610
2726
|
} else if (field.kind === "priority" && (key.rightArrow || key.leftArrow)) {
|
|
2611
2727
|
const d = key.rightArrow ? 1 : -1;
|
|
2612
2728
|
mutate(rules.map((r, i) => i === ri ? { ...r, priority: Math.max(0, r.priority + d) } : r));
|
|
2613
|
-
} else if (field.kind === "text" && key.return) {
|
|
2729
|
+
} else if ((field.kind === "text" || field.kind === "match") && key.return) {
|
|
2614
2730
|
setEditVal(field.get(rule2));
|
|
2615
2731
|
setEditing(true);
|
|
2616
2732
|
} else if (input === "s") void save();
|
|
@@ -2636,7 +2752,7 @@ function PoliciesPanel({ focused }) {
|
|
|
2636
2752
|
/* @__PURE__ */ jsx4(Text4, { color: theme.dim, children: activeBy ? ` (${activeBy})` : "" })
|
|
2637
2753
|
] }) : /* @__PURE__ */ jsx4(Text4, { color: theme.bad, bold: true, children: "\u25CB NONE \u2014 nothing enforced, grants refused" })
|
|
2638
2754
|
] }),
|
|
2639
|
-
/* @__PURE__ */ jsx4(Text4, { color: theme.dim, wrap: "truncate", children: focused ? `\u2191\u2193 select \xB7 enter open \xB7 n new
|
|
2755
|
+
/* @__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" }),
|
|
2640
2756
|
creating !== null ? /* @__PURE__ */ jsxs4(Box4, { children: [
|
|
2641
2757
|
/* @__PURE__ */ jsx4(Text4, { color: theme.warn, children: "new policy name: " }),
|
|
2642
2758
|
/* @__PURE__ */ jsx4(TextInput3, { value: creating, onChange: setCreating, onSubmit: commitNewPolicy })
|
|
@@ -2711,12 +2827,53 @@ function PoliciesPanel({ focused }) {
|
|
|
2711
2827
|
/* @__PURE__ */ jsx4(Text4, { color: theme.dim, children: rule?.id ?? "" }),
|
|
2712
2828
|
dirtyTag
|
|
2713
2829
|
] }),
|
|
2714
|
-
/* @__PURE__ */ jsx4(Text4, { color: theme.dim, children: "\u2191\u2193 field \xB7 enter edit
|
|
2830
|
+
/* @__PURE__ */ jsx4(Text4, { color: theme.dim, children: "\u2191\u2193 field \xB7 enter edit \xB7 space toggle \xB7 \u2190\u2192 move/pick \xB7 [ ] wildcards \xB7 s save \xB7 \u2190 back" }),
|
|
2715
2831
|
/* @__PURE__ */ jsx4(Box4, { marginTop: 1, flexDirection: "column", children: FIELDS.map((f, i) => {
|
|
2716
|
-
const val = rule ? f.get(rule) : "";
|
|
2717
2832
|
const active2 = i === fi;
|
|
2833
|
+
const label = /* @__PURE__ */ jsx4(Text4, { color: active2 ? theme.accentBright : void 0, children: (active2 ? "\u25B8 " : " ") + f.label.padEnd(13) });
|
|
2834
|
+
if (f.kind === "perms" && rule) {
|
|
2835
|
+
return /* @__PURE__ */ jsxs4(Box4, { children: [
|
|
2836
|
+
label,
|
|
2837
|
+
PERMS.map((p, pi2) => {
|
|
2838
|
+
const on = permList(rule).includes(p);
|
|
2839
|
+
const cur = active2 && pi2 === permCursor % PERMS.length;
|
|
2840
|
+
return /* @__PURE__ */ jsxs4(Text4, { color: on ? theme.ok : theme.bad, inverse: cur, children: [
|
|
2841
|
+
p,
|
|
2842
|
+
pi2 < PERMS.length - 1 ? " " : ""
|
|
2843
|
+
] }, p);
|
|
2844
|
+
})
|
|
2845
|
+
] }, f.label);
|
|
2846
|
+
}
|
|
2847
|
+
if (f.kind === "match" && rule) {
|
|
2848
|
+
if (active2 && editing) {
|
|
2849
|
+
return /* @__PURE__ */ jsxs4(Box4, { children: [
|
|
2850
|
+
label,
|
|
2851
|
+
/* @__PURE__ */ jsx4(
|
|
2852
|
+
TextInput3,
|
|
2853
|
+
{
|
|
2854
|
+
value: editVal,
|
|
2855
|
+
onChange: setEditVal,
|
|
2856
|
+
onSubmit: (v2) => {
|
|
2857
|
+
if (f.set) mutate(rules.map((r, idx) => idx === ri ? f.set(r, v2) : r));
|
|
2858
|
+
setEditing(false);
|
|
2859
|
+
}
|
|
2860
|
+
}
|
|
2861
|
+
)
|
|
2862
|
+
] }, f.label);
|
|
2863
|
+
}
|
|
2864
|
+
const v = f.get(rule);
|
|
2865
|
+
const lOn = starActive(rule, "left");
|
|
2866
|
+
const rOn = starActive(rule, "right");
|
|
2867
|
+
return /* @__PURE__ */ jsxs4(Box4, { children: [
|
|
2868
|
+
label,
|
|
2869
|
+
/* @__PURE__ */ jsx4(Text4, { color: lOn ? theme.ok : theme.dim, children: lOn ? "\u2731 " : "\xB7 " }),
|
|
2870
|
+
/* @__PURE__ */ jsx4(Text4, { dimColor: !v, children: v || "\u2014" }),
|
|
2871
|
+
/* @__PURE__ */ jsx4(Text4, { color: rOn ? theme.ok : theme.dim, children: rOn ? " \u2731" : " \xB7" })
|
|
2872
|
+
] }, f.label);
|
|
2873
|
+
}
|
|
2874
|
+
const val = rule ? f.get(rule) : "";
|
|
2718
2875
|
return /* @__PURE__ */ jsxs4(Box4, { children: [
|
|
2719
|
-
|
|
2876
|
+
label,
|
|
2720
2877
|
active2 && editing && f.kind === "text" ? /* @__PURE__ */ jsx4(
|
|
2721
2878
|
TextInput3,
|
|
2722
2879
|
{
|
|
@@ -2737,6 +2894,7 @@ function PoliciesPanel({ focused }) {
|
|
|
2737
2894
|
)
|
|
2738
2895
|
] }, f.label);
|
|
2739
2896
|
}) }),
|
|
2897
|
+
/* @__PURE__ */ jsx4(Text4, { color: theme.dim, children: " * = wildcard (matches any run of chars). On Match: [ toggles a leading *, ] a trailing * \xB7 e.g. *ping* matches any command containing ping" }),
|
|
2740
2898
|
status ? /* @__PURE__ */ jsx4(Text4, { color: status.startsWith("\u2717") ? theme.bad : theme.ok, children: status }) : null
|
|
2741
2899
|
] });
|
|
2742
2900
|
}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@solongate/proxy",
|
|
3
|
-
"version": "0.82.
|
|
3
|
+
"version": "0.82.50",
|
|
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": {
|