@solongate/proxy 0.83.9 → 0.83.10
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 +6 -0
- package/dist/tui/index.js +6 -0
- package/hooks/audit.mjs +6 -8
- package/hooks/guard.bundled.mjs +33 -30
- package/hooks/guard.mjs +7 -9
- package/package.json +1 -1
package/dist/index.js
CHANGED
|
@@ -12363,6 +12363,12 @@ function SettingsPanel({
|
|
|
12363
12363
|
] }, "h:" + sec)
|
|
12364
12364
|
);
|
|
12365
12365
|
lineKey.push("");
|
|
12366
|
+
if ((sec === "WEBHOOKS" || sec === "ALERTS") && local?.enabled) {
|
|
12367
|
+
lineEls.push(
|
|
12368
|
+
/* @__PURE__ */ jsx8(Text8, { wrap: "truncate", color: theme.warn, children: ` \u26A0 local logs on \u2014 real denials stay on this machine, so ${sec.toLowerCase()} do NOT fire${sec === "WEBHOOKS" ? " (t test still works)" : ""}` }, "warn:" + sec)
|
|
12369
|
+
);
|
|
12370
|
+
lineKey.push("");
|
|
12371
|
+
}
|
|
12366
12372
|
}
|
|
12367
12373
|
lineEls.push(/* @__PURE__ */ jsx8(Box8, { children: rowLine(r) }, keyOf(r)));
|
|
12368
12374
|
lineKey.push(keyOf(r));
|
package/dist/tui/index.js
CHANGED
|
@@ -5489,6 +5489,12 @@ function SettingsPanel({
|
|
|
5489
5489
|
] }, "h:" + sec)
|
|
5490
5490
|
);
|
|
5491
5491
|
lineKey.push("");
|
|
5492
|
+
if ((sec === "WEBHOOKS" || sec === "ALERTS") && local?.enabled) {
|
|
5493
|
+
lineEls.push(
|
|
5494
|
+
/* @__PURE__ */ jsx8(Text8, { wrap: "truncate", color: theme.warn, children: ` \u26A0 local logs on \u2014 real denials stay on this machine, so ${sec.toLowerCase()} do NOT fire${sec === "WEBHOOKS" ? " (t test still works)" : ""}` }, "warn:" + sec)
|
|
5495
|
+
);
|
|
5496
|
+
lineKey.push("");
|
|
5497
|
+
}
|
|
5492
5498
|
}
|
|
5493
5499
|
lineEls.push(/* @__PURE__ */ jsx8(Box8, { children: rowLine(r) }, keyOf(r)));
|
|
5494
5500
|
lineKey.push(keyOf(r));
|
package/hooks/audit.mjs
CHANGED
|
@@ -11,7 +11,7 @@ import { homedir } from 'node:os';
|
|
|
11
11
|
// Bump on every audit hook change. The cloud serves the newest version; the guard
|
|
12
12
|
// hook installs it on its next run (no re-login needed). See guard.mjs
|
|
13
13
|
// fetchAndInstallHook / maybeSelfUpdate.
|
|
14
|
-
const HOOK_VERSION =
|
|
14
|
+
const HOOK_VERSION = 26;
|
|
15
15
|
|
|
16
16
|
function loadEnvKey(dir) {
|
|
17
17
|
try {
|
|
@@ -748,11 +748,8 @@ try { input += readFileSync(0, 'utf-8'); } catch {}
|
|
|
748
748
|
try { rateLimitBurst = rateLimitObserveBurst(AGENT_ID, loadRateLimitObserve()); } catch { rateLimitBurst = false; }
|
|
749
749
|
}
|
|
750
750
|
|
|
751
|
-
// Local log storage (opt-in)
|
|
752
|
-
//
|
|
753
|
-
// project with local logs on saw an empty dashboard with nothing to say the
|
|
754
|
-
// emptiness was deliberate — while the dataroom called the same setting
|
|
755
|
-
// "mirror every decision to a file".
|
|
751
|
+
// Local log storage (opt-in): when ON, logs are kept LOCAL ONLY — we append
|
|
752
|
+
// this entry to the user's chosen file and do NOT send it to the cloud.
|
|
756
753
|
const localLogs = loadLocalLogs();
|
|
757
754
|
if (localLogs) {
|
|
758
755
|
appendLocalLog(localLogs, {
|
|
@@ -762,8 +759,9 @@ try { input += readFileSync(0, 'utf-8'); } catch {}
|
|
|
762
759
|
...(dlpMatches.length ? { dlp: dlpMatches } : {}),
|
|
763
760
|
...(rateLimitBurst ? { rate_limit_burst: true } : {}),
|
|
764
761
|
});
|
|
765
|
-
|
|
766
|
-
|
|
762
|
+
fetchDone = true;
|
|
763
|
+
maybeExit();
|
|
764
|
+
} else {
|
|
767
765
|
// Fire-and-forget: don't block tool execution waiting for API response
|
|
768
766
|
fetch(`${API_URL}/api/v1/audit-logs`, {
|
|
769
767
|
method: 'POST',
|
package/hooks/guard.bundled.mjs
CHANGED
|
@@ -6536,7 +6536,7 @@ import { resolve, join, dirname, isAbsolute } from "node:path";
|
|
|
6536
6536
|
import { homedir } from "node:os";
|
|
6537
6537
|
import { gunzipSync } from "node:zlib";
|
|
6538
6538
|
import { createHash } from "node:crypto";
|
|
6539
|
-
var HOOK_VERSION =
|
|
6539
|
+
var HOOK_VERSION = 75;
|
|
6540
6540
|
function localLogsOnly(security) {
|
|
6541
6541
|
if (security !== void 0) {
|
|
6542
6542
|
const l = security && security.localLogs;
|
|
@@ -8428,12 +8428,13 @@ if (!REFRESH_MODE) {
|
|
|
8428
8428
|
} catch {
|
|
8429
8429
|
}
|
|
8430
8430
|
try {
|
|
8431
|
-
|
|
8432
|
-
|
|
8433
|
-
|
|
8434
|
-
|
|
8435
|
-
|
|
8436
|
-
|
|
8431
|
+
if (!localLogsOnly(_sec))
|
|
8432
|
+
await fetch(API_URL + "/api/v1/audit-logs", {
|
|
8433
|
+
method: "POST",
|
|
8434
|
+
headers: { "Content-Type": "application/json", ...AUTH_HEADERS },
|
|
8435
|
+
body: JSON.stringify(_logEntry),
|
|
8436
|
+
signal: AbortSignal.timeout(3e3)
|
|
8437
|
+
});
|
|
8437
8438
|
} catch {
|
|
8438
8439
|
}
|
|
8439
8440
|
if (AGENT_TYPE !== "codex")
|
|
@@ -8545,23 +8546,24 @@ if (!REFRESH_MODE) {
|
|
|
8545
8546
|
}
|
|
8546
8547
|
writeLocalLog(securityCfg, { ts: (/* @__PURE__ */ new Date()).toISOString(), tool: toolName, arguments: args, decision: "DENY", reason: "ghost path (hidden from agent)", permission: guessPermission(toolName), source: `${AGENT_TYPE}-guard`, agent_id: AGENT_TYPE, agent_name: AGENT_NAME, session_id: call.sessionId, evaluation_time_ms: Date.now() - _evalStart });
|
|
8547
8548
|
try {
|
|
8548
|
-
|
|
8549
|
-
|
|
8550
|
-
|
|
8551
|
-
|
|
8552
|
-
|
|
8553
|
-
|
|
8554
|
-
|
|
8555
|
-
|
|
8556
|
-
|
|
8557
|
-
|
|
8558
|
-
|
|
8559
|
-
|
|
8560
|
-
|
|
8561
|
-
|
|
8562
|
-
|
|
8563
|
-
|
|
8564
|
-
|
|
8549
|
+
if (!localLogsOnly(securityCfg))
|
|
8550
|
+
await fetch(API_URL + "/api/v1/audit-logs", {
|
|
8551
|
+
method: "POST",
|
|
8552
|
+
headers: { "Content-Type": "application/json", ...AUTH_HEADERS },
|
|
8553
|
+
body: JSON.stringify({
|
|
8554
|
+
tool: toolName,
|
|
8555
|
+
arguments: args,
|
|
8556
|
+
decision: "DENY",
|
|
8557
|
+
reason: "ghost path (hidden from agent)",
|
|
8558
|
+
permission: guessPermission(toolName),
|
|
8559
|
+
source: `${AGENT_TYPE}-guard`,
|
|
8560
|
+
agent_id: AGENT_TYPE,
|
|
8561
|
+
agent_name: AGENT_NAME,
|
|
8562
|
+
session_id: call.sessionId,
|
|
8563
|
+
evaluation_time_ms: Date.now() - _evalStart
|
|
8564
|
+
}),
|
|
8565
|
+
signal: AbortSignal.timeout(3e3)
|
|
8566
|
+
});
|
|
8565
8567
|
} catch {
|
|
8566
8568
|
}
|
|
8567
8569
|
blockTool(ghostHit, true);
|
|
@@ -8642,12 +8644,13 @@ if (!REFRESH_MODE) {
|
|
|
8642
8644
|
evaluation_time_ms: Date.now() - _evalStart
|
|
8643
8645
|
};
|
|
8644
8646
|
writeLocalLog(securityCfg, { ts: (/* @__PURE__ */ new Date()).toISOString(), ...logEntry });
|
|
8645
|
-
|
|
8646
|
-
|
|
8647
|
-
|
|
8648
|
-
|
|
8649
|
-
|
|
8650
|
-
|
|
8647
|
+
if (!localLogsOnly(securityCfg))
|
|
8648
|
+
await fetch(API_URL + "/api/v1/audit-logs", {
|
|
8649
|
+
method: "POST",
|
|
8650
|
+
headers: { "Content-Type": "application/json", ...AUTH_HEADERS },
|
|
8651
|
+
body: JSON.stringify(logEntry),
|
|
8652
|
+
signal: AbortSignal.timeout(3e3)
|
|
8653
|
+
});
|
|
8651
8654
|
} catch {
|
|
8652
8655
|
}
|
|
8653
8656
|
}
|
package/hooks/guard.mjs
CHANGED
|
@@ -32,13 +32,11 @@ import { createHash } from 'node:crypto';
|
|
|
32
32
|
// the installed hook self-updates when the cloud version is higher (see
|
|
33
33
|
// maybeSelfUpdate). This is what makes guard fixes propagate without a manual
|
|
34
34
|
// reinstall — the same trust model as the OPA WASM this hook already runs.
|
|
35
|
-
const HOOK_VERSION =
|
|
35
|
+
const HOOK_VERSION = 75;
|
|
36
36
|
|
|
37
|
-
// True when local log storage is ON
|
|
38
|
-
//
|
|
39
|
-
//
|
|
40
|
-
// file"; the code diverted instead, so a project with local logs on had nothing
|
|
41
|
-
// in the dashboard and no way to tell that was deliberate.
|
|
37
|
+
// True when local log storage is ON. In that mode logs are kept LOCAL ONLY and
|
|
38
|
+
// nothing is sent to the cloud audit log — local and cloud are one or the
|
|
39
|
+
// other, never both.
|
|
42
40
|
//
|
|
43
41
|
// `undefined` is the ONLY value that means "we do not know yet". `null` is an
|
|
44
42
|
// answer: it is what the refresh writes when the API returns no security block,
|
|
@@ -2426,7 +2424,7 @@ if (!REFRESH_MODE) { try { input += readFileSync(0, 'utf-8'); } catch {} }
|
|
|
2426
2424
|
};
|
|
2427
2425
|
try { writeLocalLog(_sec, { ts: new Date().toISOString(), ..._logEntry }); } catch {}
|
|
2428
2426
|
try {
|
|
2429
|
-
await fetch(API_URL + '/api/v1/audit-logs', {
|
|
2427
|
+
if (!localLogsOnly(_sec)) await fetch(API_URL + '/api/v1/audit-logs', {
|
|
2430
2428
|
method: 'POST',
|
|
2431
2429
|
headers: { 'Content-Type': 'application/json', ...AUTH_HEADERS },
|
|
2432
2430
|
body: JSON.stringify(_logEntry),
|
|
@@ -2579,7 +2577,7 @@ if (!REFRESH_MODE) { try { input += readFileSync(0, 'utf-8'); } catch {} }
|
|
|
2579
2577
|
try { writeDenyFlag(toolName); } catch {}
|
|
2580
2578
|
writeLocalLog(securityCfg, { ts: new Date().toISOString(), tool: toolName, arguments: args, decision: 'DENY', reason: 'ghost path (hidden from agent)', permission: guessPermission(toolName), source: `${AGENT_TYPE}-guard`, agent_id: AGENT_TYPE, agent_name: AGENT_NAME, session_id: call.sessionId, evaluation_time_ms: Date.now() - _evalStart });
|
|
2581
2579
|
try {
|
|
2582
|
-
await fetch(API_URL + '/api/v1/audit-logs', {
|
|
2580
|
+
if (!localLogsOnly(securityCfg)) await fetch(API_URL + '/api/v1/audit-logs', {
|
|
2583
2581
|
method: 'POST',
|
|
2584
2582
|
headers: { 'Content-Type': 'application/json', ...AUTH_HEADERS },
|
|
2585
2583
|
body: JSON.stringify({
|
|
@@ -2721,7 +2719,7 @@ if (!REFRESH_MODE) { try { input += readFileSync(0, 'utf-8'); } catch {} }
|
|
|
2721
2719
|
writeLocalLog(securityCfg, { ts: new Date().toISOString(), ...logEntry });
|
|
2722
2720
|
// PI hook layer removed — piResult fields no longer attached.
|
|
2723
2721
|
// Local-only mode: keep the log on the user's machine, skip the cloud.
|
|
2724
|
-
await fetch(API_URL + '/api/v1/audit-logs', {
|
|
2722
|
+
if (!localLogsOnly(securityCfg)) await fetch(API_URL + '/api/v1/audit-logs', {
|
|
2725
2723
|
method: 'POST',
|
|
2726
2724
|
headers: { 'Content-Type': 'application/json', ...AUTH_HEADERS },
|
|
2727
2725
|
body: JSON.stringify(logEntry),
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@solongate/proxy",
|
|
3
|
-
"version": "0.83.
|
|
3
|
+
"version": "0.83.10",
|
|
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": {
|