@solongate/proxy 0.81.60 → 0.81.62

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.
@@ -15,3 +15,41 @@
15
15
  {"ms":2,"ts":1784326365762,"tool":"Bash","session":"f5933c65-c315-4440-9afb-7136fce6b695"}
16
16
  {"ms":3,"ts":1784326398280,"tool":"Edit","session":"f5933c65-c315-4440-9afb-7136fce6b695"}
17
17
  {"ms":1,"ts":1784326403539,"tool":"Bash","session":"f5933c65-c315-4440-9afb-7136fce6b695"}
18
+ {"ms":2,"ts":1784359530309,"tool":"Bash","session":"f5933c65-c315-4440-9afb-7136fce6b695"}
19
+ {"ms":4,"ts":1784359565744,"tool":"Bash","session":"f5933c65-c315-4440-9afb-7136fce6b695"}
20
+ {"ms":3,"ts":1784359892056,"tool":"Bash","session":"f5933c65-c315-4440-9afb-7136fce6b695"}
21
+ {"ms":2,"ts":1784359899408,"tool":"Bash","session":"f5933c65-c315-4440-9afb-7136fce6b695"}
22
+ {"ms":3,"ts":1784359942766,"tool":"Bash","session":"f5933c65-c315-4440-9afb-7136fce6b695"}
23
+ {"ms":3,"ts":1784359948446,"tool":"Bash","session":"f5933c65-c315-4440-9afb-7136fce6b695"}
24
+ {"ms":5,"ts":1784360088630,"tool":"Write","session":"f5933c65-c315-4440-9afb-7136fce6b695"}
25
+ {"ms":2,"ts":1784360093227,"tool":"Bash","session":"f5933c65-c315-4440-9afb-7136fce6b695"}
26
+ {"ms":4,"ts":1784360125661,"tool":"Bash","session":"f5933c65-c315-4440-9afb-7136fce6b695"}
27
+ {"ms":2,"ts":1784360134508,"tool":"Bash","session":"f5933c65-c315-4440-9afb-7136fce6b695"}
28
+ {"ms":5,"ts":1784360234461,"tool":"Bash","session":"f5933c65-c315-4440-9afb-7136fce6b695"}
29
+ {"ms":4,"ts":1784360256656,"tool":"Bash","session":"f5933c65-c315-4440-9afb-7136fce6b695"}
30
+ {"ms":5,"ts":1784365255204,"tool":"Bash","session":"f5933c65-c315-4440-9afb-7136fce6b695"}
31
+ {"ms":4,"ts":1784365279092,"tool":"Bash","session":"f5933c65-c315-4440-9afb-7136fce6b695"}
32
+ {"ms":4,"ts":1784365328385,"tool":"Bash","session":"f5933c65-c315-4440-9afb-7136fce6b695"}
33
+ {"ms":4,"ts":1784365360712,"tool":"Bash","session":"f5933c65-c315-4440-9afb-7136fce6b695"}
34
+ {"ms":4,"ts":1784365375478,"tool":"Write","session":"f5933c65-c315-4440-9afb-7136fce6b695"}
35
+ {"ms":3,"ts":1784365383985,"tool":"Bash","session":"f5933c65-c315-4440-9afb-7136fce6b695"}
36
+ {"ms":9,"ts":1784365415903,"tool":"Write","session":"f5933c65-c315-4440-9afb-7136fce6b695"}
37
+ {"ms":4,"ts":1784365422626,"tool":"Bash","session":"f5933c65-c315-4440-9afb-7136fce6b695"}
38
+ {"ms":5,"ts":1784365497358,"tool":"Bash","session":"f5933c65-c315-4440-9afb-7136fce6b695"}
39
+ {"ms":4,"ts":1784365509533,"tool":"Bash","session":"f5933c65-c315-4440-9afb-7136fce6b695"}
40
+ {"ms":4,"ts":1784365523577,"tool":"Bash","session":"f5933c65-c315-4440-9afb-7136fce6b695"}
41
+ {"ms":1,"ts":1784365524083,"tool":"Bash","session":"f5933c65-c315-4440-9afb-7136fce6b695"}
42
+ {"ms":2,"ts":1784365525077,"tool":"Bash","session":"f5933c65-c315-4440-9afb-7136fce6b695"}
43
+ {"ms":3,"ts":1784365525605,"tool":"Bash","session":"f5933c65-c315-4440-9afb-7136fce6b695"}
44
+ {"ms":3,"ts":1784365526627,"tool":"Bash","session":"f5933c65-c315-4440-9afb-7136fce6b695"}
45
+ {"ms":2,"ts":1784365527031,"tool":"Bash","session":"f5933c65-c315-4440-9afb-7136fce6b695"}
46
+ {"ms":5,"ts":1784366243066,"tool":"Write","session":"f5933c65-c315-4440-9afb-7136fce6b695"}
47
+ {"ms":3,"ts":1784366250503,"tool":"Bash","session":"f5933c65-c315-4440-9afb-7136fce6b695"}
48
+ {"ms":4,"ts":1784366281171,"tool":"Write","session":"f5933c65-c315-4440-9afb-7136fce6b695"}
49
+ {"ms":2,"ts":1784366288049,"tool":"Bash","session":"f5933c65-c315-4440-9afb-7136fce6b695"}
50
+ {"ms":3,"ts":1784367337998,"tool":"Write","session":"f5933c65-c315-4440-9afb-7136fce6b695"}
51
+ {"ms":2,"ts":1784367345104,"tool":"Bash","session":"f5933c65-c315-4440-9afb-7136fce6b695"}
52
+ {"ms":4,"ts":1784367420866,"tool":"Write","session":"f5933c65-c315-4440-9afb-7136fce6b695"}
53
+ {"ms":2,"ts":1784367428790,"tool":"Bash","session":"f5933c65-c315-4440-9afb-7136fce6b695"}
54
+ {"ms":3,"ts":1784369708978,"tool":"Write","session":"f5933c65-c315-4440-9afb-7136fce6b695"}
55
+ {"ms":4,"ts":1784369720104,"tool":"Bash","session":"f5933c65-c315-4440-9afb-7136fce6b695"}
@@ -1 +1 @@
1
- {"tool":"Bash","ts":1784138162061}
1
+ {"tool":"Bash","ts":1784365527031}
@@ -1 +1 @@
1
- {"ms":1,"ts":1784326403539,"tool":"Bash","session":"f5933c65-c315-4440-9afb-7136fce6b695"}
1
+ {"ms":4,"ts":1784369720104,"tool":"Bash","session":"f5933c65-c315-4440-9afb-7136fce6b695"}
@@ -1 +1 @@
1
- 1784326398329
1
+ 1784369709035
@@ -6536,11 +6536,28 @@ 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 = 34;
6539
+ var HOOK_VERSION = 36;
6540
6540
  function localLogsOnly(security) {
6541
- const l = security && security.localLogs;
6542
- return !!(l && l.enabled && typeof l.path === "string" && l.path.trim());
6541
+ if (security && typeof security === "object") {
6542
+ const l = security.localLogs;
6543
+ return !!(l && l.enabled && typeof l.path === "string" && l.path.trim());
6544
+ }
6545
+ try {
6546
+ const m = JSON.parse(readFileSync(join(resolve(homedir(), ".solongate"), ".local-logs-mode.json"), "utf-8"));
6547
+ return !!(m && m.localOnly);
6548
+ } catch {
6549
+ return false;
6550
+ }
6551
+ }
6552
+ function writeLocalMarker(security) {
6553
+ try {
6554
+ const l = security && security.localLogs;
6555
+ const localOnly = !!(l && l.enabled && typeof l.path === "string" && l.path.trim());
6556
+ writeFileSync(join(resolve(homedir(), ".solongate"), ".local-logs-mode.json"), JSON.stringify({ localOnly, ts: Date.now() }));
6557
+ } catch {
6558
+ }
6543
6559
  }
