Skip to content

Tauri 2 migration + security hardening, CodeMirror 6 editor, signed auto-updating builds#3

Open
Anic888 wants to merge 13 commits into
mainfrom
tauri-v2-migration
Open

Tauri 2 migration + security hardening, CodeMirror 6 editor, signed auto-updating builds#3
Anic888 wants to merge 13 commits into
mainfrom
tauri-v2-migration

Conversation

@Anic888

@Anic888 Anic888 commented Jun 24, 2026

Copy link
Copy Markdown
Owner

Summary

Migrates RocketNote to Tauri 2 and lands a round of security hardening, an editor swap, and a signed/auto-updating release pipeline. Full list in CHANGELOG.md.

Highlights

Security

  • Git commands hardened against config-driven RCE (core.fsmonitor/hooks disabled, GIT_CONFIG_NOSYSTEM, ext/file transports blocked) — opening an untrusted repo can't run code via .git/config.
  • Terminal quick-run no longer allows script interpreters; pty_write_force gated to confirmation keystrokes (one-shot).
  • validate_path denies credential paths (~/.ssh, ~/.aws, Keychains, …); snippet/macro ids sanitized; file_exists scope-checked; read/global-search size caps.
  • OAuth token material redacted from errors/logs; PKCE (S256) added to the cloud OAuth flow; option-injection guard on git refs.
  • Keychain secrets stored via the native Security framework (no secret value on the process command line).

Editor & performance

  • Replaced the textarea editor with CodeMirror 6 — viewport virtualization (a 50k-line file keeps ~82 line nodes in the DOM), syntax highlighting, find/replace, native undo history.
  • Production bundle code-split into cacheable vendor chunks (app-shell ~174 kB vs a single ~1.7 MB chunk).

Distribution

  • GitHub Actions release pipeline with Developer ID signing + notarization, plus a signed auto-updater. Setup checklist in docs/distribution-setup.md.

Framework

  • Tauri 1.x → 2 (capabilities/ACL permission model, split plugins, v2 menu API); cargo update pulled tauri 2.11.3.

Verification

  • npm run build (tsc + vite): clean.
  • cargo build: clean. cargo test: 39 passed, 1 ignored (live-Keychain roundtrip, run with -- --ignored).
  • npx tauri build: produces a signed .app + .dmg + updater bundle; installs and launches on macOS.
  • Still to do before release: full click-through of every feature in the GUI.

Anic888 added 13 commits June 23, 2026 17:22
Add helpers::sanitize_id() rejecting path separators, traversal, NUL and absolute paths; apply to snippet/macro save+delete (the session-name fix was never applied here). Also add exceeds_size_limit() helper used by the read/search size caps.
Route every git command through git_cmd_base(), which sets GIT_CONFIG_NOSYSTEM and disables core.fsmonitor / core.hooksPath and the ext/file protocols. An untrusted repo's .git/config can no longer execute code on the automatic git_status (C3). Reject option-like branch/ref names in git_checkout/git_create_branch.
Remove script interpreters (bash/zsh/python3/node/swift) from execute_command's quick-run allowlist so the metacharacter/danger filters can't be trivially bypassed. Gate pty_write_force to accept ONLY confirmation keystrokes (Enter/Ctrl-C) and ONLY when a danger warning is pending (consumed one-shot), closing the direct force-write bypass.
Route file_exists through validate_path to remove the arbitrary host-path existence oracle. Enforce a 50 MiB cap in read_file/read_file_bytes and a 5 MiB per-file cap in global_search to prevent memory-exhaustion hangs.
Add redact_secrets() and wrap all cloud error bodies and token-response JSON before returning them; stop logging raw error objects in CloudStoragePanel.
generate_pkce() command returns a random verifier + base64url(SHA-256) challenge (verified against the RFC 7636 test vector). The frontend sends code_challenge/code_challenge_method=S256 in the auth URL for all three providers and passes the verifier to exchange_oauth_token, which binds it into the token request. An intercepted loopback authorization code can no longer be redeemed without the verifier.
GitHub Actions release.yml (tauri-action) builds a universal macOS binary, signs it with Developer ID, notarizes+staples, and publishes a draft Release with the updater bundle + latest.json. Enable the Tauri updater (active + S256 pubkey + GitHub 'latest' endpoint) and add the 'updater' cargo feature. Add Hardened-Runtime Entitlements.plist (un-ignored so CI can use it). Full secret/setup checklist in docs/distribution-setup.md. Signing/notarization run in CI with the maintainer's Apple credentials; not verifiable here.
…-file freeze (H4)

