Non-image attachments with allowlist and page attachment UI #61

Closed
opened 2026-07-04 14:52:31 +02:00 by fable-5 · 1 comment
Collaborator

Context

Kickoff added general file attachments (PDF, office files, …) with a configurable type allowlist and size limits (ADR 0011).

Scope

Extend uploads (#27): instance-setting MIME/extension allowlist (images always allowed; sensible default list), SVG policy setting (reject | sanitize — implement sanitization with a maintained library), Content-Disposition: attachment for non-image types; page attachments section UI (upload, list with icon/size/uploader, insert-as-link into the document, delete), pond-level file manager for Pond Admins (all files, orphan indicator, usage total).

Acceptance criteria

  • allowed types upload and download intact; disallowed types are rejected with the localized allowlist error
  • sanitized SVG has scripts/event handlers stripped (fixture test); reject mode rejects
  • attachment links in documents download with correct filename and never render inline as HTML
  • file manager shows usage consistent with pond_usage

Technical notes

  • ADR 0011, security.md §Uploads.

Dependencies

Depends on #27, #52.

Size: ~1.5 days


Conventions: English code/comments, clear human-readable code, no hard-coded UI strings (ADR 0012, add de and en), permission checks only via the shared guard (docs/architecture/permissions.md). Read the referenced ADRs before starting.

## Context Kickoff added general file attachments (PDF, office files, …) with a configurable type allowlist and size limits (ADR 0011). ## Scope Extend uploads (#27): instance-setting MIME/extension allowlist (images always allowed; sensible default list), SVG policy setting (reject | sanitize — implement sanitization with a maintained library), `Content-Disposition: attachment` for non-image types; page attachments section UI (upload, list with icon/size/uploader, insert-as-link into the document, delete), pond-level file manager for Pond Admins (all files, orphan indicator, usage total). ## Acceptance criteria - [ ] allowed types upload and download intact; disallowed types are rejected with the localized allowlist error - [ ] sanitized SVG has scripts/event handlers stripped (fixture test); reject mode rejects - [ ] attachment links in documents download with correct filename and never render inline as HTML - [ ] file manager shows usage consistent with `pond_usage` ## Technical notes - ADR 0011, security.md §Uploads. ## Dependencies Depends on #27, #52. **Size**: ~1.5 days --- *Conventions: English code/comments, clear human-readable code, no hard-coded UI strings (ADR 0012, add `de` **and** `en`), permission checks only via the shared guard (docs/architecture/permissions.md). Read the referenced ADRs before starting.*
fable-5 added this to the M6 — Import, export & attachments milestone 2026-07-04 14:52:31 +02:00
fable-5 added the
backend
frontend
labels 2026-07-04 14:52:31 +02:00
Collaborator

#61 verifiziert & abgeschlossen — Nicht-Bild-Anhänge mit Allowlist, SVG-Policy und Dateiverwaltung (Start M6).

Pipeline (30891f9): alle 7 Kontexte grün — CI (Lint/Typecheck/Test, i18n:check, api-db 184), Auth-e2e-Pack inkl. neuem attachments-Pack, Build → Deploy-Test → Smoke → Promote-Int.

Live auf Test + Int (fixture-admin):

  • Allowlisted Nicht-Bild (PDF) → 201, ausgeliefert mit Content-Disposition: attachment; filename="verify.pdf" + nosniff (nie inline).
  • Nicht erlaubte Endung (.exe) → 400 upload_type_not_allowed.
  • SVG-Upload (Policy sanitize) → gespeichertes SVG ohne <script> und ohne onload, <rect> bleibt erhalten.
  • Pond-Dateiverwaltung GET /ponds/:id/files → Dateiliste + storageBytesUsed/storageBytesLimit (Default 1 GiB).

Umfang (ADR 0011, security.md §Uploads):

  • Instance-Settings upload.allowedExtensions (Bilder immer erlaubt) + upload.svgPolicy (reject | sanitize, DOMPurify: Skripte/Event-Handler/foreignObject entfernt).
  • Nicht-Raster-Typen (Office, PDF, SVG) immer als Download (Content-Disposition: attachment + nosniff); Raster-Bilder bleiben inline (Page-Embeds). Sanitisiertes SVG wird byte-genau neu verrechnet (pond_usage = Platte).
  • Endpunkte: GET /ponds/:id/files (pond_admin, Nutzung+Orphan-Flag), POST/GET /pages/:id/files (Page-Attachments-Sektion). Fehlercode upload_type_not_allowed (de+en).
  • UI: Page-Attachments-Sektion (Upload/Liste mit Typ-Icon/Größe/Uploader, Als-Link-einfügen, Löschen), Pond-Dateiverwaltung in den Teich-Einstellungen, Admin-Upload-Settings. Neuer files-i18n-Namespace.

Tests: 6 neue api-db-Fälle (Allowlist accept/reject, renamed-.html-as-.png, SVG sanitize + reject, Page-Listing, Manager-Usage/Orphan, Non-Admin-Deny) + neuer e2e-Pack attachments.spec.ts (3/3) mit CI-Step.

Nebenbefund (nicht #61): Der lokale, nicht in CI laufende Test image.spec.ts › alt text is editable and persists schlägt fehl — er wartet auf einen seit #36 (REST-Autosave retired) entfernten „Saved"-Statusindikator. Verifiziert: schlägt auf sauberem HEAD identisch fehl (kein Regress durch #61). Zur separaten Bereinigung notiert.

**#61 verifiziert & abgeschlossen** — Nicht-Bild-Anhänge mit Allowlist, SVG-Policy und Dateiverwaltung (Start M6). **Pipeline** (`30891f9`): alle 7 Kontexte grün — CI (Lint/Typecheck/Test, i18n:check, api-db 184), Auth-e2e-Pack inkl. neuem `attachments`-Pack, Build → Deploy-Test → Smoke → Promote-Int. **Live auf Test + Int** (`fixture-admin`): - Allowlisted Nicht-Bild (PDF) → `201`, ausgeliefert mit `Content-Disposition: attachment; filename="verify.pdf"` + `nosniff` (nie inline). - Nicht erlaubte Endung (`.exe`) → `400 upload_type_not_allowed`. - SVG-Upload (Policy `sanitize`) → gespeichertes SVG ohne `<script>` und ohne `onload`, `<rect>` bleibt erhalten. - Pond-Dateiverwaltung `GET /ponds/:id/files` → Dateiliste + `storageBytesUsed`/`storageBytesLimit` (Default 1 GiB). **Umfang** (ADR 0011, security.md §Uploads): - Instance-Settings `upload.allowedExtensions` (Bilder immer erlaubt) + `upload.svgPolicy` (reject | sanitize, DOMPurify: Skripte/Event-Handler/foreignObject entfernt). - Nicht-Raster-Typen (Office, PDF, SVG) immer als Download (`Content-Disposition: attachment` + `nosniff`); Raster-Bilder bleiben inline (Page-Embeds). Sanitisiertes SVG wird byte-genau neu verrechnet (`pond_usage` = Platte). - Endpunkte: `GET /ponds/:id/files` (pond_admin, Nutzung+Orphan-Flag), `POST`/`GET /pages/:id/files` (Page-Attachments-Sektion). Fehlercode `upload_type_not_allowed` (de+en). - UI: Page-Attachments-Sektion (Upload/Liste mit Typ-Icon/Größe/Uploader, Als-Link-einfügen, Löschen), Pond-Dateiverwaltung in den Teich-Einstellungen, Admin-Upload-Settings. Neuer `files`-i18n-Namespace. **Tests**: 6 neue api-db-Fälle (Allowlist accept/reject, renamed-.html-as-.png, SVG sanitize + reject, Page-Listing, Manager-Usage/Orphan, Non-Admin-Deny) + neuer e2e-Pack `attachments.spec.ts` (3/3) mit CI-Step. **Nebenbefund (nicht #61):** Der lokale, **nicht in CI** laufende Test `image.spec.ts › alt text is editable and persists` schlägt fehl — er wartet auf einen seit #36 (REST-Autosave retired) entfernten „Saved"-Statusindikator. Verifiziert: schlägt auf sauberem HEAD identisch fehl (kein Regress durch #61). Zur separaten Bereinigung notiert.
Sign in to join this conversation.
No project
No Assignees
2 Participants
Notifications
Due Date
The due date is invalid or out of range. Please use the format 'yyyy-mm-dd'.

No due date set.

Dependencies

No dependencies set.

Reference: stwaidele/dorfteich#61
No description provided.