feat: block CI on the frontend type check
svelte-check is clean, so it gates like the rest. eslint still reports without failing, now for two findings rather than 87: both are the unsanitized book description, written up in TODO.md.
This commit is contained in:
@@ -126,6 +126,35 @@ that set is worthwhile and will surface a fresh batch of findings.
|
||||
|
||||
## Frontend
|
||||
|
||||
### Book descriptions are rendered as unsanitized HTML
|
||||
|
||||
`frontend/src/lib/components/ui/collapsible-text/collapsible-text.svelte` — lines 42 and 45
|
||||
|
||||
The component renders `{@html text}`, and its only caller is the book detail page:
|
||||
`<CollapsibleText text={book.description} maxLength={500} />`. So whatever is in
|
||||
`Book.description` reaches the DOM as markup.
|
||||
|
||||
The Calibre importer is fine — `services/calibre.py:401` passes comments through
|
||||
`strip_html`, because Calibre stores HTML there. But `strip_html` is used **nowhere else in
|
||||
the backend**, and `EpubExtractor._extract_description` returns
|
||||
`epub.get_metadata("DC", "description")[0][0]` verbatim. EPUB `dc:description` routinely
|
||||
carries markup, so an uploaded book with `<img src=x onerror=…>` in that field executes
|
||||
script on the book page, with the session cookie in scope. Metadata edited through the UI
|
||||
is stored unfiltered too.
|
||||
|
||||
This is the same class as the scripted-EPUB item below — untrusted file content reaching an
|
||||
origin that holds a session — by a different route, and it does not need `allow-scripts` to
|
||||
work.
|
||||
|
||||
Fix: sanitize at ingest, next to where Calibre already does. Reuse `strip_html` in
|
||||
`_extract_description` if descriptions should be plain text, or run an allowlist sanitizer if
|
||||
the formatting is worth keeping. Either way the stored rows need backfilling through the same
|
||||
helper, since the validators only fire on write. Dropping `{@html}` to `{text}` in the
|
||||
component fixes the display side but leaves the payload in the database.
|
||||
|
||||
These are the only two findings `pnpm exec eslint .` still reports; CI's eslint step stops
|
||||
being `continue-on-error` once they are gone.
|
||||
|
||||
### Scripted EPUBs run against the app origin
|
||||
|
||||
**This is a regression from the foliate-js migration, not a pre-existing gap.**
|
||||
|
||||
Reference in New Issue
Block a user