Skip to content

fix: Tauri runtime guard + market-script sha256 verification (+ architecture notes)#603

Open
keevingfu wants to merge 6 commits into
BigPizzaV3:mainfrom
keevingfu:fix/manager-tauri-runtime-guard
Open

fix: Tauri runtime guard + market-script sha256 verification (+ architecture notes)#603
keevingfu wants to merge 6 commits into
BigPizzaV3:mainfrom
keevingfu:fix/manager-tauri-runtime-guard

Conversation

@keevingfu

@keevingfu keevingfu commented Jun 3, 2026

Copy link
Copy Markdown

Overview

This PR bundles three related pieces of work from a source walkthrough of the codebase. They are split into self-contained commits, so it reads cleanly commit-by-commit:

  1. Fix — guard the Tauri runtime before invoke in the manager.
  2. Hardening — verify a market script's sha256 before installing it.
  3. Docs — an architecture walkthrough (docs/architecture-notes.md, 11 chapters with file:line references).

If a maintainer would prefer the docs and/or the hardening split into separate PRs, happy to do that — just say the word.


1. Fix: guard Tauri runtime before invoke (923188b)

Backend commands in the manager are all reached through one call/invoke path. When the UI runs outside the Tauri webview (a browser, tauri dev opened directly, or a devtools/automation context), window.__TAURI_INTERNALS__ is undefined, so @tauri-apps/api's invoke throws:

Cannot read properties of undefined (reading 'invoke')

This was reported as "Restart Codex++" failing, but it affects every command — restart just happened to be the one clicked. The backend command (restart_codex_plus) is correctly registered and is not at fault.

Change: add a tauriRuntimeAvailable() runtime check; guard call and logDiagnostic so a missing runtime rejects with a clear, actionable message instead of the opaque TypeError (and logDiagnostic becomes a no-op so it can't re-trigger the same error). The popup now reads "Tauri 运行时不可用:请在已安装的「Codex++ 管理工具」窗口中操作…".

2. Hardening: verify market-script sha256 before install (c097924)

Market scripts are JS that get injected into the Codex page, so their integrity is security-relevant. The market manifest already carried a per-script sha256, but install_market_script_content wrote the downloaded bytes without checking it (the prior behavior was even pinned by a test that ignored mismatches).

Change: add verify_script_checksum — before writing, hash the downloaded content and compare (case-insensitive hex) against the manifest sha256. On mismatch the install aborts with a clear error: no file written, no install recorded, any existing version left intact. An empty sha256 skips the check, so hash-less legacy manifests still install. Pulls in sha2 + hex (both already resolved in Cargo.lock).

3. Docs: architecture walkthrough notes (74de9ca, 1681ffc, baffc04, db1f5c3)

docs/architecture-notes.md — a new-contributor map of the core mechanisms, each section carrying file:line references:

1–8. CDP injection · the Runtime-Binding bridge · React hooking in the injected script · the Responses⇄Chat protocol proxy (incl. apply_patch codec) · the macOS launcher · GitHub-release auto-update · Zed Remote open · upstream worktree creation.
9. Relay config & profile switching (mode × protocol, the three auth/config layouts).
10. Background consistency — provider_sync (history visibility across a provider switch) + watcher (keep-alive / stale recovery).
11. User scripts & the script market (which is where the §2 hardening was found).

Testing

  • npm run check (tsc --noEmit) passes.
  • cargo check -p codex-plus-core -p codex-plus-launcher -p codex-plus-data passes; no new warnings.
  • Rust suites run locally, all green:
    • bridge_routes 22 passed (incl. the new sha256 reject/accept/skip-when-empty cases)
    • protocol_proxy 35 · relay_config 67 · provider_sync 7 · watcher 9

🤖 Generated with Claude Code

cavinfu and others added 6 commits June 3, 2026 10:57
Backend commands are reached through a single `call`/`invoke` path. When the
UI runs outside the Tauri webview (browser, `tauri dev` opened directly, or a
devtools/automation context), `window.__TAURI_INTERNALS__` is undefined and
`@tauri-apps/api` throws "Cannot read properties of undefined (reading
'invoke')". This surfaced as "Restart Codex++" failing, but affects every
command.