6560
+ var _invalidPathNoted = false;
6544
6561
  function resolveLocalLogDir(rawPath) {
6545
6562
  const dir = String(rawPath || "").trim().replace(/[\\/]+$/, "");
6546
6563
  if (!dir)
@@ -6548,13 +6565,16 @@ function resolveLocalLogDir(rawPath) {
6548
6565
  if (isAbsolute(dir))
6549
6566
  return dir;
6550
6567
  const fallback = resolve(homedir(), ".solongate", "local-logs");
6551
- try {
6552
- mkdirSync(resolve(homedir(), ".solongate"), { recursive: true });
6553
- writeFileSync(
6554
- resolve(homedir(), ".solongate", ".local-logs-invalid-path"),
6555
- JSON.stringify({ configured: dir, fallback, ts: Date.now() })
6556
- );
6557
- } catch {
6568
+ if (!_invalidPathNoted) {
6569
+ _invalidPathNoted = true;
6570
+ try {
6571
+ mkdirSync(resolve(homedir(), ".solongate"), { recursive: true });
6572
+ writeFileSync(
6573
+ resolve(homedir(), ".solongate", ".local-logs-invalid-path"),
6574
+ JSON.stringify({ configured: dir, fallback, ts: Date.now() })
6575
+ );
6576
+ } catch {
6577
+ }
6558
6578
  }
6559
6579
  return fallback;
6560
6580
  }
@@ -6753,6 +6773,7 @@ async function refreshPolicyCache() {
6753
6773
  writeFileSync(cacheFile, JSON.stringify({ _ts: Date.now(), policy, selfProtect, security, hookVersions }));
6754
6774
  } catch {
6755
6775
  }
6776
+ writeLocalMarker(security);
6756
6777
  } catch {
6757
6778
  }
6758
6779
  }
