From 356d80c9734a3f8253b1b1f14aa8c96ace48c650 Mon Sep 17 00:00:00 2001 From: Jonathan <64296013+Joncallim@users.noreply.github.com> Date: Wed, 2 Sep 2026 23:30:12 +0800 Subject: [PATCH 1/7] feat: validate daemon-state host-port findings --- apps/api/src/daemonResponseValidation.ts | 68 ++++++++++++++++++- apps/api/test/security.test.ts | 33 +++++++-- .../fixtures/contracts/findings-response.json | 39 ++++++++++- 3 files changed, 132 insertions(+), 8 deletions(-) diff --git a/apps/api/src/daemonResponseValidation.ts b/apps/api/src/daemonResponseValidation.ts index 532fea23..8303f6b7 100644 --- a/apps/api/src/daemonResponseValidation.ts +++ b/apps/api/src/daemonResponseValidation.ts @@ -64,6 +64,9 @@ const INTERNAL_NETWORK_PORT_FINDING_RECOMMENDATION = "Review whether the host-po const DOCKER_DAEMON_STATE_FINDING_RULE = "docker.daemon_state_bind_mount"; const DOCKER_DAEMON_STATE_FINDING_SUMMARY = "A container has Docker daemon state access that may provide Docker daemon API authority."; const DOCKER_DAEMON_STATE_FINDING_RECOMMENDATION = "Review whether this container requires Docker daemon API authority."; +const DOCKER_DAEMON_STATE_PUBLISHED_PORT_FINDING_RULE = "docker.daemon_state_bind_mount_publishes_port"; +const DOCKER_DAEMON_STATE_PUBLISHED_PORT_FINDING_SUMMARY = "A container with Docker daemon state access also has a published host port."; +const DOCKER_DAEMON_STATE_PUBLISHED_PORT_FINDING_RECOMMENDATION = "Review whether the daemon-state access and host-port publication are both intended."; const COMPOSE_DECLARED_TARGET_NOT_ACTIVE_FINDING_RULE = "docker.compose_declared_target_not_active"; const COMPOSE_DECLARED_TARGET_NOT_ACTIVE_FINDING_SUMMARY = "A running Docker Compose service declares a dependency whose container is not active."; const COMPOSE_DECLARED_TARGET_NOT_ACTIVE_FINDING_RECOMMENDATION = "Review the declared dependency and the target container state."; @@ -126,6 +129,26 @@ function composeDeclaredTargetFindingId(subjectRef: string, targetRef: string): return `finding_docker_compose_declared_target_not_active_${collisionResistantIdComponent(`${subjectRef}\u001f${targetRef}`)}`; } +function daemonStatePublishedPortFindingId(subjectRef: string): string { + return `finding_docker_daemon_state_bind_mount_publishes_port_${collisionResistantIdComponent(`${subjectRef}\u001fhost_risk_docker_daemon_state`)}`; +} + +// Docker's bounded listener projection uses private/protocol for an +// un-published listener and host:private/protocol for a host binding. Keep +// this exact grammar at the browser boundary; a listener label or ID cannot +// substitute for the observed port metadata. +function isHostPublishedDockerPort(value: unknown): boolean { + if (typeof value !== "string") return false; + const parts = value.split(":"); + if (parts.length !== 2) return false; + const [host, privateAndProtocol] = parts; + const privateParts = privateAndProtocol.split("/"); + if (privateParts.length !== 2) return false; + const [privatePort, protocol] = privateParts; + const isNonZeroPort = (port: string) => /^\d+$/.test(port) && Number(port) > 0 && Number(port) <= 65_535; + return isNonZeroPort(host) && isNonZeroPort(privatePort) && (protocol === "tcp" || protocol === "udp" || protocol === "sctp"); +} + function hasCompleteProviderStateVector(payload: unknown): boolean { if (!payload || typeof payload !== "object") return false; const providerStates = (payload as { providerStates?: unknown }).providerStates; @@ -195,8 +218,16 @@ function hasCoherentProviderFreshness(payload: unknown): boolean { function hasCoherentRuntimeEvidence(payload: unknown): boolean { if (!payload || typeof payload !== "object") return false; + const nodes = (payload as { nodes?: unknown }).nodes; const edges = (payload as { edges?: unknown }).edges; - if (!Array.isArray(edges)) return false; + if (!Array.isArray(nodes) || !Array.isArray(edges)) return false; + const nodesById = new Map>(); + for (const candidate of nodes) { + if (!candidate || typeof candidate !== "object") return false; + const node = candidate as Record; + if (typeof node.id !== "string" || nodesById.has(node.id)) return false; + nodesById.set(node.id, node); + } return edges.every((edge) => { if (!edge || typeof edge !== "object") return false; const candidate = edge as { source?: unknown; target?: unknown; relationship?: unknown; evidenceRefs?: unknown }; @@ -242,6 +273,12 @@ function hasCoherentRuntimeEvidence(payload: unknown): boolean { if (value.subjectRef !== candidate.source || !candidate.source.startsWith(expected.sourcePrefix) || !candidate.target.startsWith(expected.targetPrefix)) return false; if (isV4 && candidate.target !== "host_local") return false; if (value.kind === "docker_daemon_state_bind_mount" && candidate.target !== "host_risk_docker_daemon_state") return false; + if (value.kind === "docker_port_publication") { + const listener = nodesById.get(candidate.target); + if (listener?.provider !== "network" || listener.type !== "network_listener" + || !listener.metadata || typeof listener.metadata !== "object" + || !isHostPublishedDockerPort((listener.metadata as Record).port)) return false; + } if (candidate.source === candidate.target) return false; // An opaque observation token must never be the collection timestamp // re-labelled as a revision. The daemon produces it independently. @@ -310,6 +347,35 @@ function hasCoherentFindings(payload: unknown): boolean { && typeof evidence.providerRevision === "string" && evidence.providerRevision !== String(evidence.collectedAt); })(); + if (finding.ruleId === DOCKER_DAEMON_STATE_PUBLISHED_PORT_FINDING_RULE) return finding.severity === "warning" + && finding.summary === DOCKER_DAEMON_STATE_PUBLISHED_PORT_FINDING_SUMMARY + && finding.recommendation === DOCKER_DAEMON_STATE_PUBLISHED_PORT_FINDING_RECOMMENDATION + && typeof finding.subjectRef === "string" + && finding.subjectRef.startsWith("docker_container_") + && finding.targetRef === "host_risk_docker_daemon_state" + && finding.id === daemonStatePublishedPortFindingId(finding.subjectRef) + && Array.isArray(finding.evidenceRefs) + && finding.evidenceRefs.length === 2 + && (() => { + const [daemonState, port] = finding.evidenceRefs; + if (!daemonState || typeof daemonState !== "object" || !port || typeof port !== "object") return false; + const daemonEvidence = daemonState as Record; + const portEvidence = port as Record; + const isFreshDockerEvidence = (evidence: Record, kind: string, summary: string) => evidence.version === 1 + && evidence.provider === "docker" + && evidence.kind === kind + && evidence.assertionKind === "observed" + && evidence.summary === summary + && evidence.subjectRef === finding.subjectRef + && (evidence.providerSlot === undefined || evidence.providerSlot === null) + && evidence.freshness === "fresh" + && typeof evidence.providerRevision === "string" + && evidence.providerRevision !== String(evidence.collectedAt); + return isFreshDockerEvidence(daemonEvidence, "docker_daemon_state_bind_mount", "Docker reported a bind mount exposing Docker daemon state") + && isFreshDockerEvidence(portEvidence, "docker_port_publication", "Docker reported container port publication") + && daemonEvidence.collectedAt === portEvidence.collectedAt + && daemonEvidence.providerRevision === portEvidence.providerRevision; + })(); if (finding.ruleId === COMPOSE_DECLARED_TARGET_NOT_ACTIVE_FINDING_RULE) return finding.severity === "advisory" && finding.summary === COMPOSE_DECLARED_TARGET_NOT_ACTIVE_FINDING_SUMMARY && finding.recommendation === COMPOSE_DECLARED_TARGET_NOT_ACTIVE_FINDING_RECOMMENDATION diff --git a/apps/api/test/security.test.ts b/apps/api/test/security.test.ts index cad9ae3a..21f67546 100644 --- a/apps/api/test/security.test.ts +++ b/apps/api/test/security.test.ts @@ -1049,12 +1049,20 @@ test("daemon model responses require non-empty revision and complete provider st ["/daemon/findings", (() => { const value = structuredClone(findings); value.findings[2].targetRef = "host_risk_untrusted"; return value; })()], ["/daemon/findings", (() => { const value = structuredClone(findings); value.findings[1].evidenceRefs[1].kind = "docker_volume_mount"; return value; })()], ["/daemon/findings", (() => { const value = structuredClone(findings); value.findings[1].evidenceRefs[0].providerRevision = String(value.findings[1].evidenceRefs[0].collectedAt); return value; })()], - ["/daemon/findings", (() => { const value = structuredClone(findings); value.findings[3].summary = "DOCKERMAP_TEST_FORGED_COMPOSE_FINDING"; return value; })()], - ["/daemon/findings", (() => { const value = structuredClone(findings); value.findings[3].id = "finding_docker_compose_declared_target_not_active_forged"; return value; })()], - ["/daemon/findings", (() => { const value = structuredClone(findings); value.findings[3].evidenceRefs[0].freshness = "stale"; return value; })()], - ["/daemon/findings", (() => { const value = structuredClone(findings); value.findings[3].evidenceRefs[0].kind = "docker_network_membership"; return value; })()], - ["/daemon/findings", (() => { const value = structuredClone(findings); value.findings[3].evidenceRefs[0].providerSlot = "project_npm"; return value; })()], - ["/daemon/findings", (() => { const value = structuredClone(findings); value.findings[3].unsafe = "DOCKERMAP_TEST_EXTRA"; return value; })()] + ["/daemon/findings", (() => { const value = structuredClone(findings); [value.findings[3].evidenceRefs[0], value.findings[3].evidenceRefs[1]] = [value.findings[3].evidenceRefs[1], value.findings[3].evidenceRefs[0]]; return value; })()], + ["/daemon/findings", (() => { const value = structuredClone(findings); value.findings[3].evidenceRefs[1].collectedAt += 1; return value; })()], + ["/daemon/findings", (() => { const value = structuredClone(findings); value.findings[3].evidenceRefs[1].providerRevision = "mismatched-revision"; return value; })()], + ["/daemon/findings", (() => { const value = structuredClone(findings); value.findings[3].evidenceRefs[1].freshness = "stale"; return value; })()], + ["/daemon/findings", (() => { const value = structuredClone(findings); value.findings[3].targetRef = "host_risk_untrusted"; return value; })()], + ["/daemon/findings", (() => { const value = structuredClone(findings); value.findings[3].id = "finding_docker_daemon_state_bind_mount_publishes_port_forged"; return value; })()], + ["/daemon/findings", (() => { const value = structuredClone(findings); value.findings[3].evidenceRefs[1].summary = "DOCKERMAP_TEST_FORGED_PORT"; return value; })()], + ["/daemon/findings", (() => { const value = structuredClone(findings); value.findings[3].unsafe = "DOCKERMAP_TEST_EXTRA"; return value; })()], + ["/daemon/findings", (() => { const value = structuredClone(findings); value.findings[4].summary = "DOCKERMAP_TEST_FORGED_COMPOSE_FINDING"; return value; })()], + ["/daemon/findings", (() => { const value = structuredClone(findings); value.findings[4].id = "finding_docker_compose_declared_target_not_active_forged"; return value; })()], + ["/daemon/findings", (() => { const value = structuredClone(findings); value.findings[4].evidenceRefs[0].freshness = "stale"; return value; })()], + ["/daemon/findings", (() => { const value = structuredClone(findings); value.findings[4].evidenceRefs[0].kind = "docker_network_membership"; return value; })()], + ["/daemon/findings", (() => { const value = structuredClone(findings); value.findings[4].evidenceRefs[0].providerSlot = "project_npm"; return value; })()], + ["/daemon/findings", (() => { const value = structuredClone(findings); value.findings[4].unsafe = "DOCKERMAP_TEST_EXTRA"; return value; })()] ] as const; for (const [daemonPath, body] of invalidResponses) { const daemon = await startStubDaemon((req, res) => { @@ -1271,6 +1279,19 @@ test("runtime evidence is required and fails closed before browser publication", "Docker daemon-state evidence has one canonical synthetic target" ); + const hostPort = structuredClone(fixture); + const hostPortEdge = hostPort.edges.find((edge: { evidenceRefs?: Array<{ kind?: unknown }> }) => + edge.evidenceRefs?.[0]?.kind === "docker_port_publication" + ); + assert.ok(hostPortEdge, "canonical daemon fixture carries a Docker host-port publication"); + const hostPortNode = hostPort.nodes.find((node: { id?: unknown }) => node.id === hostPortEdge.target); + assert.ok(hostPortNode, "host-port publication targets a listener node"); + hostPortNode.metadata.port = "8080/tcp"; + assert.throws( + () => validateDaemonResponse("/daemon/runtime/map", hostPort), + "Docker port-publication evidence requires host:private/protocol listener metadata" + ); + const npmEdge = fixture.edges.find((edge: { source?: unknown }) => edge.source === "npm_project_dockermap"); assert.ok(npmEdge, "canonical daemon fixture carries a V3 NPM manifest declaration"); assert.doesNotThrow(() => validateDaemonResponse("/daemon/runtime/map", fixture)); diff --git a/tests/fixtures/contracts/findings-response.json b/tests/fixtures/contracts/findings-response.json index db3f7007..97ae54ff 100644 --- a/tests/fixtures/contracts/findings-response.json +++ b/tests/fixtures/contracts/findings-response.json @@ -53,7 +53,7 @@ "provider": "docker", "kind": "docker_port_publication", "assertionKind": "observed", - "summary": "Docker reported a published container port", + "summary": "Docker reported container port publication", "subjectRef": "docker_container_api", "collectedAt": 1710000000000, "providerRevision": "fixture-docker-observation", @@ -86,6 +86,43 @@ } ] }, + { + "id": "finding_docker_daemon_state_bind_mount_publishes_port_docker_container_api-host_risk_docker_daemon_sta--7c39cf98a47c28348c58f8e56ae21c68de6104082cc126499d18cb0eecaa0bcc", + "ruleId": "docker.daemon_state_bind_mount_publishes_port", + "severity": "warning", + "summary": "A container with Docker daemon state access also has a published host port.", + "recommendation": "Review whether the daemon-state access and host-port publication are both intended.", + "subjectRef": "docker_container_api", + "targetRef": "host_risk_docker_daemon_state", + "evidenceRefs": [ + { + "version": 1, + "id": "docker_daemon_state_bind_mount:docker_container_api:host_risk_docker_daemon_state", + "provider": "docker", + "kind": "docker_daemon_state_bind_mount", + "assertionKind": "observed", + "summary": "Docker reported a bind mount exposing Docker daemon state", + "subjectRef": "docker_container_api", + "collectedAt": 1710000000000, + "providerRevision": "fixture-docker-observation", + "providerSlot": null, + "freshness": "fresh" + }, + { + "version": 1, + "id": "docker_port_publication:docker_container_api:network_listener_api_8080", + "provider": "docker", + "kind": "docker_port_publication", + "assertionKind": "observed", + "summary": "Docker reported container port publication", + "subjectRef": "docker_container_api", + "collectedAt": 1710000000000, + "providerRevision": "fixture-docker-observation", + "providerSlot": null, + "freshness": "fresh" + } + ] + }, { "id": "finding_docker_compose_declared_target_not_active_docker_container_compose_source-docker_container--bf87ab2e9b1b8b7f49304dbc1fa040e0bf4c561c8585b686be888110fb646715", "ruleId": "docker.compose_declared_target_not_active", From 9aa811ca1d9ea511f002072edbef76229a92dc26 Mon Sep 17 00:00:00 2001 From: Jonathan <64296013+Joncallim@users.noreply.github.com> Date: Wed, 2 Sep 2026 23:45:17 +0800 Subject: [PATCH 2/7] fix: scope runtime listener ambiguity validation --- apps/api/src/daemonResponseValidation.ts | 9 ++++++--- apps/api/test/security.test.ts | 19 +++++++++++++++++++ 2 files changed, 25 insertions(+), 3 deletions(-) diff --git a/apps/api/src/daemonResponseValidation.ts b/apps/api/src/daemonResponseValidation.ts index 8303f6b7..22107d93 100644 --- a/apps/api/src/daemonResponseValidation.ts +++ b/apps/api/src/daemonResponseValidation.ts @@ -221,12 +221,15 @@ function hasCoherentRuntimeEvidence(payload: unknown): boolean { const nodes = (payload as { nodes?: unknown }).nodes; const edges = (payload as { edges?: unknown }).edges; if (!Array.isArray(nodes) || !Array.isArray(edges)) return false; - const nodesById = new Map>(); + // Only a V1 port-publication edge needs a node lookup. Keep ambiguity local + // to that referenced listener rather than treating unrelated duplicate + // provider nodes as an API contract violation. + const nodesById = new Map | null>(); for (const candidate of nodes) { if (!candidate || typeof candidate !== "object") return false; const node = candidate as Record; - if (typeof node.id !== "string" || nodesById.has(node.id)) return false; - nodesById.set(node.id, node); + if (typeof node.id !== "string") return false; + nodesById.set(node.id, nodesById.has(node.id) ? null : node); } return edges.every((edge) => { if (!edge || typeof edge !== "object") return false; diff --git a/apps/api/test/security.test.ts b/apps/api/test/security.test.ts index 21f67546..21c82a76 100644 --- a/apps/api/test/security.test.ts +++ b/apps/api/test/security.test.ts @@ -1292,6 +1292,25 @@ test("runtime evidence is required and fails closed before browser publication", "Docker port-publication evidence requires host:private/protocol listener metadata" ); + const unrelatedDuplicate = structuredClone(fixture); + unrelatedDuplicate.nodes.push(structuredClone(unrelatedDuplicate.nodes[0])); + assert.doesNotThrow( + () => validateDaemonResponse("/daemon/runtime/map", unrelatedDuplicate), + "a port-evidence lookup must not turn unrelated provider-node duplication into a publication failure" + ); + const duplicateListener = structuredClone(fixture); + const duplicateListenerEdge = duplicateListener.edges.find((edge: { evidenceRefs?: Array<{ kind?: unknown }> }) => + edge.evidenceRefs?.[0]?.kind === "docker_port_publication" + ); + assert.ok(duplicateListenerEdge, "canonical daemon fixture carries a Docker host-port publication"); + const listener = duplicateListener.nodes.find((node: { id?: unknown }) => node.id === duplicateListenerEdge.target); + assert.ok(listener, "host-port publication targets a listener node"); + duplicateListener.nodes.push(structuredClone(listener)); + assert.throws( + () => validateDaemonResponse("/daemon/runtime/map", duplicateListener), + "Docker port-publication evidence must fail closed when its listener identity is ambiguous" + ); + const npmEdge = fixture.edges.find((edge: { source?: unknown }) => edge.source === "npm_project_dockermap"); assert.ok(npmEdge, "canonical daemon fixture carries a V3 NPM manifest declaration"); assert.doesNotThrow(() => validateDaemonResponse("/daemon/runtime/map", fixture)); From fe00cc28804495c7457834fe7d3cb0ec6dbe7908 Mon Sep 17 00:00:00 2001 From: Jonathan <64296013+Joncallim@users.noreply.github.com> Date: Wed, 2 Sep 2026 23:58:53 +0800 Subject: [PATCH 3/7] test: classify production runtime validation failures --- apps/api/src/daemonClient.ts | 4 ++++ apps/api/src/daemonResponseValidation.ts | 12 +++++++----- tests/e2e/dockermap.spec.ts | 10 ++++++++++ tests/e2e/dockermapHarness.ts | 11 +++++++++++ 4 files changed, 32 insertions(+), 5 deletions(-) diff --git a/apps/api/src/daemonClient.ts b/apps/api/src/daemonClient.ts index 09d3eb4d..c2097a1e 100644 --- a/apps/api/src/daemonClient.ts +++ b/apps/api/src/daemonClient.ts @@ -61,6 +61,10 @@ export function createDaemonClient(options: DaemonClientOptions) { // failure, not an availability event. Never substitute mock data for // authentication or authorization denial. if (error instanceof DaemonResponseValidationError) { + // This is an internal, closed diagnostic label. It is intentionally + // not added to the browser error: a daemon response must never choose + // its own validation detail or use it as an exfiltration channel. + console.error(`[DockerMap] daemon response validation rejected ${path}: ${error.reason}`); throw new HttpError(502, { code: "daemon_invalid_response", message: "Daemon response did not match its declared contract" diff --git a/apps/api/src/daemonResponseValidation.ts b/apps/api/src/daemonResponseValidation.ts index 22107d93..edcae762 100644 --- a/apps/api/src/daemonResponseValidation.ts +++ b/apps/api/src/daemonResponseValidation.ts @@ -460,7 +460,7 @@ export function daemonResponseSchemaId(path: string): RustResponseSchemaId | und /** A daemon response is syntactically JSON but violates its Rust-owned model. */ export class DaemonResponseValidationError extends Error { - constructor() { + constructor(readonly reason: "schema" | "provider_state_vector" | "provider_freshness" | "runtime_evidence" | "findings") { // Keep the public error deliberately independent of schema paths/errors: // a compromised daemon must not use validator output as an exfiltration channel. super("Daemon response did not match its declared contract"); @@ -475,10 +475,12 @@ export class DaemonResponseValidationError extends Error { export function validateDaemonResponse(path: string, payload: unknown) { const schema = daemonResponseSchemaId(path); const validator = schema && validators.get(schema); - if (!validator || !validator(payload) - || (schema === "RuntimeMap" && (!hasCompleteProviderStateVector(payload) || !hasCoherentProviderFreshness(payload) || !hasCoherentRuntimeEvidence(payload))) - || (schema === "FindingsResponse" && !hasCoherentFindings(payload))) { - throw new DaemonResponseValidationError(); + if (!validator || !validator(payload)) throw new DaemonResponseValidationError("schema"); + if (schema === "RuntimeMap") { + if (!hasCompleteProviderStateVector(payload)) throw new DaemonResponseValidationError("provider_state_vector"); + if (!hasCoherentProviderFreshness(payload)) throw new DaemonResponseValidationError("provider_freshness"); + if (!hasCoherentRuntimeEvidence(payload)) throw new DaemonResponseValidationError("runtime_evidence"); } + if (schema === "FindingsResponse" && !hasCoherentFindings(payload)) throw new DaemonResponseValidationError("findings"); return payload; } diff --git a/tests/e2e/dockermap.spec.ts b/tests/e2e/dockermap.spec.ts index 9a4ecf55..5ba92453 100644 --- a/tests/e2e/dockermap.spec.ts +++ b/tests/e2e/dockermap.spec.ts @@ -757,6 +757,16 @@ test.describe("DockerMap GUI", () => { })).json(); expect(snapshot.containers.map((container: { name: string }) => container.name).sort()).toEqual(expectedNames); + const runtimeResponse = await request.get(`${stack.apiUrl}/api/runtime/map`, { + headers: { Authorization: "Bearer dockermap-production-e2e-token" } + }); + if (!runtimeResponse.ok()) { + // The harness reports only a closed API-side validation label from the + // production container, never raw daemon bytes or general container logs. + console.info(`production runtime-map validation diagnostics: ${stack.productionValidationDiagnostics?.().join(",") ?? "unavailable"}`); + } + expect(runtimeResponse.status()).toBe(200); + await page.goto(stack.webUrl); await page.getByRole("textbox", { name: "API token" }).fill("dockermap-production-e2e-token"); await page.getByRole("button", { name: "Connect" }).click(); diff --git a/tests/e2e/dockermapHarness.ts b/tests/e2e/dockermapHarness.ts index fe9e4595..253e4d3b 100644 --- a/tests/e2e/dockermapHarness.ts +++ b/tests/e2e/dockermapHarness.ts @@ -14,6 +14,11 @@ export type Stack = { projectName: string | null; controlContainerName: string | null; productionSocketReadOnly?: boolean; + /** + * Closed API-validation labels collected from the production container for + * E2E failure diagnosis. Never returns daemon payloads or generic logs. + */ + productionValidationDiagnostics?: () => string[]; postProductionSessionBurst?: (client: "a" | "b", spoofedXForwardedForPrefix: string) => { elapsedMs: number; responses: Array<{ status: number; body: string }>; @@ -159,6 +164,12 @@ export async function startProductionImageStack(options: { liveDocker?: boolean projectName: fixture?.projectName ?? null, controlContainerName: fixture?.controlContainerName ?? null, productionSocketReadOnly: fixture ? productionSocketIsReadOnly(docker, container) : undefined, + productionValidationDiagnostics: () => dockerOutput(docker, ["logs", "--tail", "100", container], repoRoot) + .split("\n") + .flatMap((line) => { + const match = line.match(/\[DockerMap\] daemon response validation rejected \/daemon\/runtime\/map: (schema|provider_state_vector|provider_freshness|runtime_evidence|findings)/); + return match ? [match[1]] : []; + }), postProductionSessionBurst: (client, spoofedXForwardedForPrefix) => { const started = Date.now(); const output = dockerOutput( From 20939e1cb31dbb2244af8ec53575cbfe6a12fab4 Mon Sep 17 00:00:00 2001 From: Jonathan <64296013+Joncallim@users.noreply.github.com> Date: Thu, 3 Sep 2026 00:05:17 +0800 Subject: [PATCH 4/7] test: read production validation stderr diagnostics --- tests/e2e/dockermapHarness.ts | 12 +++++++++++- 1 file changed, 11 insertions(+), 1 deletion(-) diff --git a/tests/e2e/dockermapHarness.ts b/tests/e2e/dockermapHarness.ts index 253e4d3b..6e8b08f9 100644 --- a/tests/e2e/dockermapHarness.ts +++ b/tests/e2e/dockermapHarness.ts @@ -164,7 +164,7 @@ export async function startProductionImageStack(options: { liveDocker?: boolean projectName: fixture?.projectName ?? null, controlContainerName: fixture?.controlContainerName ?? null, productionSocketReadOnly: fixture ? productionSocketIsReadOnly(docker, container) : undefined, - productionValidationDiagnostics: () => dockerOutput(docker, ["logs", "--tail", "100", container], repoRoot) + productionValidationDiagnostics: () => dockerOutputIncludingStderr(docker, ["logs", "--tail", "100", container], repoRoot) .split("\n") .flatMap((line) => { const match = line.match(/\[DockerMap\] daemon response validation rejected \/daemon\/runtime\/map: (schema|provider_state_vector|provider_freshness|runtime_evidence|findings)/); @@ -900,6 +900,16 @@ function dockerOutput(docker: string[], args: string[], cwd: string) { return result.stdout; } +// `docker logs` preserves container stdout and stderr on the corresponding +// client streams. The production API deliberately writes validation labels to +// stderr, so the test-only diagnostic reader must combine both. Its caller +// still filters the result to a closed allowlist before reporting anything. +function dockerOutputIncludingStderr(docker: string[], args: string[], cwd: string) { + const result = spawnSync(docker[0], [...docker.slice(1), ...args], { cwd, encoding: "utf8", timeout: 120_000 }); + if (result.status !== 0) throw new Error(`Docker command failed: ${docker.join(" ")} ${args.join(" ")}\n${result.stderr}`); + return `${result.stdout}${result.stderr}`; +} + /** Select an unused /24 from a dedicated private range for an ephemeral E2E network. */ function unusedFixtureSubnet(docker: string[]): string { return unusedFixtureSubnets(docker, 1)[0]; From 08bb50da299029029a0fa9b737264311cf2ec5c1 Mon Sep 17 00:00:00 2001 From: Jonathan <64296013+Joncallim@users.noreply.github.com> Date: Thu, 3 Sep 2026 00:15:01 +0800 Subject: [PATCH 5/7] test: classify runtime evidence validation failures --- apps/api/src/daemonResponseValidation.ts | 65 +++++++++++++++--------- tests/e2e/dockermapHarness.ts | 2 +- 2 files changed, 42 insertions(+), 25 deletions(-) diff --git a/apps/api/src/daemonResponseValidation.ts b/apps/api/src/daemonResponseValidation.ts index edcae762..0f8d23be 100644 --- a/apps/api/src/daemonResponseValidation.ts +++ b/apps/api/src/daemonResponseValidation.ts @@ -216,27 +216,41 @@ function hasCoherentProviderFreshness(payload: unknown): boolean { }); } -function hasCoherentRuntimeEvidence(payload: unknown): boolean { - if (!payload || typeof payload !== "object") return false; +type RuntimeEvidenceDiagnostic = + | "runtime_evidence_edge_shape" + | "runtime_evidence_base_tuple" + | "runtime_evidence_edge_binding" + | "runtime_evidence_source_binding" + | "runtime_evidence_daemon_state_target" + | "runtime_evidence_port_listener_missing" + | "runtime_evidence_port_listener_ambiguous" + | "runtime_evidence_port_listener_shape" + | "runtime_evidence_port_listener_grammar" + | "runtime_evidence_revision"; + +// This returns only a fixed category for container-internal E2E diagnostics. +// It must never contain daemon-supplied text, IDs, paths, or metadata values. +function runtimeEvidenceDiagnostic(payload: unknown): RuntimeEvidenceDiagnostic | null { + if (!payload || typeof payload !== "object") return "runtime_evidence_edge_shape"; const nodes = (payload as { nodes?: unknown }).nodes; const edges = (payload as { edges?: unknown }).edges; - if (!Array.isArray(nodes) || !Array.isArray(edges)) return false; + if (!Array.isArray(nodes) || !Array.isArray(edges)) return "runtime_evidence_edge_shape"; // Only a V1 port-publication edge needs a node lookup. Keep ambiguity local // to that referenced listener rather than treating unrelated duplicate // provider nodes as an API contract violation. const nodesById = new Map | null>(); for (const candidate of nodes) { - if (!candidate || typeof candidate !== "object") return false; + if (!candidate || typeof candidate !== "object") return "runtime_evidence_edge_shape"; const node = candidate as Record; - if (typeof node.id !== "string") return false; + if (typeof node.id !== "string") return "runtime_evidence_edge_shape"; nodesById.set(node.id, nodesById.has(node.id) ? null : node); } - return edges.every((edge) => { - if (!edge || typeof edge !== "object") return false; + for (const edge of edges) { + if (!edge || typeof edge !== "object") return "runtime_evidence_edge_shape"; const candidate = edge as { source?: unknown; target?: unknown; relationship?: unknown; evidenceRefs?: unknown }; - if (!Array.isArray(candidate.evidenceRefs)) return false; - return candidate.evidenceRefs.every((evidence) => { - if (!evidence || typeof evidence !== "object") return false; + if (!Array.isArray(candidate.evidenceRefs)) return "runtime_evidence_edge_shape"; + for (const evidence of candidate.evidenceRefs) { + if (!evidence || typeof evidence !== "object") return "runtime_evidence_edge_shape"; const value = evidence as { version?: unknown; provider?: unknown; kind?: unknown; assertionKind?: unknown; freshness?: unknown; providerRevision?: unknown; collectedAt?: unknown; subjectRef?: unknown; @@ -262,7 +276,7 @@ function hasCoherentRuntimeEvidence(payload: unknown): boolean { && value.assertionKind === "declared" && value.providerSlot === "cron" && (value.freshness === "fresh" || value.freshness === "stale" || value.freshness === "timed_out"); - if (!isV1 && !isV2 && !isV3 && !isV4) return false; + if (!isV1 && !isV2 && !isV3 && !isV4) return "runtime_evidence_base_tuple"; const expected = typeof value.kind === "string" ? (isV1 ? V1_EVIDENCE_EDGE[value.kind as keyof typeof V1_EVIDENCE_EDGE] @@ -272,22 +286,24 @@ function hasCoherentRuntimeEvidence(payload: unknown): boolean { ? V3_EVIDENCE_EDGE[value.kind as keyof typeof V3_EVIDENCE_EDGE] : V4_EVIDENCE_EDGE[value.kind as keyof typeof V4_EVIDENCE_EDGE]) : undefined; - if (!expected || candidate.relationship !== expected.relationship || typeof candidate.source !== "string" || typeof candidate.target !== "string") return false; - if (value.subjectRef !== candidate.source || !candidate.source.startsWith(expected.sourcePrefix) || !candidate.target.startsWith(expected.targetPrefix)) return false; - if (isV4 && candidate.target !== "host_local") return false; - if (value.kind === "docker_daemon_state_bind_mount" && candidate.target !== "host_risk_docker_daemon_state") return false; + if (!expected || candidate.relationship !== expected.relationship || typeof candidate.source !== "string" || typeof candidate.target !== "string") return "runtime_evidence_edge_binding"; + if (value.subjectRef !== candidate.source || !candidate.source.startsWith(expected.sourcePrefix) || !candidate.target.startsWith(expected.targetPrefix) || candidate.source === candidate.target) return "runtime_evidence_source_binding"; + if (isV4 && candidate.target !== "host_local") return "runtime_evidence_edge_binding"; + if (value.kind === "docker_daemon_state_bind_mount" && candidate.target !== "host_risk_docker_daemon_state") return "runtime_evidence_daemon_state_target"; if (value.kind === "docker_port_publication") { const listener = nodesById.get(candidate.target); - if (listener?.provider !== "network" || listener.type !== "network_listener" - || !listener.metadata || typeof listener.metadata !== "object" - || !isHostPublishedDockerPort((listener.metadata as Record).port)) return false; + if (listener === undefined) return "runtime_evidence_port_listener_missing"; + if (listener === null) return "runtime_evidence_port_listener_ambiguous"; + if (listener.provider !== "network" || listener.type !== "network_listener" + || !listener.metadata || typeof listener.metadata !== "object") return "runtime_evidence_port_listener_shape"; + if (!isHostPublishedDockerPort((listener.metadata as Record).port)) return "runtime_evidence_port_listener_grammar"; } - if (candidate.source === candidate.target) return false; // An opaque observation token must never be the collection timestamp // re-labelled as a revision. The daemon produces it independently. - return typeof value.providerRevision === "string" && value.providerRevision !== String(value.collectedAt); - }); - }); + if (typeof value.providerRevision !== "string" || value.providerRevision === String(value.collectedAt)) return "runtime_evidence_revision"; + } + } + return null; } // Findings are a deliberately tiny conclusion vocabulary, not a daemon-supplied @@ -460,7 +476,7 @@ export function daemonResponseSchemaId(path: string): RustResponseSchemaId | und /** A daemon response is syntactically JSON but violates its Rust-owned model. */ export class DaemonResponseValidationError extends Error { - constructor(readonly reason: "schema" | "provider_state_vector" | "provider_freshness" | "runtime_evidence" | "findings") { + constructor(readonly reason: "schema" | "provider_state_vector" | "provider_freshness" | RuntimeEvidenceDiagnostic | "findings") { // Keep the public error deliberately independent of schema paths/errors: // a compromised daemon must not use validator output as an exfiltration channel. super("Daemon response did not match its declared contract"); @@ -479,7 +495,8 @@ export function validateDaemonResponse(path: string, payload: unknown) { if (schema === "RuntimeMap") { if (!hasCompleteProviderStateVector(payload)) throw new DaemonResponseValidationError("provider_state_vector"); if (!hasCoherentProviderFreshness(payload)) throw new DaemonResponseValidationError("provider_freshness"); - if (!hasCoherentRuntimeEvidence(payload)) throw new DaemonResponseValidationError("runtime_evidence"); + const evidenceDiagnostic = runtimeEvidenceDiagnostic(payload); + if (evidenceDiagnostic) throw new DaemonResponseValidationError(evidenceDiagnostic); } if (schema === "FindingsResponse" && !hasCoherentFindings(payload)) throw new DaemonResponseValidationError("findings"); return payload; diff --git a/tests/e2e/dockermapHarness.ts b/tests/e2e/dockermapHarness.ts index 6e8b08f9..910c1ae0 100644 --- a/tests/e2e/dockermapHarness.ts +++ b/tests/e2e/dockermapHarness.ts @@ -167,7 +167,7 @@ export async function startProductionImageStack(options: { liveDocker?: boolean productionValidationDiagnostics: () => dockerOutputIncludingStderr(docker, ["logs", "--tail", "100", container], repoRoot) .split("\n") .flatMap((line) => { - const match = line.match(/\[DockerMap\] daemon response validation rejected \/daemon\/runtime\/map: (schema|provider_state_vector|provider_freshness|runtime_evidence|findings)/); + const match = line.match(/\[DockerMap\] daemon response validation rejected \/daemon\/runtime\/map: (schema|provider_state_vector|provider_freshness|runtime_evidence_(?:edge_shape|base_tuple|edge_binding|source_binding|daemon_state_target|port_listener_missing|port_listener_ambiguous|port_listener_shape|port_listener_grammar|revision)|findings)/); return match ? [match[1]] : []; }), postProductionSessionBurst: (client, spoofedXForwardedForPrefix) => { From db8948c8f39d50132b682ade5d2f7daa239af36a Mon Sep 17 00:00:00 2001 From: Jonathan <64296013+Joncallim@users.noreply.github.com> Date: Thu, 3 Sep 2026 00:22:00 +0800 Subject: [PATCH 6/7] fix: accept equivalent duplicate Docker listeners --- apps/api/src/daemonResponseValidation.ts | 33 +++++++++++++++++++----- apps/api/test/security.test.ts | 12 +++++++-- 2 files changed, 37 insertions(+), 8 deletions(-) diff --git a/apps/api/src/daemonResponseValidation.ts b/apps/api/src/daemonResponseValidation.ts index 0f8d23be..bb4e0a2f 100644 --- a/apps/api/src/daemonResponseValidation.ts +++ b/apps/api/src/daemonResponseValidation.ts @@ -238,12 +238,14 @@ function runtimeEvidenceDiagnostic(payload: unknown): RuntimeEvidenceDiagnostic // Only a V1 port-publication edge needs a node lookup. Keep ambiguity local // to that referenced listener rather than treating unrelated duplicate // provider nodes as an API contract violation. - const nodesById = new Map | null>(); + const nodesById = new Map[]>(); for (const candidate of nodes) { if (!candidate || typeof candidate !== "object") return "runtime_evidence_edge_shape"; const node = candidate as Record; if (typeof node.id !== "string") return "runtime_evidence_edge_shape"; - nodesById.set(node.id, nodesById.has(node.id) ? null : node); + const sameId = nodesById.get(node.id); + if (sameId) sameId.push(node); + else nodesById.set(node.id, [node]); } for (const edge of edges) { if (!edge || typeof edge !== "object") return "runtime_evidence_edge_shape"; @@ -291,12 +293,31 @@ function runtimeEvidenceDiagnostic(payload: unknown): RuntimeEvidenceDiagnostic if (isV4 && candidate.target !== "host_local") return "runtime_evidence_edge_binding"; if (value.kind === "docker_daemon_state_bind_mount" && candidate.target !== "host_risk_docker_daemon_state") return "runtime_evidence_daemon_state_target"; if (value.kind === "docker_port_publication") { - const listener = nodesById.get(candidate.target); - if (listener === undefined) return "runtime_evidence_port_listener_missing"; - if (listener === null) return "runtime_evidence_port_listener_ambiguous"; + const listeners = nodesById.get(candidate.target); + if (!listeners) return "runtime_evidence_port_listener_missing"; + const listener = listeners[0]; if (listener.provider !== "network" || listener.type !== "network_listener" || !listener.metadata || typeof listener.metadata !== "object") return "runtime_evidence_port_listener_shape"; - if (!isHostPublishedDockerPort((listener.metadata as Record).port)) return "runtime_evidence_port_listener_grammar"; + const metadata = listener.metadata as Record; + const port = metadata.port; + if (!isHostPublishedDockerPort(port)) return "runtime_evidence_port_listener_grammar"; + // Duplicate provider records are tolerable only when they carry the + // exact same closed listener fact. Do not silently choose one record + // when a duplicate differs in listener identity, state, or metadata. + if (!listeners.every((candidateListener) => { + const candidateMetadata = candidateListener.metadata; + return candidateListener.provider === "network" + && candidateListener.type === "network_listener" + && candidateListener.label === listener.label + && candidateListener.status === listener.status + && candidateListener.layer === listener.layer + && (candidateListener.service === null || candidateListener.service === undefined) + && (candidateListener.package === null || candidateListener.package === undefined) + && candidateMetadata && typeof candidateMetadata === "object" + && Object.keys(candidateMetadata).length === 1 + && (candidateMetadata as Record).port === port + && isHostPublishedDockerPort((candidateMetadata as Record).port); + })) return "runtime_evidence_port_listener_ambiguous"; } // An opaque observation token must never be the collection timestamp // re-labelled as a revision. The daemon produces it independently. diff --git a/apps/api/test/security.test.ts b/apps/api/test/security.test.ts index 21c82a76..22ddc3bf 100644 --- a/apps/api/test/security.test.ts +++ b/apps/api/test/security.test.ts @@ -1306,9 +1306,17 @@ test("runtime evidence is required and fails closed before browser publication", const listener = duplicateListener.nodes.find((node: { id?: unknown }) => node.id === duplicateListenerEdge.target); assert.ok(listener, "host-port publication targets a listener node"); duplicateListener.nodes.push(structuredClone(listener)); - assert.throws( + assert.doesNotThrow( () => validateDaemonResponse("/daemon/runtime/map", duplicateListener), - "Docker port-publication evidence must fail closed when its listener identity is ambiguous" + "duplicate Docker listener records may support one publication only when their closed listener facts are identical" + ); + + const conflictingListener = structuredClone(duplicateListener); + const duplicate = conflictingListener.nodes[conflictingListener.nodes.length - 1]; + duplicate.metadata.port = "8081:8080/tcp"; + assert.throws( + () => validateDaemonResponse("/daemon/runtime/map", conflictingListener), + "Docker port-publication evidence must fail closed when duplicate listener records disagree" ); const npmEdge = fixture.edges.find((edge: { source?: unknown }) => edge.source === "npm_project_dockermap"); From 125c64256c2431d4f2344e111a07ebd3eb78d220 Mon Sep 17 00:00:00 2001 From: Jonathan <64296013+Joncallim@users.noreply.github.com> Date: Thu, 3 Sep 2026 00:27:45 +0800 Subject: [PATCH 7/7] fix: redact daemon validation route logs --- apps/api/src/daemonClient.ts | 2 +- apps/api/src/daemonResponseValidation.ts | 15 ++++++----- apps/api/test/security.test.ts | 33 ++++++++++++++++++++++++ tests/e2e/dockermapHarness.ts | 2 +- 4 files changed, 44 insertions(+), 8 deletions(-) diff --git a/apps/api/src/daemonClient.ts b/apps/api/src/daemonClient.ts index c2097a1e..d5ac1813 100644 --- a/apps/api/src/daemonClient.ts +++ b/apps/api/src/daemonClient.ts @@ -64,7 +64,7 @@ export function createDaemonClient(options: DaemonClientOptions) { // This is an internal, closed diagnostic label. It is intentionally // not added to the browser error: a daemon response must never choose // its own validation detail or use it as an exfiltration channel. - console.error(`[DockerMap] daemon response validation rejected ${path}: ${error.reason}`); + console.error(`[DockerMap] daemon response validation rejected schema=${error.schema} reason=${error.reason}`); throw new HttpError(502, { code: "daemon_invalid_response", message: "Daemon response did not match its declared contract" diff --git a/apps/api/src/daemonResponseValidation.ts b/apps/api/src/daemonResponseValidation.ts index bb4e0a2f..d67c0209 100644 --- a/apps/api/src/daemonResponseValidation.ts +++ b/apps/api/src/daemonResponseValidation.ts @@ -497,7 +497,10 @@ export function daemonResponseSchemaId(path: string): RustResponseSchemaId | und /** A daemon response is syntactically JSON but violates its Rust-owned model. */ export class DaemonResponseValidationError extends Error { - constructor(readonly reason: "schema" | "provider_state_vector" | "provider_freshness" | RuntimeEvidenceDiagnostic | "findings") { + constructor( + readonly schema: RustResponseSchemaId | "unknown", + readonly reason: "schema" | "provider_state_vector" | "provider_freshness" | RuntimeEvidenceDiagnostic | "findings", + ) { // Keep the public error deliberately independent of schema paths/errors: // a compromised daemon must not use validator output as an exfiltration channel. super("Daemon response did not match its declared contract"); @@ -512,13 +515,13 @@ export class DaemonResponseValidationError extends Error { export function validateDaemonResponse(path: string, payload: unknown) { const schema = daemonResponseSchemaId(path); const validator = schema && validators.get(schema); - if (!validator || !validator(payload)) throw new DaemonResponseValidationError("schema"); + if (!validator || !validator(payload)) throw new DaemonResponseValidationError(schema ?? "unknown", "schema"); if (schema === "RuntimeMap") { - if (!hasCompleteProviderStateVector(payload)) throw new DaemonResponseValidationError("provider_state_vector"); - if (!hasCoherentProviderFreshness(payload)) throw new DaemonResponseValidationError("provider_freshness"); + if (!hasCompleteProviderStateVector(payload)) throw new DaemonResponseValidationError(schema, "provider_state_vector"); + if (!hasCoherentProviderFreshness(payload)) throw new DaemonResponseValidationError(schema, "provider_freshness"); const evidenceDiagnostic = runtimeEvidenceDiagnostic(payload); - if (evidenceDiagnostic) throw new DaemonResponseValidationError(evidenceDiagnostic); + if (evidenceDiagnostic) throw new DaemonResponseValidationError(schema, evidenceDiagnostic); } - if (schema === "FindingsResponse" && !hasCoherentFindings(payload)) throw new DaemonResponseValidationError("findings"); + if (schema === "FindingsResponse" && !hasCoherentFindings(payload)) throw new DaemonResponseValidationError(schema, "findings"); return payload; } diff --git a/apps/api/test/security.test.ts b/apps/api/test/security.test.ts index 22ddc3bf..45437b94 100644 --- a/apps/api/test/security.test.ts +++ b/apps/api/test/security.test.ts @@ -989,6 +989,32 @@ test("authenticated daemon schema violations fail closed instead of reaching bro ); }); +test("internal daemon validation logs use closed schema and reason labels, never request paths", async () => { + const { createDaemonClient, HttpError } = await import("../src/daemonClient.js"); + const sentinel = "DOCKERMAP_TEST_PATH_SENTINEL?token=DOCKERMAP_TEST_QUERY_SENTINEL"; + const daemon = await startStubDaemon((_req, res) => sendJson(res, 200, { forged: true })); + const emitted: string[] = []; + const originalConsoleError = console.error; + console.error = (...values: unknown[]) => emitted.push(values.map(String).join(" ")); + try { + const fetchDaemon = createDaemonClient({ + baseUrl: `http://127.0.0.1:${daemon.port}`, + token: null, + allowMockFallback: false, + exposeErrorDetails: false, + mockResponse: () => ({}) + }); + await assert.rejects( + () => fetchDaemon(`/daemon/snapshot/${sentinel}`), + (error: unknown) => error instanceof HttpError && error.status === 502 && error.body.code === "daemon_invalid_response" + ); + } finally { + console.error = originalConsoleError; + } + assert.deepEqual(emitted, ["[DockerMap] daemon response validation rejected schema=unknown reason=schema"]); + assert.ok(emitted.every((line) => !line.includes("DOCKERMAP_TEST_PATH_SENTINEL") && !line.includes("DOCKERMAP_TEST_QUERY_SENTINEL"))); +}); + test("daemon model responses require non-empty revision and complete provider state bytes", async () => { const fixture = async (name: string) => JSON.parse( await readFile(new URL(`../../../tests/fixtures/contracts/${name}`, import.meta.url), "utf8") @@ -1319,6 +1345,13 @@ test("runtime evidence is required and fails closed before browser publication", "Docker port-publication evidence must fail closed when duplicate listener records disagree" ); + const malformedDuplicateListener = structuredClone(duplicateListener); + malformedDuplicateListener.nodes[malformedDuplicateListener.nodes.length - 1].metadata.extra = "not-a-listener-fact"; + assert.throws( + () => validateDaemonResponse("/daemon/runtime/map", malformedDuplicateListener), + "Docker port-publication evidence must reject duplicate listeners with extra metadata" + ); + const npmEdge = fixture.edges.find((edge: { source?: unknown }) => edge.source === "npm_project_dockermap"); assert.ok(npmEdge, "canonical daemon fixture carries a V3 NPM manifest declaration"); assert.doesNotThrow(() => validateDaemonResponse("/daemon/runtime/map", fixture)); diff --git a/tests/e2e/dockermapHarness.ts b/tests/e2e/dockermapHarness.ts index 910c1ae0..174568b6 100644 --- a/tests/e2e/dockermapHarness.ts +++ b/tests/e2e/dockermapHarness.ts @@ -167,7 +167,7 @@ export async function startProductionImageStack(options: { liveDocker?: boolean productionValidationDiagnostics: () => dockerOutputIncludingStderr(docker, ["logs", "--tail", "100", container], repoRoot) .split("\n") .flatMap((line) => { - const match = line.match(/\[DockerMap\] daemon response validation rejected \/daemon\/runtime\/map: (schema|provider_state_vector|provider_freshness|runtime_evidence_(?:edge_shape|base_tuple|edge_binding|source_binding|daemon_state_target|port_listener_missing|port_listener_ambiguous|port_listener_shape|port_listener_grammar|revision)|findings)/); + const match = line.match(/\[DockerMap\] daemon response validation rejected schema=RuntimeMap reason=(schema|provider_state_vector|provider_freshness|runtime_evidence_(?:edge_shape|base_tuple|edge_binding|source_binding|daemon_state_target|port_listener_missing|port_listener_ambiguous|port_listener_shape|port_listener_grammar|revision)|findings)/); return match ? [match[1]] : []; }), postProductionSessionBurst: (client, spoofedXForwardedForPrefix) => {