Skip to content

Releases: Dim145/fdroid-store

1.4.5

Choose a tag to compare

@Dim145 Dim145 released this 05 Jul 01:20

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

Choose a tag to compare

@Dim145 Dim145 released this 04 Jul 13:43

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 use proxy_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 via X-Accel-Redirect to an internal /_protected/ location — sendfile, real Content-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 to 0 if your front nginx lacks that location.

⚠️ Operator note: if you run your own reverse proxy / CDN in front (nginx, BunkerWeb, Traefik, Cloudflare, …), you must also disable response buffering (or give its temp dir room) and raise its timeouts for the download route — a short timeout or a small proxy temp filesystem there still truncates large downloads regardless of these app-side changes.

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

Choose a tag to compare

@Dim145 Dim145 released this 20 Jun 01:10

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

Choose a tag to compare

@Dim145 Dim145 released this 20 Jun 00:47

Patch release.

1.4.2

  • Index signing resolves jarsigner explicitly (JAVA_HOME/bin → PATH → bundled JRE) with an actionable error instead of an opaque FileNotFoundError when a worker lacks the JDK on its PATH. Index signing only works in the worker image (which bundles the JDK) — never run rebuild_index from the API image.

Also includes 1.4.1 (never released):

  • Clears the CodeQL py/unsafe-deserialization alert on the metadata.yml importer (yaml.safe_load + a separate yaml.scan alias 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

Choose a tag to compare

@Dim145 Dim145 released this 20 Jun 00:44
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

Choose a tag to compare

@Dim145 Dim145 released this 30 May 11:28
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

Choose a tag to compare

@Dim145 Dim145 released this 23 May 22:21
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

Choose a tag to compare

@Dim145 Dim145 released this 23 May 01:06
Fix: harden SSRF guard on /reproducibility/verify-from-url (CodeQL py…

1.1.1: Anchor parse_apk to the system temp dir (CodeQL py/path-injection #4)

Choose a tag to compare

@Dim145 Dim145 released this 21 May 23:21
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

Choose a tag to compare

@Dim145 Dim145 released this 21 May 22:45
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>