Skip to content

fix(vault): guard exports from overwriting the vault and harden error/format contracts - #33

Merged
italiancoder1950 merged 2 commits into
mainfrom
fix/encrypted-export-hardening
Nov 5, 2026
Merged

italiancoder1950 merged 2 commits into
mainfrom
fix/encrypted-export-hardening

Conversation

@italiancoder1950

Copy link
Copy Markdown
Contributor

Follow-up to #29 (encrypted export). Addresses the residuals from the post-merge review — no change to the on-disk format or the live vault write path.

What this does

  • Blocks exporting over the live vault. export_connections and export_connections_encrypted now refuse — before any write — when the save target resolves to ~/.config/remota/connections.dat. Previously the OS save dialog let the user overwrite their own vault with an export sealed under a different password, locking themselves out on the next unlock. New VaultError::WouldOverwriteVault; the UI shows a friendly notice.
  • Pins the Rust↔TS error contract. The frontend classifies errors by matching the serialized VaultError against bare strings ("NeedsPassword", "Crypto", …). A test now pins that serialization so a future rename breaks the build instead of silently breaking the import/export UX.
  • Pins the KDF/VERSION coupling. open_document rejects any header whose KDF params differ from KdfParams::default(), and it's shared by the live-vault load path — so changing the defaults would make every existing vault stop opening. A test pins the defaults to VERSION, forcing a VERSION bump + migration if they ever change.
  • Import provenance. The import message now says whether the file was an encrypted or a plaintext export, instead of always "Remota JSON".
  • Readable IO errors. VaultError::Io(String) serializes to {"Io":"..."}, which String(e) rendered as [object Object]. New vaultErrorText() formats it readably and is used in the export/import catch blocks.

Tests

  • export_guard_refuses_vault_path_and_leaves_it_untouched, export_guard_allows_a_different_path, export_guard_matches_noncanonical_spelling
  • vault_error_unit_variants_serialize_to_bare_strings
  • kdf_defaults_and_version_are_pinned
  • decode_* updated to assert the encrypted/plaintext flag

cargo test (from src-tauri/): 70 passed, 5 ignored. npm run build (tsc): clean.

Modeneis Marcio added 2 commits November 5, 2026 07:18
…/format contracts

- Refuse export_connections / export_connections_encrypted when the target
  resolves to the live vault file (new VaultError::WouldOverwriteVault),
  before any write, so an export can't clobber the vault and lock the user out.
- Pin the serde serialization of VaultError unit variants that the frontend
  string-matches, so a rename breaks the build instead of the UX.
- Pin KdfParams::default() and the on-disk VERSION together: changing the KDF
  defaults now requires bumping VERSION and migrating in open_document.
- Report whether an import came from an encrypted or plaintext export.
- Add vaultErrorText() so VaultError::Io renders readably instead of
  "[object Object]", and give the vault-overwrite case a friendly notice.
The guard resolved only the target or its immediate parent, so a path spelled
through a non-existent intermediate dir (<dir>/missing/../vault) resolved to
nothing and slipped past; the encrypted export's create_dir_all then
materialized the dir and the rename landed on the vault, overwriting it under a
different password (lockout). Walk up to the longest existing ancestor and apply
the remaining components lexically, and compare device+inode when the target
exists (which also catches a hardlink to the vault).
@italiancoder1950
italiancoder1950 merged commit 8813202 into main Nov 5, 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