fix: break the TechLog CSRF bootstrap cycle and close the review's fix-round-1 items
C1 (Critical): getStudioSession was stamped with the same TECH_LOG_STUDIO_SESSION auth profile as every other Studio operation, and that profile requires the CSRF header it is getStudioSession's own job to issue -- an unconditional cycle that recursed without bound in HTTP mode. Fixed with a credential-free TECH_LOG_STUDIO_BOOTSTRAP auth profile for getStudioSession alone, a synchronous re-entrancy guard in createCsrfTokenProvider as defense in depth, and a throwing stub in place of the prior `let x!: T` assertion. Added a composition-level regression test that wires the real executor, CSRF provider, and credential-attach function together and proves getStudioSession dispatches exactly once while its token reaches both a JSON operation and the multipart upload. Also: invalidate the cached CSRF token on a 401/403 from the upload path (I2), a throwing useStudioAssetGateway() accessor so Task 11 cannot silently compile a null-gateway UI (I3), and the M1-M5 minors from the review (guard a malformed success body, cover the untested error fallbacks, align aborted uploads with the JSON path's non-retryable CANCELLED mapping, derive the credential header name from one source instead of two, and correct the adapter review doc's operation count). Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
This commit is contained in:
co-authored by
Claude Opus 5
parent
c9c832c365
commit
2cab4974b7
@@ -83,6 +83,25 @@ export const REST_AUTH_PROFILES = Object.freeze({
|
||||
allowedCredentialHeaders: Object.freeze(["x-csrf-token"] as const),
|
||||
requiredCredentialHeaders: Object.freeze(["x-csrf-token"] as const),
|
||||
}),
|
||||
/**
|
||||
* Task 7 fix round 1 (C1). `getStudioSession` is the operation that
|
||||
* *issues* the CSRF token, so it cannot itself require one —
|
||||
* `TECH_LOG_STUDIO_SESSION.requiredCredentialHeaders` demanding
|
||||
* `x-csrf-token` on every request using that profile, including this one,
|
||||
* made fetching the token depend on already having it. Same session cookie
|
||||
* (`SAME_ORIGIN_COOKIE` / `credentials: "include"`), but zero credential
|
||||
* headers allowed or required: `attachCredentials` returns `READY` with an
|
||||
* empty header set for this profile, synchronously, so no cycle exists.
|
||||
* Only `getStudioSession` uses this profile; the other seventeen operations
|
||||
* stay on `TECH_LOG_STUDIO_SESSION`.
|
||||
*/
|
||||
TECH_LOG_STUDIO_BOOTSTRAP: Object.freeze({
|
||||
authProfileId: "TECH_LOG_STUDIO_BOOTSTRAP",
|
||||
transport: "SAME_ORIGIN_COOKIE",
|
||||
credentials: "include",
|
||||
allowedCredentialHeaders: Object.freeze([]),
|
||||
requiredCredentialHeaders: Object.freeze([]),
|
||||
}),
|
||||
} satisfies Readonly<Record<string, RestAuthProfile>>);
|
||||
|
||||
function isCredentialHeaderName(value: unknown): value is CredentialHeaderName {
|
||||
|
||||
Reference in New Issue
Block a user