Security and file handling
Every uploaded file is treated as hostile input. This page describes what that means in practice.
Last updated
The assumption everything is built on
Every uploaded file is treated as hostile. Not "probably fine" — hostile. An EPUB is a zip archive full of HTML, CSS, fonts and images from an unknown source, about to be handed to a real browser engine. That is close to a worst case, and the design starts from there.
Archive handling
- Decompression bombs. Every entry’s uncompressed size is read and checked before any decompression happens. Output buffers are pre-allocated at that validated size, so a header claiming one thing and expanding into another cannot exhaust memory. Entries with an implausible compression ratio are refused outright.
- Path traversal. Entry names containing
.., absolute paths, drive letters or null bytes cause the whole archive to be rejected, even though entries are never written to disk. - Entry count and total size. Both are capped, so an archive with a million tiny files does not become a denial of service.
- Integrity. Each entry’s checksum is verified, which is how a truncated download is reported as a damaged file rather than producing a silently broken PDF.
- Duplicate names. The first entry for a name wins; later ones are ignored rather than replacing content that has already been validated.
Markup and stylesheet handling
- Allowlist, not blocklist. Only known-safe elements and attributes survive. Anything unrecognised has its text kept and the element discarded.
- Scripts removed entirely. Script elements, event handler attributes,
javascript:URLs, embedded objects, iframes and forms never reach the renderer. - SVG restricted. SVG is supported, but scripts, foreign objects, event handlers and external references inside it are stripped. SVG data URIs are refused because they are a script carrier.
- External references dropped. A book that points at a remote image, stylesheet or tracker has those references removed during sanitising.
- XML entity expansion refused. Documents declaring custom entities are rejected rather than expanded.
- CSS cleaned. Remote
url()references,@import,expression(),behaviorand binding properties are removed.
The renderer
- No script engine. JavaScript is disabled in the rendering context. There is nothing for a script to run in even if one survived sanitising.
- No network. Every request the renderer makes is answered from an in-memory map of that book’s own files; anything else is refused. It cannot reach the internet, the local network, or a cloud metadata endpoint, which closes off server-side request forgery entirely.
- No filesystem. Book assets are never written to disk, so there are no temporary files to leak or clean up.
- Isolation per job. Each conversion gets a fresh browser context, and the browser process itself is recycled regularly.
- Hard limits. Every job has a wall-clock timeout and runs in a worker separate from the web server, so a pathological file cannot take the site down with it.
Output and delivery
- Every PDF is opened and verified before it is offered: correct header, correct end-of-file marker, parses cleanly, at least one page. Anything that fails is discarded rather than delivered.
- Download filenames are rebuilt from scratch, stripped of control characters, path separators and reserved names, and encoded correctly in the response header.
- Downloads require a secret token separate from the job identifier, compared in constant time.
- Every API response is marked no-store, so no proxy or CDN retains a document.
The website itself
- A strict Content Security Policy that only widens when an analytics or advertising provider is actually configured.
- HSTS, frame denial, nosniff, a restrictive Permissions-Policy and same-origin isolation headers.
- Same-origin checks on every state-changing API request.
- Rate limiting and concurrency limits per client, evaluated atomically.
Reporting a problem
If you find a security issue, please report it before disclosing it publicly — the contact page has the address. Testing against your own uploads is fine; denial-of-service testing and anything affecting other people’s conversions is not.
There is no bug bounty. There is a fast fix and genuine credit if you want it.