Repository navigation
fix: the four self-contained findings left from the logic-bug audit - #175
Merged
Merged
Conversation
…reduction `resolve_oi_addrs` warned and continued when a named interface had no addresses, so an interface that had not come up yet — a boot-time race — left the daemon binding whichever subset did resolve, or nothing at all when that was the only interface. It then logged `seedling ready` with no management plane, which looks identical from outside to a daemon that is up but unreachable. The flag's own documentation already called this fatal. The decision moves into `select_oi_addrs`, which takes the host's interface list rather than reading it, so it is testable without a process exit; `resolve_oi_addrs` keeps the I/O and the `fatal!`. That also drops a `get_if_addrs` call per named interface.
`w[wt.cert.rotation]` requires rotating before expiry, but the swap was gated on `not_after <= now` and reconsidered hourly, so the endpoint served an already-expired certificate for up to a full tick. Advertising both hashes during the overlap does not help: a browser enforces the validity period even when the hash arrives via `serverCertificateHashes`, so every new session failed TLS for the duration. The swap now happens `SWAP_LOOKAHEAD` ahead of expiry, and the tick interval moves into `wt_cert` as `ROTATION_TICK` so the margin's dependency on it is checked by a test rather than asserted in a comment. Two existing tests primed `next` with a not_after 60 s out, which is inside the new swap window; they now use a time clear of it, and the 60-second case became the test that the swap pre-empts expiry.
Contributor
Code Coverage OverviewLanguages: TypeScript, Rust TypeScript / code-coverage/vitestThe overall line coverage in commit 9fd4d89 in the Rust / code-coverage/rustThe overall line coverage in commit 9fd4d89 in the Show a line coverage summary of the most impacted files.
Updated |
…ns on The advertised port was `args.wt_port` when no explicit addresses were given and `DEFAULT_WT_PORT` otherwise — so with `--wt-listen` or `--listen`, where the argument is not the port anything binds, clients were handed 7893 regardless. That reaches them twice: in `wt_url` from `POST /connect`, and in the CSP's `connect-src` origin. Both were wrong together, so the documented explicit-address combinations had no working WebTransport at all. It now derives from the addresses actually bound. Addresses that disagree on a port are rejected at startup, since only one port reaches the client and picking one would misadvertise the others.
`find_active_for_hostname` matched on `state = 'active'` alone. Because the exact-hostname path takes the highest id, a newer expired row shadowed an older one that was still valid, and the SAN-coverage scan below it never ran — so the proxy was handed the expired certificate and nothing on the serve path checked `notAfter`. Both paths now skip rows whose expiry has passed. A row with no recorded `not_after` is still served: unparsed and expired are not the same, and conflating them would withhold a usable cert. The sibling matcher in `state` keeps its behaviour — the renewal scheduler reaches an expiring cert through it, so filtering there would stop renewals — and its comment now says so rather than claiming the two mirror each other. The only other caller, the Tailscale fetch guard, already fell through on an expired cert, so it is unaffected.
passcod
force-pushed
the
fix/audit-easy-sweep
branch
from
September 9, 2026 11:15
ae4051d to
9fd4d89
Compare
passcod
enabled auto-merge
September 9, 2026 11:18
Merged
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
🤖 The logic-bug audit had 23 critical/high findings; the eight theme PRs closed 16 of them, all the ones that were instances of a recurring pattern. Seven single-site findings were left over. These are the four that are self-contained; the other three are on cards.
Each commit changes the spec first where the spec was silent, then the code, then tests.
An unresolvable listen interface silently dropped the management plane
resolve_oi_addrswarned and continued when a named interface had no addresses, so an interface that had not come up yet — a boot-time race — left the daemon binding whichever subset resolved, or nothing at all when that was the only interface. It then loggedseedling readywith no OI listener, which from outside is indistinguishable from a daemon that is up but unreachable. The flag's own doc comment already called this fatal.The decision moved into
select_oi_addrs, which receives the host's interface list instead of reading it, so it is testable without a process exit. That also drops aget_if_addrscall per interface.The WebTransport certificate rotated only once it had expired
w[wt.cert.rotation]already required rotating before expiry, but the swap was gated onnot_after <= nowand reconsidered hourly, so the endpoint served an expired certificate for up to a full tick. Advertising both hashes across the overlap does not help — a browser enforces the validity period even when the hash arrives throughserverCertificateHashes, so every new session failed TLS for as long as the window lasted.The tick interval moved into
wt_certasROTATION_TICKso the swap margin's dependency on it is a test rather than a comment.The advertised WebTransport port ignored explicit listen addresses
The advertised port was
args.wt_portwith no explicit addresses andDEFAULT_WT_PORTotherwise — so under--wt-listenor--listen, where that argument is not the port anything binds, clients were told 7893 regardless. It reaches them twice, inwt_urland in the CSPconnect-srcorigin, and both were wrong together, leaving the documented explicit-address combinations with no working WebTransport at all.It now derives from the addresses actually bound. Addresses disagreeing on a port are rejected at startup, since one port reaches the client and picking one would misadvertise the rest.
An expired certificate was served in preference to a valid one
find_active_for_hostnamematched onstate = 'active'alone. The exact-hostname path takes the highest id, so a newer expired row shadowed an older valid one and the SAN-coverage scan beneath it never ran — the proxy got the expired certificate and nothing on the serve path checkednotAfter.Both paths now skip expired rows. A row with no recorded
not_afteris still served, because unparsed and expired are not the same thing. The sibling matcher instatekeeps its behaviour, since the renewal scheduler reaches an expiring cert through it; its comment now records the divergence instead of claiming the two mirror each other.