| 1 | | Previous finding | Disposition at `98c0139b` | |
| 2 | |---|---| |
| 3 | | R1-1 — Readiness without established database state | **Resolved.** Unknown pointers block readiness; off and expired pointers require empty mirrors. | |
| 4 | | R1-2 — `off` waits behind publication | **Resolved.** `TryLock` returns 409 while publication holds the slot. | |
| 5 | | R1-3 — Unbounded console pointer reads | **Resolved for reads.** Pointer reads are streamed and capped. | |
| 6 | | R1-4 — Recovery generation deleted after failed synchronization | **Resolved for the reported paths.** Failed installations preserve loaded and retained generations. | |
| 7 | | R1-5 — Rejected-upload test does not establish zero reads | **Resolved at the reservation boundary.** The panicking-reader test detects reads before reservation. | |
| 8 | | R1-6 — Countries overflow ordering | **Resolved.** `_other` remains intact until rendering and sorts last. | |
| 9 | | R2-1 — Feed status writes bypass the read cap | **Still partly resolved; P2 remains.** `geoipfeed.go:174` calls `Put`; `release/store.go:408` reads the entire existing object before checking its precondition. An oversized `feed.json` can exhaust memory. Apply the stat/streamed-hash checks used by `PutFrom`. | |
| 10 | | R2-2 — Timed-out checks never persist failures | **Resolved for download timeouts.** Status persistence gets a separate bounded context. | |
| 11 | | R2-3 — Unreadable feed status clears an alert | **Resolved.** Read and parse failures preserve established feed state. | |
| 12 | | R2-4 — Unusable directory prevents “not loaded” alert | **Resolved.** Pointer state is retained before directory preparation. | |
| 13 | | R2-5 — Published alert reasons never change while unhealthy | **Resolved.** Reason changes update `Value` and emit an event. | |
| 14 | | R3-1 — Console follower keeps serving an expired database | **Resolved.** Loaded-reader expiry is checked independently of local state and candidate loading. | |
| 15 | | R3-2 — Transient status-read failure erases feed history | **Resolved.** Unknown history is not overwritten. | |
| 16 | | R4-1 — Unbounded bucket operations block startup or publication | **Resolved for the reported operations.** Startup cleanup is asynchronous; maintenance and staging reads have deadlines. | |
| 17 | | R4-2 — Restart falsely clears a persisted GeoIP alert | **Resolved.** Unknown inputs cannot establish recovery. | |
| 18 | | R4-3 — Email delivery suppresses active reason changes | **Resolved.** Successful delivery records the last reason per recipient. | |
| 19 | | R4-4 — Region guesses carry another generation’s attribution | **Resolved.** Location and source are captured under one reader lock. | |
| 20 | | R5-1 — Unknown feed state suppresses confirmed expiry | **Resolved.** Established unhealthy conditions precede the unknown-state guard. | |
| 21 | | R6-1 — Failed startup deletion leaves a disabled feed alert firing indefinitely | **Resolved.** Deletion retries with bounded attempts and capped backoff. | |
| 22 | | R7-1 — Unbounded feed edition metadata makes control records unreadable | **Resolved.** Editions are normalized HTTP dates; oversized serialized pointers are refused. | |
| 23 | | R7-2 — Unchanged-edition checks leave the feed reported as “not run yet” | **Resolved.** Confirmed editions update success history. | |
| 24 | | R8-1 — MMDB verification indefinitely holds the publish slot | **Resolved for the reported traversal attacks.** Verification runs in a child killed at its deadline; staging cleanup and slot release are deferred. | |
| 25 | | R9-1 — Malformed MMDB metadata can crash the entire console | **Resolved for the reported arithmetic panic.** Initial parsing occurs in the verifier child. | |
| 26 | | R10-1 — Verifier child has no memory bound | **Resolved on Linux for the reported allocation attack.** The child applies a 512 MiB `RLIMIT_DATA` before parsing and refuses verification if setting it fails. | |
| 27 | | R10-2 — Retained recovery generations survive GeoLite2 expiry | **Resolved for the reported synchronization failure.** Unchanged-pointer polls retry synchronization and cleanup; retained generations receive independent expiry checks. | |
| 28 | |
| 29 | New findings: |
| 30 | |
| 31 | 1. **P1 — A database can pass verification and then crash serving processes during a lookup.** |
| 32 | [internal/console/geoipverify.go:58](/home/paulca/amadan.net/carlos/platform/.claude/worktrees/geoip-for-apps/internal/console/geoipverify.go:58), [internal/geo/mmdb.go:186](/home/paulca/amadan.net/carlos/platform/.claude/worktrees/geoip-for-apps/internal/geo/mmdb.go:186) |
| 33 | |
| 34 | The pinned library’s `Verify` decodes records into generic values. Live lookups decode into `mmdbRecord`, which takes a different path: `decodeStruct` skips unknown fields using `nextValueOffset`. That function recursively calls itself once per skipped value, without the decoder’s depth limit. |
| 35 | |
| 36 | A crafted database can return ordinary countries for the five probe addresses while placing a large flat boolean array in an unknown field on another public network. Generic verification iterates the array; sixteen million elements require roughly 256 MB of array storage, within the child’s memory budget. The probes never exercise that record’s struct-decoding path. |
| 37 | |
| 38 | After publication, a lookup in that network creates millions of recursive stack frames in the serving process. Stack exhaustion is fatal, so a visitor request can terminate an edge, and an authenticated `/geo` request can terminate the console. The verifier child’s memory limit and deadline do not protect these later lookups. |
| 39 | |
| 40 | **Fix:** validate every reachable record through the production decoding schema inside the bounded child, with a per-record complexity limit, or replace the recursive skip path with bounded iteration. Add a regression containing a large ignored array outside the probe networks and verify that it cannot reach an unsafe serving lookup. |
| 41 | |
| 42 | Validation was static, including inspection of the pinned decoder. Targeted Go tests could not start because creating the build directory failed with “read-only file system”; runtime reproduction and cross-compilation remain unverified. |
| 43 | |
| 44 | Verdict: revise. |
| 45 | |