mirror of
https://github.com/J3vb/OwnCord.git
synced 2026-09-03 03:50:00 +03:00
* docs(b3-9): record B3-2's merge (#1450 =75d64dd4); B3-9 in progress Plan status line, B3-2 step-table row (DONE) and evidence block carry the squash SHA; docs/plans/README.md B3 row updated. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01A17Uq3d2C36rN82Jitf3wo * fix(api): panic log carries trace_id — tracing ahead of recoverer (OC-0346) recoverer snapshots telemetry.TraceIDFromContext before dispatch, so it needs the otelhttp span to exist already; it was mounted two slots ahead of telemetry.HTTPMiddleware and the trace_id attribute was always dropped. Move the tracing middleware above it; request-id binding, security headers and the body cap keep their relative positions. Test (otel build only — the default build hard-wires TraceIDFromContext to ""): go test -tags otel -run TestRecoverer_PanicLogCarriesTraceID ./api/ Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01A17Uq3d2C36rN82Jitf3wo * fix(admin): owner gate answers 503 on a role read fault, not 403 (OC-0345) ownerOnlyMiddleware collapsed `err != nil || role == nil` into 403 "role not found", so a transient GetRoleByID failure told the Owner they lack the Owner role. Split the outcomes: a store error logs and answers 503 SERVICE_UNAVAILABLE (the perimeter's contract); a genuinely missing role still answers 403. The existing whitebox tests, which inject only the user into the context, are unchanged. Test: TestOwnerOnlyMiddleware_RoleLookupFailureIs503 (roles table renamed, whitebox — through the full stack the perimeter would answer its own 503). Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01A17Uq3d2C36rN82Jitf3wo * fix(service): verify-totp reports a store fault as 500, uncounted (OC-0377) challengeSecret folded a GetUserByID error into the same 401 "invalid or expired two-factor challenge" an expired challenge earns, and the attempt had already been charged to the per-user totp_fail cap. Split the outcome: a store error logs and returns the new service.ErrTOTPUnavailable (ErrInternal, "two-factor verification temporarily unavailable"); an unknown user or a missing secret still answers 401. The limiter reservation moves after the store read — the rule authenticate already applies — and still precedes the code compare, so the check-then-act it closes stays closed. Characterization row flipped in the same commit: `VerifyTOTPFailurePaths/ user lookup fails -> 500, challenge kept, attempt not counted` — after the fault ten wrong codes still answer 401 (the tenth would be 429 had the fault counted), then the eleventh is refused. `per-user failure cap spans challenges` unchanged. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01A17Uq3d2C36rN82Jitf3wo * fix(service): verify-totp keeps the verified second factor when the session insert fails (OC-0378) VerifyTOTP consumed the partial challenge before issueSession, so a store fault on the session insert discarded a verified second factor and sent the user back to the password step; the code was also marked used, so an immediate retry would have been refused as a replay. The claim stays atomic and first (two concurrent verifies can never both reach issueSession). On issueSession failure the challenge is restored under the same partial token — the client still holds it — and the accepted code is released, so the retry completes the login without another password step. auth gains PartialAuthStore.Restore and UsedTOTPCodeStore.Unmark, each tested in the leaf package. Characterization row flipped in the same commit: `VerifyTOTPFailurePaths/ session insert fails -> 500, the challenge and the code survive` — once the trigger is dropped the same token and the same code answer 200 with a token. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01A17Uq3d2C36rN82Jitf3wo * fix(service): registration commits the account, the invite use and the first session together (OC-0376) CreateUserWithInvite committed the user and burned the invite; the session insert ran outside that transaction, so a store fault there answered 500 with a half-registered account — a retry got "invalid invite or credentials" while a login with the same password worked. Option B from the ledger: the session token is generated first and the session row is inserted inside the same transaction (db.insertSession through dbgen.Queries.WithTx; no query or migration change, so no sqlc regen). A fault at any step rolls the whole registration back and the caller simply retries. The H-6 cap needs no eviction for a user with no sessions. Characterization row flipped in the same commit: `RegisterPolicyAndFailurePaths/ session insert fails -> 500, nothing committed` — user row absent, invite use_count 0, message "registration failed — please try again". db tests pass the three new arguments; the happy-path test asserts the session row. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01A17Uq3d2C36rN82Jitf3wo * refactor(service): one session-token path for Register and issueSession OC-0376 gave Register its own auth.GenerateToken failure branch — a duplicate of the unreachable one issueSession already carried — and the auth slice's statement coverage dipped from 91.8% to 91.7% on that one statement. newSessionToken generates the token and hands its hash to a persist callback: CreateSession for login and verify-totp, the CreateUserWithInvite transaction for registration. Behaviour identical (the characterization file is green before and after); slice coverage 402/437 = 92.0%, service/auth.go 250/263 = 95.1%. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01A17Uq3d2C36rN82Jitf3wo * chore(ledger): close the five B3-tagged findings; counts re-derived (PR #1454) OC-0346 →775eba50, OC-0345 →fb1afb8a, OC-0377 →f7015809, OC-0378 →be37d7ee, OC-0376 →85d86dc7: status fixed, fix.test, revertProof pass (hand reverse-apply per commit + verify-fixes.mjs). Ledger 315 fixed / 59 open → 320 / 54 (3 declined, 1 duplicate, 378). The four count-carrying documents are re-derived around every number, not just the totals (obs #100): docs/plans/README.md, hp-0-scorecard (54 open = 1 high / 12 medium / 41 low; three hunts; 53 of 54 resolve; Client 33 / Server 21), repo-health-issue-register (table, "eleven of which", OC-0345/OC-0346 rows marked fixed with this PR), b0-baseline. OC-0323 stays open — it rides B3-8's message/read-state family. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01A17Uq3d2C36rN82Jitf3wo * fix(service): verify-totp — cap check before the store read; release the code on a lost claim (Codex P2s, #1454) Two P2s from Codex on PR #1454, both verified against the code and fixed test-first: 1. An exhausted totp_fail window is refused by the read-only limiter.Check before challengeSecret, so rotating source IPs cannot drive user reads and secret decryptions past the per-user cap. The atomic Allow that records the attempt still runs after the store read (OC-0377: an outage charges nothing); the cap boundary is unchanged. Test: api/totp_cap_before_store_test.go — budget filled through the limiter, users table hidden, expects 429 (RED: 500 "temporarily unavailable", the store was read first). 2. A verify whose claim loses at Consume releases the code it marked, so a winner mid-recovery (Consume → issueSession failed → Restore) is not left with a live token behind a dead code until the authenticator rolls over. Test: service/auth_lost_claim_test.go — forces the interleaving through the store's GetUserByID (RED: "the losing claim left its code marked as used"). Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01A17Uq3d2C36rN82Jitf3wo * docs(b3-9): evidence block, status line, step-table row; README B3 row (PR #1454) Per finding: pre-squash SHA, RED and GREEN lines, revert-proof, the two negative controls, what changed; the ledger diff and the re-derived count paragraphs; auth-slice coverage 402/437 = 92.0% (floor 392/427 = 91.8%), service/auth.go 250/263 = 95.1%; the otel-tagged run. OC-0323 recorded as riding B3-8. hp-3-scorecard untouched — the owner signs it as drafted. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01A17Uq3d2C36rN82Jitf3wo --------- Co-authored-by: Claude Fable 5 <noreply@anthropic.com>