SECURITY.md
Rama main · réplica de lectura del repositorio original.
# Security Policy
## Reporting a vulnerability
Please report security issues privately through
[GitHub Security Advisories](https://github.com/fabriziosalmi/pdf-ocr/security/advisories/new),
or by email to fabrizio.salmi@gmail.com. Do not open a public issue for a
vulnerability.
Expect an acknowledgement within a few days. This is a spare-time project, so
there is no formal SLA and no bug bounty.
## Supported versions
Only the latest commit on `main` (and the image built from it) is supported.
## Threat model — read this before exposing the app
`pdf-ocr` is designed as a **single-tenant, self-hosted tool**. It accepts an
uploaded PDF and hands it to Poppler and an OCR engine, both of which are large
C/C++ codebases that parse untrusted input. If you put it on the public internet
you are exposing that parsing surface to anyone.
What the app does provide:
- `SECRET_KEY` is required outside development, so session cookies are signed
with a stable key.
- Uploads must have a `.pdf` extension **and** the `%PDF-` magic bytes.
- Upload size (`MAX_UPLOAD_MB`, default 64 MB) and page count (`MAX_PAGES`,
default 200) are capped; pages are rendered in small batches so peak memory
does not scale with document length.
- Engine, output format and language are validated against allowlists.
- A conversion is only readable by the browser 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 under `static/vendor/`).
- The image runs as an unprivileged user (uid 10001). The read-only root
filesystem, dropped capabilities and `no-new-privileges` are **runtime**
options, not properties of the image: `docker-compose.yml` sets them, and the
`docker run` command in the README passes the equivalent flags. A plain
`docker run` without them still runs unprivileged, but with a writable root
filesystem and the default capability set.
- 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 after
`STALE_TASK_TIMEOUT` rather than pending forever.
- Static analysis (CodeQL, `security-extended`) runs on every push and pull
request, plus weekly, because its query pack updates independently of this
code.
What it does **not** provide, and what you must add yourself before exposing it:
- **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. Put it behind a reverse proxy
that enforces limits, or behind an authenticating proxy.
- **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, not on a token.
- **No audit log.** Conversions are logged, but nothing is retained about who
did what — there is no identity to retain.
The intended deployment is on a private network, or behind an authenticating
reverse proxy, with TLS terminated in front of it (set
`SESSION_COOKIE_SECURE=true` in that case).
## Never set these in a deployment
- **`FLASK_ENV=development`** turns on the Werkzeug debugger, which allows
arbitrary code execution over HTTP. It exists for local development only.
- **`PDF_OCR_TESTING=1`** makes the app start without `SECRET_KEY`, using a key
generated per process. The test suite sets it so importing the app does not
require configuration; in a deployment it means every worker signs cookies
with a different key and every restart invalidates all sessions.
Neither is read from `.env.example`, and neither is set by the image.