| Channel | Version | Support |
|---|---|---|
beta (main, printifier.apk) |
1.3.x | security fixes land here first |
stable (stable, printifier-stable.apk) |
1.0.x | fixes are cherry-picked per release |
Please open a GitHub Security Advisory (private) or an issue marked security. Include the affected file/route and a minimal reproduction. You'll get a response within a week; fixes ship in the next beta build.
Everything user-facing is client-side. PDF parsing, filtering, composition and OCR run in the browser/WebView. Source files are held in memory (and, for the "recent jobs" convenience list, in the browser's own IndexedDB). There is no account system, no analytics SDK, no third-party network call from the web app.
Data that leaves the device:
- Anonymous usage stat — after a successful job the app POSTs page/byte counts,
color mode and layout to
/api/prints. ThefileNamefield is a truncated SHA-256 hash of the joined file names — real names are never transmitted. - Cloud import (optional, self-hosted only) — if the user pastes a Drive/Dropbox link, the server fetches that URL once and streams it back. The URL (not the file) transits the server; nothing is written to disk or logged.
Android app: ships the entire site inside the APK; it runs from a synthetic local
origin, requests only INTERNET, and allowBackup is disabled. The JS download bridge
sanitizes file names before writing to Downloads/Printifier.
GET /api/cloud-importfollows redirects manually and validates every hop: public DNS-resolved addresses only, private/link-local/CGNAT/loopback ranges blocked, integer/hex/octal IP encodings decoded and checked, 75 MB cap, 90 s total budget. Residual risk: a hostile DNS server re-resolving between validation and fetch (TOCTOU) — acceptable for this threat model; do not deploy the route on networks where that matters./api/printsis unauthenticated by design (aggregate counts only). If you self-host publicly, put a rate limit in front of it.- TypeScript errors are fatal:
tsc --noEmitgates CI and there is noignoreBuildErrorsescape hatch. ESLint runs on every push.
Last full audit: 2026-10-18 (fresh OSV querybatch over every resolved name@version
in bun.lock; prior full audit 2026-10-14 via bun audit).
- Removed unused scaffold packages that carried advisories:
next-auth(critical auth-bypass advisory, never used),@mdxeditor/editor,react-syntax-highlighter,@reactuses/core,next-intl,@tanstack/react-query,uuid,date-fns. - Updated
next16.1.3 → 16.4.0, clearing the critical Image-Optimization RCE (GHSA-2xp9-vwfh-vxw4) and the Windows-hosted RCE (GHSA-p293-qw3h-jr36) advisories. - Removed unused
sharp(GHSA-wq5f-xc86-pv6w, GHSA-rgj7-g3m4-5g8c, GHSA-f88m-g3jw-g9cj — the three Dependabot "high" alerts; sharp is not used anywhere: the static export runs withimages: { unoptimized: true }) and the unusedrecharts+ui/chart.tsxwrapper (itslodashchain carried advisories of its own). - Pinned 17 vulnerable transitive dependencies via
package.json→overrides(ajv, js-yaml, minimatch, brace-expansion, @humanfs/node, flatted, defu, deepmerge-ts, effect, nanoid, source-map-js, baseline-browser-mapping, picomatch, browserslist, @babel/core, postcss) — all in dev/toolchain chains, all fixed in their existing major lines. - Result: 104 advisories (3 critical) → 1 (1 high). The single remaining finding
is
braces 3.0.3(GHSA-vfj7-8cjw-p6xm / CVE-2026-93687, stack-exhaustion ReDoS via deeply nested glob patterns) which has no upstream patch — 3.0.3 is the newest release. It is used only by the ESLint glob chain at lint time on trusted local files, never on user input; risk is accepted until upstream ships a fix. - The shipped client runtime (
next,react,pdfjs-dist,pdf-lib,tesseract.js,zustand,framer-motion) is current and advisory-free. Self-hosters running the API routes should re-runbun auditafterbun installand keep dependencies updated. Dependabot (.github/dependabot.yml) opens weekly update PRs. - About the Dependabot banner over-reporting (and its fix): the repo previously
used the
npmecosystem, which readspackage.jsonranges and ignores bothbun.lockand theoverridesblock — so it alerted on OLD in-range versions of toolchain transitives (the threesharp"high" alerts vianext's optional deps,minimatch/brace-expansion/ajvchains) that are never actually installed. Re-verified on 2026-10-18 by querying the OSV database for every resolvedname@versioninbun.lock(708 packages): exactly ONE real finding — the acceptedbracesentry above. The config now uses Dependabot's nativebunecosystem (readsbun.lock, override-aware), which should recompute the alert set from resolved versions. If any phantom alert lingers after the next scan, dismiss it on the Security tab as "Inaccurate" with a pointer to this file. Re-verified again on 2026-10-24 (bun auditagainst the refreshedbun.lock): unchanged — exactly one finding, the acceptedbracesentry.braces3.0.3 is still the newest upstream release (no fix available). - Dependabot PR policy (2026-10-24): the five weekly GitHub-Actions bumps
(checkout v7, upload-pages-artifact v5, configure-pages v6, codeql-action v4,
action-gh-release v3) were merged CI-green. Major-version bumps of runtime
libraries (typescript 7, prisma 7, eslint 10, framer-motion 14, lucide-react 1,
react-day-picker 10, @tanstack/react-table 9, react-resizable-panels 4) are
deliberately deferred and now ignored in
.github/dependabot.yml— the current major lines are advisory-free. In-range minor/patch updates are applied on the main line with the pinned bun 1.3.14: Dependabot's newer bun rewritesbun.locktolockfileVersion: 3, which the pinned toolchain cannot consume, so its lockfile-editing PRs are closed in favour of local updates.