Repository navigation
fix(vault): guard exports from overwriting the vault and harden error/format contracts - #33
Merged
Merged
Conversation
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).
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
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
export_connectionsandexport_connections_encryptednow 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. NewVaultError::WouldOverwriteVault; the UI shows a friendly notice.VaultErroragainst 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.open_documentrejects any header whose KDF params differ fromKdfParams::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.VaultError::Io(String)serializes to{"Io":"..."}, whichString(e)rendered as[object Object]. NewvaultErrorText()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_spellingvault_error_unit_variants_serialize_to_bare_stringskdf_defaults_and_version_are_pinneddecode_*updated to assert the encrypted/plaintext flagcargo test(fromsrc-tauri/): 70 passed, 5 ignored.npm run build(tsc): clean.