@@ -7751,8 +7772,10 @@ if (!REFRESH_MODE) {
7751
7772
  dashboardPolicy = cached.policy;
7752
7773
  if (typeof cached.selfProtect === "boolean")
7753
7774
  selfProtectEnabled = cached.selfProtect;
7754
- if (cached.security !== void 0)
7775
+ if (cached.security !== void 0) {
7755
7776
  securityCfg = cached.security;
7777
+ writeLocalMarker(securityCfg);
7778
+ }
7756
7779
  if (cached.hookVersions)
7757
7780
  CLOUD_HOOK_VERSIONS = cached.hookVersions;
7758
7781
  }
package/hooks/guard.mjs CHANGED
@@ -32,13 +32,36 @@ 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 = 34;
35
+ const HOOK_VERSION = 36;
36
36
 
37
37
  // True when local log storage is ON. In that mode logs are kept LOCAL ONLY and
38
38
  // nothing is sent to the cloud audit log.
39
39
  function localLogsOnly(security) {
40
- const l = security && security.localLogs;
41
- return !!(l && l.enabled && typeof l.path === 'string' && l.path.trim());
40
+ if (security && typeof security === 'object') {
41
+ const l = security.localLogs;
42
+ return !!(l && l.enabled && typeof l.path === 'string' && l.path.trim());
43
+ }
44
+ // security UNKNOWN (cold start / policy-cache miss / mid-toggle): consult the
45
+ // persisted local-mode marker so a DENY is NEVER leaked to the cloud when this
46
+ // device is in local-only mode. Without this fallback the first call(s) before
47
+ // the policy cache warms POST the denial to the cloud and fire webhooks/alerts
48
+ // even though the user chose local-only.
49
+ try {
50
+ const m = JSON.parse(readFileSync(join(resolve(homedir(), '.solongate'), '.local-logs-mode.json'), 'utf-8'));
51
+ return !!(m && m.localOnly);
52
+ } catch { return false; }
53
+ }
54
+
55
+ // Persist whether this device is in local-only mode, INDEPENDENT of the policy
56
+ // cache, so localLogsOnly() answers correctly even on a cache-miss call. Written
57
+ // on every policy refresh; switching back to cloud sets localOnly:false so cloud
58
+ // POSTs resume.
59
+ function writeLocalMarker(security) {
60
+ try {
61
+ const l = security && security.localLogs;
62
+ const localOnly = !!(l && l.enabled && typeof l.path === 'string' && l.path.trim());
63
+ writeFileSync(join(resolve(homedir(), '.solongate'), '.local-logs-mode.json'), JSON.stringify({ localOnly, ts: Date.now() }));
64
+ } catch {}
42
65
  }
