fix(csrf): send X-XSRF-TOKEN on all client-side mutating fetch calls
Some checks failed
CI / Unit & Component Tests (push) Has been cancelled
CI / OCR Service Tests (push) Has been cancelled
CI / Backend Unit Tests (push) Has been cancelled
CI / fail2ban Regex (push) Has been cancelled
CI / Semgrep Security Scan (push) Has been cancelled
CI / Compose Bucket Idempotency (push) Has been cancelled
CI / Unit & Component Tests (pull_request) Successful in 3m34s
CI / OCR Service Tests (pull_request) Successful in 20s
CI / fail2ban Regex (pull_request) Has been cancelled
CI / Semgrep Security Scan (pull_request) Has been cancelled
CI / Compose Bucket Idempotency (pull_request) Has been cancelled
CI / Backend Unit Tests (pull_request) Has been cancelled
Some checks failed
CI / Unit & Component Tests (push) Has been cancelled
CI / OCR Service Tests (push) Has been cancelled
CI / Backend Unit Tests (push) Has been cancelled
CI / fail2ban Regex (push) Has been cancelled
CI / Semgrep Security Scan (push) Has been cancelled
CI / Compose Bucket Idempotency (push) Has been cancelled
CI / Unit & Component Tests (pull_request) Successful in 3m34s
CI / OCR Service Tests (pull_request) Successful in 20s
CI / fail2ban Regex (pull_request) Has been cancelled
CI / Semgrep Security Scan (pull_request) Has been cancelled
CI / Compose Bucket Idempotency (pull_request) Has been cancelled
CI / Backend Unit Tests (pull_request) Has been cancelled
hooks.server.ts already forwards the CSRF token for server-side fetch (form actions, load). Client-side XHR calls bypassed it, causing Spring Security to return 403 before PermissionAspect even ran. Adds getCsrfToken/withCsrf/makeCsrfFetch to cookies.ts. useTranscriptionBlocks wraps its injectable fetchImpl with makeCsrfFetch (covers all block mutations and saveBlockWithConflictRetry). useBlockAutoSave, TranscriptionEditView, BulkDocumentEditLayout, OcrTrainingCard, and SegmentationTrainingCard apply withCsrf inline. Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
This commit was merged in pull request #648.
This commit is contained in:
@@ -1,3 +1,46 @@
|
||||
/**
|
||||
* Reads the XSRF-TOKEN cookie set by Spring Security's CookieCsrfTokenRepository.
|
||||
* Returns null outside the browser or when the cookie is absent.
|
||||
*/
|
||||
export function getCsrfToken(): string | null {
|
||||
if (typeof document === 'undefined') return null;
|
||||
const match = document.cookie.match(/(?:^|;\s*)XSRF-TOKEN=([^;]+)/);
|
||||
return match ? decodeURIComponent(match[1]) : null;
|
||||
}
|
||||
|
||||
/**
|
||||
* Merges the X-XSRF-TOKEN header into a RequestInit so Spring Security's
|
||||
* CSRF filter accepts the request. Safe to call server-side (no-op when the
|
||||
* cookie is absent).
|
||||
*/
|
||||
export function withCsrf(init?: RequestInit): RequestInit {
|
||||
const token = getCsrfToken();
|
||||
if (!token) return init ?? {};
|
||||
const headers = new Headers(init?.headers);
|
||||
headers.set('X-XSRF-TOKEN', token);
|
||||
return { ...init, headers };
|
||||
}
|
||||
|
||||
/**
|
||||
* Wraps a fetch implementation so that every state-mutating call (POST, PUT,
|
||||
* PATCH, DELETE) automatically includes the X-XSRF-TOKEN header. GET/HEAD
|
||||
* requests pass through unchanged.
|
||||
*
|
||||
* Used to CSRF-protect client-side hooks that accept an injectable fetchImpl.
|
||||
* In unit tests the injected mock is wrapped but getCsrfToken() returns null
|
||||
* (no browser cookie), so no header is added and existing test expectations
|
||||
* are unaffected.
|
||||
*/
|
||||
export function makeCsrfFetch(inner: typeof fetch): typeof fetch {
|
||||
return (input: RequestInfo | URL, init?: RequestInit): Promise<Response> => {
|
||||
const method = (init?.method ?? 'GET').toUpperCase();
|
||||
if (['POST', 'PUT', 'PATCH', 'DELETE'].includes(method)) {
|
||||
return inner(input, withCsrf(init));
|
||||
}
|
||||
return inner(input, init);
|
||||
};
|
||||
}
|
||||
|
||||
/**
|
||||
* Extracts the fa_session cookie value from a list of Set-Cookie response headers.
|
||||
*
|
||||
|
||||
Reference in New Issue
Block a user