Skip to content

fix: rebuild on the SQLite 3.54.1 engines with scoped native exports; release 0.3.2 - #5

Merged
zknpr merged 3 commits into
mainfrom
chore/viewer-1.8.3
Oct 24, 2026
Merged

zknpr merged 3 commits into
mainfrom
chore/viewer-1.8.3

Conversation

@zknpr

@zknpr zknpr commented Oct 23, 2026 •

Copy link
Copy Markdown
Owner

Problem

Desktop 0.3.1 bundles the SQLite Explorer 1.8.1 engines byte for byte: native SQLite 3.51.2 (txiki.js 62b02dc) and WebAssembly SQLite 3.49.1 (sql.js 424163d). Both carry the SQLite CVEs fixed in the extension's 1.8.2 release: CVE-2025-7709 (FTS5, WebAssembly), CVE-2026-11822 and CVE-2026-11824 (FTS5 heap overflow with defensive mode off, both engines) and CVE-2025-6965 (WebAssembly).

The new native runtime cannot simply be dropped in. It refuses SQL-level ATTACH, DETACH and VACUUM INTO in C, and the desktop shim exported with VACUUM INTO ?, so every native export would fail with "authorization denied".

Solution

  • Sync viewer-dist/ from the extension's desktop-dev-port branch at b4a3341. That branch merges extension dev, which holds the 1.8.2 release and the extension's unreleased runtime and text-handling fixes, into the desktop port and changes one shim function.
  • The shim's vacuumInto now calls backing.vacuumInto(target), the runtime's one exception to its ATTACH refusal. It runs a fixed VACUUM main INTO ? and accepts the destination only for that statement, on the thread running it.
  • Re-pin the third-party inventory and notices, and release as 0.3.2.

Architecture

  • One choke point. Every desktop export goes through the shim's vacuumInto (core/native/sqljs-shim.js): exportToPath (shell route, OS save dialog) and exportAsync (sidecar temp file). The Rust shell issues no SQL. The export destination stays chosen by the shell or the sidecar, never by the webview.
  • Native method surface unchanged. sync-viewer confirms the 38 audited methods match KNOWN_METHODS at b4a3341, so native.rs needs no change.
  • TEXT handling. Both engines now read and write TEXT containing NUL whole; before, WebAssembly cut such a value short when binding it, and native cut it short on synchronous reads. The grid keeps showing these cells as bytes on both engines, and a WITHOUT ROWID row whose key contains NUL stays read-only with a reason that says so. TEXT with an unpaired surrogate is stored by WebAssembly exactly as native stores it.
  • Editor dependencies. The port's CodeMirror and Lezer versions are kept exactly as they were; the merges did not move them.

Per-file changes

File Change
viewer-dist/native/tjs txiki.js 9875455 (SQLite 3.54.1, C authorizer, vacuumInto, NUL-preserving reads), from Actions run 40021160289 and sha-pinned by the extension's refresh-natives.mjs.
viewer-dist/sql-wasm.js, viewer-dist/sql-wasm.wasm sql.js 35ac205 on SQLite 3.54.1: upstream #634 (TEXT read and bound by length) plus the fork's fix for the byte length #634 bound for unpaired surrogates.
viewer-dist/native/native-worker-desktop.js, viewer-dist/worker.js Rebuilt from the port: the shim's export call, the NUL containment rule and the NUL-key reason.
viewer-dist/manifest.json ref b4a3341, new per-file sha256. viewer.html, codicons and query-plan.dylib are byte-identical to 0.3.1.
third-party/inventory.json, THIRD_PARTY_NOTICES.txt txiki.js 62b02dc → 9875455, sql.js 424163d → 35ac205, viewerSource → b4a3341. Every LICENSE, AUTHORS and embedded notice text is byte-identical at the new pins except the sqlite3.c amalgamation header (version and Fossil check-in line). Its notice text and hash (raw header through */, the same convention as the old entry) are updated.
README.md Sync instructions point to desktop-dev-port at b4a3341; the version paragraph names 0.3.2 and the extension's 1.8.2 release plus its unreleased fixes.
package.json, package-lock.json, src-tauri/Cargo.toml, src-tauri/Cargo.lock, src-tauri/tauri.conf.json 0.3.1 → 0.3.2. The lockfile root version was written by npm install --package-lock-only.

Not changed on purpose: docs/extension-1.8.1-port.md, docs/linux-windows-release-qa.md and docs/release-readiness.md record checks run against d503473. Rewriting their pins would claim checks that have not been re-run for b4a3341.

