carlos / platform Public
| Previous finding | Round 11 assessment | |---|---| | R1-1: flat tenant borrows a nested socket | Fixed by peer-cgroup authentication (§5.4). | | R1-2: aliases and path routes remain public | Fixed by checking the final upstream (§5.1). | | R1-3: new box starts public | Fixed by classification before serving (§5.1). | | R1-4: revoked grants return after restart | Fixed for retained state; fresh-box replay remains an explicit residual (§5.3). | | R1-5: door exceptions fail across routing | Resolved by removing the owner exception and defining the identity transition (§5.4). | | R1-6: socket lifecycle conflicts | Fixed by instance-level ownership (§5.4). | | R1-7: transport and body limits | Resolved at spec level (§5.5). | | R2-1: stamp follows serving | Fixed by the admission barrier (§5.1). | | R2-2: upgrade serves unclassified engines | Fixed by startup classification and refusal of NULL (§5.1). | | R2-3: flat callers borrow door credentials | Fixed by the door’s UID restriction (§§5.4, 6.2). | | R2-4: deployment verification fails | Fixed by watch tickets (§5.8). | | R2-5: port-80 refusal cannot work remotely | Resolved by the narrowed contract (§5.1). | | R3-1: previously admitted requests cross the mark | Fixed by waiting for cancellation and completion before acknowledging the fence (§5.1). | | R3-2: flat tenant injects into a nested caller | Fixed by excluding unisolated execution while armed; historical compromise remains explicitly excluded (§6.2). | | R3-3: grant-only routes lack watch tickets | Fixed: every deploy target receives a ticket (§5.8). | | R4-1: foreign instances and workers remain unisolated | Fixed by arming prerequisites and launch refusals (§6.2). | | R4-2: path routing widens an engine grant | Fixed by exact-host engine dispatch (§5.4). | | R4-3: watch tickets expire mid-watch | Fixed by renewal (§5.8). | | R5-1: platform-minted hostnames cannot classify | Fixed by execution-owner inheritance (§5.1). | | R5-2: arming rejects isolated unit-backed routes | Fixed by distinguishing backing from layout (§6.2). | | R5-3: home edge lacks the probe path | Fixed by signing the probe path (§5.8). | | R6-1: minted row’s own grant is ignored | Fixed by aggregating group grants (§5.1). | | R6-2: static platform domains are refused | Fixed by restricting inheritance to minted instance rows (§5.1). | | R7-1: clearing exposes a still-serving generation | Fixed by stopping the group and proving its cgroups empty (§5.1). | | R7-2: TCP-backed minted rows have no owner | Fixed by explicit ownership and unique non-empty backend matching (§5.1). | | R8-1: upgrade classifies a running declaring build public | Reported case fixed by scanning staged manifests; missing-manifest exposure remains an explicit residual (§5.1). | | R8-2: sleeping unit-backed route cannot clear | Resolved by operator-controlled stop-and-clear (§5.1). | | R9-1: recovery launches a declaring GoodVersion after clearing | Fixed by gating the actual selected build (§5.1). | | R10-1: a written but unfenced 1 permits recovery launch | Fixed: every declaring launch waits for the current mark’s fence, including an already-written 1 (§5.1). |
Reviewed 24c79f8a against locally available amadan origin/main at f550afa2. The implementation files cited below match that revision. No files changed.
1. P1 — Granted TCP sockets expose a declaring build while I1 holds (§§5.1, 6.3).
A concrete sequence:
- A unit-backed HTTP service declares port
844, holdsport:844, and serves HTTP on its inherited listener. The deployment permits public ingress to that port. - Deploy a release that retains the port declaration and adds
doc_engine; supply thedoc-enginegrant for its resource requirements. - Complete the specified launch gate: write
1, load the internal serving map, drain earlier public edge requests, acknowledge the fence, and start the declaring build. - A public client sends HTTP directly to the box’s port
844. The inherited TCP listener delivers it to the declaring build without traversing the edge.
I1 holds throughout step 4: the declaring process started after its group was fenced, and nothing cleared the mark. Nevertheless, the public reaches it.
This combination is supported by existing code. Capability validation checks ports independently and rejects doc_engine together with host_role, but permits it together with ports (caps.go:359, caps.go:382). RenderSocket renders each declared-and-granted port as a bare ListenStream=<port>, without consulting DocEngine (render.go:286, render.go:319). The service receives the bound sockets directly (carlos-app@.service:21).
Hibernation does not close this path. Capability convergence explicitly permits granted ports on hibernating routes and records that a connection starts the unit without the activator (converge.go:241). That autonomous start executes the live binary directly; crash restarts also belong to systemd (carlos-app@.service:42). Thus the claimed launch-path coverage also misses systemd-initiated starts.
Suggested fix: Make internal classification incompatible with public TCP listeners for that execution group. Close existing generated listeners before completing the internal transition and prevent capability convergence from restoring them. Enforce the launch gate at the unit’s actual execution entrypoint so socket activation and automatic restarts cannot bypass it. Test a declaring build with an existing port grant: after fencing, a direct public connection must never reach it.
Overall verdict: reject. R10 is fixed, but an edge-only fence leaves a supported public ingress path open while I1 holds.