Please report security issues privately via GitHub Security Advisories ("Report a vulnerability" on the repository's Security tab) rather than a public issue. You'll get a response as soon as is practical for a hobby project.
Comic Cascade is a read-only comic reader with no built-in authentication — by design. It binds a port and expects to be fronted by your own reverse proxy / SSO if you need access control (see the README "Security" section). "Add authentication" is therefore intentionally out of scope for the app itself.
What the app does defend against — even for an allowed/LAN user, and for an untrusted comic file placed in a library (e.g. on a shared NAS):
- Path traversal — every library/path is resolved and confined to its root
(
app/paths.py); requests can't read files outside the configured libraries. - Zip-Slip — extracted pages are written by index (
00000.jpg…), never using archive-internal names as output paths. - Symlink smuggling — symlinks inside a RAR/7z are never followed or served; only regular files that resolve inside the extraction dir are used.
- Decompression bombs — extraction is capped by
max_archive_bytes(total uncompressed) and PDFs bymax_pdf_pages; oversized comics are rejected422. - Command injection —
unaris run with an argument list (never a shell) and a--end-of-options separator; a single output file is size-limited viaRLIMIT_FSIZE. - SQL injection — all cache-index queries are parameterized.
- Info disclosure —
/api/healthzreports readiness only (no host paths) and extraction errors are logged server-side, not returned to clients. - Container — the image runs as a non-root user; responses send
X-Content-Type-Options: nosniff.
CASCADE_USER_HEADER + CASCADE_ADMINS (and/or CASCADE_GROUPS_HEADER +
CASCADE_ADMIN_GROUPS) let you restrict library management to named users or
groups, using an identity your forward-auth proxy injects. The app performs no
authentication itself — it trusts the header(s). That trust is only valid if
your proxy sets the header and strips any client-supplied copy, and the app is
reachable only through the proxy. Exposed directly, the header is trivially
spoofable. Unset by default (anonymous/global). It gates management only —
reading is always open — and fails closed (no recognized identity ⇒ not an admin).
- Cache eviction race: under heavy concurrent extraction pressure, an archive
being streamed by an in-flight
/api/pagerequest can be evicted, yielding a retriable error for that page (no data loss). A simple retry succeeds. - Decompression-bomb residual (many tiny files): the per-comic byte cap plus a 512 MB free-space floor bound disk use, but a pathological many-files archive may briefly consume disk before being rolled back.
- Directory picker exposure: when
CASCADE_BROWSE_ROOTis set (UI library management), the picker exposes that root's directory structure to anyone who can reach the app. Keep it unset for unauthenticated installs.
Security fixes target the latest 0.1.x release.