@camstack/server 1.2.594 → 1.2.595
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-root-inventory.js +50 -11
- package/dist/agent/addon-scan.js +44 -6
- package/dist/agent/agent-addon-runtime.js +8 -3
- package/dist/agent/agent-addon-updates.js +52 -12
- package/dist/agent/agent-cap-dispatch-service.js +5 -3
- package/dist/agent/agent-config.js +42 -7
- package/dist/agent/agent-deploy-swap.js +41 -5
- package/dist/agent/agent-group-runner.js +47 -8
- package/dist/agent/agent-http-auth.js +22 -10
- package/dist/agent/agent-http.js +113 -64
- package/dist/agent/agent-hub-addon-update.js +16 -10
- package/dist/agent/agent-hub-registration.js +7 -4
- package/dist/agent/agent-infra-capabilities.js +9 -5
- package/dist/agent/agent-json-file.js +42 -5
- package/dist/agent/agent-mesh-gate.js +8 -5
- package/dist/agent/agent-node-certificates.js +28 -25
- package/dist/agent/agent-pairing-local.js +47 -10
- package/dist/agent/agent-pairing-routes.js +20 -16
- package/dist/agent/agent-pairing-store.js +58 -18
- package/dist/agent/agent-reconnect.js +4 -1
- package/dist/agent/agent-service.js +71 -35
- package/dist/agent/agent-sniffing-listener.js +48 -11
- package/dist/agent/agent-undeploy.js +41 -5
- package/dist/agent/agent-update-routes.js +19 -14
- package/dist/agent/agent-update-service.js +80 -38
- package/dist/agent/apply-model-distribution.js +4 -1
- package/dist/agent/apply-stage-dev-uploads.js +9 -6
- package/dist/agent/derive-hub-url.js +8 -3
- package/dist/agent/fetch-bundle-from-hub.js +9 -6
- package/dist/agent/hubs-found.js +43 -6
- package/dist/agent/infra-boot-guard.js +4 -1
- package/dist/agent/infra-boot-plan.js +6 -2
- package/dist/agent/main.js +183 -148
- package/dist/agent/register-agent-cap-dispatch.js +6 -3
- package/dist/agent/serial-queue.js +7 -2
- package/dist/agent-status-page.js +4 -1
- package/dist/api/addon-upload.js +61 -22
- package/dist/api/agent-addon-delivery.js +11 -6
- package/dist/api/auth-whoami.js +4 -1
- package/dist/api/backup-download-pump.js +9 -5
- package/dist/api/backup-download.js +10 -7
- package/dist/api/core/addon-settings.router.js +19 -16
- package/dist/api/core/agent-pairing.router.js +79 -76
- package/dist/api/core/agents.router.js +24 -21
- package/dist/api/core/auth-admin-tokens.procedures.js +52 -48
- package/dist/api/core/auth-device-cert.procedures.js +67 -62
- package/dist/api/core/auth-limits.js +25 -16
- package/dist/api/core/auth-reauth.procedures.js +37 -32
- package/dist/api/core/auth-revoke-credentials.procedures.js +26 -22
- package/dist/api/core/auth-sessions.procedures.js +56 -52
- package/dist/api/core/auth.router.js +304 -300
- package/dist/api/core/cap-providers.js +121 -74
- package/dist/api/core/capabilities.router.js +37 -34
- package/dist/api/core/capability-preference-writes.js +17 -10
- package/dist/api/core/certificates.router.js +109 -106
- package/dist/api/core/cluster-nodes.router.js +18 -15
- package/dist/api/core/collection-preference.js +6 -2
- package/dist/api/core/event-bus-proxy.router.js +37 -34
- package/dist/api/core/failure-contributions.js +8 -4
- package/dist/api/core/hwaccel.router.js +20 -17
- package/dist/api/core/integration-secrets.js +12 -8
- package/dist/api/core/lifecycle-job-runner.js +49 -13
- package/dist/api/core/live-events.router.js +32 -29
- package/dist/api/core/load-contributions.js +8 -4
- package/dist/api/core/logging-settings.js +43 -30
- package/dist/api/core/logs.router.js +43 -40
- package/dist/api/core/notifications.router.js +18 -15
- package/dist/api/core/public-event-filter.js +8 -4
- package/dist/api/core/repl.router.js +21 -18
- package/dist/api/core/request-census-settings.js +16 -11
- package/dist/api/core/server-management.provider.js +4 -1
- package/dist/api/core/server-name.js +17 -9
- package/dist/api/core/site-location.js +24 -16
- package/dist/api/core/stream-probe.router.js +24 -21
- package/dist/api/core/system-events.router.js +26 -23
- package/dist/api/cors/cors-policy.js +29 -20
- package/dist/api/cors/register-cors-policy.js +14 -9
- package/dist/api/deploy-stage-registry.js +10 -6
- package/dist/api/dev-uploads-bundle.js +50 -12
- package/dist/api/health/adaptive-probe.routes.js +13 -9
- package/dist/api/health/health.routes.js +13 -8
- package/dist/api/hub-cluster-url.js +9 -3
- package/dist/api/model-distributor.js +50 -12
- package/dist/api/oauth2/consent-page.js +13 -10
- package/dist/api/oauth2/oauth2-routes.js +25 -17
- package/dist/api/oauth2/private-host.js +4 -1
- package/dist/api/server-upload-propagate.js +51 -15
- package/dist/api/server-upload.js +50 -13
- package/dist/api/sse-no-compression.js +4 -1
- package/dist/api/static/frame-policy.js +9 -4
- package/dist/api/static/precompressed-asset.js +57 -15
- package/dist/api/static/spa-mount.js +47 -8
- package/dist/api/static/spa-static.js +12 -5
- package/dist/api/tarball-manifest.js +44 -7
- package/dist/api/trpc/cap-mount-helpers.js +12 -7
- package/dist/api/trpc/cap-route-error-formatter.js +13 -10
- package/dist/api/trpc/cap-router-runtime.js +51 -40
- package/dist/api/trpc/client-ip.js +20 -9
- package/dist/api/trpc/core-cap-bridge.js +42 -32
- package/dist/api/trpc/device-collection-dispatch.js +27 -23
- package/dist/api/trpc/device-config-secret-redaction.js +40 -24
- package/dist/api/trpc/generated-cap-mounts.js +222 -218
- package/dist/api/trpc/generated-cap-routers.js +4856 -4676
- package/dist/api/trpc/http-request-census-hook.js +11 -8
- package/dist/api/trpc/http-request-census.js +41 -34
- package/dist/api/trpc/http-request-census.singleton.js +8 -4
- package/dist/api/trpc/localized-schema-paths.js +4 -1
- package/dist/api/trpc/principal-visibility.js +22 -14
- package/dist/api/trpc/remote-addon-update-grant.js +4 -1
- package/dist/api/trpc/scope-access.js +15 -10
- package/dist/api/trpc/server-provided-caps.js +7 -3
- package/dist/api/trpc/share-view-access.js +14 -6
- package/dist/api/trpc/trpc-error-device-tags.js +6 -3
- package/dist/api/trpc/trpc-error-principal.js +6 -2
- package/dist/api/trpc/trpc.context.js +25 -20
- package/dist/api/trpc/trpc.middleware.js +46 -38
- package/dist/api/trpc/trpc.router.js +99 -58
- package/dist/api/trpc/user-error.js +8 -5
- package/dist/api/trpc/ws-request-census.js +24 -17
- package/dist/api/trpc/ws-slow-consumer-guard.js +16 -11
- package/dist/api/upload-auth.js +9 -5
- package/dist/auth/addon-route-jwt-gate.js +7 -4
- package/dist/auth/addon-route-share-gate.js +12 -7
- package/dist/auth/app-confirm-page.js +22 -17
- package/dist/auth/app-confirm-routes.js +31 -26
- package/dist/auth/app-confirmations.js +44 -7
- package/dist/auth/app-page-shell.js +30 -17
- package/dist/auth/app-page-strings.js +17 -11
- package/dist/auth/app-passkey-page.js +21 -16
- package/dist/auth/app-passkey-routes.js +41 -37
- package/dist/auth/app-refused-route.js +18 -13
- package/dist/auth/auth-network-policy.js +15 -11
- package/dist/auth/auth-provider-gate.js +9 -4
- package/dist/auth/auth-rate-limit.js +7 -3
- package/dist/auth/client-address.js +9 -6
- package/dist/auth/device-enroll-proofs.js +43 -6
- package/dist/auth/external-link-intents.js +7 -3
- package/dist/auth/hub-sessions.js +12 -6
- package/dist/auth/native-link-completions.js +11 -7
- package/dist/auth/native-return.js +13 -9
- package/dist/auth/passkey-provider.js +32 -26
- package/dist/auth/second-factors.js +10 -6
- package/dist/auth/session-connections.js +12 -6
- package/dist/auth/session-cookie.js +23 -12
- package/dist/auth/session-lifetimes.js +12 -8
- package/dist/auth/session-mint.js +11 -4
- package/dist/auth/sso-finish-app.js +16 -13
- package/dist/auth/sso-finish-deps.js +27 -21
- package/dist/auth/sso-finish.js +38 -28
- package/dist/auth/sso-routes.js +25 -22
- package/dist/auth/used-bridge-ids.js +5 -1
- package/dist/boot/boot-config.js +55 -16
- package/dist/boot/init-sequence.js +4 -1
- package/dist/boot/integration-id-backfill.js +8 -3
- package/dist/boot/platform-keychain-key.js +42 -5
- package/dist/boot/post-boot.service.js +18 -14
- package/dist/boot/reconcile-lifecycle-jobs.js +6 -3
- package/dist/boot/retired-public-url-env.js +8 -4
- package/dist/bootstrap-packages.js +4 -1
- package/dist/closure-node-modules.js +38 -2
- package/dist/core/addon/addon-call-gateway.js +5 -1
- package/dist/core/addon/addon-package.service.js +123 -83
- package/dist/core/addon/addon-process-view.js +6 -2
- package/dist/core/addon/addon-realign-plan.js +4 -1
- package/dist/core/addon/addon-registry.service.js +187 -146
- package/dist/core/addon/addon-row-manifest.js +4 -1
- package/dist/core/addon/addon-search.service.js +7 -3
- package/dist/core/addon/addon-settings-provider.js +3 -1
- package/dist/core/addon/addon-uninstall.js +11 -4
- package/dist/core/addon/addon.tokens.js +5 -2
- package/dist/core/addon/device-meta-mirror.js +13 -8
- package/dist/core/addon/forked-custom-actions.js +11 -6
- package/dist/core/addon/integration-visibility.js +6 -3
- package/dist/core/addon/materialize-route-path.js +4 -1
- package/dist/core/addon/package-dir-utils.js +45 -8
- package/dist/core/addon/prune-misplaced-addons.js +43 -5
- package/dist/core/addon/runner-convergence.js +4 -1
- package/dist/core/addon/runner-serialization.js +4 -1
- package/dist/core/addon/runner-spawn-fanout.js +8 -3
- package/dist/core/addon/settings-door-wiring.js +8 -5
- package/dist/core/addon/single-flight-refresh.js +4 -1
- package/dist/core/addon-bridge/addon-bridge.service.js +44 -7
- package/dist/core/addon-pages/addon-pages.service.js +40 -3
- package/dist/core/addon-widgets/addon-widgets.service.js +42 -4
- package/dist/core/agent/agent-addon-backfill.js +17 -9
- package/dist/core/agent/agent-delivery-ledger.js +7 -3
- package/dist/core/agent/agent-liveness-event.js +6 -2
- package/dist/core/agent/agent-pairing-http.js +45 -41
- package/dist/core/agent/agent-registry.service.js +89 -50
- package/dist/core/agent/cluster-node-history-mirror.js +7 -3
- package/dist/core/agent/cluster-node-history-store.js +35 -31
- package/dist/core/agent/hub-agent-pairing.js +5 -1
- package/dist/core/agent/node-certificates-client.js +6 -3
- package/dist/core/agent/node-directory.js +4 -1
- package/dist/core/app-links/well-known-app-links.js +6 -2
- package/dist/core/auth/auth.service.js +8 -3
- package/dist/core/auth/handoff-code.service.js +49 -11
- package/dist/core/auth/record-kinds.js +19 -16
- package/dist/core/auth/share-token.service.js +79 -42
- package/dist/core/capability/capability.service.js +5 -1
- package/dist/core/config/config.schema.js +6 -1
- package/dist/core/config/config.service.js +6 -2
- package/dist/core/events/event-bus.service.js +7 -3
- package/dist/core/feature/feature.service.js +6 -2
- package/dist/core/lifecycle/lifecycle-runner.singleton.js +10 -5
- package/dist/core/lifecycle/lifecycle-state-machine.js +5 -1
- package/dist/core/logging/log-ring-buffer.js +5 -1
- package/dist/core/logging/logging.service.js +9 -5
- package/dist/core/logging/scoped-logger.js +5 -1
- package/dist/core/moleculer/agent-readiness-pull.js +4 -1
- package/dist/core/moleculer/agent-registration-request.js +7 -3
- package/dist/core/moleculer/cap-call-fn.js +7 -4
- package/dist/core/moleculer/cap-route-authority.js +8 -3
- package/dist/core/moleculer/hub-mesh-guard.js +9 -5
- package/dist/core/moleculer/moleculer.service.js +86 -79
- package/dist/core/network/network-quality.service.js +6 -2
- package/dist/core/notification/notification-wrapper.service.js +7 -3
- package/dist/core/notification/toast-wrapper.service.js +7 -3
- package/dist/core/provider/provider.tokens.js +4 -1
- package/dist/core/repl/repl-engine.service.js +8 -4
- package/dist/core/runner-health/runner-boot-check.js +15 -7
- package/dist/core/runner-health/update-preflight.js +14 -9
- package/dist/core/server-update/server-update.service.js +49 -12
- package/dist/core/server-update/system-apply-closure-patches.js +42 -6
- package/dist/core/server-update/system-ensure-prebuilds.js +6 -3
- package/dist/core/server-update/system-exec-npm.js +5 -2
- package/dist/core/storage/fs-storage-backend.js +5 -1
- package/dist/core/storage/storage-location-manager.js +5 -1
- package/dist/core/storage/storage.service.js +6 -2
- package/dist/core/streaming/ffmpeg-stream-info.js +8 -3
- package/dist/core/streaming/stream-probe.service.js +16 -12
- package/dist/core/streaming/ttl-cache.js +5 -1
- package/dist/core/topology/topology-emitter.service.js +23 -19
- package/dist/core/update-availability-emitter.js +7 -3
- package/dist/core/update-availability-store.js +45 -8
- package/dist/core/updates/agent-installed-packages.js +9 -5
- package/dist/core/updates/restart-marker.js +8 -3
- package/dist/core/updates/update-check-scheduler.js +18 -13
- package/dist/core/updates/wrapper-update-checker.js +7 -3
- package/dist/first-boot-addon-plan.js +6 -2
- package/dist/framework-nodepath.js +38 -2
- package/dist/i18n/request-lang.js +11 -7
- package/dist/launcher.js +65 -30
- package/dist/lifecycle-journal-path.js +38 -2
- package/dist/main.js +324 -286
- package/dist/manual-boot.js +156 -117
- package/dist/node-role.js +4 -1
- package/dist/package-inventory.js +48 -7
- package/dist/pending-restore.js +43 -7
- package/dist/single-copy-cleanup-runner.js +45 -9
- package/dist/single-copy-cleanup.js +15 -7
- package/dist/sqlite-restore.js +48 -9
- package/dist/starter.js +39 -4
- package/dist/ui-serving/lazy-ui-serving.js +39 -3
- package/package.json +1 -1
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
"use strict";
|
|
1
2
|
/**
|
|
2
3
|
* Core-capability mesh bridge.
|
|
3
4
|
*
|
|
@@ -16,13 +17,22 @@
|
|
|
16
17
|
* service mounted on the hub node, `brokerTransportLink` resolves and
|
|
17
18
|
* routes the call exactly like any addon-provided cap.
|
|
18
19
|
*/
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
20
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
21
|
+
exports.REMOTE_PRIVILEGE_LOG_ONLY = exports.REMOTE_UPDATE_PACKAGE_PATH = exports.REMOTE_PRIVILEGE_ALLOWLIST = exports.CORE_CAP_REMOTE_FORBIDDEN_TYPE = exports.REMOTE_DENIED_NAMESPACES = void 0;
|
|
22
|
+
exports.setRemotePrivilegeLogSink = setRemotePrivilegeLogSink;
|
|
23
|
+
exports.__resetRemotePrivilegeObservationForTests = __resetRemotePrivilegeObservationForTests;
|
|
24
|
+
exports.observeRemotePrivilege = observeRemotePrivilege;
|
|
25
|
+
exports.observeRemotePrivilegeRefused = observeRemotePrivilegeRefused;
|
|
26
|
+
exports.isCoreServiceCapName = isCoreServiceCapName;
|
|
27
|
+
exports.describeMeshOrigin = describeMeshOrigin;
|
|
28
|
+
exports.buildCoreCapService = buildCoreCapService;
|
|
29
|
+
const system_1 = require("@camstack/system");
|
|
30
|
+
const moleculer_1 = require("moleculer");
|
|
31
|
+
const remote_addon_update_grant_js_1 = require("./remote-addon-update-grant.js");
|
|
32
|
+
const http_request_census_singleton_js_1 = require("./http-request-census.singleton.js");
|
|
33
|
+
const server_provided_caps_js_1 = require("./server-provided-caps.js");
|
|
34
|
+
const trpc_context_js_1 = require("./trpc.context.js");
|
|
35
|
+
const trpc_middleware_js_1 = require("./trpc.middleware.js");
|
|
26
36
|
/**
|
|
27
37
|
* appRouter namespaces that back the CORE API surface and must be
|
|
28
38
|
* reachable cross-process: every server-provided hub-service cap
|
|
@@ -44,7 +54,7 @@ import { createCallerFactory } from './trpc.middleware.js';
|
|
|
44
54
|
*/
|
|
45
55
|
const CORE_NAMESPACES = new Set([
|
|
46
56
|
// Service-backed cap routers (no addon provider) — derived, never copied.
|
|
47
|
-
...HUB_SERVICE_CAP_NAMES.map(capNameToNamespace),
|
|
57
|
+
...server_provided_caps_js_1.HUB_SERVICE_CAP_NAMES.map(server_provided_caps_js_1.capNameToNamespace),
|
|
48
58
|
// Hand-written single-impl core routers.
|
|
49
59
|
'capabilities',
|
|
50
60
|
'notifications',
|
|
@@ -61,18 +71,18 @@ const CORE_NAMESPACES = new Set([
|
|
|
61
71
|
* (they read their own settings through the settings door), so refusing them
|
|
62
72
|
* to an agent costs nothing and takes them off the LAN.
|
|
63
73
|
*/
|
|
64
|
-
|
|
74
|
+
exports.REMOTE_DENIED_NAMESPACES = new Set([
|
|
65
75
|
'logs',
|
|
66
76
|
'addonSettingsRaw',
|
|
67
77
|
]);
|
|
68
78
|
/** Moleculer error `type` of a bridged call another node may not make. */
|
|
69
|
-
|
|
79
|
+
exports.CORE_CAP_REMOTE_FORBIDDEN_TYPE = 'CORE_CAP_REMOTE_FORBIDDEN';
|
|
70
80
|
/**
|
|
71
81
|
* Hub-core procedure paths another node may call after {@link REMOTE_DENIED_NAMESPACES}.
|
|
72
82
|
* Seeded from armed transport census on the live hub (2026-10): remote agents
|
|
73
83
|
* (`mesh:<agent>`) called none of these; hub runners used this surface only.
|
|
74
84
|
*/
|
|
75
|
-
|
|
85
|
+
exports.REMOTE_PRIVILEGE_ALLOWLIST = new Set([
|
|
76
86
|
'addons.listCapabilityProviders',
|
|
77
87
|
'nodes.topology',
|
|
78
88
|
'integrations.getByAddonId',
|
|
@@ -82,20 +92,20 @@ export const REMOTE_PRIVILEGE_ALLOWLIST = new Set([
|
|
|
82
92
|
'system.getSiteLocation',
|
|
83
93
|
]);
|
|
84
94
|
/** The one path outside the allowlist a node may call, under {@link authorizeRemoteUpdatePackage}. */
|
|
85
|
-
|
|
95
|
+
exports.REMOTE_UPDATE_PACKAGE_PATH = 'addons.updatePackage';
|
|
86
96
|
/**
|
|
87
97
|
* When true, remote bridged calls outside {@link REMOTE_PRIVILEGE_ALLOWLIST} are
|
|
88
98
|
* logged once per `(nodeId, path)` per process — still admin, not refused.
|
|
89
99
|
*/
|
|
90
|
-
|
|
100
|
+
exports.REMOTE_PRIVILEGE_LOG_ONLY = false;
|
|
91
101
|
let remotePrivilegeLogSink = null;
|
|
92
102
|
/** Attach the warn sink used by {@link observeRemotePrivilege}. */
|
|
93
|
-
|
|
103
|
+
function setRemotePrivilegeLogSink(sink) {
|
|
94
104
|
remotePrivilegeLogSink = sink;
|
|
95
105
|
}
|
|
96
106
|
const remotePrivilegeObserved = new Set();
|
|
97
107
|
/** Clears dedupe state and the sink — tests only. */
|
|
98
|
-
|
|
108
|
+
function __resetRemotePrivilegeObservationForTests() {
|
|
99
109
|
remotePrivilegeObserved.clear();
|
|
100
110
|
remotePrivilegeLogSink = null;
|
|
101
111
|
}
|
|
@@ -115,8 +125,8 @@ function warnRemotePrivilegeOnce(nodeId, path, message) {
|
|
|
115
125
|
* One warn per `(nodeId, path)` while {@link REMOTE_PRIVILEGE_LOG_ONLY} is on.
|
|
116
126
|
* Call only after {@link REMOTE_DENIED_NAMESPACES} has passed.
|
|
117
127
|
*/
|
|
118
|
-
|
|
119
|
-
if (!REMOTE_PRIVILEGE_LOG_ONLY)
|
|
128
|
+
function observeRemotePrivilege(nodeId, path) {
|
|
129
|
+
if (!exports.REMOTE_PRIVILEGE_LOG_ONLY)
|
|
120
130
|
return;
|
|
121
131
|
warnRemotePrivilegeOnce(nodeId, path, `mesh privilege: node:${nodeId} called ${path} (log-only; still allowed)`);
|
|
122
132
|
}
|
|
@@ -124,7 +134,7 @@ export function observeRemotePrivilege(nodeId, path) {
|
|
|
124
134
|
* One warn per `(nodeId, path)` when enforcement refuses a path not on
|
|
125
135
|
* {@link REMOTE_PRIVILEGE_ALLOWLIST}. Call immediately before throwing.
|
|
126
136
|
*/
|
|
127
|
-
|
|
137
|
+
function observeRemotePrivilegeRefused(nodeId, path) {
|
|
128
138
|
warnRemotePrivilegeOnce(nodeId, path, `mesh privilege: node:${nodeId} called ${path} (refused; not on grant)`);
|
|
129
139
|
}
|
|
130
140
|
/**
|
|
@@ -152,7 +162,7 @@ const CORE_CAP_NAMES = new Set([
|
|
|
152
162
|
* already-maintained {@link CORE_NAMESPACES} — a new addon cap is covered with
|
|
153
163
|
* zero changes here.
|
|
154
164
|
*/
|
|
155
|
-
|
|
165
|
+
function isCoreServiceCapName(capName) {
|
|
156
166
|
return CORE_CAP_NAMES.has(capName);
|
|
157
167
|
}
|
|
158
168
|
function isProcedureNode(value) {
|
|
@@ -212,7 +222,7 @@ function countMeshCall(census, path, origin) {
|
|
|
212
222
|
* principal. Never a token: there is none, the mesh context is trusted by
|
|
213
223
|
* construction (`createMeshTrpcContext`).
|
|
214
224
|
*/
|
|
215
|
-
|
|
225
|
+
function describeMeshOrigin(origin) {
|
|
216
226
|
const node = origin.nodeId ?? 'unknown';
|
|
217
227
|
return origin.caller === null ? `mesh:${node}` : `mesh:${node} via ${origin.caller}`;
|
|
218
228
|
}
|
|
@@ -241,13 +251,13 @@ function resolveInvoker(caller, path) {
|
|
|
241
251
|
* Each call runs under a principal naming its origin (D802): `mesh` for the
|
|
242
252
|
* hub's own runners, `node:<nodeId>` for another node.
|
|
243
253
|
*/
|
|
244
|
-
|
|
245
|
-
const callerFactory = createCallerFactory(appRouter);
|
|
254
|
+
function buildCoreCapService(appRouter, census = (0, http_request_census_singleton_js_1.getHttpRequestCensus)(), options = {}) {
|
|
255
|
+
const callerFactory = (0, trpc_middleware_js_1.createCallerFactory)(appRouter);
|
|
246
256
|
const callers = new Map();
|
|
247
257
|
const callerFor = (principal) => {
|
|
248
258
|
let caller = callers.get(principal);
|
|
249
259
|
if (caller === undefined) {
|
|
250
|
-
caller = callerFactory(createMeshTrpcContext(principal));
|
|
260
|
+
caller = callerFactory((0, trpc_context_js_1.createMeshTrpcContext)(principal));
|
|
251
261
|
callers.set(principal, caller);
|
|
252
262
|
}
|
|
253
263
|
return caller;
|
|
@@ -272,22 +282,22 @@ export function buildCoreCapService(appRouter, census = getHttpRequestCensus(),
|
|
|
272
282
|
invoke: async (input, origin) => {
|
|
273
283
|
countMeshCall(census, path, origin);
|
|
274
284
|
const remoteNode = remoteOrigin(origin, options.ownNodeId);
|
|
275
|
-
if (remoteNode !== null && REMOTE_DENIED_NAMESPACES.has(namespace)) {
|
|
276
|
-
throw new Errors.MoleculerClientError(`"${path}" is not callable from another node (caller "${remoteNode}")`, 403, CORE_CAP_REMOTE_FORBIDDEN_TYPE);
|
|
285
|
+
if (remoteNode !== null && exports.REMOTE_DENIED_NAMESPACES.has(namespace)) {
|
|
286
|
+
throw new moleculer_1.Errors.MoleculerClientError(`"${path}" is not callable from another node (caller "${remoteNode}")`, 403, exports.CORE_CAP_REMOTE_FORBIDDEN_TYPE);
|
|
277
287
|
}
|
|
278
288
|
if (remoteNode !== null) {
|
|
279
|
-
if (REMOTE_PRIVILEGE_LOG_ONLY) {
|
|
289
|
+
if (exports.REMOTE_PRIVILEGE_LOG_ONLY) {
|
|
280
290
|
observeRemotePrivilege(remoteNode, path);
|
|
281
291
|
}
|
|
282
|
-
else if (path === REMOTE_UPDATE_PACKAGE_PATH && options.remoteUpdateGrant) {
|
|
283
|
-
const decision = await authorizeRemoteUpdatePackage(remoteNode, input, options.remoteUpdateGrant);
|
|
292
|
+
else if (path === exports.REMOTE_UPDATE_PACKAGE_PATH && options.remoteUpdateGrant) {
|
|
293
|
+
const decision = await (0, remote_addon_update_grant_js_1.authorizeRemoteUpdatePackage)(remoteNode, input, options.remoteUpdateGrant);
|
|
284
294
|
if (!decision.allowed) {
|
|
285
|
-
throw new Errors.MoleculerClientError(`"${path}" refused for caller "${remoteNode}": ${decision.reason ?? 'not permitted'}`, 403, CORE_CAP_REMOTE_FORBIDDEN_TYPE);
|
|
295
|
+
throw new moleculer_1.Errors.MoleculerClientError(`"${path}" refused for caller "${remoteNode}": ${decision.reason ?? 'not permitted'}`, 403, exports.CORE_CAP_REMOTE_FORBIDDEN_TYPE);
|
|
286
296
|
}
|
|
287
297
|
}
|
|
288
|
-
else if (!REMOTE_PRIVILEGE_ALLOWLIST.has(path)) {
|
|
298
|
+
else if (!exports.REMOTE_PRIVILEGE_ALLOWLIST.has(path)) {
|
|
289
299
|
observeRemotePrivilegeRefused(remoteNode, path);
|
|
290
|
-
throw new Errors.MoleculerClientError(`"${path}" is not on the remote privilege grant (caller "${remoteNode}")`, 403, CORE_CAP_REMOTE_FORBIDDEN_TYPE);
|
|
300
|
+
throw new moleculer_1.Errors.MoleculerClientError(`"${path}" is not on the remote privilege grant (caller "${remoteNode}")`, 403, exports.CORE_CAP_REMOTE_FORBIDDEN_TYPE);
|
|
291
301
|
}
|
|
292
302
|
}
|
|
293
303
|
const invoke = resolveInvoker(callerFor(remoteNode === null ? 'mesh' : `node:${remoteNode}`), path);
|
|
@@ -297,7 +307,7 @@ export function buildCoreCapService(appRouter, census = getHttpRequestCensus(),
|
|
|
297
307
|
},
|
|
298
308
|
});
|
|
299
309
|
}
|
|
300
|
-
return createCoreCapService({ actions });
|
|
310
|
+
return (0, system_1.createCoreCapService)({ actions });
|
|
301
311
|
}
|
|
302
312
|
/** The calling node when it is not the hub itself; `null` for the hub's own calls. */
|
|
303
313
|
function remoteOrigin(origin, ownNodeId) {
|
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
"use strict";
|
|
1
2
|
/**
|
|
2
3
|
* The per-device COLLECTION dispatcher — a capability whose providers are
|
|
3
4
|
* SOURCES of one device, listed beside each other.
|
|
@@ -59,9 +60,12 @@
|
|
|
59
60
|
* camera with one provider behaves exactly as it did before this mount
|
|
60
61
|
* existed, and it is pinned by spec.
|
|
61
62
|
*/
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
63
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
64
|
+
exports.SOURCE_LISTING_METHOD = void 0;
|
|
65
|
+
exports.requireDeviceCollection = requireDeviceCollection;
|
|
66
|
+
const system_1 = require("@camstack/system");
|
|
67
|
+
const types_1 = require("@camstack/types");
|
|
68
|
+
const server_1 = require("@trpc/server");
|
|
65
69
|
/**
|
|
66
70
|
* The device-collection CONVENTION: a cap whose providers are sources declares
|
|
67
71
|
* this method, taking `{ deviceId }` and returning one row per source with a
|
|
@@ -69,7 +73,7 @@ import { TRPCError } from '@trpc/server';
|
|
|
69
73
|
* without a second registry of id prefixes — the provider that mints the ids
|
|
70
74
|
* is the one that names them.
|
|
71
75
|
*/
|
|
72
|
-
|
|
76
|
+
exports.SOURCE_LISTING_METHOD = 'listSources';
|
|
73
77
|
/** The field on a source row that carries its id / clip-id namespace. */
|
|
74
78
|
const SOURCE_ID_FIELD = 'source';
|
|
75
79
|
/** The field on a source row that names the addon serving it (D555/D557). */
|
|
@@ -215,10 +219,10 @@ function isFanOutMethod(registry, capName, method) {
|
|
|
215
219
|
const def = registry.getDefinition(capName);
|
|
216
220
|
if (def === undefined)
|
|
217
221
|
return false;
|
|
218
|
-
const schema = expandCapMethods(def)[method];
|
|
222
|
+
const schema = (0, types_1.expandCapMethods)(def)[method];
|
|
219
223
|
if (schema === undefined || schema.kind === 'subscription')
|
|
220
224
|
return false;
|
|
221
|
-
return isArrayOutputSchema(schema.output);
|
|
225
|
+
return (0, types_1.isArrayOutputSchema)(schema.output);
|
|
222
226
|
}
|
|
223
227
|
function errMessage(err) {
|
|
224
228
|
return err instanceof Error ? err.message : String(err);
|
|
@@ -227,7 +231,7 @@ function errMessage(err) {
|
|
|
227
231
|
async function callSource(source, method, input, capName, deviceId) {
|
|
228
232
|
const fn = source.provider[method];
|
|
229
233
|
if (typeof fn !== 'function') {
|
|
230
|
-
throw new TRPCError({
|
|
234
|
+
throw new server_1.TRPCError({
|
|
231
235
|
code: 'NOT_IMPLEMENTED',
|
|
232
236
|
message: notImplementedMessage(capName, source.addonId, deviceId, method),
|
|
233
237
|
});
|
|
@@ -242,8 +246,8 @@ async function callSource(source, method, input, capName, deviceId) {
|
|
|
242
246
|
return await fn.call(source.provider, input);
|
|
243
247
|
}
|
|
244
248
|
catch (err) {
|
|
245
|
-
if (errMessage(err).includes(CAP_METHOD_NOT_IMPLEMENTED)) {
|
|
246
|
-
throw new TRPCError({
|
|
249
|
+
if (errMessage(err).includes(system_1.CAP_METHOD_NOT_IMPLEMENTED)) {
|
|
250
|
+
throw new server_1.TRPCError({
|
|
247
251
|
code: 'NOT_IMPLEMENTED',
|
|
248
252
|
message: notImplementedMessage(capName, source.addonId, deviceId, method),
|
|
249
253
|
});
|
|
@@ -328,18 +332,18 @@ function unreachableSourceRow(addonId, reason) {
|
|
|
328
332
|
* behaviour, D554 decision 6).
|
|
329
333
|
*/
|
|
330
334
|
const CAP_FAILURE_ROWS = {
|
|
331
|
-
[deviceExtensionCapability.name]: {
|
|
332
|
-
[EXTENSION_SLOT_LISTING_METHOD]: unreachableExtensionSlotRow,
|
|
335
|
+
[types_1.deviceExtensionCapability.name]: {
|
|
336
|
+
[types_1.EXTENSION_SLOT_LISTING_METHOD]: types_1.unreachableExtensionSlotRow,
|
|
333
337
|
},
|
|
334
338
|
};
|
|
335
339
|
function failureRowFor(capName, method) {
|
|
336
340
|
const own = CAP_FAILURE_ROWS[capName]?.[method];
|
|
337
341
|
if (own !== undefined)
|
|
338
342
|
return own;
|
|
339
|
-
return method === SOURCE_LISTING_METHOD ? unreachableSourceRow : null;
|
|
343
|
+
return method === exports.SOURCE_LISTING_METHOD ? unreachableSourceRow : null;
|
|
340
344
|
}
|
|
341
345
|
async function fanOut(sources, method, input, capName, deviceId, logger) {
|
|
342
|
-
const isSourceListing = method === SOURCE_LISTING_METHOD;
|
|
346
|
+
const isSourceListing = method === exports.SOURCE_LISTING_METHOD;
|
|
343
347
|
const failureRow = failureRowFor(capName, method);
|
|
344
348
|
const settled = await Promise.allSettled(sources.map(async (source) => await callSource(source, method, input, capName, deviceId)));
|
|
345
349
|
const rows = [];
|
|
@@ -399,7 +403,7 @@ function finishRows(source, rows, isSourceListing, capName, deviceId, logger) {
|
|
|
399
403
|
}
|
|
400
404
|
const id = Reflect.get(row, SOURCE_ID_FIELD);
|
|
401
405
|
if (typeof id === 'string') {
|
|
402
|
-
const owner = reservedSourceOwner(capName, id);
|
|
406
|
+
const owner = (0, types_1.reservedSourceOwner)(capName, id);
|
|
403
407
|
if (owner !== null && owner !== source.addonId) {
|
|
404
408
|
// A RESERVED id (D625) is the DEFAULT of a picker, expressed as a
|
|
405
409
|
// value rather than as a sort order. A second row spelling it would
|
|
@@ -409,7 +413,7 @@ function finishRows(source, rows, isSourceListing, capName, deviceId, logger) {
|
|
|
409
413
|
// and emptying a camera's picker over one bad row is worse than the
|
|
410
414
|
// bad row. The guard in `scripts/` refuses the same spelling at build
|
|
411
415
|
// time; this is what happens when an old build reaches a new hub.
|
|
412
|
-
logger?.warn(`${capName}.${SOURCE_LISTING_METHOD}: source id "${id}" is RESERVED for "${owner}" — this addon's row is dropped`, {
|
|
416
|
+
logger?.warn(`${capName}.${exports.SOURCE_LISTING_METHOD}: source id "${id}" is RESERVED for "${owner}" — this addon's row is dropped`, {
|
|
413
417
|
tags: { deviceId, addonId: source.addonId },
|
|
414
418
|
meta: { capability: capName, sourceId: id, reservedFor: owner },
|
|
415
419
|
});
|
|
@@ -423,7 +427,7 @@ function finishRows(source, rows, isSourceListing, capName, deviceId, logger) {
|
|
|
423
427
|
async function claimsOf(sources, capName, deviceId, logger) {
|
|
424
428
|
const claimed = new Map();
|
|
425
429
|
const unknownClaims = [];
|
|
426
|
-
const settled = await Promise.allSettled(sources.map(async (source) => await callSource(source, SOURCE_LISTING_METHOD, { deviceId }, capName, deviceId)));
|
|
430
|
+
const settled = await Promise.allSettled(sources.map(async (source) => await callSource(source, exports.SOURCE_LISTING_METHOD, { deviceId }, capName, deviceId)));
|
|
427
431
|
for (const [index, result] of settled.entries()) {
|
|
428
432
|
const source = sources[index];
|
|
429
433
|
if (source === undefined)
|
|
@@ -431,7 +435,7 @@ async function claimsOf(sources, capName, deviceId, logger) {
|
|
|
431
435
|
if (result.status === 'rejected' || !Array.isArray(result.value)) {
|
|
432
436
|
// It could not name its sources, so it cannot be excluded on that basis
|
|
433
437
|
// either — a read that FAILS must not destroy work (D49).
|
|
434
|
-
logger?.warn(`${capName}.${SOURCE_LISTING_METHOD}: source could not name its ids — routing falls back to it only if it is the only such source`, {
|
|
438
|
+
logger?.warn(`${capName}.${exports.SOURCE_LISTING_METHOD}: source could not name its ids — routing falls back to it only if it is the only such source`, {
|
|
435
439
|
tags: { deviceId, addonId: source.addonId },
|
|
436
440
|
meta: {
|
|
437
441
|
capability: capName,
|
|
@@ -497,7 +501,7 @@ async function dispatchScalar(sources, method, input, capName, deviceId, logger)
|
|
|
497
501
|
tags: { deviceId },
|
|
498
502
|
meta: { capability: capName, method, knownSources: known, input },
|
|
499
503
|
});
|
|
500
|
-
throw new TRPCError({
|
|
504
|
+
throw new server_1.TRPCError({
|
|
501
505
|
code: 'BAD_REQUEST',
|
|
502
506
|
message: `Capability "${capName}.${method}": no source of device ${deviceId} claims ` +
|
|
503
507
|
`${JSON.stringify(input)}. Sources: ${known.length > 0 ? known.join(', ') : '(none named)'}. ` +
|
|
@@ -522,7 +526,7 @@ function pinnedProvider(sources, input, capName, deviceId) {
|
|
|
522
526
|
const picked = sources.filter((source) => source.addonId === pin);
|
|
523
527
|
if (picked.length > 0)
|
|
524
528
|
return picked;
|
|
525
|
-
throw new TRPCError({
|
|
529
|
+
throw new server_1.TRPCError({
|
|
526
530
|
code: 'BAD_REQUEST',
|
|
527
531
|
message: `Capability "${capName}": device ${deviceId} is not bound to provider "${pin}". ` +
|
|
528
532
|
`Bound providers: ${sources.map((source) => source.addonId).join(', ') || '(none)'}. ` +
|
|
@@ -537,7 +541,7 @@ function pinnedProvider(sources, input, capName, deviceId) {
|
|
|
537
541
|
* Returns null without a registry (the router then surfaces
|
|
538
542
|
* PRECONDITION_FAILED), exactly as the singleton dispatcher does.
|
|
539
543
|
*/
|
|
540
|
-
|
|
544
|
+
function requireDeviceCollection(registry, capName, logger) {
|
|
541
545
|
if (!registry)
|
|
542
546
|
return null;
|
|
543
547
|
const dispatcher = new Proxy({}, {
|
|
@@ -547,7 +551,7 @@ export function requireDeviceCollection(registry, capName, logger) {
|
|
|
547
551
|
return async (input) => {
|
|
548
552
|
const deviceId = input !== null && typeof input === 'object' ? Reflect.get(input, 'deviceId') : undefined;
|
|
549
553
|
if (typeof deviceId !== 'number') {
|
|
550
|
-
throw new TRPCError({
|
|
554
|
+
throw new server_1.TRPCError({
|
|
551
555
|
code: 'BAD_REQUEST',
|
|
552
556
|
message: `${capName}.${prop}: input must carry numeric "deviceId"`,
|
|
553
557
|
});
|
|
@@ -555,7 +559,7 @@ export function requireDeviceCollection(registry, capName, logger) {
|
|
|
555
559
|
const bound = await sourcesForDevice(registry, capName, deviceId, logger);
|
|
556
560
|
const sources = pinnedProvider(bound, input, capName, deviceId);
|
|
557
561
|
if (sources.length === 0) {
|
|
558
|
-
throw new TRPCError({
|
|
562
|
+
throw new server_1.TRPCError({
|
|
559
563
|
code: 'PRECONDITION_FAILED',
|
|
560
564
|
message: `Capability "${capName}" not registered for device ${deviceId}`,
|
|
561
565
|
});
|
|
@@ -576,7 +580,7 @@ export function requireDeviceCollection(registry, capName, logger) {
|
|
|
576
580
|
const answered = await callSource(first, prop, input, capName, deviceId);
|
|
577
581
|
if (!Array.isArray(answered))
|
|
578
582
|
return answered;
|
|
579
|
-
return finishRows(first, answered, prop === SOURCE_LISTING_METHOD, capName, deviceId, logger);
|
|
583
|
+
return finishRows(first, answered, prop === exports.SOURCE_LISTING_METHOD, capName, deviceId, logger);
|
|
580
584
|
}
|
|
581
585
|
return await fanOut(sources, prop, input, capName, deviceId, logger);
|
|
582
586
|
}
|
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
"use strict";
|
|
1
2
|
/**
|
|
2
3
|
* Device-config secret redaction for NON-ADMIN principals.
|
|
3
4
|
*
|
|
@@ -38,11 +39,26 @@
|
|
|
38
39
|
* Pure module — no tRPC machinery — so the spec exercises it directly (same
|
|
39
40
|
* pattern as `scope-access.ts` and `share-view-access.ts`).
|
|
40
41
|
*/
|
|
42
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
43
|
+
exports.NON_ADMIN_CONFIG_REDACTED_METHODS = exports.REDACTED_SECRET = void 0;
|
|
44
|
+
exports.isSecretConfigKey = isSecretConfigKey;
|
|
45
|
+
exports.redactDeviceInfoSecrets = redactDeviceInfoSecrets;
|
|
46
|
+
exports.redactDeviceInfoRecordSecrets = redactDeviceInfoRecordSecrets;
|
|
47
|
+
exports.redactSettingsSections = redactSettingsSections;
|
|
48
|
+
exports.redactSettingsAggregate = redactSettingsAggregate;
|
|
49
|
+
exports.redactSettingsForm = redactSettingsForm;
|
|
50
|
+
exports.redactExportSetup = redactExportSetup;
|
|
51
|
+
exports.redactDeepSecrets = redactDeepSecrets;
|
|
52
|
+
exports.redactStatusAggregateSetup = redactStatusAggregateSetup;
|
|
53
|
+
exports.redactStatusAggregateBatchSetup = redactStatusAggregateBatchSetup;
|
|
54
|
+
exports.redactConfigEntries = redactConfigEntries;
|
|
55
|
+
exports.redactStreamUrlSecrets = redactStreamUrlSecrets;
|
|
56
|
+
exports.maskUrlCredentialsDeep = maskUrlCredentialsDeep;
|
|
41
57
|
/** What a non-admin sees instead of the value. Matches the `***` convention
|
|
42
58
|
* `maskUrlCredentials` already uses for credentials in log output, so one
|
|
43
59
|
* redaction marker means one thing across the system. */
|
|
44
|
-
|
|
45
|
-
|
|
60
|
+
const types_1 = require("@camstack/types");
|
|
61
|
+
exports.REDACTED_SECRET = '***';
|
|
46
62
|
/**
|
|
47
63
|
* Config keys whose VALUE is a credential.
|
|
48
64
|
*
|
|
@@ -63,7 +79,7 @@ const SECRET_KEY_SUBSTRINGS = [
|
|
|
63
79
|
'api_key',
|
|
64
80
|
];
|
|
65
81
|
/** True when a config key names a credential. Case-insensitive substring match. */
|
|
66
|
-
|
|
82
|
+
function isSecretConfigKey(key) {
|
|
67
83
|
const lower = key.toLowerCase();
|
|
68
84
|
return SECRET_KEY_SUBSTRINGS.some((needle) => lower.includes(needle));
|
|
69
85
|
}
|
|
@@ -87,7 +103,7 @@ function declaredPathsOf(row) {
|
|
|
87
103
|
/** Withhold every non-empty string at or below `value` (a declared secret node). */
|
|
88
104
|
function withholdAll(value) {
|
|
89
105
|
if (typeof value === 'string')
|
|
90
|
-
return value.length > 0 ? REDACTED_SECRET : value;
|
|
106
|
+
return value.length > 0 ? exports.REDACTED_SECRET : value;
|
|
91
107
|
if (Array.isArray(value))
|
|
92
108
|
return value.map(withholdAll);
|
|
93
109
|
if (!isRecord(value))
|
|
@@ -124,7 +140,7 @@ function withholdPath(value, segments) {
|
|
|
124
140
|
*/
|
|
125
141
|
function backstop(value) {
|
|
126
142
|
if (typeof value === 'string') {
|
|
127
|
-
return value.includes('://') ? maskUrlCredentials(value) : value;
|
|
143
|
+
return value.includes('://') ? (0, types_1.maskUrlCredentials)(value) : value;
|
|
128
144
|
}
|
|
129
145
|
if (Array.isArray(value))
|
|
130
146
|
return value.map(backstop);
|
|
@@ -134,7 +150,7 @@ function backstop(value) {
|
|
|
134
150
|
for (const [key, child] of Object.entries(value)) {
|
|
135
151
|
out[key] =
|
|
136
152
|
isSecretConfigKey(key) && typeof child === 'string' && child.length > 0
|
|
137
|
-
? REDACTED_SECRET
|
|
153
|
+
? exports.REDACTED_SECRET
|
|
138
154
|
: backstop(child);
|
|
139
155
|
}
|
|
140
156
|
return out;
|
|
@@ -158,7 +174,7 @@ function redactConfigBlob(config, declared) {
|
|
|
158
174
|
* Anything that is not a device row passes through untouched — a route that
|
|
159
175
|
* errored upstream must not be rewritten into a plausible-looking shape.
|
|
160
176
|
*/
|
|
161
|
-
|
|
177
|
+
function redactDeviceInfoSecrets(data) {
|
|
162
178
|
if (Array.isArray(data))
|
|
163
179
|
return data.map((row) => redactDeviceInfoSecrets(row));
|
|
164
180
|
if (!isRecord(data))
|
|
@@ -177,7 +193,7 @@ export function redactDeviceInfoSecrets(data) {
|
|
|
177
193
|
* its own, so the function returns early having redacted nothing. A batch of a
|
|
178
194
|
* single-device read must never be the ungated way to ask the same question.
|
|
179
195
|
*/
|
|
180
|
-
|
|
196
|
+
function redactDeviceInfoRecordSecrets(data) {
|
|
181
197
|
if (!isRecord(data))
|
|
182
198
|
return data;
|
|
183
199
|
const out = {};
|
|
@@ -189,7 +205,7 @@ export function redactDeviceInfoRecordSecrets(data) {
|
|
|
189
205
|
* Project a `SettingsSchemaWithValues` (`{ sections: [{ fields: [...] }] }`).
|
|
190
206
|
* Used by `getDeviceSettingsAggregate` and `getSettingsSchema`.
|
|
191
207
|
*/
|
|
192
|
-
|
|
208
|
+
function redactSettingsSections(data) {
|
|
193
209
|
if (!isRecord(data))
|
|
194
210
|
return data;
|
|
195
211
|
const sections = data['sections'];
|
|
@@ -214,7 +230,7 @@ export function redactSettingsSections(data) {
|
|
|
214
230
|
* Project the combined aggregate, whose settings live one level down under
|
|
215
231
|
* `settings`. The live half carries no config blob.
|
|
216
232
|
*/
|
|
217
|
-
|
|
233
|
+
function redactSettingsAggregate(data) {
|
|
218
234
|
if (!isRecord(data))
|
|
219
235
|
return data;
|
|
220
236
|
const settings = data['settings'];
|
|
@@ -259,8 +275,8 @@ function redactFormValue(value, secret, itemFields) {
|
|
|
259
275
|
if (value.length === 0)
|
|
260
276
|
return value;
|
|
261
277
|
if (secret)
|
|
262
|
-
return REDACTED_SECRET;
|
|
263
|
-
return value.includes('://') ? maskUrlCredentials(value) : value;
|
|
278
|
+
return exports.REDACTED_SECRET;
|
|
279
|
+
return value.includes('://') ? (0, types_1.maskUrlCredentials)(value) : value;
|
|
264
280
|
}
|
|
265
281
|
if (Array.isArray(value))
|
|
266
282
|
return value.map((el) => redactFormValue(el, secret, itemFields));
|
|
@@ -302,7 +318,7 @@ function redactFormField(field) {
|
|
|
302
318
|
// The HomeKit QR caption spells the setup code out in prose.
|
|
303
319
|
const caption = field['caption'];
|
|
304
320
|
return field['type'] === 'qr-code' && typeof caption === 'string' && caption.length > 0
|
|
305
|
-
? { ...redacted, caption: REDACTED_SECRET }
|
|
321
|
+
? { ...redacted, caption: exports.REDACTED_SECRET }
|
|
306
322
|
: redacted;
|
|
307
323
|
}
|
|
308
324
|
/**
|
|
@@ -319,7 +335,7 @@ function redactFormField(field) {
|
|
|
319
335
|
* and `updateGlobalSettings` / `updateDeviceSettings` are admin-only, so a
|
|
320
336
|
* redacted value can never be written back (D634).
|
|
321
337
|
*/
|
|
322
|
-
|
|
338
|
+
function redactSettingsForm(data) {
|
|
323
339
|
if (!isRecord(data))
|
|
324
340
|
return data;
|
|
325
341
|
const sections = data['sections'];
|
|
@@ -344,7 +360,7 @@ export function redactSettingsForm(data) {
|
|
|
344
360
|
* panel renders it, so a non-admin keeps the link state and counts and never
|
|
345
361
|
* receives the block (D634 r2). One object or a fanned-out array.
|
|
346
362
|
*/
|
|
347
|
-
|
|
363
|
+
function redactExportSetup(data) {
|
|
348
364
|
if (Array.isArray(data))
|
|
349
365
|
return data.map((row) => redactExportSetup(row));
|
|
350
366
|
if (!isRecord(data) || !('setup' in data))
|
|
@@ -359,7 +375,7 @@ export function redactExportSetup(data) {
|
|
|
359
375
|
* `access_token` and `?token=` picture URLs), an integration's `info`, a
|
|
360
376
|
* broker list's `url` (D634 r3).
|
|
361
377
|
*/
|
|
362
|
-
|
|
378
|
+
function redactDeepSecrets(data) {
|
|
363
379
|
return redactFormValue(data, false, undefined);
|
|
364
380
|
}
|
|
365
381
|
/** The status-aggregate entry that carries export pairing material. */
|
|
@@ -369,13 +385,13 @@ const DEVICE_EXPORT_CAP = 'device-export';
|
|
|
369
385
|
* `device-export` entry loses its `setup` (D634 r3 — defence in depth; the
|
|
370
386
|
* aggregation itself no longer asks a system collection cap per device).
|
|
371
387
|
*/
|
|
372
|
-
|
|
388
|
+
function redactStatusAggregateSetup(data) {
|
|
373
389
|
if (!isRecord(data) || !(DEVICE_EXPORT_CAP in data))
|
|
374
390
|
return data;
|
|
375
391
|
return { ...data, [DEVICE_EXPORT_CAP]: redactExportSetup(data[DEVICE_EXPORT_CAP]) };
|
|
376
392
|
}
|
|
377
393
|
/** Project a batch status aggregate (`Record<deviceId, Record<capName, status>>`). */
|
|
378
|
-
|
|
394
|
+
function redactStatusAggregateBatchSetup(data) {
|
|
379
395
|
if (!isRecord(data))
|
|
380
396
|
return data;
|
|
381
397
|
const out = {};
|
|
@@ -385,7 +401,7 @@ export function redactStatusAggregateBatchSetup(data) {
|
|
|
385
401
|
return out;
|
|
386
402
|
}
|
|
387
403
|
/** Project a flat `ConfigEntry[]` — `getConfigSchema`'s `{ key, value }` rows. */
|
|
388
|
-
|
|
404
|
+
function redactConfigEntries(data) {
|
|
389
405
|
if (!Array.isArray(data))
|
|
390
406
|
return data;
|
|
391
407
|
return data.map(redactFormField);
|
|
@@ -412,7 +428,7 @@ const URL_FIELD_KEYS = ['url', 'mutedUrl'];
|
|
|
412
428
|
* (`codec`, `label`) and a non-string value pass through. Non-row payloads
|
|
413
429
|
* pass through untouched — an upstream error must not be rewritten.
|
|
414
430
|
*/
|
|
415
|
-
|
|
431
|
+
function redactStreamUrlSecrets(data) {
|
|
416
432
|
if (Array.isArray(data))
|
|
417
433
|
return data.map((row) => redactStreamUrlSecrets(row));
|
|
418
434
|
if (!isRecord(data))
|
|
@@ -421,7 +437,7 @@ export function redactStreamUrlSecrets(data) {
|
|
|
421
437
|
for (const [key, value] of Object.entries(data)) {
|
|
422
438
|
out[key] =
|
|
423
439
|
URL_FIELD_KEYS.includes(key) && typeof value === 'string' && value.length > 0
|
|
424
|
-
? maskUrlCredentials(value)
|
|
440
|
+
? (0, types_1.maskUrlCredentials)(value)
|
|
425
441
|
: value;
|
|
426
442
|
}
|
|
427
443
|
return out;
|
|
@@ -435,9 +451,9 @@ export function redactStreamUrlSecrets(data) {
|
|
|
435
451
|
* any non-admin (D783). Only strings holding `://` are touched; no key is
|
|
436
452
|
* judged by its name here, so `eventTypeTokens` & co. keep their values.
|
|
437
453
|
*/
|
|
438
|
-
|
|
454
|
+
function maskUrlCredentialsDeep(data) {
|
|
439
455
|
if (typeof data === 'string')
|
|
440
|
-
return data.includes('://') ? maskUrlCredentials(data) : data;
|
|
456
|
+
return data.includes('://') ? (0, types_1.maskUrlCredentials)(data) : data;
|
|
441
457
|
if (Array.isArray(data))
|
|
442
458
|
return data.map(maskUrlCredentialsDeep);
|
|
443
459
|
if (!isRecord(data))
|
|
@@ -464,7 +480,7 @@ export function maskUrlCredentialsDeep(data) {
|
|
|
464
480
|
* `device-config-secret-redaction.spec.ts` pins the list so the addition is a
|
|
465
481
|
* failing test rather than a silent leak.
|
|
466
482
|
*/
|
|
467
|
-
|
|
483
|
+
exports.NON_ADMIN_CONFIG_REDACTED_METHODS = new Map([
|
|
468
484
|
// ── Addon settings forms (D634) — hydrated with the STORED secret ──
|
|
469
485
|
['addonSettings.getGlobalSettings', redactSettingsForm],
|
|
470
486
|
['addonSettings.getIntegrationSettings', redactSettingsForm],
|