43
66
 
44
67
  // Resolve the FOLDER local logs may be written into. It MUST be absolute on
@@ -49,16 +72,24 @@ function localLogsOnly(security) {
49
72
  // When the configured path isn't absolute here, fall back to a fixed home folder
50
73
  // so entries are never lost and never leak into a project, and record the bad
51
74
  // path so the dashboard/user can be told their path isn't valid on this device.
75
+ let _invalidPathNoted = false;
52
76
  function resolveLocalLogDir(rawPath) {
53
77
  const dir = String(rawPath || '').trim().replace(/[\\/]+$/, '');
54
78
  if (!dir) return null;
55
79
  if (isAbsolute(dir)) return dir;
56
80
  const fallback = resolve(homedir(), '.solongate', 'local-logs');
57
- try {
58
- mkdirSync(resolve(homedir(), '.solongate'), { recursive: true });
59
- writeFileSync(resolve(homedir(), '.solongate', '.local-logs-invalid-path'),
60
- JSON.stringify({ configured: dir, fallback, ts: Date.now() }));
61
- } catch { /* ignore */ }
81
+ // Non-absolute path (e.g. a Windows path on Linux): fall back. The diagnostic
82
+ // marker is written at most ONCE per process, NOT on every denial — a per-call
83
+ // writeFileSync here widened the window where a concurrent guard process dies
84
+ // before its audit append lands (dropped denials under a simultaneous burst).
85
+ if (!_invalidPathNoted) {
86
+ _invalidPathNoted = true;
87
+ try {
88
+ mkdirSync(resolve(homedir(), '.solongate'), { recursive: true });
89
+ writeFileSync(resolve(homedir(), '.solongate', '.local-logs-invalid-path'),
90
+ JSON.stringify({ configured: dir, fallback, ts: Date.now() }));
91
+ } catch { /* ignore */ }
92
+ }
62
93
  return fallback;
63
94
  }
64
95
 
@@ -279,6 +310,7 @@ async function refreshPolicyCache() {
279
310
  } catch {}
280
311
  // Always advance _ts (success or failure) so the hot path backs off between refreshes.
281
312
  try { writeFileSync(cacheFile, JSON.stringify({ _ts: Date.now(), policy, selfProtect, security, hookVersions })); } catch {}
313
+ writeLocalMarker(security);
282
314
  } catch {}
283
315
  }
284
316
  if (REFRESH_MODE) { try { setTimeout(() => { try { process.exit(process.exitCode || 0); } catch {} }, 8000).unref(); } catch {} refreshPolicyCache().finally(() => { process.exitCode = 0; }); }
@@ -1553,7 +1585,7 @@ if (!REFRESH_MODE) { try { input += readFileSync(0, 'utf-8'); } catch {} }
1553
1585
  refreshDue = false;
1554
1586
  if (cached.policy) dashboardPolicy = cached.policy;
1555
1587
  if (typeof cached.selfProtect === 'boolean') selfProtectEnabled = cached.selfProtect;
1556
- if (cached.security !== undefined) securityCfg = cached.security;
1588
+ if (cached.security !== undefined) { securityCfg = cached.security; writeLocalMarker(securityCfg); }
1557
1589
  if (cached.hookVersions) CLOUD_HOOK_VERSIONS = cached.hookVersions;
1558
1590
  }
1559
1591
  }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@solongate/proxy",
3
- "version": "0.81.60",
3
+ "version": "0.81.62",
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": {