fix(ocr): send CSRF token when starting an OCR run #705

Merged
marcel merged 2 commits from fix/ocr-trigger-csrf into main 2026-05-31 22:29:01 +02:00
Owner

Problem

Starting an OCR run failed with "Sitzungsfehler. Bitte laden Sie die Seite neu."

Root cause

createOcrJob (frontend/src/lib/ocr/useOcrJob.svelte.ts) defaulted its fetchImpl to bare fetch. The document page instantiates it without a fetchImpl, so the OCR trigger POST /api/documents/{id}/ocr carried no X-XSRF-TOKEN header. Spring Security's CSRF filter rejected it, returning CSRF_TOKEN_MISSING, which maps to the German "Sitzungsfehler…" message.

This was the one remaining mutating client call still on bare fetch — the transcription autosave hook (useBlockAutoSave) already used csrfFetch.

Fix

Default the controller's fetchImpl to csrfFetch. It injects the token only on mutating methods (POST/PUT/PATCH/DELETE) and passes GET polling (/ocr/jobs/{id}, /ocr-status) through unchanged. Injected test mocks are unaffected.

Test

Added a failing-first test asserting the trigger POST carries X-XSRF-TOKEN on the default (no-fetchImpl) path. All 23 tests in the file pass; prettier + eslint clean.

🤖 Generated with Claude Code

## Problem Starting an OCR run failed with **"Sitzungsfehler. Bitte laden Sie die Seite neu."** ## Root cause `createOcrJob` (`frontend/src/lib/ocr/useOcrJob.svelte.ts`) defaulted its `fetchImpl` to bare `fetch`. The document page instantiates it without a `fetchImpl`, so the OCR trigger `POST /api/documents/{id}/ocr` carried **no `X-XSRF-TOKEN` header**. Spring Security's CSRF filter rejected it, returning `CSRF_TOKEN_MISSING`, which maps to the German "Sitzungsfehler…" message. This was the one remaining mutating client call still on bare `fetch` — the transcription autosave hook (`useBlockAutoSave`) already used `csrfFetch`. ## Fix Default the controller's `fetchImpl` to `csrfFetch`. It injects the token only on mutating methods (POST/PUT/PATCH/DELETE) and passes GET polling (`/ocr/jobs/{id}`, `/ocr-status`) through unchanged. Injected test mocks are unaffected. ## Test Added a failing-first test asserting the trigger POST carries `X-XSRF-TOKEN` on the default (no-`fetchImpl`) path. All 23 tests in the file pass; prettier + eslint clean. 🤖 Generated with [Claude Code](https://claude.com/claude-code)
marcel added 1 commit 2026-05-31 21:10:14 +02:00
fix(ocr): send CSRF token when starting an OCR run
All checks were successful
CI / Unit & Component Tests (pull_request) Successful in 3m16s
CI / OCR Service Tests (pull_request) Successful in 21s
CI / Backend Unit Tests (pull_request) Successful in 3m38s
CI / fail2ban Regex (pull_request) Successful in 42s
CI / Semgrep Security Scan (pull_request) Successful in 19s
CI / Compose Bucket Idempotency (pull_request) Successful in 1m5s
aab4fe37ae
The OCR trigger POST went through bare `fetch`, so it carried no
X-XSRF-TOKEN header. Spring Security rejected it and the UI showed
"Sitzungsfehler. Bitte laden Sie die Seite neu." (CSRF_TOKEN_MISSING).

Default the job controller's fetchImpl to csrfFetch — matching the
autosave hook — so mutating requests are CSRF-protected while GET
polling passes through unchanged.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
marcel added 1 commit 2026-05-31 22:10:28 +02:00
refactor(ocr): CSRF-wrap injected fetchImpl too, not just the default
All checks were successful
CI / Unit & Component Tests (pull_request) Successful in 3m19s
CI / OCR Service Tests (pull_request) Successful in 21s
CI / Backend Unit Tests (pull_request) Successful in 3m33s
CI / fail2ban Regex (pull_request) Successful in 43s
CI / Semgrep Security Scan (pull_request) Successful in 21s
CI / Compose Bucket Idempotency (pull_request) Successful in 1m2s
CI / Unit & Component Tests (push) Successful in 3m24s
CI / OCR Service Tests (push) Successful in 20s
CI / Backend Unit Tests (push) Successful in 3m32s
CI / fail2ban Regex (push) Successful in 44s
CI / Semgrep Security Scan (push) Successful in 21s
CI / Compose Bucket Idempotency (push) Successful in 1m2s
nightly / deploy-staging (push) Successful in 3m47s
246568301a
Mirror the useTranscriptionBlocks pattern: makeCsrfFetch(options.fetchImpl
?? fetch) wraps both the default and any injected fetch, so CSRF protection
holds regardless of how the hook is constructed — defense-in-depth against a
future caller injecting a bare fetch. Simplifies the CSRF test to assert on
the injected path instead of stubbing global fetch.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
marcel merged commit 246568301a into main 2026-05-31 22:29:01 +02:00
marcel deleted branch fix/ocr-trigger-csrf 2026-05-31 22:29:02 +02:00
Sign in to join this conversation.
No Reviewers
No Label
1 Participants
Notifications
Due Date
No due date set.
Dependencies

No dependencies set.

Reference: marcel/familienarchiv#705