Security
The authoritative copy is SECURITY.md in the repository. This page says the same things.
Reporting a vulnerability
Machine-readable contact
/security.txt carries the same contacts in RFC 9116 format. It is served from the site root rather than /.well-known/, because GitHub Pages does not serve paths whose segment begins with a dot.
Report privately through GitHub Security Advisories, or by email to fabrizio.salmi@gmail.com. Please do not open a public issue.
Expect an acknowledgement within a few days. This is a spare-time project: no formal SLA, no bug bounty. Only the latest commit on main, and the image built from it, is supported.
Threat model
pdf-ocr is a single-tenant, self-hosted tool. It accepts an uploaded PDF and hands it to Poppler and an OCR engine — large C and C++ codebases that parse untrusted input. Putting it on the public internet exposes that parsing surface to anyone who finds it.
What it does provide
SECRET_KEYis required, so session cookies are signed with a stable key.- Uploads need a
.pdfextension and the%PDF-magic bytes. - Upload size and page count are capped; pages render in small batches, so peak memory does not scale with document length.
- Engine, output format and language are validated against allowlists.
- A conversion is readable only by the session that started it, and the result's path on disk is never sent to the client.
- Baseline security headers including a same-origin CSP. No third-party assets are loaded — Tailwind is vendored.
- The image runs as an unprivileged user (uid 10001).
- Uploads, results and finished tasks are deleted automatically; a cancelled conversion leaves nothing behind at all.
- A conversion whose worker dies is reported as failed rather than left pending.
- CodeQL (
security-extended) runs on every push and pull request, and weekly.
What it does not provide
- No authentication. Anyone who can reach the port can convert documents.
- No rate limiting. OCR is CPU- and memory-intensive; a handful of concurrent uploads will saturate a small host.
- No sandboxing of Poppler or Tesseract beyond the container boundary.
- No encryption at rest for uploaded or converted files.
- No CSRF tokens. The session cookie is
SameSite=Lax, which keeps mainstream browsers from attaching it to a cross-site POST, but that is a mitigation rather than a guarantee. State-changing routes rely on the session-ownership check. - No audit log. There is no identity to record.
The container hardening is a runtime choice
The image runs unprivileged on its own. The read-only root filesystem, dropped capabilities and no-new-privileges come from how you run it — docker-compose.yml sets them and the deployment command passes the equivalent flags. A bare docker run gives you neither.
Intended deployment
A private network, or behind an authenticating reverse proxy, with TLS terminated in front and SESSION_COOKIE_SECURE=true.
Never set these in a deployment
FLASK_ENV=developmentenables the Werkzeug debugger: arbitrary code execution over HTTP.PDF_OCR_TESTING=1starts the app withoutSECRET_KEY, using a per-process key. The test suite sets it so importing the app needs no configuration.
Neither is set by the image, and neither is enabled in .env.example.