18 KiB
P5-15-01R-03 — Analytics authentication return-path repair
Source: 339e3ba591c91dcd3590d4cbc451ad31268f1aec, development codex/ui-redesign-phase4. This checkpoint owns AUTH-01 and directly related return-target validation only. Production main remains unchanged and NO-GO. No Analytics data model, saved-search/Rules implementation, P5-15-02, P5-13, Docker, B07 or deployment work is authorized here.
Before-fix native evidence and demonstrated cause
The unchanged original functional-acceptance.spec.ts anonymous case was rerun against the disposable Development/SQLite API and actual local sign-in on 2026-10-10. All route census steps complete; the assertion at line154 fails: requested /analytics?months=6, returned /analytics. The native run has zero passes/one failure, zero skipped/flaky/retries. The trace, screenshot and JSON are retained under ignored .artifacts/p5-15-01R-03/browser/auth-original-red*; portable final evidence follows validation. This is the original desired-state assertion, not a weakened replacement.
App.Shell defines path = location.pathname and sends only that value as state.from when redirecting to the public landing page. Search and hash are lost at this boundary, before sign-in. Landing forwards the same router state to Login. Login already uses the supplied string for local, two-factor, Google and Microsoft completion; Analytics receives a query-free destination and chooses its existing twelve-month default. Its only supported reporting query is months:6 or24, otherwise12. No date/status/view controls are invented.
The original Login slash-only validation also permits raw control characters, malformed encoded paths and normalized auth-entry destinations. Local URL normalization shows the literal-tab target /\t/evil.example can become a different origin; this is a demonstrated validation gap, not a demonstrated native external redirect or tenant exposure. Installed router replace handling may reject it with a history SecurityError. The bounded repair must choose the existing safe dashboard fallback instead.
Before application changes, the Login component regression run has 17passing/12failing out of29. Sixteen cases are new:12 untrusted target variants plus valid encoded URL, valid existing Jobs context, unsafe nested context and account-disposal entry. Existing Login cases remain passing; failures demonstrate missing safe fallback/context handling. JSON and log are retained separately; the initial shell wrapper incorrectly reported exit0, so the test JSON/failed assertions, not that wrapper exit, are authoritative RED evidence.
Bounded execution and invariants
- Retain the genuine native RED and add browser/local return-boundary regressions; commit/push this independently before application edits.
- Preserve pathname/search/hash and valid existing Jobs return context at the existing App/Login owners. Validate untrusted destinations before every existing completion path; no new persistent redirect store or provider contract.
- Exercise native Analytics periods/encoding/defaults/refresh/history, failed-login retry, expiry/logout, owned/foreign resources and unsafe client state. Keep saved searches, Rules, P2-D01, revision/fidelity and Production withholding green.
- Run full fresh validation, reconcile57 groups without promoting incomplete groups to PASS, record evidence and immediately commit/push each completed unit.
Existing session/account invalidation intentionally disposes the previous page/providers and replaces with /login?sessionChanged=1; previous-account private context must remain discarded. Backend ownership and role checks remain authoritative. Synthetic accounts/disposable SQLite only; no real external identity provider or production data.
Bounded implementation checkpoint
App.Shell now carries pathname/search/hash plus the existing navigation context through the same landing/sign-in state. safeAuthReturnPath rejects external/protocol-relative targets, controls/backslashes, malformed encoding, recursively encoded unsafe paths and normalized authentication-entry loops, including case variants. It returns valid internal strings unchanged. Login uses that validated destination across local, two-factor, Google and Microsoft completion, replaying only validated existing Jobs return context. Account-disposal entries explicitly abandon old destinations. No URL storage, Analytics aggregation, authentication API or provider changes.
Final application changes have fresh 1,033/1,033 backend, 490/490 frontend, 31/31 focused Login, the unchanged original native AUTH-01 1/1, and new native return/security journeys 5/5 passing. TypeScript, lint and both builds pass. The case-insensitive login-loop gap also has separate RED evidence before correction: two failures, two selected passes and27 not selected; final31 cases pass. External identity callbacks/two-factor are component fixtures, not live provider acceptance.
At the separately pushed code checkpoint, full browser/security/fidelity reconciliation was still pending and no final acceptance was claimed. The final evidence below now establishes the bounded repair result. P5-15-02 is not started or automatically authorized; production remains NO-GO. Broader human/device/font qualification, historical preference500 causes, the CV Unsaved intermittent observation and P5-13 deferral remain open.
Return-path and security contract
The existing route state remains the single transport: protected location → landing → Login → authenticated destination, using replacement navigation at the existing boundaries. No destination is persisted in localStorage, sent to an identity provider, placed in a token URL or copied into the CV/API domains. Search and fragment are preserved literally, including correctly encoded values. Analytics still interprets only months, with6/24 supported alternatives and12 as the established default. Multiple independent report parameters do not exist; a fragment is navigation state, not another reporting filter.
Before every completion callback, the shared helper requires a relative same-origin path, validates encoding and decoded path meaning, rejects external/protocol-relative paths, controls/backslashes and normalized /, /login or /register returns, and selects /dashboard on rejection. Repeated decoding detects nested unsafe path encodings without rewriting an accepted URL's query/fragment. Only the existing nested Jobs from and positive safe-integer focusJobId are replayed; arbitrary client state is dropped. Backend authentication/ownership remains authoritative even for a valid path containing a foreign resource ID.
Deliberate account/session disposal is distinct from an expired cookie. The existing /login?sessionChanged=1 path abandons the old destination and context. Cookie expiry followed by reload captures the currently requested safe deep link; it does not revive another account's retained editing state. No extra history push or second concurrency/save model is introduced.
Native journeys and evidence boundaries
| Journey | Executed assertion and result |
|---|---|
| Original AUTH-01 | Unchanged anonymous census fails before repair at /analytics, then passes with exact /analytics?months=6 after real local authentication |
| Reporting parameters | Exact24, encoded %36→6, explicit12 and invalid3→12 survive sign-in; actual Analytics API requests return200 and selected controls match effective periods |
| Default/failure/recovery | No query→12; incorrect password401 stays at Login with error, retry restores intended destination; no premature success |
| Browser history | 6→24→Back6→Back default12→Forward6; reload retains6; authenticated direct24#activity stays signed in |
| Session lifecycle | Expired jobtracker_auth cookie is absent and /auth/me returns401; reload/sign-in restores24#activity. Explicit logout204→sessionChanged→login returns dashboard |
| Other protected deep link | Owned Job Documents query/fragment survives sign-in and refresh; region is visible, preserving existing section routes |
| Account/resource isolation | A new account receives404 for both foreign workspace and Job API read, sees no private Job title, while original owner readback is unchanged |
| Client tampering | Protocol-relative and encoded protocol-relative router state falls back to dashboard after real sign-in; fourteen component target variants also fail closed |
| Existing methods | Local sign-in native; existing two-factor and Google/Microsoft completion/cancellation paths use deterministic component fixtures. Real provider/2FA infrastructure is not qualified |
Five new ordinary browser cases use the actual disposable SQLite API, cookies, CSRF/session context, synthetic accounts and supported page actions. They do not mock successful report/ownership/authentication responses. Non-local requests are blocked. Three saved-search and four Rules repair cases remain separate existing requirements in the full run. P2-D01 remains an ordinary passing requirement, never an expected failure.
The initial focused browser attempt contained four passes and one strict-locator harness failure: the toast and framework announcer both matched role=alert. The correction narrows the assertion to the existing Unauthorized feedback; it does not weaken the expected401/retry behavior. That attempt remains retained separately. The final focused run is5/5 with zero retries, skipped or flaky cases. The first full frontend488/488 run preceded the final two case-variant regressions; it is historical, not the final490/490 evidence.
Preservation traceability
| Contract | Existing behavior → bounded change → regression evidence | Remaining qualification |
|---|---|---|
| PRES-E02 | Local/challenge sign-in return → full validated internal destination → original AUTH-01,31 Login cases and native retry/expiry | Real external challenge/email/reset/Turnstile combinations and broader human acceptance |
| PRES-E03 | Google/Microsoft entry/callbacks → same shared validated return → existing callback/cancellation fixtures strengthened for query/hash | Real providers, credentials, cancellation/network/redirect infrastructure |
| PRES-E04 | Existing two-factor completion → same complete destination → pending challenge fixture restores24#activity | Human/native authenticator workflows and full security-settings acceptance |
| PRES-E07 | Existing route/shell continuity → keep query/hash through guard → native Analytics and Job section, history and refresh | All manual names/focus/device and complete long-tail navigation obligations |
| PRES-E09 | Private account/session disposal and ownership → sessionChanged fallback, restricted context replay → native logout/foreign404 plus account-isolation suites | Production/assistive-technology/BFCache combinations beyond recorded fixtures |
| PRES-W01 / J05 | Job section URLs and Jobs list return context → validated existing from/focus replay → component context assertion, owned Documents deep link and retained Jobs/Back/filter suites |
Full workspace/provider/device and long-tail Jobs list interaction qualifications |
| PRES-J02 (supporting) | Saved discovery search persistence/ownership → implementation untouched → three existing native saved-search repair cases remain passing | Scheduler/alert delivery, live source providers and human/device acceptance |
No original PRES contract, API authorization, Analytics metric/query model, provider integration, saved-search or Rules implementation is changed. E02 moves FAIL→PARTIAL for the repaired defect; the group is not granted PASS. The current reconciliation is PASS0 / PARTIAL56 / FAIL0 / DEFERRED1 / BLOCKED0 / NOT VERIFIED0 =57. Only E02 changes from the preceding R-02 column. Historical00/01/R-01/R-02 columns remain intact. All32 route patterns pass finite entry/refresh/read assertions:192 observations,98 selected reads and no500. Full-route acceptance remains32PARTIAL/0FAIL because long-tail/provider/human obligations are not replaced by this finite census.
Visual and accessibility evidence
The four native captures cover EN/light1440, Bokmål/dark390, EN/dark768 and Bokmål/light320. The agent inspected their actual report headings, wrapping and selected period states; no screen reader, physical touchscreen, software keyboard or independent human review was used. The existing transient sign-in toast appears over report content in captures; broad overlay/reflow acceptance is not closed by this routing repair. This checkpoint introduces no user-facing text or visual redesign.
Native API/URL proof, source RED results and screenshot fingerprints, complete fresh validation and repository/static checks retain exact commands/times/result hashes and limits. Captures: EN/light desktop, Bokmål/dark phone, EN/dark tablet, Bokmål/light narrow phone.
Full267axe scans report0violations and 2,041incomplete node occurrences: color-contrast1,769; aria-hidden-focus41; aria-prohibited-attr209; aria-valid-attr-value18; th-has-data-cells4. These are occurrences across scans, not2,041 independently confirmed defects. Classification and human/manual acceptance stay assigned to the later accessibility audit; this checkpoint neither removes incompletes nor certifies spoken names/announcements. Zero automated violations cannot close those obligations.
Fresh validation and count reconciliation
These results are fresh after the last application change on2026-10-10. Portable command/report fingerprints distinguish original RED, intermediate harness/full-suite attempts, final GREEN and independent/subset lanes. The complete ordinary run finished at12:54:24UTC; independent security/diagnostics completed at12:55:17UTC, with zero retries, skips, unexpected or flaky outcomes in final runs.
| Gate | Fresh result | Count explanation |
|---|---|---|
| Backend | 1,033/1,033 | Unchanged; no backend code/tests added |
| Frontend | 490/490,82 suites | 472→490:18 new Login cases, comprising14 unsafe-target variants and4 valid-context/disposal cases; two existing provider/2FA assertions strengthened |
| Focused Login | 31/31 | Subset of490, not additional tests |
| Original AUTH-01 | 1/1 GREEN after0/1 RED | Same original desired-state assertion, not a replacement or expected failure |
| New native auth cases | 5/5 | Included in134 ordinary browser cases, not added twice |
| Complete ordinary browser | 134/134,0fail | 129 declared cases before repair→134 with five new cases;128pass/1fail→134pass/0fail includes the former failure becoming an actual pass |
| Separate diagnostics | 2/2,0fail | Two declared existing preference/Rules diagnostic cases, outside ordinary browser totals |
| Sidecar | 49/49 | Unchanged deterministic/offline suite, not configured-model qualification |
| Focused AI / revision | 108/108 /5/5 | Unchanged subsets of1,033 backend tests |
| Real-HTTP / rate-limit / ingress / PDF admission | 2/2 /9/9 /6/6 /1/1 | Existing independent suites; local contracts, not production evidence |
| Configuration | 3/3 | Existing offline checks; production configuration remains unverified |
| Standard fidelity | 16/16,36=36 pages | Exact page count/membership; unchanged0.5mm page dimension,2mm position,0.5pt font and1/255 RGB tolerances |
| Settings exports | 8/8,24=24 pages | Same tolerances and fixture contents; native exports from the full browser run |
| Extra offline helpers | Supply-chain3 / evaluation5 / extension3 | Existing independent fixtures, not live provider/production calls |
| Static/build/hygiene | TypeScript, lint, frontend production and API Release builds pass; final E2E TypeScript/secret/link/whitespace proof in repository manifest | Raw logs and actual command exits are retained separately from runtime totals |
Standard/settings measurement passes were completed at12:42UTC against the final native exported files. No renderer geometry, pagination, typography, revision, canonical profile, override, frozen package, save/conflict or AI withholding implementation is changed. The original CV Unsaved intermittent observation remains OPEN despite current passing runs; this repair neither investigates nor establishes its cause.
Immediate commit/push ledger
c37cab560afa2d94cc4244c09dfa7aa720563bbf— reproducing regression tests and initial evidence, committed/pushed before production-code changes.68437671762aa9a4d93036a23a06810f1f47dd57— bounded App/auth/Login repair,18 additional Login cases, five browser cases and verified intermediate record; committed/pushed after final application-level validation.- Final acceptance evidence/documentation — this independently reviewable checkpoint; immediately commit/push after repository verification. Its exact hash, remote equality and clean-tree result are reported after creation rather than inventing a self-referential hash here.
Development remote equality is verified after each push. Local/remote production main remains 27657f31131cd95d42f0e1290853ec9c9b3959a4. No production merge/push/deployment, P5-15-02, P5-13 qualification/UI, Docker recovery, B07 or unrelated repair occurs. F04/current backup+restore/production configuration-schema-secrets/tested rollback remain release blockers. P5-13 stays deferred/unqualified/Production-withheld with0/366 calls,0 human reviews and no review UI. The historical CV Unsaved intermittent observation, preference500 causes, Board scale/native touch, followMode UI and human/device/font/photo acceptance remain explicitly open.
Decision
P5-15-01R-03: GO for the bounded Analytics authentication redirect repair. AUTH-01 is fixed; all required current automated/native preservation lanes are green. Existing saved-search and Rules fixes remain green, and no new major security/data-preservation regression is observed. This is not full Phase5, actual provider/human/device or production acceptance. Stop before P5-15-02, P5-13, B07 and production deployment.