docs: round-3 currency pass
All checks were successful
CI / Unit & Component Tests (pull_request) Successful in 4m17s
CI / OCR Service Tests (pull_request) Successful in 22s
CI / Backend Unit Tests (pull_request) Successful in 4m9s
CI / fail2ban Regex (pull_request) Successful in 47s
CI / Semgrep Security Scan (pull_request) Successful in 23s
CI / Compose Bucket Idempotency (pull_request) Successful in 1m7s
All checks were successful
CI / Unit & Component Tests (pull_request) Successful in 4m17s
CI / OCR Service Tests (pull_request) Successful in 22s
CI / Backend Unit Tests (pull_request) Successful in 4m9s
CI / fail2ban Regex (pull_request) Successful in 47s
CI / Semgrep Security Scan (pull_request) Successful in 23s
CI / Compose Bucket Idempotency (pull_request) Successful in 1m7s
- extractText.ts states the real safety invariant (output is text-only; JOURNEY intros arrive unsanitised by design) - geschichte README: stale glyph/pill cells updated, formatAuthorName no longer claims an email fallback, formatDocumentMetaLine documented - reader spec HTMLs: bg-white list-card cell and the mobile BottomSheet rows struck with the implemented decision (inline metabar actions) Review round 3: Nora (2), Markus (2), Felix, Elicit (3). Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
This commit is contained in:
@@ -23,16 +23,17 @@ Utilities: `utils.ts`.
|
||||
| `JourneyItemRow.svelte` | `JourneyEditor.svelte` | Item row: drag handle, move-up/down, note textarea (PATCH on blur), inline remove confirm |
|
||||
| `JourneyAddBar.svelte` | `JourneyEditor.svelte` | Two add buttons: document picker (`DocumentPickerDropdown`) and interlude draft form |
|
||||
| `GeschichtenCard.svelte` | Person-detail sidebar | Sidebar card showing the 3 most recent stories linked to a person — NOT the list page |
|
||||
| `GeschichteListRow.svelte` | `/geschichten` (list) | Row component for the list; shows JOURNEY type badge (`text-xs` orange pill) |
|
||||
| `GeschichteListRow.svelte` | `/geschichten` (list) | Editorial list row: meta column (avatar, author, date, REISE badge), title + excerpt content column |
|
||||
| `StoryReader.svelte` | `/geschichten/[id]` (STORY branch) | Renders sanitised rich-text body, persons section, documents section, and author actions |
|
||||
| `JourneyReader.svelte` | `/geschichten/[id]` (JOURNEY branch) | Renders intro paragraph, ordered items list, empty-state; delegates to ItemCard/Interlude |
|
||||
| `JourneyItemCard.svelte` | `JourneyReader.svelte` | Whole-card `<a>` for a document item; dated/undated aria-label, ✎ annotation glyph |
|
||||
| `JourneyInterlude.svelte` | `JourneyReader.svelte` | Typographic aside between letters; ❦ glyph, `aria-label="Kuratorennotiz"` |
|
||||
| `JourneyItemCard.svelte` | `JourneyReader.svelte` | Card per document item: title, meta line (date · von X an Y), "Brief öffnen →" link, mint-border note |
|
||||
| `JourneyInterlude.svelte` | `JourneyReader.svelte` | Left-accent interlude box between letters (mode-aware tokens); `aria-label="Kuratorennotiz"` |
|
||||
|
||||
## utils.ts
|
||||
|
||||
`formatAuthorName(author)` — joins `firstName + lastName`, falls back to `email` (for list/summary shapes).
|
||||
`formatAuthorDisplayName(author)` — returns `displayName` (for detail `AuthorView` shape).
|
||||
`formatAuthorName(author)` — joins `firstName + lastName`, falls back to the localized `person_unknown` key (for list/summary shapes; email is not exposed).
|
||||
`formatAuthorDisplayName(author)` — returns `displayName`, localizing the server's `[Unbekannt]` fallback (for detail `AuthorView` shape).
|
||||
`formatDocumentMetaLine(doc)` — `"12.07.1938 · von Franz an Emma"`; shared by `JourneyItemCard`, `JourneyItemRow`, and the story doc-reference cards.
|
||||
`formatPublishedAt(publishedAt, style)` — wraps `formatDate` with null check; `style` is `'short'` (list) or `'long'` (detail).
|
||||
|
||||
## Public list is PUBLISHED-only
|
||||
|
||||
@@ -1,13 +1,18 @@
|
||||
/**
|
||||
* **Not a sanitizer.** This module extracts visible text from a (presumed
|
||||
* already-sanitised) HTML string for excerpt rendering. It is safe ONLY
|
||||
* because the Geschichte body is sanitised against the OWASP allow-list
|
||||
* on the server before persistence, and via DOMPurify on render.
|
||||
* **Not a sanitizer.** This module extracts visible text from an HTML (or
|
||||
* plain-text) string for excerpt rendering. The safety invariant is: the
|
||||
* OUTPUT must only ever be rendered via Svelte text interpolation — never
|
||||
* `{@html}`. The DOMParser document is inert (scripts don't execute), but
|
||||
* the returned string is whatever text the input carried.
|
||||
*
|
||||
* Note on inputs: STORY bodies are additionally sanitised against the OWASP
|
||||
* allow-list on the server; JOURNEY intros are stored VERBATIM (unsanitised
|
||||
* by design — see GeschichteService.bodyForType) and arrive here untrusted.
|
||||
*
|
||||
* Do not use these helpers to defend against XSS — `safeHtml()` in
|
||||
* `./sanitize.ts` is the only sanitiser. Calling `extractText()` on
|
||||
* untrusted input that has not been sanitised does not protect against
|
||||
* `javascript:` URLs, event-handler attributes, or `<svg/onload>` payloads.
|
||||
* untrusted input does not protect against `javascript:` URLs,
|
||||
* event-handler attributes, or `<svg/onload>` payloads.
|
||||
*/
|
||||
|
||||
/**
|
||||
|
||||
Reference in New Issue
Block a user