@camstack/system 1.2.381 → 1.2.382
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/addon-runner.js +4 -2
- package/dist/addon-runner.mjs +4 -2
- package/dist/addon-utils.js +1 -1
- package/dist/addon-utils.mjs +1 -1
- package/dist/auth/admin-token-manager.d.ts +75 -0
- package/dist/auth/api-key-manager.d.ts +1 -14
- package/dist/auth/auth-audit-log.d.ts +59 -0
- package/dist/auth/auth-manager.d.ts +28 -16
- package/dist/auth/cidr.d.ts +22 -0
- package/dist/auth/login-session-manager.d.ts +158 -0
- package/dist/auth/user-management-internal.d.ts +69 -0
- package/dist/builtins/addon-pages-aggregator/addon-pages-aggregator.addon.js +1 -1
- package/dist/builtins/addon-pages-aggregator/addon-pages-aggregator.addon.mjs +1 -1
- package/dist/builtins/addon-widgets-aggregator/addon-widgets-aggregator.addon.js +1 -1
- package/dist/builtins/addon-widgets-aggregator/addon-widgets-aggregator.addon.mjs +1 -1
- package/dist/builtins/alerts/alerts.addon.js +1 -1
- package/dist/builtins/alerts/alerts.addon.mjs +1 -1
- package/dist/builtins/autotrack/index.js +1 -1
- package/dist/builtins/autotrack/index.mjs +1 -1
- package/dist/builtins/backup-orchestrator/backup-orchestrator.addon.d.ts +14 -0
- package/dist/builtins/backup-orchestrator/backup-orchestrator.addon.js +291 -3631
- package/dist/builtins/backup-orchestrator/backup-orchestrator.addon.mjs +284 -3617
- package/dist/builtins/backup-orchestrator/encryption-key-store.d.ts +36 -0
- package/dist/builtins/backup-orchestrator/manifest-store.d.ts +19 -1
- package/dist/builtins/backup-orchestrator/restore-archive.d.ts +24 -0
- package/dist/builtins/calendars/calendars.addon.js +1 -1
- package/dist/builtins/calendars/calendars.addon.mjs +1 -1
- package/dist/builtins/camera-grid/index.js +1 -1
- package/dist/builtins/camera-grid/index.mjs +1 -1
- package/dist/builtins/composer/composer.addon.js +2 -2
- package/dist/builtins/composer/composer.addon.mjs +2 -2
- package/dist/builtins/console-logging/index.js +1 -1
- package/dist/builtins/console-logging/index.mjs +1 -1
- package/dist/builtins/core-blocks/core-blocks.addon.js +2 -2
- package/dist/builtins/core-blocks/core-blocks.addon.mjs +2 -2
- package/dist/builtins/device-manager/device-manager.addon.js +2 -2
- package/dist/builtins/device-manager/device-manager.addon.mjs +2 -2
- package/dist/builtins/hub-forwarder/index.js +1 -1
- package/dist/builtins/hub-forwarder/index.mjs +1 -1
- package/dist/builtins/keychain/index.js +1108 -733
- package/dist/builtins/keychain/index.mjs +1108 -733
- package/dist/builtins/keychain/keychain-reset-trigger.d.ts +25 -0
- package/dist/builtins/keychain/keychain-service.d.ts +36 -3
- package/dist/builtins/keychain/keychain-store.d.ts +24 -1
- package/dist/builtins/liveness-monitor/liveness-monitor.addon.js +1 -1
- package/dist/builtins/liveness-monitor/liveness-monitor.addon.mjs +1 -1
- package/dist/builtins/local-auth/local-auth.addon.d.ts +16 -0
- package/dist/builtins/local-auth/local-auth.addon.js +1021 -118
- package/dist/builtins/local-auth/local-auth.addon.mjs +1034 -141
- package/dist/builtins/local-network/local-network.addon.js +3 -3
- package/dist/builtins/local-network/local-network.addon.mjs +3 -3
- package/dist/builtins/loki-logging/index.js +97 -29
- package/dist/builtins/loki-logging/index.mjs +97 -29
- package/dist/builtins/loki-logging/loki-logging.addon.d.ts +7 -23
- package/dist/builtins/loki-logging/retire-enabled-flag.d.ts +23 -0
- package/dist/builtins/native-metrics/native-metrics.addon.js +1 -1
- package/dist/builtins/native-metrics/native-metrics.addon.mjs +1 -1
- package/dist/builtins/platform-probe/index.js +1 -1
- package/dist/builtins/platform-probe/index.mjs +1 -1
- package/dist/builtins/remote-access-orchestrator/remote-access-orchestrator.addon.js +1 -1
- package/dist/builtins/remote-access-orchestrator/remote-access-orchestrator.addon.mjs +1 -1
- package/dist/builtins/snapshot/index.js +1 -1
- package/dist/builtins/snapshot/index.mjs +1 -1
- package/dist/builtins/sqlite-storage/filesystem-storage.addon.js +1 -1
- package/dist/builtins/sqlite-storage/filesystem-storage.addon.mjs +1 -1
- package/dist/builtins/sqlite-storage/sqlite-settings.addon.js +0 -0
- package/dist/builtins/sqlite-storage/sqlite-settings.addon.mjs +0 -0
- package/dist/builtins/storage-orchestrator/storage-orchestrator.addon.js +2 -2
- package/dist/builtins/storage-orchestrator/storage-orchestrator.addon.mjs +2 -2
- package/dist/builtins/system-backup/backup-encryption.d.ts +57 -0
- package/dist/builtins/system-backup/system-backup.service.d.ts +16 -1
- package/dist/builtins/system-config/cluster-secret-keeper.d.ts +7 -0
- package/dist/builtins/system-config/system-config.addon.d.ts +13 -3
- package/dist/builtins/system-config/system-config.addon.js +324 -38
- package/dist/builtins/system-config/system-config.addon.mjs +312 -39
- package/dist/builtins/winston-logging/index.js +1 -1
- package/dist/builtins/winston-logging/index.mjs +1 -1
- package/dist/{child-cap-dispatch-hf-Gce_4.js → child-cap-dispatch-BrMdKHfp.js} +6 -3
- package/dist/{child-cap-dispatch-BTe4eKep.mjs → child-cap-dispatch-WsYPNvnK.mjs} +10 -7
- package/dist/{cluster-secret-BUiG8DCo.js → cluster-secret-BEPLh3LL.js} +1 -1
- package/dist/{cluster-secret-DgJTZ4qY.mjs → cluster-secret-DhPdZ0VA.mjs} +1 -1
- package/dist/{composition-sources-4SmsP0O4.js → composition-sources-C6ZynCZ2.js} +1 -1
- package/dist/{composition-sources-CgOQVIwa.mjs → composition-sources-XqRX51db.mjs} +1 -1
- package/dist/{dist-BC0GnGeg.mjs → dist-CP5TTjwd.mjs} +1710 -158
- package/dist/{dist-BugrtARh.js → dist-tnAO12ak.js} +1721 -157
- package/dist/{event-loop-stall-monitor-CnQ-K6Gn.js → event-loop-stall-monitor-9kR0sAPS.js} +15 -15
- package/dist/{event-loop-stall-monitor-GgpnfAC0.mjs → event-loop-stall-monitor-WkssIXjq.mjs} +15 -15
- package/dist/{hub-cap-forward-CH6_VWVO.mjs → hub-cap-forward-CDiN1p98.mjs} +18 -2
- package/dist/{hub-cap-forward-COCmYaup.js → hub-cap-forward-W8oEm4ME.js} +23 -1
- package/dist/index.d.ts +16 -1
- package/dist/index.js +3341 -79
- package/dist/index.mjs +3567 -354
- package/dist/{jwt-signing-key-CsuVKA1W.mjs → jwt-signing-key-CAbozwvF.mjs} +1 -1
- package/dist/{jwt-signing-key-CjoZcol8.js → jwt-signing-key-Dgf8sIXY.js} +1 -1
- package/dist/kernel/index.d.ts +12 -0
- package/dist/kernel/moleculer/broker-factory.d.ts +8 -1
- package/dist/kernel/moleculer/child-addon-call-dispatch.d.ts +1 -0
- package/dist/kernel/moleculer/hub-service.d.ts +9 -0
- package/dist/kernel/moleculer/mesh-access-middleware.d.ts +28 -0
- package/dist/kernel/moleculer/mesh-auth-mode.d.ts +64 -0
- package/dist/kernel/moleculer/mesh-authenticator.d.ts +71 -0
- package/dist/kernel/moleculer/mesh-auto-enforce.d.ts +26 -0
- package/dist/kernel/moleculer/mesh-join-client.d.ts +51 -0
- package/dist/kernel/moleculer/mesh-join-proof.d.ts +39 -0
- package/dist/kernel/system-settings-policy.d.ts +4 -3
- package/dist/kernel/transport/hub-cap-forward.d.ts +14 -3
- package/dist/kernel/transport/index.d.ts +1 -1
- package/dist/restore-archive-BV39nY46.mjs +4045 -0
- package/dist/restore-archive-CbvrfPuB.js +4139 -0
- package/dist/{retired-settings-keys-DCuetwOt.js → retired-settings-keys-4bHpL-0m.js} +1 -1
- package/dist/{retired-settings-keys-MCVhxPkJ.mjs → retired-settings-keys-CzpIkwS-.mjs} +1 -1
- package/dist/{tls-CJHogwTl.mjs → tls-4cRH24Id.mjs} +3 -3
- package/package.json +1 -1
- package/dist/{tls-CCMX7MjX.js → tls-CljF057D.js} +1 -1
|
@@ -1,18 +1,151 @@
|
|
|
1
|
-
import {
|
|
2
|
-
import { d as setProcessClusterSecretGate, l as hashClusterSecret, n as CLUSTER_SECRET_REF } from "../../cluster-secret-
|
|
1
|
+
import { $r as transformSecretWrite, Ar as hydrateSchema, Fr as isSecretMarker, Lr as makeSecretMarker, Pr as isSecretClear, Yr as secretTreeFromSchema, lr as errMsg, ur as BaseAddon } from "../../dist-CP5TTjwd.mjs";
|
|
2
|
+
import { d as setProcessClusterSecretGate, l as hashClusterSecret, n as CLUSTER_SECRET_REF } from "../../cluster-secret-DhPdZ0VA.mjs";
|
|
3
|
+
import { randomBytes } from "node:crypto";
|
|
4
|
+
import { BlockList, isIP } from "node:net";
|
|
5
|
+
//#region src/auth/cidr.ts
|
|
6
|
+
/**
|
|
7
|
+
* CIDR lists for the auth settings that name networks: the trusted reverse
|
|
8
|
+
* proxies whose forwarding headers the hub believes, and the trusted local
|
|
9
|
+
* networks a login may skip its second factor from.
|
|
10
|
+
*
|
|
11
|
+
* Built on `net.BlockList`, so IPv4 and IPv6 are matched by the platform, not
|
|
12
|
+
* by string prefixes. A bare address is read as a single host. Anything that
|
|
13
|
+
* does not parse is REPORTED back (`invalid`) so the settings form can refuse
|
|
14
|
+
* it — a list that silently dropped a typo would quietly trust less than the
|
|
15
|
+
* operator wrote, and one that guessed would trust more.
|
|
16
|
+
*/
|
|
17
|
+
/** Strip an IPv4-mapped IPv6 prefix and a zone id, so ranges see a clean address. */
|
|
18
|
+
function normalizeIp(ip) {
|
|
19
|
+
let out = ip.trim();
|
|
20
|
+
if (out.toLowerCase().startsWith("::ffff:") && isIP(out.slice(7)) === 4) out = out.slice(7);
|
|
21
|
+
const pct = out.indexOf("%");
|
|
22
|
+
if (pct !== -1) out = out.slice(0, pct);
|
|
23
|
+
return out;
|
|
24
|
+
}
|
|
25
|
+
function parseCidr(raw) {
|
|
26
|
+
const text = raw.trim();
|
|
27
|
+
const slash = text.indexOf("/");
|
|
28
|
+
const address = normalizeIp(slash === -1 ? text : text.slice(0, slash));
|
|
29
|
+
const version = isIP(address);
|
|
30
|
+
if (version === 0) return null;
|
|
31
|
+
const max = version === 4 ? 32 : 128;
|
|
32
|
+
let prefix = max;
|
|
33
|
+
if (slash !== -1) {
|
|
34
|
+
const bits = text.slice(slash + 1);
|
|
35
|
+
if (!/^\d{1,3}$/.test(bits)) return null;
|
|
36
|
+
prefix = Number.parseInt(bits, 10);
|
|
37
|
+
if (prefix > max) return null;
|
|
38
|
+
}
|
|
39
|
+
return {
|
|
40
|
+
network: address,
|
|
41
|
+
prefix,
|
|
42
|
+
family: version === 4 ? "ipv4" : "ipv6"
|
|
43
|
+
};
|
|
44
|
+
}
|
|
45
|
+
/** Parse a list of CIDR strings; blanks are skipped, unparseable entries returned. */
|
|
46
|
+
function parseCidrList(entries) {
|
|
47
|
+
const valid = [];
|
|
48
|
+
const invalid = [];
|
|
49
|
+
for (const entry of entries) {
|
|
50
|
+
if (entry.trim().length === 0) continue;
|
|
51
|
+
const parsed = parseCidr(entry);
|
|
52
|
+
if (parsed === null) invalid.push(entry);
|
|
53
|
+
else valid.push(parsed);
|
|
54
|
+
}
|
|
55
|
+
return {
|
|
56
|
+
valid,
|
|
57
|
+
invalid
|
|
58
|
+
};
|
|
59
|
+
}
|
|
60
|
+
/** A stored setting as a string list — accepts a list, or one comma/space separated string. */
|
|
61
|
+
function readCidrSetting(value) {
|
|
62
|
+
if (Array.isArray(value)) return value.filter((v) => typeof v === "string");
|
|
63
|
+
if (typeof value === "string") return value.split(/[\s,]+/).filter((v) => v.length > 0);
|
|
64
|
+
return [];
|
|
65
|
+
}
|
|
66
|
+
/** A matcher over the VALID entries of `entries`; invalid ones match nothing. */
|
|
67
|
+
function createCidrMatcher(entries) {
|
|
68
|
+
const { valid } = parseCidrList(entries);
|
|
69
|
+
const list = new BlockList();
|
|
70
|
+
for (const cidr of valid) list.addSubnet(cidr.network, cidr.prefix, cidr.family);
|
|
71
|
+
return {
|
|
72
|
+
size: valid.length,
|
|
73
|
+
contains(ip) {
|
|
74
|
+
if (valid.length === 0) return false;
|
|
75
|
+
const address = normalizeIp(ip);
|
|
76
|
+
const version = isIP(address);
|
|
77
|
+
if (version === 0) return false;
|
|
78
|
+
return list.check(address, version === 4 ? "ipv4" : "ipv6");
|
|
79
|
+
}
|
|
80
|
+
};
|
|
81
|
+
}
|
|
82
|
+
//#endregion
|
|
83
|
+
//#region src/kernel/moleculer/mesh-auth-mode.ts
|
|
84
|
+
/** The field key inside the `cluster` section. */
|
|
85
|
+
var MESH_AUTH_MODE_FIELD = "meshAuth";
|
|
86
|
+
function isMeshAuthMode(value) {
|
|
87
|
+
return value === "transition" || value === "enforced";
|
|
88
|
+
}
|
|
89
|
+
/** A stored value that is not a mode reads as `transition`: never refuse on a typo. */
|
|
90
|
+
function parseMeshAuthMode(value) {
|
|
91
|
+
return isMeshAuthMode(value) ? value : "transition";
|
|
92
|
+
}
|
|
93
|
+
var MODE_SLOT = Symbol.for("camstack.cluster.mesh-auth-mode");
|
|
94
|
+
var ENFORCER_SLOT = Symbol.for("camstack.cluster.mesh-auth-enforcer");
|
|
95
|
+
var STATUS_SLOT = Symbol.for("camstack.cluster.mesh-auth-status");
|
|
96
|
+
/** Published by `system-config` whenever the stored mode is (re)read. */
|
|
97
|
+
function setProcessMeshAuthMode(mode) {
|
|
98
|
+
Reflect.set(globalThis, MODE_SLOT, mode);
|
|
99
|
+
}
|
|
100
|
+
/**
|
|
101
|
+
* The hub's mode. Until `system-config` has read it: `transition` — the safe
|
|
102
|
+
* side for an upgrading cluster (nothing that worked before is refused), and
|
|
103
|
+
* exactly what an unread setting means.
|
|
104
|
+
*/
|
|
105
|
+
function getProcessMeshAuthMode() {
|
|
106
|
+
return parseMeshAuthMode(Reflect.get(globalThis, MODE_SLOT));
|
|
107
|
+
}
|
|
108
|
+
function setProcessMeshEnforcer(enforcer) {
|
|
109
|
+
if (enforcer === null) Reflect.deleteProperty(globalThis, ENFORCER_SLOT);
|
|
110
|
+
else Reflect.set(globalThis, ENFORCER_SLOT, enforcer);
|
|
111
|
+
}
|
|
112
|
+
/** Ask the owner of the setting to switch to `enforced`. `false` when nobody can. */
|
|
113
|
+
async function requestProcessMeshEnforcement(reason) {
|
|
114
|
+
const enforcer = Reflect.get(globalThis, ENFORCER_SLOT);
|
|
115
|
+
if (typeof enforcer !== "function") return false;
|
|
116
|
+
await enforcer(reason);
|
|
117
|
+
return true;
|
|
118
|
+
}
|
|
119
|
+
/** Published by the hub's broker; read by System → Cluster. */
|
|
120
|
+
function setProcessMeshAuthStatusSource(source) {
|
|
121
|
+
if (source === null) Reflect.deleteProperty(globalThis, STATUS_SLOT);
|
|
122
|
+
else Reflect.set(globalThis, STATUS_SLOT, source);
|
|
123
|
+
}
|
|
124
|
+
function getProcessMeshAuthStatus() {
|
|
125
|
+
const source = Reflect.get(globalThis, STATUS_SLOT);
|
|
126
|
+
return typeof source === "function" ? source() : null;
|
|
127
|
+
}
|
|
128
|
+
//#endregion
|
|
3
129
|
//#region src/builtins/system-config/cluster-secret-keeper.ts
|
|
4
130
|
/**
|
|
5
|
-
* Keeps the hub's cluster-secret gate in step with System → Cluster (D797
|
|
131
|
+
* Keeps the hub's cluster-secret gate in step with System → Cluster (D797,
|
|
132
|
+
* D802).
|
|
6
133
|
*
|
|
7
134
|
* The field is a `password` of `system-config`: its `system-settings` row
|
|
8
135
|
* (`cluster.clusterSecret`) holds a keychain marker, the value lives in the
|
|
9
|
-
* keychain (owner `system`). The gate the
|
|
136
|
+
* keychain (owner `system`). The gate the mesh join reads:
|
|
10
137
|
*
|
|
11
|
-
* - no marker →
|
|
12
|
-
*
|
|
13
|
-
*
|
|
138
|
+
* - no marker → the secret is MANDATORY (D802): the keeper asks
|
|
139
|
+
* its owner to generate one (`generateSecret`), and
|
|
140
|
+
* until that succeeds the gate is `off` — no node can
|
|
141
|
+
* authenticate, and only `transition` lets them in;
|
|
142
|
+
* - marker + value → `on` — nodes prove the secret;
|
|
14
143
|
* - marker, no answer → `unknown` — refused, retryably, and retried here.
|
|
15
|
-
* A locked keychain or a value lost to a reset NEVER opens the gate
|
|
144
|
+
* A locked keychain or a value lost to a reset NEVER opens the gate, and
|
|
145
|
+
* never triggers a generation (the operator decides what replaces a value
|
|
146
|
+
* the keychain lost).
|
|
147
|
+
*
|
|
148
|
+
* It also publishes the mesh mode (`cluster.meshAuth`, `mesh-auth-mode.ts`).
|
|
16
149
|
*/
|
|
17
150
|
/** The field key inside the `cluster` section. */
|
|
18
151
|
var CLUSTER_SECRET_FIELD = "clusterSecret";
|
|
@@ -41,22 +174,35 @@ var ClusterSecretKeeper = class {
|
|
|
41
174
|
/** Re-read the row and the keychain; publish the gate. */
|
|
42
175
|
async reload() {
|
|
43
176
|
if (this.stopped) return;
|
|
44
|
-
|
|
177
|
+
let gate = await this.compute();
|
|
178
|
+
if (gate.kind === "off" && this.deps.generateSecret !== void 0 && !this.stopped) gate = await this.generate();
|
|
45
179
|
if (this.stopped) return;
|
|
46
180
|
setProcessClusterSecretGate(gate);
|
|
47
181
|
this.report(gate);
|
|
48
|
-
if (gate.kind === "unknown") this.scheduleRetry();
|
|
182
|
+
if (gate.kind === "unknown" || gate.kind === "off" && this.deps.generateSecret !== void 0) this.scheduleRetry();
|
|
183
|
+
}
|
|
184
|
+
async generate() {
|
|
185
|
+
try {
|
|
186
|
+
await this.deps.generateSecret?.();
|
|
187
|
+
} catch (err) {
|
|
188
|
+
this.deps.logger.error("The cluster secret is mandatory but could not be generated yet — retrying. Agents cannot authenticate until it exists.", { meta: { error: errText(err) } });
|
|
189
|
+
return { kind: "off" };
|
|
190
|
+
}
|
|
191
|
+
this.deps.logger.info("Generated the cluster secret (none was stored). Reveal it on System → Cluster and enter it in each agent.");
|
|
192
|
+
return this.compute();
|
|
49
193
|
}
|
|
50
194
|
async compute() {
|
|
51
|
-
let
|
|
195
|
+
let section;
|
|
52
196
|
try {
|
|
53
|
-
|
|
197
|
+
section = await this.deps.readSection();
|
|
54
198
|
} catch (err) {
|
|
55
199
|
return {
|
|
56
200
|
kind: "unknown",
|
|
57
201
|
reason: `settings unreadable: ${errText(err)}`
|
|
58
202
|
};
|
|
59
203
|
}
|
|
204
|
+
setProcessMeshAuthMode(parseMeshAuthMode(section[MESH_AUTH_MODE_FIELD]));
|
|
205
|
+
const stored = section[CLUSTER_SECRET_FIELD];
|
|
60
206
|
if (stored === void 0 || stored === null || stored === "") return { kind: "off" };
|
|
61
207
|
if (!isSecretMarker(stored)) return {
|
|
62
208
|
kind: "unknown",
|
|
@@ -106,7 +252,7 @@ var ClusterSecretKeeper = class {
|
|
|
106
252
|
this.deps.logger.error("Agents cannot register: a cluster secret is set but cannot be read from the keychain. Registrations are refused until it can (no fallback).", { meta: { reason: gate.reason } });
|
|
107
253
|
return;
|
|
108
254
|
}
|
|
109
|
-
if (changed) this.deps.logger.info(gate.kind === "on" ? "Cluster secret set — agents
|
|
255
|
+
if (changed) this.deps.logger.info(gate.kind === "on" ? "Cluster secret set — agents prove it to join the mesh" : "No cluster secret yet — no node can authenticate to the mesh");
|
|
110
256
|
}
|
|
111
257
|
scheduleRetry() {
|
|
112
258
|
if (this.stopped || this.retryTimer !== null) return;
|
|
@@ -133,10 +279,12 @@ function errText(err) {
|
|
|
133
279
|
*
|
|
134
280
|
* Sections exposed — this list is the whole of it:
|
|
135
281
|
* - server (port, host, dataPath — READ-ONLY, loaded from config.yaml at bootstrap)
|
|
136
|
-
* - auth (tokenExpiry
|
|
282
|
+
* - auth (tokenExpiry = session lifetime, accessTokenExpiry, trustedProxies,
|
|
283
|
+
* trustedNetworks)
|
|
137
284
|
* - cluster (clusterSecret — a `password` field: the value goes to the
|
|
138
285
|
* keychain, owner `system`; the row keeps a marker. Also drawn
|
|
139
|
-
* on System → Cluster. D797
|
|
286
|
+
* on System → Cluster. D797. MANDATORY and generated at first
|
|
287
|
+
* boot; `meshAuth` — transition / enforced. D802)
|
|
140
288
|
*
|
|
141
289
|
* Sections NOT exposed (each owned by the addon that implements them):
|
|
142
290
|
* - logging → `winston-logging` addon (level + retention)
|
|
@@ -177,8 +325,32 @@ var KEY_TO_SECTION = {
|
|
|
177
325
|
host: "server",
|
|
178
326
|
dataPath: "server",
|
|
179
327
|
tokenExpiry: "auth",
|
|
180
|
-
|
|
328
|
+
accessTokenExpiry: "auth",
|
|
329
|
+
trustedProxies: "auth",
|
|
330
|
+
trustedNetworks: "auth",
|
|
331
|
+
[CLUSTER_SECRET_FIELD]: "cluster",
|
|
332
|
+
[MESH_AUTH_MODE_FIELD]: "cluster"
|
|
333
|
+
};
|
|
334
|
+
/** Bytes of a generated cluster secret (D802) — 256 bits, base64url (43 chars). */
|
|
335
|
+
var GENERATED_SECRET_BYTES = 32;
|
|
336
|
+
var STATE_LABELS = {
|
|
337
|
+
authenticated: "authenticated",
|
|
338
|
+
legacy: "legacy hash only — update the agent",
|
|
339
|
+
unauthenticated: "not authenticated — enter the cluster secret in this agent"
|
|
181
340
|
};
|
|
341
|
+
/** The Cluster page's live line per node (D802). */
|
|
342
|
+
function describeMeshStatus(status) {
|
|
343
|
+
if (status === null) return "The mesh status is available on the hub only.";
|
|
344
|
+
const head = status.mode === "enforced" ? "Enforced: a node that has not authenticated is refused." : "Transition: nodes without the secret are still let in (with a warning). The hub switches to Enforced by itself once every known agent has authenticated.";
|
|
345
|
+
if (status.nodes.length === 0) return `${head}\nNo other node is connected.`;
|
|
346
|
+
return `${head}\n${status.nodes.map((n) => `• ${n.nodeId}: ${STATE_LABELS[n.state] ?? n.state}`).join("\n")}`;
|
|
347
|
+
}
|
|
348
|
+
/**
|
|
349
|
+
* Auth keys holding a list of networks (CIDR or single address). A save with
|
|
350
|
+
* an entry that does not parse is REFUSED whole: dropping it would trust less
|
|
351
|
+
* than the operator wrote, guessing would trust more.
|
|
352
|
+
*/
|
|
353
|
+
var CIDR_LIST_KEYS = new Set(["trustedProxies", "trustedNetworks"]);
|
|
182
354
|
/** The section id of the cluster form — System → Cluster renders only this one. */
|
|
183
355
|
var SYSTEM_CONFIG_CLUSTER_SECTION_ID = "system-config-cluster";
|
|
184
356
|
var SystemConfigAddon = class extends BaseAddon {
|
|
@@ -191,12 +363,18 @@ var SystemConfigAddon = class extends BaseAddon {
|
|
|
191
363
|
this.clusterSecretKeeper = new ClusterSecretKeeper({
|
|
192
364
|
secrets: this.systemSecrets(),
|
|
193
365
|
readSection: async () => await this.ctx.settings?.getSection("cluster") ?? {},
|
|
194
|
-
logger: this.ctx.logger
|
|
366
|
+
logger: this.ctx.logger,
|
|
367
|
+
generateSecret: () => this.generateClusterSecret()
|
|
368
|
+
});
|
|
369
|
+
setProcessMeshEnforcer(async (reason) => {
|
|
370
|
+
await this.writeMeshAuthMode("enforced");
|
|
371
|
+
this.ctx.logger.info("Mesh authentication is now ENFORCED", { meta: { reason } });
|
|
195
372
|
});
|
|
196
373
|
await this.clusterSecretKeeper.start();
|
|
197
374
|
this.ctx.logger.info("Initialized — exposes yml-backed sections via getGlobalSettings");
|
|
198
375
|
}
|
|
199
376
|
async onShutdown() {
|
|
377
|
+
setProcessMeshEnforcer(null);
|
|
200
378
|
this.clusterSecretKeeper?.stop();
|
|
201
379
|
this.clusterSecretKeeper = null;
|
|
202
380
|
}
|
|
@@ -249,26 +427,80 @@ var SystemConfigAddon = class extends BaseAddon {
|
|
|
249
427
|
title: SECTION_TITLES["auth"],
|
|
250
428
|
description: "Token and session settings.",
|
|
251
429
|
columns: 1,
|
|
252
|
-
fields: [
|
|
253
|
-
|
|
254
|
-
|
|
255
|
-
|
|
256
|
-
|
|
257
|
-
|
|
258
|
-
|
|
259
|
-
|
|
430
|
+
fields: [
|
|
431
|
+
{
|
|
432
|
+
type: "text",
|
|
433
|
+
key: "tokenExpiry",
|
|
434
|
+
label: "Session lifetime",
|
|
435
|
+
description: "How long a sign-in lasts without being used, as a duration string (e.g. 7d, 30d). Every refresh starts it again, so a device in use stays signed in; one left unopened this long must sign in again. A bare number is rejected — it would be read as milliseconds. Applies from the next sign-in or refresh.",
|
|
436
|
+
placeholder: "30d",
|
|
437
|
+
default: "30d"
|
|
438
|
+
},
|
|
439
|
+
{
|
|
440
|
+
type: "text",
|
|
441
|
+
key: "accessTokenExpiry",
|
|
442
|
+
label: "Access token lifetime",
|
|
443
|
+
description: "How long one access token lives before the app silently swaps it for a new one (e.g. 2h; between 5m and 24h, never longer than the session). Shorter limits how long a copied token is useful. Apps that predate refresh tokens get one token for the whole session instead.",
|
|
444
|
+
placeholder: "2h",
|
|
445
|
+
default: "2h"
|
|
446
|
+
},
|
|
447
|
+
{
|
|
448
|
+
type: "tags",
|
|
449
|
+
key: "trustedProxies",
|
|
450
|
+
label: "Trusted reverse proxies",
|
|
451
|
+
description: "Addresses or CIDR ranges (e.g. 192.168.1.2 or 10.0.0.0/24) of reverse proxies in front of the hub. Only a request whose direct sender is one of these — or this host itself, as with the Cloudflare tunnel — has its X-Forwarded-For / CF-Connecting-IP read as the real client address for rate limits. Leave empty unless a proxy on another host forwards to the hub.",
|
|
452
|
+
creatable: true
|
|
453
|
+
},
|
|
454
|
+
{
|
|
455
|
+
type: "info",
|
|
456
|
+
key: "trustedNetworksWarning",
|
|
457
|
+
label: "Skipping two-factor authentication",
|
|
458
|
+
variant: "danger",
|
|
459
|
+
content: "A sign-in made DIRECTLY from a network listed below skips the second factor (TOTP or passkey): anyone on that network who knows or guesses a password gets in with the password alone — including a compromised camera or a guest device. Requests that come through the Cloudflare tunnel, the public address or any reverse proxy never skip it, whatever address they claim. Every skip is recorded in the authentication audit log. Leave empty (the default) to always ask for the second factor."
|
|
460
|
+
},
|
|
461
|
+
{
|
|
462
|
+
type: "tags",
|
|
463
|
+
key: "trustedNetworks",
|
|
464
|
+
label: "Trusted networks (skip the second factor)",
|
|
465
|
+
description: "Addresses or CIDR ranges, e.g. 192.168.1.0/24. Keep it as narrow as you can — a single trusted VLAN, not the whole LAN.",
|
|
466
|
+
creatable: true
|
|
467
|
+
}
|
|
468
|
+
]
|
|
260
469
|
},
|
|
261
470
|
{
|
|
262
471
|
id: SYSTEM_CONFIG_CLUSTER_SECTION_ID,
|
|
263
472
|
title: SECTION_TITLES["cluster"],
|
|
264
|
-
description: "Agents joining this hub.
|
|
473
|
+
description: "Agents joining this hub. Every agent proves the cluster secret when it connects (challenge–response; the secret never crosses the network).",
|
|
265
474
|
columns: 1,
|
|
266
|
-
fields: [
|
|
267
|
-
|
|
268
|
-
|
|
269
|
-
|
|
270
|
-
|
|
271
|
-
|
|
475
|
+
fields: [
|
|
476
|
+
{
|
|
477
|
+
type: "password",
|
|
478
|
+
key: CLUSTER_SECRET_FIELD,
|
|
479
|
+
label: "Cluster secret",
|
|
480
|
+
description: "Mandatory — generated by the hub at first boot and stored in the keychain. To pair an agent: Reveal and copy it here, then enter it in the agent (its status page, or the desktop setup window). Saving a new value rotates it: every agent must then be given the new one."
|
|
481
|
+
},
|
|
482
|
+
{
|
|
483
|
+
type: "select",
|
|
484
|
+
key: MESH_AUTH_MODE_FIELD,
|
|
485
|
+
label: "Agents without the secret",
|
|
486
|
+
description: "Transition lets an agent that has not authenticated keep working (with a warning) while you give each agent the secret; it ends by itself once every known agent has authenticated. Enforced refuses it.",
|
|
487
|
+
default: "transition",
|
|
488
|
+
options: [{
|
|
489
|
+
value: "transition",
|
|
490
|
+
label: "Transition — allow, warn"
|
|
491
|
+
}, {
|
|
492
|
+
value: "enforced",
|
|
493
|
+
label: "Enforced — refuse"
|
|
494
|
+
}]
|
|
495
|
+
},
|
|
496
|
+
{
|
|
497
|
+
type: "info",
|
|
498
|
+
key: "meshAuthStatus",
|
|
499
|
+
label: "Mesh authentication",
|
|
500
|
+
content: describeMeshStatus(getProcessMeshAuthStatus()),
|
|
501
|
+
variant: "info"
|
|
502
|
+
}
|
|
503
|
+
]
|
|
272
504
|
}
|
|
273
505
|
] };
|
|
274
506
|
}
|
|
@@ -302,6 +534,10 @@ var SystemConfigAddon = class extends BaseAddon {
|
|
|
302
534
|
this.ctx.logger.warn("update ignored read-only server-section key", { meta: { key } });
|
|
303
535
|
continue;
|
|
304
536
|
}
|
|
537
|
+
if (CIDR_LIST_KEYS.has(key)) {
|
|
538
|
+
const { invalid } = parseCidrList(readCidrSetting(value));
|
|
539
|
+
if (invalid.length > 0) throw new Error(`Not a network address or CIDR range: ${invalid.join(", ")}`);
|
|
540
|
+
}
|
|
305
541
|
let bucket = perSection.get(section);
|
|
306
542
|
if (!bucket) {
|
|
307
543
|
bucket = {};
|
|
@@ -323,13 +559,52 @@ var SystemConfigAddon = class extends BaseAddon {
|
|
|
323
559
|
}
|
|
324
560
|
/**
|
|
325
561
|
* The cluster secret's save path: the value goes to the keychain (owner
|
|
326
|
-
* `system`), the row gets the marker; `''` / a view keeps
|
|
327
|
-
*
|
|
328
|
-
* inline (D787, P5).
|
|
562
|
+
* `system`), the row gets the marker; `''` / a view keeps. `$secretClear` is
|
|
563
|
+
* REFUSED: the secret is mandatory (D802). A value the keychain cannot store
|
|
564
|
+
* is REFUSED — never written inline (D787, P5). `meshAuth` is a plain value.
|
|
329
565
|
*/
|
|
330
566
|
async writeClusterSection(patch) {
|
|
567
|
+
const { [MESH_AUTH_MODE_FIELD]: mode, ...secretPatch } = patch;
|
|
568
|
+
if (mode !== void 0) {
|
|
569
|
+
if (!isMeshAuthMode(mode)) throw new Error(`system-config: meshAuth must be 'transition' or 'enforced'`);
|
|
570
|
+
await this.writeMeshAuthMode(mode);
|
|
571
|
+
}
|
|
572
|
+
if (Object.keys(secretPatch).length === 0) return;
|
|
573
|
+
if (isSecretClear(secretPatch["clusterSecret"])) throw new Error("The cluster secret is mandatory and cannot be cleared — save a new value to rotate it");
|
|
574
|
+
await this.storeClusterSecret(secretPatch);
|
|
575
|
+
this.ctx.logger.info("Cluster secret updated");
|
|
576
|
+
await this.clusterSecretKeeper?.reload();
|
|
577
|
+
}
|
|
578
|
+
/** `cluster.meshAuth` (D802), then republish it. */
|
|
579
|
+
async writeMeshAuthMode(mode) {
|
|
580
|
+
await this.ctx.settings.setSection("cluster", { [MESH_AUTH_MODE_FIELD]: mode });
|
|
581
|
+
await this.clusterSecretKeeper?.reload();
|
|
582
|
+
}
|
|
583
|
+
/**
|
|
584
|
+
* First boot, or a keychain reset that cleared the marker (D802): a fresh
|
|
585
|
+
* random secret, stored like an operator save. A NEW secret means every
|
|
586
|
+
* agent must be given it, so the mesh goes (back) to `transition`; on a hub
|
|
587
|
+
* with no agent yet, the hub's auto-enforce ends it at once.
|
|
588
|
+
*/
|
|
589
|
+
async generateClusterSecret() {
|
|
331
590
|
const settings = this.ctx.settings;
|
|
332
591
|
const secrets = this.systemSecrets();
|
|
592
|
+
if (!settings || secrets === null) throw new Error("system-config: no keychain door — the cluster secret cannot be generated");
|
|
593
|
+
const existing = (await secrets.resolve([CLUSTER_SECRET_REF]))[CLUSTER_SECRET_REF];
|
|
594
|
+
if (existing?.kind === "value") {
|
|
595
|
+
await settings.setSection("cluster", { [CLUSTER_SECRET_FIELD]: makeSecretMarker(CLUSTER_SECRET_REF) });
|
|
596
|
+
this.ctx.logger.warn("The cluster secret row was empty but the keychain holds a value — re-adopted it");
|
|
597
|
+
return;
|
|
598
|
+
}
|
|
599
|
+
if (existing?.kind !== "missing") throw new Error(`the keychain cannot answer for the cluster secret (${existing?.kind ?? "no answer"})`);
|
|
600
|
+
const value = randomBytes(GENERATED_SECRET_BYTES).toString("base64url");
|
|
601
|
+
await this.storeClusterSecret({ [CLUSTER_SECRET_FIELD]: value });
|
|
602
|
+
await settings.setSection("cluster", { [MESH_AUTH_MODE_FIELD]: "transition" });
|
|
603
|
+
}
|
|
604
|
+
async storeClusterSecret(patch) {
|
|
605
|
+
const settings = this.ctx.settings;
|
|
606
|
+
if (!settings) throw new Error("system-config: ctx.settings not available");
|
|
607
|
+
const secrets = this.systemSecrets();
|
|
333
608
|
if (secrets === null) throw new Error("system-config: no keychain door — the cluster secret cannot be stored");
|
|
334
609
|
const stored = await settings.getSection("cluster");
|
|
335
610
|
const result = await transformSecretWrite({
|
|
@@ -341,10 +616,8 @@ var SystemConfigAddon = class extends BaseAddon {
|
|
|
341
616
|
});
|
|
342
617
|
if (Object.keys(result.patch).length > 0) await settings.setSection("cluster", result.patch);
|
|
343
618
|
const { orphaned } = await result.afterWrite();
|
|
344
|
-
if (orphaned.length > 0) this.ctx.logger.warn("cluster secret
|
|
345
|
-
this.ctx.logger.info("Cluster secret updated");
|
|
346
|
-
await this.clusterSecretKeeper?.reload();
|
|
619
|
+
if (orphaned.length > 0) this.ctx.logger.warn("an old cluster secret value could not be deleted from the keychain", { meta: { refs: orphaned } });
|
|
347
620
|
}
|
|
348
621
|
};
|
|
349
622
|
//#endregion
|
|
350
|
-
export { SYSTEM_CONFIG_CLUSTER_SECTION_ID, SystemConfigAddon, SystemConfigAddon as default };
|
|
623
|
+
export { SYSTEM_CONFIG_CLUSTER_SECTION_ID, SystemConfigAddon, SystemConfigAddon as default, parseMeshAuthMode as a, setProcessMeshAuthStatusSource as c, normalizeIp as d, parseCidrList as f, isMeshAuthMode as i, setProcessMeshEnforcer as l, getProcessMeshAuthMode as n, requestProcessMeshEnforcement as o, readCidrSetting as p, getProcessMeshAuthStatus as r, setProcessMeshAuthMode as s, MESH_AUTH_MODE_FIELD as t, createCidrMatcher as u };
|
|
@@ -3,7 +3,7 @@ Object.defineProperties(exports, {
|
|
|
3
3
|
[Symbol.toStringTag]: { value: "Module" }
|
|
4
4
|
});
|
|
5
5
|
const require_chunk = require("../../chunk-Cek0wNdY.js");
|
|
6
|
-
const require_dist = require("../../dist-
|
|
6
|
+
const require_dist = require("../../dist-tnAO12ak.js");
|
|
7
7
|
const require_formatter = require("../../formatter-DqAKDlvN.js");
|
|
8
8
|
let node_path = require("node:path");
|
|
9
9
|
node_path = require_chunk.__toESM(node_path);
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import {
|
|
1
|
+
import { Qt as logDestinationCapability, ur as BaseAddon } from "../../dist-CP5TTjwd.mjs";
|
|
2
2
|
import { t as formatLogLine } from "../../formatter-B7qW8bPJ.mjs";
|
|
3
3
|
import * as path$1 from "node:path";
|
|
4
4
|
import path from "node:path";
|
|
@@ -1,10 +1,11 @@
|
|
|
1
1
|
const require_chunk = require("./chunk-Cek0wNdY.js");
|
|
2
|
-
const require_dist = require("./dist-
|
|
2
|
+
const require_dist = require("./dist-tnAO12ak.js");
|
|
3
3
|
const require_proc_stat = require("./proc-stat-DL-WUOhl.js");
|
|
4
|
-
require("./hub-cap-forward-
|
|
4
|
+
require("./hub-cap-forward-W8oEm4ME.js");
|
|
5
5
|
let node_crypto = require("node:crypto");
|
|
6
6
|
node_crypto = require_chunk.__toESM(node_crypto);
|
|
7
7
|
let _camstack_types_node = require("@camstack/types/node");
|
|
8
|
+
let node_net = require("node:net");
|
|
8
9
|
let node_fs_promises = require("node:fs/promises");
|
|
9
10
|
let node_path = require("node:path");
|
|
10
11
|
node_path = require_chunk.__toESM(node_path);
|
|
@@ -23,7 +24,6 @@ let node_vm = require("node:vm");
|
|
|
23
24
|
node_vm = require_chunk.__toESM(node_vm);
|
|
24
25
|
let _camstack_types_addon = require("@camstack/types/addon");
|
|
25
26
|
let node_events = require("node:events");
|
|
26
|
-
let node_net = require("node:net");
|
|
27
27
|
let _trpc_client = require("@trpc/client");
|
|
28
28
|
//#region src/kernel/event-plane-report.ts
|
|
29
29
|
/**
|
|
@@ -3223,11 +3223,14 @@ function isCredentialKeyName(key) {
|
|
|
3223
3223
|
return SECRET_KEY_SUBSTRINGS.some((needle) => lower.includes(needle));
|
|
3224
3224
|
}
|
|
3225
3225
|
var URL_IN_TEXT = /\b[a-z][a-z0-9+.-]*:\/\/[^\s"'<>`]+/gi;
|
|
3226
|
+
/** A root-relative path WITH a query, as request logs print it. */
|
|
3227
|
+
var RELATIVE_URL_IN_TEXT = /(^|[\s"'(=])(\/[^\s"'<>`?]*\?[^\s"'<>`]+)/g;
|
|
3226
3228
|
var CREDENTIAL_FLAG = /(--(?:token|auth-?key|password|passwd|pass|secret|api-?key|access-?token|refresh-?token)(?:=|\s+))(\S+)/gi;
|
|
3227
3229
|
/** Redact one free-form string. */
|
|
3228
3230
|
function redactLogText(text) {
|
|
3229
3231
|
let out = require_dist.scrubKnownSecrets(text);
|
|
3230
3232
|
if (out.includes("://")) out = out.replace(URL_IN_TEXT, (url) => require_dist.maskUrlCredentials(url));
|
|
3233
|
+
if (out.includes("/") && out.includes("?") && out.includes("=")) out = out.replace(RELATIVE_URL_IN_TEXT, (_m, lead, path) => `${lead}${require_dist.maskUrlCredentials(path)}`);
|
|
3231
3234
|
if (out.includes("--")) out = out.replace(CREDENTIAL_FLAG, (_m, flag, value) => value.startsWith("‹secret:") ? `${flag}${value}` : `${flag}${REDACTED}`);
|
|
3232
3235
|
return out;
|
|
3233
3236
|
}
|
|
@@ -1,9 +1,10 @@
|
|
|
1
|
-
import {
|
|
1
|
+
import { Tr as createEvent, ei as EventCategory$1, qr as scrubKnownSecrets, tn as maskUrlCredentials } from "./dist-CP5TTjwd.mjs";
|
|
2
2
|
import { a as parseProcStat, i as nodeProcReader } from "./proc-stat-CbC55Kqi.mjs";
|
|
3
|
-
import "./hub-cap-forward-
|
|
3
|
+
import "./hub-cap-forward-CDiN1p98.mjs";
|
|
4
4
|
import * as crypto$1 from "node:crypto";
|
|
5
5
|
import { randomBytes, randomUUID } from "node:crypto";
|
|
6
6
|
import { ensureBinary, ensureFfmpeg, ensurePython, installPythonPackages, installPythonRequirements } from "@camstack/types/node";
|
|
7
|
+
import { connect, createConnection, createServer } from "node:net";
|
|
7
8
|
import { mkdir, rm, stat, unlink } from "node:fs/promises";
|
|
8
9
|
import * as path$1 from "node:path";
|
|
9
10
|
import { dirname, isAbsolute, join } from "node:path";
|
|
@@ -13,14 +14,13 @@ import * as fs from "node:fs";
|
|
|
13
14
|
import { createReadStream, createWriteStream, existsSync, readFileSync, readdirSync } from "node:fs";
|
|
14
15
|
import { execFile } from "node:child_process";
|
|
15
16
|
import { promisify } from "node:util";
|
|
16
|
-
import { createServer, request } from "node:http";
|
|
17
|
+
import { createServer as createServer$1, request } from "node:http";
|
|
17
18
|
import * as v8 from "node:v8";
|
|
18
19
|
import { Session } from "node:inspector/promises";
|
|
19
20
|
import { monitorEventLoopDelay } from "node:perf_hooks";
|
|
20
21
|
import * as vm from "node:vm";
|
|
21
22
|
import { BOOT_RECOVERY_BACKOFF_MS, DATAPLANE_SECRET_HEADER, DEVICE_CHILDREN_BATCH_MAX, DeviceType, DisposerChain, EventCategory, PEER_BYTES_CHUNK_BYTES, PEER_BYTES_MAX_OUTSTANDING, PEER_BYTES_STALL_MS, PEER_BYTES_TICKET_TTL_MS, ReadinessRegistry, asJsonObject, asString, collectZodSecretPaths, createDeviceProxy, createEventBusSliceSource, createSecretsAccess, deviceOpsCapability, emitReadiness, errMsg, expandCapMethods, readNodePin, scopeKey, sleep, withCapInputDefaults } from "@camstack/types/addon";
|
|
22
23
|
import { once } from "node:events";
|
|
23
|
-
import { connect, createConnection, createServer as createServer$1 } from "node:net";
|
|
24
24
|
import { TRPCClientError, createTRPCClient } from "@trpc/client";
|
|
25
25
|
//#region src/kernel/event-plane-report.ts
|
|
26
26
|
/**
|
|
@@ -2545,7 +2545,7 @@ function createAddonDataPlaneFacility(args) {
|
|
|
2545
2545
|
};
|
|
2546
2546
|
const ensureServer = async () => {
|
|
2547
2547
|
if (server) return;
|
|
2548
|
-
const s = createServer((req, res) => route(req, res));
|
|
2548
|
+
const s = createServer$1((req, res) => route(req, res));
|
|
2549
2549
|
await new Promise((resolve, reject) => {
|
|
2550
2550
|
s.once("error", reject);
|
|
2551
2551
|
s.listen(0, bindHost, () => resolve());
|
|
@@ -2764,7 +2764,7 @@ function createAddonPeerBytesFacility(args) {
|
|
|
2764
2764
|
const listen = () => {
|
|
2765
2765
|
if (listening !== null) return listening;
|
|
2766
2766
|
listening = new Promise((resolve, reject) => {
|
|
2767
|
-
const created = createServer((req, res) => {
|
|
2767
|
+
const created = createServer$1((req, res) => {
|
|
2768
2768
|
route(req, res);
|
|
2769
2769
|
});
|
|
2770
2770
|
created.on("error", (err) => {
|
|
@@ -3220,11 +3220,14 @@ function isCredentialKeyName(key) {
|
|
|
3220
3220
|
return SECRET_KEY_SUBSTRINGS.some((needle) => lower.includes(needle));
|
|
3221
3221
|
}
|
|
3222
3222
|
var URL_IN_TEXT = /\b[a-z][a-z0-9+.-]*:\/\/[^\s"'<>`]+/gi;
|
|
3223
|
+
/** A root-relative path WITH a query, as request logs print it. */
|
|
3224
|
+
var RELATIVE_URL_IN_TEXT = /(^|[\s"'(=])(\/[^\s"'<>`?]*\?[^\s"'<>`]+)/g;
|
|
3223
3225
|
var CREDENTIAL_FLAG = /(--(?:token|auth-?key|password|passwd|pass|secret|api-?key|access-?token|refresh-?token)(?:=|\s+))(\S+)/gi;
|
|
3224
3226
|
/** Redact one free-form string. */
|
|
3225
3227
|
function redactLogText(text) {
|
|
3226
3228
|
let out = scrubKnownSecrets(text);
|
|
3227
3229
|
if (out.includes("://")) out = out.replace(URL_IN_TEXT, (url) => maskUrlCredentials(url));
|
|
3230
|
+
if (out.includes("/") && out.includes("?") && out.includes("=")) out = out.replace(RELATIVE_URL_IN_TEXT, (_m, lead, path) => `${lead}${maskUrlCredentials(path)}`);
|
|
3228
3231
|
if (out.includes("--")) out = out.replace(CREDENTIAL_FLAG, (_m, flag, value) => value.startsWith("‹secret:") ? `${flag}${value}` : `${flag}${REDACTED}`);
|
|
3229
3232
|
return out;
|
|
3230
3233
|
}
|
|
@@ -7459,7 +7462,7 @@ var UdsLocalTransportServer = class {
|
|
|
7459
7462
|
await unlink(this.path);
|
|
7460
7463
|
} catch {}
|
|
7461
7464
|
await new Promise((resolve, reject) => {
|
|
7462
|
-
const srv = createServer
|
|
7465
|
+
const srv = createServer((socket) => {
|
|
7463
7466
|
const ch = new SocketChannel(socket);
|
|
7464
7467
|
this.channels.add(ch);
|
|
7465
7468
|
ch.onClose(() => this.channels.delete(ch));
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import {
|
|
1
|
+
import { lr as errMsg, nr as compositionSourceKey } from "./dist-CP5TTjwd.mjs";
|
|
2
2
|
//#region src/builtins/core-blocks/composition-sources.ts
|
|
3
3
|
/**
|
|
4
4
|
* Describe what a composition reads and writes for SAVE-time validation
|