The old editor rendered the entire document as a highlight overlay + one DOM node per line, freezing the UI on multi-MB / many-line files. CodeMirror 6 virtualizes the viewport. Verified in-browser: a 50,000-line document keeps only ~82 line nodes in the DOM. Preserves the full EditorProps/EditorRef contract (App.tsx untouched): line numbers, per-language syntax highlighting, find/replace + navigate via @codemirror/search, native undo/redo history, word wrap, tab size, Cmd-wheel font zoom, theme, and the minimap. tsc + vite build pass.

Note: bundle grew (~1.7MB) from CodeMirror + language packages; code-splitting is a separate optimization.
Replace the single ~1.74MB JS chunk with manualChunks: codemirror, xterm, markdown, react-vendor, icons. App-shell entry drops from 1739kB to ~174kB; heavy libs load in parallel and stay cached across releases. Verified via npm run build.
validate_path previously allowed read/write/delete anywhere under $HOME. Add is_sensitive_path() denying secret stores (.ssh, .aws, .gnupg, .kube, .docker), macOS Keychains (~/Library/Keychains), and credential/history files (.netrc, .git-credentials, .npmrc, .pypirc, shell history). Ordinary files and ~/.config remain editable. Pure-path helper unit-tested.
Replace the 'security add/find-generic-password -w <value>' shell-outs with the keyring crate (native Security framework). The secret value no longer appears on the process command line, which is world-readable via ps/KERN_PROCARGS2 and was a cross-local-user disclosure of API keys / OAuth tokens. Service/account naming is preserved, so secrets stored by older versions stay readable. Verified: cargo test (39 passed) + live Keychain roundtrip (store/get/delete) passes.
Ran the official tauri migrate, then completed the manual parts: rewrote main.rs to the v2 menu builder API (SubmenuBuilder/MenuItemBuilder), on_menu_event(|app,event|) + Emitter, get_webview_window; added the seven tauri-plugin-* (dialog/clipboard/shell/fs/http/notification/updater); moved allowlist -> capabilities/*.json; tauri.conf.json -> v2 schema (app/bundle/plugins.updater with the existing pubkey, fileDropEnabled->dragDropEnabled). Frontend: invoke -> @tauri-apps/api/core, dialog -> @tauri-apps/plugin-dialog (type->kind), shell -> @tauri-apps/plugin-shell. Added Emitter import in terminal.rs. cargo update brought tauri 2.11.3 (fixes a wry 0.54 build incompat). Verified: npm run build (tsc+vite) clean, cargo build clean, cargo test 39 passed/1 ignored. Full bundle + GUI feature-test still pending.
…der icon & license

README: editor is now CodeMirror 6 (virtualized), backend Tauri 2, v2 build instructions (npm run tauri), auto-update + signed-build note. Add CHANGELOG.md (Unreleased) documenting the security hardening, PKCE, code-split, distribution pipeline and Tauri 2 migration. Add app-icon.png so the README header renders. Align Cargo.toml license to GPL-3.0 (matches LICENSE/README).

@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: 996fc754a2

ℹ️ 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 on lines +50 to +51
TAURI_PRIVATE_KEY: ${{ secrets.TAURI_PRIVATE_KEY }}
TAURI_KEY_PASSWORD: ${{ secrets.TAURI_KEY_PASSWORD }}

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

P1 Badge Use Tauri 2 updater signing environment variables

For tagged releases, this step passes the old updater key names, but Tauri 2's bundler reads TAURI_SIGNING_PRIVATE_KEY and TAURI_SIGNING_PRIVATE_KEY_PASSWORD. Because src-tauri/tauri.conf.json now contains an updater pubkey, the release build will not see the private key under the names it checks, so updater artifacts/signatures won't be produced correctly and the release job can fail.

Useful? React with 👍 / 👎.

Comment thread src-tauri/src/main.rs
.plugin(tauri_plugin_clipboard_manager::init())
.plugin(tauri_plugin_shell::init())
.plugin(tauri_plugin_fs::init())
.plugin(tauri_plugin_updater::Builder::new().build())

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 Start the updater check from the app

Registering the updater plugin only exposes the updater API; it does not fetch latest.json or install anything by itself in Tauri 2. I searched src and src-tauri and there is no @tauri-apps/plugin-updater check()/downloadAndInstall() call or Rust UpdaterExt::check() path, so even signed releases will never be discovered by installed clients.

Useful? React with 👍 / 👎.

Comment thread src-tauri/tauri.conf.json
"decorations": true,
"fileDropEnabled": true,
"transparent": false
"dragDropEnabled": true,

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 file-drop listener for Tauri 2

With Tauri 2 drag/drop enabled here, the frontend still listens for the old v1 tauri://file-drop event in src/hooks/useFileManager.ts; Tauri 2 renamed these to tauri://drag-drop/drag-enter and the drop payload now carries paths for type === 'drop'. As a result, dropping files onto the window no longer opens them after this migration.

Useful? React with 👍 / 👎.

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