Releases: Dim145/fdroid-store
Release list
1.4.5
Patch release — fixes staged APK uploads.
Problem: uploading an APK through the web UI (or creating an app from a staged APK) failed with 400 "Invalid APK: APK basename must be a tempfile-style filename". The inspect step returned 200, but redeeming the staging token (upload-staged) always 400'd.
Cause: the path-injection allowlist in parse_apk only permitted [A-Za-z0-9_], but _materialise_staged_apk names its temp file with a fdroid-staged- prefix. The hyphens were rejected, so every staged redemption failed. Direct uploads slipped through only because they use the default tmp prefix.
Fix: the allowlist now also accepts -. This stays traversal-safe — . and / remain excluded and the barrier still reconstructs the path from a constant temp-dir prefix + the validated basename, so the CodeQL py/path-injection guarantee is intact. Added backend/tests/test_apk_parser_basename.py (staged name accepted, tmp name accepted, traversal/dot/space/wrong-suffix names rejected).
Images (backend / worker / frontend) are published to GHCR by CI for this release — pull :1.4.5 and redeploy the backend (the fix is backend-only; frontend/worker bumps are just the version). Reminder: the worker service must use the *-worker image.
1.4.4
Patch release — large-APK download fix.
Problem: big APKs (100s of MB) could be cut mid-download on slow clients (NS_BINDING_ABORTED / truncated file), with nothing in the app logs, when a proxy in the chain buffered the response to a temp file that filled up.
Changes
- Bundled nginx:
/fdroid/and/r/now useproxy_buffering off+ 3600 s read/send/client timeouts → downloads stream client-paced instead of being buffered to a size-limited temp file. - New opt-in
X_ACCEL_REDIRECT_ENABLED(on by default in the reference compose): local-storage APK downloads are handed to nginx viaX-Accel-Redirectto an internal/_protected/location — sendfile, realContent-Length, native Range/resume, no Python nor backend read-timeout in the byte path. Requires the serving nginx to define/_protected/+ mount the storage volume (already true in the reference compose). Set to0if your front nginx lacks that location.
Images (backend / worker / frontend) are published to GHCR by CI for this release — pull :1.4.4 and redeploy. Reminder: the worker service must use the *-worker image (bundles the JDK for index signing).
1.4.3
Patch release — dependency security.
Clears the 8 Dependabot alerts in the frontend lockfile (npm overrides):
- dompurify 3.4.5 → 3.4.11 (7 advisories; mostly IN_PLACE-mode bypasses not used here — the markdown-view XSS neutralisation was re-verified on 3.4.11)
- markdown-it 14.1.1 → 14.2.0 (smartquotes quadratic-complexity DoS, CVE-2026-48988)
Also bumped from the same dependency tree:
- undici 7.25.0 → 7.28.0 (high; via jsdom, unreachable since DOMPurify makes no network calls)
- @babel/core → 7.29.7 (low, build-time)
npm audit reports 0 vulnerabilities. Container images (backend / worker / frontend) are published to GHCR by CI for this release — pull :1.4.3 and redeploy. Reminder: the worker service must use the *-worker image (bundles the JDK for index signing), never the *-backend image.
1.4.2
Patch release.
1.4.2
- Index signing resolves
jarsignerexplicitly (JAVA_HOME/bin → PATH → bundled JRE) with an actionable error instead of an opaqueFileNotFoundErrorwhen a worker lacks the JDK on its PATH. Index signing only works in the worker image (which bundles the JDK) — never runrebuild_indexfrom the API image.
Also includes 1.4.1 (never released):
- Clears the CodeQL
py/unsafe-deserializationalert on the metadata.yml importer (yaml.safe_load+ a separateyaml.scanalias rejection; same billion-laughs protection).
Container images (backend / worker / frontend) are published to GHCR by CI for this release — pull :1.4.2 or :latest and redeploy.
Release: v1.4.1
Patch on top of 1.4.0: clears the CodeQL py/unsafe-deserialization alert on the metadata.yml importer (yaml.safe_load + a separate yaml.scan alias rejection instead of yaml.load with a SafeLoader subclass). No behaviour change; same billion-laughs protection. Bumps backend, frontend and docs to 1.4.1 (User-Agent stays fdroid-store/1.4 — patch release). Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Release: v1.4.0
Security + hardening release (no new features) covering everything since 1.3.0: stored-XSS fix on the public app page, DNS-rebinding-safe outbound HTTP + SSRF blocklist hardening, HKDF at-rest key derivation, rate-limit / auth hardening (loopback API bind, MFA limits, YAML-alias DoS, locale path validation, proxy header allowlist), the F-Droid index timestamp fix, a dependency refresh (Tiptap v3, marked 18, dompurify 3, PyJWT 2.13, mypy 2) with six mypy-2 bug fixes, and ops hardening (worker healthcheck + app re-copy). Bumps backend, frontend, docs and the User-Agent to 1.4. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
1.3.0: Ops: healthcheck on the worker container
arq publishes a heartbeat in Redis on every cycle; ``python -m arq … --check`` reads it and exits 0 iff fresh. Wired as a compose healthcheck so the container flips to unhealthy if Redis becomes unreachable or the worker stops pumping (silent crash, deadlock, restart-loop catcher upstream). Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
1.2.0
1.1.1: Anchor parse_apk to the system temp dir (CodeQL py/path-injection #4)
CodeQL flagged ``parse_apk(path)`` as reachable from user-controlled data via the upload flow: UploadFile -> save_upload_to_temp -> tempfile.NamedTemporaryFile -> Path(tmp.name) -> parse_apk(path) -> Path(path).exists() In practice the path comes from ``tempfile.NamedTemporaryFile`` (or the rescan service's ``_download_apk`` which uses the same primitive) and an attacker controls none of it — the path is an OS-allocated random name under the system temp dir. But CodeQL's tracker can't see that, and we'd be exposed if a future caller ever passed a caller-controlled path by mistake. Fix: validate up-front in ``parse_apk`` that the resolved path lives under ``tempfile.gettempdir()`` and points at a regular file. Two wins: * Defence-in-depth — a stray future caller can't aim androguard at ``/etc/passwd`` (or any non-APK file) by accident. We fail loudly with ``ApkParseError`` before the FS open. * The explicit ``Path.resolve(strict=True).relative_to(tmpdir)`` check is the sanitiser shape CodeQL's ``py/path-injection`` rule recognises, so the flow no longer trips the analyser. Smoke-tested in the running container: missing paths and out-of-tmp paths both raise ``ApkParseError`` with the new messages; a tempfile fed garbage bytes still reaches androguard (validation passed, parse fails downstream — expected). Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Bump to 1.1.0
User-facing 1.0.0 → 1.1.0 highlights, in operator-relevant order: * Screenshot lightbox on /apps/[package] replaces "open in new tab" * Progressive multi-file screenshot upload on the editor page * Download-origin chips on /history (F-Droid / web / CLI / other) * /admin/repo keystore read 3-10× faster (PKCS#12 in-process via cryptography, no more keytool JVM cold-start) * Visual polish pass across the SPA — stat-digit slashed-zero fix, curated category palette, screenshots fade indicator + counter, scan-now disabled-reason, draft-watermark localisation README's "Changelog" section now opens with a 1.1.0 block above the existing 1.0.0 → 1.0.9 notes. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>