From 4af203755d807ae317c9ffe4922f5f1e7989a66b Mon Sep 17 00:00:00 2001 From: Huang Xin Date: Wed, 8 Jul 2026 03:17:35 +0900 Subject: [PATCH] release: version 0.11.18 (#5003) --- apps/readest-app/.claude/memory/MEMORY.md | 4 +-- .../opds-autodownload-tls-skipssl-4988.md | 2 +- .../opds-preemptive-basic-digest-400.md | 2 +- .../memory/security-advisories-web-2026-06.md | 2 +- apps/readest-app/package.json | 2 +- apps/readest-app/release-notes.json | 14 +++++++++ data/metainfo/appdata.xml | 29 ++++++++++--------- data/metainfo/appdata.xml.sha256 | 2 +- 8 files changed, 36 insertions(+), 21 deletions(-) diff --git a/apps/readest-app/.claude/memory/MEMORY.md b/apps/readest-app/.claude/memory/MEMORY.md index f3692dd9..98a05eb9 100644 --- a/apps/readest-app/.claude/memory/MEMORY.md +++ b/apps/readest-app/.claude/memory/MEMORY.md @@ -5,7 +5,7 @@ - [Layout & UI](layout-ui-fixes.md) insets/z-index · [Platform Compat](platform-compat-fixes.md) · [Annotator & Reader](annotator-reader-fixes.md) ## Safety & Security - [In-place delete wiped originals](in-place-delete-wiped-originals.md) never `fs.removeFile` `external` · [Backup zip Windows paths #4703](backup-windows-zip-paths-4703.md) normalize `\` · [download_file scope Android #4639](download-file-scope-android-regression.md) -- [Security advisories 2026-06](security-advisories-web-2026-06.md) 4 GHSA #4638; SSRF guard broke dev-LAN OPDS, dev-only exemption 2026-07 +- [Security advisories 2026-06](security-advisories-web-2026-06.md) 4 GHSA #4638; SSRF guard broke dev-LAN OPDS, dev-only exemption 2026-07 PR#5002 ## Paginator & Scroll - Reading ruler: [line-aware](reading-ruler-line-aware.md) frame-offset map; [vertical-rl backwards #4865](reading-ruler-vertical-rtl-4865.md) - [Vertical-rl horizontal pagination (#624)](vertical-rl-horizontal-pagination-624.md) — horizontal inputs + two-phase slide; rtl gated `!vertical` @@ -103,7 +103,7 @@ - [Table dark-mode tint #4419](table-dark-mode-tint-4419.md) · [footnote aside border #4438](footnote-aside-namespace-order-4438.md) - Proofread: [enhancements #4700](proofread-enhancements-4700.md); [per-book CRDT #4781](proofread-per-book-crdt-sync.md); [edit Find + toggle #4859](proofread-edit-toggle-4859.md) - [Russian NBSP (#4769)](russian-hanging-prepositions-nbsp-4769.md) -- OPDS: [Firefox strict-XML #4479](opds-firefox-strict-xml-4479.md); [JSON search #4502](opds2-json-search-4502.md); [HTML desc #4503](opds-html-description-4503.md); [self-link #4749](opds-self-link-metadata-4749.md); [popular dedup #4782](opds-popular-catalog-dedup-4782.md); [auto-download subdir crawl #4272](opds-autodownload-subdir-crawl-4272.md) bounded BFS, never crawl newest-feed catalogs; [preemptive Basic 400s digest Calibre](opds-preemptive-basic-digest-400.md) bare-retry on 400; [auto-download TLS #4988](opds-autodownload-tls-skipssl-4988.md) skipSslVerification parity +- OPDS: [Firefox strict-XML #4479](opds-firefox-strict-xml-4479.md); [JSON search #4502](opds2-json-search-4502.md); [HTML desc #4503](opds-html-description-4503.md); [self-link #4749](opds-self-link-metadata-4749.md); [popular dedup #4782](opds-popular-catalog-dedup-4782.md); [auto-download subdir crawl #4272](opds-autodownload-subdir-crawl-4272.md) bounded BFS, never crawl newest-feed catalogs; [preemptive Basic 400s digest Calibre](opds-preemptive-basic-digest-400.md) bare-retry on 400 PR#5002; [auto-download TLS #4988](opds-autodownload-tls-skipssl-4988.md) skipSslVerification parity PR#5002 - [D-pad Navigation](dpad-navigation.md) - [koplugin cover upload (#4374)](koplugin-cover-upload.md) - [koplugin Library slow open #4954](koplugin-library-open-mosaic-cache-4954.md) group mosaics recomposed every paint; PR#4974 availability-keyed cache + async compose + cache nil result diff --git a/apps/readest-app/.claude/memory/opds-autodownload-tls-skipssl-4988.md b/apps/readest-app/.claude/memory/opds-autodownload-tls-skipssl-4988.md index 16c53dce..80e9600f 100644 --- a/apps/readest-app/.claude/memory/opds-autodownload-tls-skipssl-4988.md +++ b/apps/readest-app/.claude/memory/opds-autodownload-tls-skipssl-4988.md @@ -11,6 +11,6 @@ Issue #4988: OPDS auto-download failed while manual browse/download of the same **Real signature:** feed GETs and HEAD probes appear in the server log, download GETs never do → the native `download_file` dies client-side in the TLS handshake. The Tauri http-plugin path (`opdsReq.ts`) always passes `danger: {acceptInvalidCerts: true}`, but `transfer_file.rs` builds its reqwest client with **rustls**, which ignores the OS trust store — self-signed, private-CA, or incomplete-chain certs all fail unless `skip_ssl_verification` is set. Manual download (`page.tsx handleDownload`) got `skipSslVerification: true` in #2900 (for #2871); `autoDownload.ts downloadAndImport` never did. -**Fix (2026-07-08):** pass `skipSslVerification: true` in autoDownload's `downloadFile` call. Test in `opds-auto-download.test.ts`. +**Fix (2026-07-08, PR #5002):** pass `skipSslVerification: true` in autoDownload's `downloadFile` call. Test in `opds-auto-download.test.ts`. **How to apply:** any new code path that downloads via native `download_file`/`tauriDownload` from a user-configured server must mirror the manual path's `skipSslVerification` — TLS behavior differs between the http plugin (danger flags on) and transfer_file (strict rustls by default), so "browse works but download fails, nothing in server logs" = check this first. "curl works without -k" on another machine proves nothing about rustls trust. diff --git a/apps/readest-app/.claude/memory/opds-preemptive-basic-digest-400.md b/apps/readest-app/.claude/memory/opds-preemptive-basic-digest-400.md index d346f340..c813febc 100644 --- a/apps/readest-app/.claude/memory/opds-preemptive-basic-digest-400.md +++ b/apps/readest-app/.claude/memory/opds-preemptive-basic-digest-400.md @@ -9,7 +9,7 @@ metadata: Calibre's content server in `digest` (or `auto` over http) auth mode responds to a `Basic` Authorization header with **400 "Unsupported authentication method"** — not a 401 challenge. PR #4206 (commit 83607d14e) made `fetchWithAuth` (`src/app/opds/utils/opdsReq.ts`) send Basic preemptively (for Calibre-Web-style servers that return anonymous 200 without a challenge), which dead-ended all digest-mode Calibre servers: the retry logic only fired on 401/403, so users saw "Failed to load OPDS feed: 400 Bad Request" (reported on Android, but platform-independent — web proxy relays the 400 too). -**Fix (2026-07-08):** in `fetchWithAuth`, when the first response is 400 AND preemptive Basic was sent, re-issue the request once *without* credentials to surface `WWW-Authenticate`, then let the existing 401/403 negotiation pick Digest. Direct path strips the Authorization header; proxy path strips the `auth=` query param. Tests in `src/__tests__/utils/opds-req.test.ts`. +**Fix (2026-07-08, PR #5002):** in `fetchWithAuth`, when the first response is 400 AND preemptive Basic was sent, re-issue the request once *without* credentials to surface `WWW-Authenticate`, then let the existing 401/403 negotiation pick Digest. Direct path strips the Authorization header; proxy path strips the `auth=` query param. Tests in `src/__tests__/utils/opds-req.test.ts`. **Why:** the two auth-server archetypes conflict — anonymous-200 servers need preemptive creds (#4206), strict digest servers reject them with 400. Only runtime negotiation satisfies both; don't "fix" one archetype by regressing the other. diff --git a/apps/readest-app/.claude/memory/security-advisories-web-2026-06.md b/apps/readest-app/.claude/memory/security-advisories-web-2026-06.md index 461d16b2..70d8578b 100644 --- a/apps/readest-app/.claude/memory/security-advisories-web-2026-06.md +++ b/apps/readest-app/.claude/memory/security-advisories-web-2026-06.md @@ -21,7 +21,7 @@ After both merge: comment on each GHSA noting the fixing PR (pending merge). Not **C — Tauri native (PR #4639)** GHSA-55vr-pvq5-6fmg: unscoped `download_file`/`upload_file` in `src-tauri/src/transfer_file.rs` → arbitrary local read/write. FIXED: added `app: AppHandle` param + `ensure_path_allowed` (rejects relative + `..` via `has_disallowed_components`, then `fs_scope().is_allowed()`). Chose STRICT `is_allowed` (NOT read_dir's `|| contains("Readest")` substring hatch) because all legit callers (cloud sync, WebDAV, self-updater APK→`'Cache'`, OPDS→`'Cache'`) resolve under static scope ($APPDATA/Readest, $APPCACHE, $TEMP) OR persisted dialog grants (custom root via `setCustomRootDir`→picker→`allow_paths_in_scopes`; external folders re-granted at startup; `tauri_plugin_persisted_scope` makes sticky). Clippy needed `#[allow(clippy::too_many_arguments)]` on download_file (8 args). AppHandle auto-injected → JS invoke unchanged. NOTE: shared `target/` (worktree) was polluted with a deleted sibling worktree's abs plugin-permission paths → build failed `failed to read .../readest-feat-nightly-update-channel/.../fs/permissions/app.toml`; fix = `rm -rf` the `target/debug/build/-` dirs grepping for the stale path, then rebuild. skip_ssl_verification left as-is (OPDS needs it). read_dir's own `contains("Readest")` hatch left untouched (out of scope). -**Regression found 2026-07-08:** the "blocking private hosts removes no functionality" assumption below missed `pnpm dev-web` — in `next dev` the server runs on the developer's own machine and LAN catalogs (e.g. Calibre at 192.168.x.x) are the normal dev workflow; the unconditional `isBlockedHost` made the proxy 400 them ("This URL is not allowed"). CatalogManager already gates its "no LAN URLs" error on `NODE_ENV === 'production'`, so the proxy now mirrors that: `isPrivateHostAllowed() = NODE_ENV === 'development'` skips the blocklist (both preflight and per-redirect-hop). Vitest runs under `NODE_ENV=test` so the SSRF tests still exercise blocking; the dev-exemption test uses `vi.stubEnv('NODE_ENV', 'development')`. Self-hosted production deployments remain blocked (unresolved if anyone complains — would need an env-var opt-in). Also: Calibre's server throttles after repeated failed auth with transient 503s — don't mistake those for a proxy bug. +**Regression found 2026-07-08:** the "blocking private hosts removes no functionality" assumption below missed `pnpm dev-web` — in `next dev` the server runs on the developer's own machine and LAN catalogs (e.g. Calibre at 192.168.x.x) are the normal dev workflow; the unconditional `isBlockedHost` made the proxy 400 them ("This URL is not allowed"). CatalogManager already gates its "no LAN URLs" error on `NODE_ENV === 'production'`, so the proxy now mirrors that (PR #5002): `isPrivateHostAllowed() = NODE_ENV === 'development'` skips the blocklist (both preflight and per-redirect-hop). Vitest runs under `NODE_ENV=test` so the SSRF tests still exercise blocking; the dev-exemption test uses `vi.stubEnv('NODE_ENV', 'development')`. Self-hosted production deployments remain blocked (unresolved if anyone complains — would need an env-var opt-in). Also: Calibre's server throttles after repeated failed auth with transient 503s — don't mistake those for a proxy bug. **Non-obvious decision:** OPDS proxy can't require Readest auth — it's consumed from the browser via `` (covers) and `window.fetch` WITHOUT a Readest token; the `auth` query param is the *upstream* OPDS server cred, not the user token. So auth would break OPDS browsing/images. SSRF host-filter is the non-breaking high-value fix; residual relay/CORS-bypass on hosted CF (Medium) left for maintainer. On web the proxy is a CF Worker that can't reach a user LAN anyway (desktop bypasses via `needsProxy`), so blocking private hosts removes no functionality. diff --git a/apps/readest-app/package.json b/apps/readest-app/package.json index d9199635..67248471 100644 --- a/apps/readest-app/package.json +++ b/apps/readest-app/package.json @@ -1,6 +1,6 @@ { "name": "@readest/readest-app", - "version": "0.11.17", + "version": "0.11.18", "private": true, "type": "module", "scripts": { diff --git a/apps/readest-app/release-notes.json b/apps/readest-app/release-notes.json index 66925029..4df04555 100644 --- a/apps/readest-app/release-notes.json +++ b/apps/readest-app/release-notes.json @@ -1,5 +1,19 @@ { "releases": { + "0.11.18": { + "date": "2026-07-08", + "notes": [ + "Reading: Added Auto Scroll, a hands-free mode that scrolls the page for you at an adjustable pace, plus middle-click autoscroll on the desktop", + "Reading: Added slide and page-curl animations when turning pages for a more natural feel", + "Reading: Fixed page turning and the reading ruler for vertical right-to-left books, PDF text selection when system font scaling is on, and header/footer readability over light PDFs in dark mode", + "Text-to-Speech: Redesigned read-aloud as a mini player with an expandable full player, and playback now keeps going even after you close the book", + "Text-to-Speech: Online voices now play gap-free with chapter seeking and lock-screen controls, plus Android Auto support so you can listen in the car", + "Sync: Added S3-compatible cloud storage and a new Cloud Sync screen to choose and switch between your sync providers", + "Integrations: Added a Calibre plugin to push books and custom columns into Readest, and improved OPDS sign-in and auto-download for self-hosted catalogs", + "Library: New books dropped into watched folders now import automatically, and you can sort your shelf by reading progress", + "More: Added a speak button in the dictionary popup, a cleaner theme switcher, and per-rule toggles for proofreading" + ] + }, "0.11.17": { "date": "2026-06-29", "notes": [ diff --git a/data/metainfo/appdata.xml b/data/metainfo/appdata.xml index 75dfadbf..c72de025 100644 --- a/data/metainfo/appdata.xml +++ b/data/metainfo/appdata.xml @@ -70,6 +70,21 @@ + + +
    +
  • Reading: Added Auto Scroll, a hands-free mode that scrolls the page for you at an adjustable pace, plus middle-click autoscroll on the desktop
  • +
  • Reading: Added slide and page-curl animations when turning pages for a more natural feel
  • +
  • Reading: Fixed page turning and the reading ruler for vertical right-to-left books, PDF text selection when system font scaling is on, and header/footer readability over light PDFs in dark mode
  • +
  • Text-to-Speech: Redesigned read-aloud as a mini player with an expandable full player, and playback now keeps going even after you close the book
  • +
  • Text-to-Speech: Online voices now play gap-free with chapter seeking and lock-screen controls, plus Android Auto support so you can listen in the car
  • +
  • Sync: Added S3-compatible cloud storage and a new Cloud Sync screen to choose and switch between your sync providers
  • +
  • Integrations: Added a Calibre plugin to push books and custom columns into Readest, and improved OPDS sign-in and auto-download for self-hosted catalogs
  • +
  • Library: New books dropped into watched folders now import automatically, and you can sort your shelf by reading progress
  • +
  • More: Added a speak button in the dictionary popup, a cleaner theme switcher, and per-rule toggles for proofreading
  • +
+
+
    @@ -209,20 +224,6 @@
- - -
    -
  • Reading: Added continuous scroll and spread layout support for EPUBs
  • -
  • Reading: Added current time and battery display in footer
  • -
  • Reading: Added TTS, annotation support, pinch-to-zoom, and scroll mode for PDFs
  • -
  • Library: Added backup/restore to a zip file and batch metadata refresh
  • -
  • KOReader: Added bookmark and annotation sync between KOReader and Readest devices
  • -
  • Footnotes: Added back navigation with link history and more responsive popup sizing
  • -
  • Annotations: Added loupe display when selecting text in instant annotation mode
  • -
  • Text-to-Speech: Fixed TTS reading position tracking in scrolled mode
  • -
-
-
FSFAP diff --git a/data/metainfo/appdata.xml.sha256 b/data/metainfo/appdata.xml.sha256 index 91260f6e..1b3db23f 100644 --- a/data/metainfo/appdata.xml.sha256 +++ b/data/metainfo/appdata.xml.sha256 @@ -1 +1 @@ -c0fae8cab8e75871573d679d28bf0c1b778ef9e8f7c0b42da678396d1749fd96 ../../data/metainfo/appdata.xml +06a808e93fc9e371f9916d99b7d1eba29170faf07f977fe5cdc52f3e51e0c1ed ../../data/metainfo/appdata.xml