Guard `call`/`logDiagnostic` with a runtime check that rejects with a clear,
actionable message instead of the opaque TypeError.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Source-read notes covering the core mechanisms: CDP injection, the
Runtime-Binding bridge, React hooking in the injected script, the
Responses<->Chat protocol proxy (incl. apply_patch codec), the macOS
launcher, GitHub-release auto-update, Zed Remote open, and upstream
worktree creation. Each section carries file:line references.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Source-read notes for the relay-config writer (relay_config.rs +
settings.rs): the mode × protocol axes, how Official/MixedApi/PureApi map
onto auth.json vs config.toml, the four-step profile-switch pipeline
(filter common -> complete provider -> merge+limits -> mode-specific
auth), shared common-config, atomic write+backup+validate, backfill/clear,
ChatGPT JWT account label, and the Tauri-command (not bridge) entry point.
Verified by running the relay_config suite: 67 passed.

Also marks ch.9 done in the dev log; remaining modules deferred to ch.10+.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Source-read notes for the two background-consistency mechanisms:
provider_sync (codex-plus-data) migrates session-history visibility across
a provider switch by rewriting the model_provider tag in three sources
(rollout JSONL first line, state_5.sqlite threads index, global-state
workspace roots), preserving mtime, dir-locking against concurrent runs,
backing up before changes, rolling back on failure, skipping OS-locked
files, and warning that synced-but-encrypted history may not resume; and
watcher (codex-plus-core) keeps the launcher/injection alive via CDP
liveness probe, on-demand stale-launcher recovery with ancestor-chain
self-protection, and Windows auto-start install.

Honestly notes the three pacing constants have no in-tree polling loop and
recovery is on-demand (main.rs:64), not a 3s daemon. Verified by running
both suites: provider_sync 7 passed, watcher 9 passed.

Also marks ch.10 done in the dev log; user_scripts/script_market -> ch.11+.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Source-read notes for user-authored / market-installed enhancement JS:
user_scripts (builtin+user two-source scan, global + per-script toggles
behind a Mutex+atomic_write, build_enabled_bundle wrapping each script in
an isolated try/catch IIFE, injected both via addScriptToNewDocument at
launch and via live Runtime.evaluate on /user-scripts/reload), and
script_market (GitHub-raw index.json manifest with strict per-entry
filtering, download + atomic install + recorded metadata).

Records an honest finding: the sha256 field is parsed and stored but never
verified on install (no Sha256/sha2 anywhere; the behavior is pinned by
install_market_script_ignores_checksum_mismatch...). Flagged as a future
hardening point. Verified by running the bridge_routes suite: 20 passed.

Also marks ch.11 done in the dev log; architecture notes now cover all
major modules.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Market scripts are JS injected into the Codex page, so their integrity is
security-relevant. The manifest already carried a per-script sha256, but
install_market_script_content wrote the downloaded bytes without checking
it (the old behavior was even pinned by a test that ignored mismatches).

Add verify_script_checksum: before writing, hash the downloaded content and
compare (case-insensitive hex) against the manifest sha256. On mismatch,
abort the install with a clear error — no file written, no install
recorded, any existing version left intact. An empty sha256 skips the check
so hash-less legacy manifests still install. Pull in sha2 + hex (both
already resolved in Cargo.lock).

Rewrite the ignore-mismatch test into reject-mismatch-and-keep-existing,
and add accept-matching + skip-when-empty. bridge_routes 20 -> 22 passed.
Sync architecture-notes ch.11.6 to describe the now-verified behavior.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
keevingfu pushed a commit to keevingfu/CodexPlusPlus that referenced this pull request Jun 4, 2026
Running development worklog distinct from the release-facing CHANGELOG:
records the 2026-06-03 source walkthrough, the manager invoke runtime-guard
fix, branch/PR status (BigPizzaV3#603, pending maintainer CI approval), and the
next-step plan.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
@keevingfu keevingfu force-pushed the fix/manager-tauri-runtime-guard branch from c6d88bd to c097924 Compare June 4, 2026 01:31
@keevingfu keevingfu changed the title fix: guard Tauri runtime before invoke in manager fix: Tauri runtime guard + market-script sha256 verification (+ architecture notes) Jun 4, 2026
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