fix(cli): zeroize image_secret + correct atomic_write temp path

atomic_write now appends .tmp instead of replacing the extension
(manifest.enc.tmp, not manifest.tmp). image_secret is wrapped in
Zeroizing so both KDF inputs wipe on drop. Caught in Task 4 review.
This commit is contained in:
adlee-was-taken
2026-04-19 21:57:42 -04:00
parent 06d21bf7c9
commit 589d7b90b4

View File

@@ -37,7 +37,7 @@ impl UnlockedVault {
let image_path = get_image_path()?;
let image_bytes = fs::read(&image_path)
.with_context(|| format!("failed to read reference image {}", image_path.display()))?;
let image_secret = imgsecret::extract(&image_bytes)?;
let image_secret = Zeroizing::new(imgsecret::extract(&image_bytes)?);
let passphrase = Zeroizing::new(
rpassword::prompt_password("Passphrase: ")
@@ -46,7 +46,7 @@ impl UnlockedVault {
let master_key = derive_master_key(
passphrase.as_bytes(),
&image_secret,
&*image_secret,
&salt,
&params,
)?;
@@ -132,7 +132,9 @@ pub fn get_image_path() -> Result<PathBuf> {
/// Atomic write: write to <path>.tmp, then rename over <path>. Keeps the
/// vault file consistent if we crash mid-write.
fn atomic_write(path: &Path, data: &[u8]) -> Result<()> {
let tmp = path.with_extension("tmp");
let mut tmp = path.as_os_str().to_owned();
tmp.push(".tmp");
let tmp = PathBuf::from(tmp);
fs::write(&tmp, data).with_context(|| format!("failed to write {}", tmp.display()))?;
fs::rename(&tmp, path).with_context(|| format!("failed to rename {}", path.display()))?;
Ok(())