Reviewer: Astra (GPT-6-Astra via Codex), posting as the configured agent account.
VERDICT: APPROVE-WITH-NITS
Whole-branch review of feat/account-dropup at 0d8dac3b2349a967dfd219e625c22a059257dc0c against freshly fetched origin/design/rulings-2026-09-19 (83f6d62), following REVIEW-BRIEF.md and AGENTS.md. No blocking correctness or security finding.
- P3 β
internal/fichas/config.go:189β195: tighten the origin validator and cover default-port loopback URLs.isLocal(v+"/")rejects validhttp://localhostandhttp://127.0.0.1; IPv6 loopback without a port is also rejected. Explicit-port loopback URLs work. Conversely,https://:443passes because Host is nonempty although Hostname is empty, and/becomes the unset value after slash trimming. I reproduced each case. The consequence is avoidable local boot refusal or a broken/omitted All apps link under malformed operator configuration, not a request-driven redirect vulnerability. Fix: distinguish truly empty input before normalization, require a nonempty parsed hostname, and validate the parsed loopback hostname independently of port/trailing slash. Add positive no-port IPv4/localhost/IPv6 cases and negative empty-host/slash cases to the config test.
Verification:
CGO_ENABLED=0 make ciandmake browserpassed using Playwright Chromium 1148 via RASTRILLO_CHROME. Remote CI passed at the exact head.TestTheAccountMenuOpensUpwardalso passed independently after mutation/restoration.- Mutation
Rail: falsemade that browser test fail on downward opening, off-screen bounds and clipped edges at desktop width. Restoring Rail made it green. - The supplied
r2-fichas-chrome-check.mjsagainst an isolated preview gave 193 passes, 0 failures across 1440/840/800/390/320 and seven signed-in pages. Panel bounds and hit tests pass, including Sign out at 390 and 320. It opens upward at and above 800px and downward inside the narrow Menu disclosure. Account trigger radius is 0px and floating panel radius 9px. Escape closes and returns focus; outside click closes. Measured CLS was 0.0000 for opening at every width and list β card β fields β list. The known lowercase titles and plain 404 are unchanged and reported separately by the script. Inspected supplied before/after and account-open screenshots. /appearancesits inside the router-wide csrf.Protect and outside the session guard. Additional router probes refused absent/hostile Origin with 403 and no cookie, refused invalid modes, and reduced absolute, scheme-relative, backslash and control-character return targets to/. Dark is present in the server-rendered html attribute and selected menu state; System removes the attribute. Door states and ErrorPage are wired to the same cookie reader.- Precise redirect scope: All apps comes solely from validated HOMEDIRECTORYORIGIN and does not use request Host, Referer or query parameters. However, the literal statement βno redirect target is taken from the requestβ is not true of Appearance: the shared handler deliberately accepts
return_tothroughsessions.SafeReturn. The new app test explicitly expects a same-site/cards/newreturn. This is a validated local return, not an open redirect. A policy requiring a fixed destination would need the handler to return to/instead; I have not represented the current implementation as request-independent. - Normal HTTPS origins work; credentials, paths, nonempty queries/fragments, remote HTTP, javascript URLs and loopback-lookalike hosts are rejected. Config loads at boot. The validation edge cases are the nit above.
- The account contains identity, optional All apps, Appearance and exactly one Sign out. No unimplemented Home connection or credits destination is offered. Labels/URLs remain template-escaped. Native details and POST forms preserve basic use with scripts off; chrome.js supplies Escape/outside-click enhancement. New assets are pinned by TestDoorFilesAreEstilos.
Landing: estilo #23 first, then Fichas #6 as a fast-forward of the live design line. Rebase this branch onto it, resolve dependency/asset-test conflicts, and change account_test.go:15 from the old db import to amadan.net/rastrillo/rastrillo/db. Otherwise the helper's db type is incompatible with the migrated AppForTest and TestNoPreMoveRastrillo also forbids the import. Re-pin estilo to its landed main head and rerun all gates and the chrome check on the combined result before the next fast-forward. Do not use stale origin/main as Fichas' landing base.
Review only: no existing checkout edits/moves and no commit, push or merge. All disposable /tmp worktrees and fixture processes removed. Requested disk check: 40 GiB free before, 37 GiB after.