Technical notes
Security model
What reader.me protects against, what it doesn't, and how decisions like "use AES-128 instead of AES-256" or "keep 'unsafe-inline' in CSP" were actually made. Written for security reviewers and skeptical readers — no marketing.
Threat model
We do defend against
- Document leak via upload. The whole architecture is built to make uploading documents structurally impossible.
- Document leak via third-party JS. The CSP locks
script-srcdown to'self'+ a small allowlist. - Malicious PDFs targeting PDF.js. Pinned to ≥4.2.67 (CVE-2024-4367 fix) with
isEvalSupported: falseandenableXfa: false. - Password brute-force on PDF Protect. AES-128 with random IV — conservative choice over the AES-256 mode with known hash-iteration weaknesses (Cryptanalysis of PDF Security Handler, 2020).
- XSS via filename. All filenames rendered as text; metadata strings written as
PDFHexString.fromText. - Memory pressure on big files. Operations over 20 MB dispatched to a Comlink Web Worker with zero-copy transferables.
We do not defend against
- Browser-extension keyloggers / screen recorders. An extension with
activeTabcan read everything we read. Outside our perimeter. - OS-level compromise. If the host machine is rooted, the attacker has the file already.
- Network operator MitM with cooperating CA. A nation-state attacker with a CA can do bad things to any HTTPS site.
- CPU side channels. Spectre/Meltdown-class — we rely on browser mitigations.
Content Security Policy
reader-web/public/_headers and is enforced by Cloudflare Pages on every response. Highlights:default-src 'self'— every fetch defaults to first-party.script-src 'self' 'wasm-unsafe-eval' 'unsafe-inline'+ analytics origins. The'unsafe-inline'relaxation is required by Astro hydration; chosen in Phase 1 because hashing inlines hurt LCP by ~600 ms on mobile. Tracked for revisit.connect-srcrestricted to'self'+ analytics origins + Tesseract CDN. No endpoint accepts document data.frame-ancestors 'none'— clickjacking impossible.upgrade-insecure-requests— anything HTTP forced to HTTPS.
Dependency policy & CVE response
- Pinned ranges in
package.jsonwith tilde, patch releases via Dependabot. - Critical-CVE patch SLA: 72 h from public disclosure when the CVE affects code paths we exercise. PDF.js CVE-2024-4367 was the calibrating event.
- Tesseract WASM and trained data are self-hosted at
/tesseract/to avoid third-party CDN dependency for security-critical bytes. - Verify the privacy claim in your browser — DevTools → Network tab while running any tool. Zero document requests leave your machine. See the 30-second verification recipe.
Quality gates in CI
- Engine purity check — enforces that
pdf-engine/core/**never touches DOM, fetch, window, navigator. - Unit tests — Vitest covers every pure function at 97% lines / 100% functions.
- End-to-end tests — Playwright drives real browser flows for merge/compress/split/PDF-A on every PR.
- Accessibility — axe-core asserts zero WCAG 2.1 / 2.2 AA violations on 8 critical routes (incl. RTL).
- Lighthouse CI — performance ≥95, a11y =100, SEO =100 on every push to main.
Reporting a vulnerability
If you find a security issue, please disclose it responsibly via email so users aren't put at risk by a public report:
[email protected] — include a description and minimal reproduction steps.
We aim to acknowledge within 48 h and patch critical issues within 72 h. Credit will be given in the changelog unless you prefer otherwise.
Related
- How it works → — architectural walkthrough.
- Privacy → — plain-language version of the privacy promise.
- vs iLovePDF / Smallpdf → — side-by-side architectural comparison.
reader.me is an idea by David Carrero, built at Color Vivo Internet S.L.