Release EPUB Tools 0.2.0
Verify / verify (push) Canceled after 0s

This commit is contained in:
2026-09-02 08:53:49 +02:00
parent f68dcfe4ea
commit efd80eb18a
41 changed files with 3250 additions and 614 deletions
+1 -1
View File
@@ -1,5 +1,5 @@
# Accessibility
The application uses labelled file inputs, fields, tabs, status/error regions, keyboard-operable chapter and action buttons, visible focus indicators, responsive layouts, and scrollable tables/readers. The iframe has a chapter-specific accessible title.
The application uses labelled file inputs, fields, pressed-state workspace buttons, status/error regions, keyboard-operable chapter and action buttons, visible focus indicators, responsive layouts, and scrollable tables/readers. Opening progress is announced as a polite status with the selected filename and a labelled native progress element. The iframe has a chapter-specific accessible title and explicit light/dark background and foreground colours.
Reader sanitization can remove scripted or form-based interactions by design. EPUB-internal navigation is disabled in v0.1; the Toolbox reading-order sidebar remains keyboard accessible. Publisher content can itself have poor semantics, contrast, directionality, alt text, or reading order. The safe reader does not certify EPUB accessibility and its fallback stylesheet may change publisher presentation.
+9 -4
View File
@@ -2,12 +2,17 @@
The app is a relocatable static React application using the Toolbox shell and contract. EPUB work is divided into small TypeScript modules under `src/epub/`:
- `archive.ts` applies ZIP/path/entry/ratio limits and opens the container with zip.js.
- `archive.ts` applies ZIP/path/entry/ratio limits, opens the container with zip.js, and reports bounded phase/item progress to the workbench.
- `adapters.ts` turns bounded UTF-8 HTML/XHTML, Markdown, and plain text into a
deterministic one-chapter EPUB workspace before the same archive validation
path runs. The HTML adapter retains only a small structural element/attribute
allowlist; Markdown implements a documented lightweight subset.
- `xml.ts`, `package.ts`, and `paths.ts` decode bounded XML, reject DTD/entity input, parse OPF/navigation/NCX, and resolve package-relative references without traversal.
- `validation.ts` performs bounded package and cross-document diagnostics and reports skipped scope.
- `reader.ts` sanitizes content with DOMPurify, neutralizes navigation and CSS URLs, resolves bounded local images, injects a restrictive iframe CSP, and returns object URLs with an explicit revocation lifecycle.
- `export.ts` patches Dublin Core metadata and streams entries into a fresh normalized EPUB. It also produces JSON, cover, chapter, and reading-order text exports.
- `reader.ts` sanitizes content with DOMPurify, neutralizes regular/namespaced navigation, rewrites bounded packaged stylesheets/fonts/images/media and CSS URLs to local object URLs, injects a restrictive iframe CSP plus direction/layout/theme metadata, and returns cached URLs with an explicit revocation lifecycle.
- `search.ts` extracts bounded inert reading-order text and performs cancellable literal search. `annotations.ts` validates deterministic, publication-scoped bookmark/annotation interchange without silently persisting book data.
- `export.ts` patches Dublin Core metadata and streams entries into a fresh normalized EPUB. It also produces JSON, cover, chapter, reading-order text, text-centric Markdown, and standalone HTML. The HTML path replaces packaged media, removes links and active controls, then applies a strict DOMPurify structural element/attribute allowlist before adding a deny-by-default CSP.
zip.js receives a lazy browser `BlobReader`, performs CRC checks when entry data is read, and can use web workers. React retains one open reader and closes the previous reader when a new book replaces it. The original File is immutable; all changes exist only in staged React state or a newly downloaded Blob.
The safe reader uses both sanitization and an iframe without sandbox permissions. Its `srcdoc` adds `default-src 'none'` and permits only inline styles plus local data/blob image/font/media URLs. Publisher stylesheet links are intentionally removed in v0.1.
The safe reader uses both sanitization and an iframe with `allow-same-origin` but without scripts, forms, popups, navigation, downloads, modals, or device permissions. Same-origin access lets the trusted parent intercept inert EPUB links, scroll to a target fragment, and read an explicitly selected quote. Its `srcdoc` adds `default-src 'none'` and permits only inline styles plus local data/blob image/font/media URLs. Publisher stylesheets are size/count bounded; imports and network URLs are removed, while CSP remains the final network boundary. A mutation observer synchronizes explicit light/dark reader tokens with Toolbox preference changes; system mode follows the browser media preference.
+14 -3
View File
@@ -2,8 +2,19 @@
All source and generated publication data remains in the browser. The app has no telemetry, account, analytics, remote font, CDN, or default network integration. The production CSP keeps `connect-src` and worker sources same-origin.
EPUB input is adversarial. Before extraction the app enforces file, entry, expanded-size, per-entry, expansion-ratio, duplicate-name, and path rules. Required XML is size-bounded, supports UTF-8/UTF-16, and rejects DTD/entity declarations. ZIP-encrypted entries cannot be read. `META-INF/encryption.xml` is reported because it can describe valid font obfuscation or DRM; the app does not distinguish every scheme and never attempts circumvention.
EPUB input is adversarial. Before extraction the app enforces file, entry, expanded-size, per-entry, expansion-ratio, duplicate-name, and path rules. Required XML is size-bounded, supports UTF-8/UTF-16, and rejects DTD/entity declarations. ZIP-encrypted entries cannot be read. `META-INF/encryption.xml` is inspected: the standardized IDPF font-obfuscation mask is decoded locally, while unsupported protection is reported and never circumvented.
Content documents are never mounted into the application DOM. DOMPurify removes active elements and event handlers, navigation and resource references are neutralized or replaced with bounded local object URLs, and rendering happens inside a permissionless sandboxed iframe with its own restrictive CSP. Temporary object URLs are revoked when chapters change.
HTML/XHTML, Markdown, and plain-text adapters accept at most 16 MiB of valid
UTF-8. HTML is parsed inertly and copied through a small structural
element/attribute allowlist; scripts, styles, forms, event handlers, embedded
media, external resources, and non-fragment links are not transferred. The
adapter bounds nodes, nesting and Markdown lines, derives a local content
identifier with Web Crypto, and then sends its generated one-chapter EPUB
through the same strict archive and reader path. The reported losses are part
of the resulting workspace rather than a claim of source fidelity.
Validation is intentionally bounded and incomplete. It does not establish publication safety, conformance, accessibility, authenticity, ownership, or freedom from hidden data. Reports identify the checks performed and relevant unsupported areas. Rebuilding changes compressed bytes and invalidates signatures; it is disabled for encrypted publications.
Content documents are never mounted into the application DOM. DOMPurify removes active elements and event handlers; regular and namespaced SVG navigation/resource attributes are neutralized or replaced with bounded local object URLs; and rendering happens inside a scriptless sandboxed iframe with its own restrictive CSP. The iframe has `allow-same-origin` solely so trusted application code can intercept internal links, scroll to fragments, and capture a quote the user explicitly selected; it receives no script, form, popup, top-navigation, download, modal, or device permissions. Stylesheets/resources are bounded, CSS imports and external URLs are removed, object URLs are reused per resource and revoked when chapters change.
Search operates on bounded extracted text. Bookmarks and annotations remain in React memory unless the user explicitly downloads their JSON; importing validates schema, counts, path safety, field sizes, and publication identifier. Derived HTML/Markdown/text exports intentionally omit active content and network resources and are lossy reading-order representations, not publication-preserving conversions. Standalone HTML is rebuilt through a strict DOMPurify structural allowlist that excludes meta refreshes, base URLs, templates, frames, links, media and event/style attributes, then receives a deny-by-default CSP.
Validation is intentionally bounded and incomplete. It does not establish publication safety, conformance, accessibility, authenticity, ownership, or freedom from hidden data. Reports identify the checks performed and relevant unsupported areas. Rebuilding changes compressed bytes and invalidates signatures; it is disabled for unsupported encrypted publications. IDPF-obfuscated fonts are re-keyed if the unique publication identifier changes.