Security

  • Fixed: the four SQLite CVEs above in both bundled engines.
  • New enforcement: native SQL-level ATTACH, DETACH and VACUUM INTO are refused by the runtime's authorizer, beneath the shim's existing guard (ERR_NATIVE_SQL_ATTACH_BLOCKED / ERR_NATIVE_SQL_VACUUM_INTO_BLOCKED). An authorizer refusal surfaces as plain "authorization denied", because core/native/sqlite-errors.js has no code for it. That is cosmetic and left for a follow-up.
  • Export exception: scoped to one fixed statement and its destination. An operation queued on the same connection before or after it never sees the destination accepted; the runtime's contract test checks this on all five targets. A differential fuzz of the guard (39,990 distinct statements, run 40031649705) found no attached database or file write on any of the five targets.
  • Unchanged: the Rust gate, the method allowlist and the path rules for initializeDatabase and exports.

Review findings

Finding Verdict Resolution
Codex P2 on c3745cf (package.json:3): README still calls 0.3.1 and extension 1.8.1 current CONFIRMED (README.md:8, :109) Both lines updated in 9155464, now be339fa
Owner: the extension branch was named desktop-v1.8.3-port, but extension 1.8.3 does not exist yet CONFIRMED (README.md:104, :109; c3745cf message; the port's merge commit message) Branch renamed desktop-dev-port. Its two commits were recreated with identical trees: the merge a0be535 → 13802cf with a corrected message, and 638b4ed → e597c1d with its message unchanged on the new parent. This PR's commits were rewritten to the new name and pin: c3745cf → f7e7555, 9155464 → be339fa.
Consumer review, Important: sql.js 7245c26 binds TEXT with an unpaired surrogate using lengthBytesUTF8, which miscounts it: 'x\uD800' is stored as 78EDA08000, '\uD83D中文' loses its last character and gains a NUL CONFIRMED (sql.js src/api.js:598, :1353; reproduced against 1.8.2's glue as the control) sql.js 35ac205 counts the bytes the way stringToUTF8 writes them and throws on a mismatch; extension d733e63. raw_text_transport.test.ts fails on 7245c26's glue and passes on 35ac205's; the expected bytes are the native runtime's, measured with the bundled tjs. Synced here in ce76506.
Consumer review, Minor: a WITHOUT ROWID key containing NUL is read-only with "is 3 bytes and exceeds the 838860-byte inline limit" CONFIRMED (src/core/cell-containment.ts:1189, :1088) Extension c6f49f8: the key checks use the same NUL rule as cells; the reason names the NUL and a NUL-key insert rolls back. Fails before and passes after in without_rowid_identity.test.ts (WASM) and native_real_smoke.test.ts (real binary).
Consumer review, Minor: tables with more than 999 columns check only key columns for NUL CONFIRMED (WasmDatabaseEngine.ts:4454) Behaviour kept (both engines bind by length, so nothing is lost); the extension changelog now states the limit.
Consumer review, Minor: the extension changelog claimed exports changed and that every connection contains NUL cells CONFIRMED (tableExportStreaming.ts:291 already decoded raw bytes) Reworded in c6f49f8.
Consumer review, Minor: no real-binary test of NUL TEXT on native CONFIRMED native_real_smoke.test.ts now covers bound NUL and lone-surrogate writes, NUL cells on a grid page and the NUL-key reason.
Runtime review of txiki.js 0353db57..98754556 No Critical or Important findings Six Minor items (comments, an out-of-memory-only statement reset, error detail, a test gap). None is reachable from this app: the worker finalizes every statement, and no SQL callbacks are registered. Left for a runtime follow-up.

This PR's own head branch is still chore/viewer-1.8.3. GitHub closes a pull request whose head branch is renamed, and the squash merge does not keep the branch name.

Test plan

Run on macOS aarch64 unless noted.

This PR (re-run on ce76506)

  • npm test: 27/27.
  • node scripts/release/notices.mjs: inventory and notices match the release dependency graph and viewer pin.
  • node scripts/sync-viewer.mjs --verify: 10 files verified at b4a3341.
  • node scripts/release/version.mjs: 0.3.2 in all three manifests.
  • cargo test --lib --locked: 138 passed, 4 ignored.
  • cargo test --lib --locked -- --ignored: 4/4 live tests, including live_export_route_end_to_end and live_sidecar_end_to_end against the bundled tjs.
  • Failing before, passing after: with the viewer synced from the port's first merge commit (new runtime, old shim), live_export_route_end_to_end fails with ERR_NATIVE_EXPORT_FAILED: the sidecar export failed: Error: authorization denied. That run used a0be535, which has the same tree as 13802cf; it was not repeated after the rename. At b4a3341 it passes.

Extension port (desktop-dev-port at b4a3341)

  • npm run native-lane against the real binary: 78 shared fixtures, 4 errno checks, 15 fork-only checks, 20 transport checks, 108 sidecar checks and 122 method-sweep checks passed. On the first merge commit, native/export-async-roundtrip failed with "authorization denied".
  • npm test 3237/3237, npm run test:native-smoke 112/112, tsc --noEmit and build-query-plan.mjs --check clean. desktop/sql-wasm.js is byte-identical to the extension's vendor/sql.js/sql-wasm.js.

Extension dev

  • At 12f2572: npm test 2585/2585, npm run test:native-smoke 112/112, tsc --noEmit and build-query-plan.mjs --check clean.
  • Native fuzz run 40031649705 at e13819b on all five targets (Linux x64 and arm64, Windows x64, macOS Intel and Apple Silicon): 40,000 statements, 39,990 distinct, 0 traces on each. The seed is fixed, so this is one corpus run on five binaries.

Not run

  • GUI smoke (scripts/qa/) and npm run test:gui.
  • Linux and Windows builds and installed-app QA. CI here builds them; release.yml builds the release packages.
  • The sql.js fork's own WASM test variants: its CI has failed in the asm variant's test_issue482 since before these changes, which stops the run before the WASM variants. The WASM evidence above is the extension's tests on the glue this app ships.

… release 0.3.2

Sync the viewer from the extension's desktop-dev-port branch at
e597c1d. That branch merges extension dev, which holds the 1.8.2 release
and the extension's unreleased runtime and NUL-text fixes, into the
desktop port:

- Both engines move from SQLite 3.51.2 (native) and 3.49.1 (WebAssembly)
  to 3.54.1, fixing CVE-2025-7709, CVE-2026-11822, CVE-2026-11824 and
  CVE-2025-6965 as described in the extension's 1.8.2 notes.
- The native runtime (zknpr/txiki.js 9875455) refuses SQL-level ATTACH,
  DETACH and VACUUM INTO in C. Desktop exports now go through the
  runtime's statement-scoped vacuumInto; with the old shim, exports fail
  with "authorization denied" (live_export_route_end_to_end fails on the
  port's merge commit and passes on the pinned commit).
- TEXT containing NUL is read and written whole by both engines; the grid
  keeps showing it as bytes.

Third-party inventory and notices: txiki.js 62b02dc -> 9875455 and sql.js
424163d -> 7245c26. Their LICENSE, AUTHORS and embedded notice texts are
unchanged except the sqlite3.c amalgamation header (version and check-in
line), whose notice and hash are updated. viewerSource and the README pin
follow e597c1d.
@coderabbitai

coderabbitai Bot commented Oct 23, 2026 •

Copy link
Copy Markdown

Important

  • 🔍 Trigger review

This repository does not receive automatic reviews because it has fewer than 10 stars.

⚙️ Run configuration
  • Source mode: Auto
  • Repository: Unavailable
  • Branch: Unavailable
  • File: No configuration file from this repository applied
  • Commit: Unavailable
  • Layers: Organization UI
  • Review profile: ASSERTIVE
  • Plan: Advanced
  • Run ID: ded16462-f365-4dd1-abac-fef1889ce167
  • Autofix · Keep fixing CodeRabbit findings and required CI, and resolving merge conflicts

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands.

@chatgpt-codex-connector

chatgpt-codex-connector Bot commented Oct 23, 2026 •

Copy link
Copy Markdown

Codex Review Summary

This comment shows the latest Codex review activity on this pull request.

Review Status Commit Review trigger
📝 Code Review ✅ Completed 2026-10-23T22:39:43.022579Z ce76506 New commits
ℹ️ About Codex in GitHub

Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review" or "@codex security review".

Codex reacts with 👀 while any review is running, comments if it has suggestions, and reacts with 👍 once all reviews finish with no findings.

@chatgpt-codex-connector chatgpt-codex-connector Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

💡 Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit: c3745cf12c

ℹ️ About Codex in GitHub

Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".

Comment thread package.json
{
"name": "sqlite-explorer-desktop",
"version": "0.3.1",
"version": "0.3.2",

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P2 Badge Update the README version with the release bump

When this 0.3.2 release is published, the README still identifies 0.3.1 and extension 1.8.1 as current, while the package now pins the 1.8.3 desktop port. Users checking the project overview will see outdated release information; update it alongside the version bump.

Useful? React with 👍 / 👎.

Codex on c3745cf: the README still called 0.3.1 and extension 1.8.1 current.
@zknpr
zknpr force-pushed the chore/viewer-1.8.3 branch from 9155464 to be339fa Compare October 23, 2026 21:56
Review of the extension changes behind this release found that sql.js
7245c26 bound TEXT containing an unpaired surrogate with the wrong byte
length: 'x\uD800' was stored with a NUL appended, and some values also
lost their end. Desktop 0.3.2 would have shipped that in its WebAssembly
engine.

Sync from desktop-dev-port at b4a3341, which merges the extension fix:
sql.js 35ac205 stores those values as the native engine does. The same
merge gives WITHOUT ROWID rows whose key contains NUL a read-only reason
that names the NUL instead of a size limit.

viewer-dist: worker.js, sql-wasm.js and native/native-worker-desktop.js
change; sql-wasm.wasm, viewer.html and the native runtime are identical.
Inventory and notices: sql.js 7245c26 -> 35ac205 (LICENSE and AUTHORS
unchanged between them), viewerSource -> b4a3341. README pin follows.
@zknpr
zknpr merged commit 43ced98 into main Oct 24, 2026
6 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant