Skip to content

Commit a475bf3

Browse files
fix: tighten encrypted archive key mutation tests
1 parent d0922f5 commit a475bf3

1 file changed

Lines changed: 87 additions & 71 deletions

File tree

src/pages/key_management.rs

Lines changed: 87 additions & 71 deletions
Original file line numberDiff line numberDiff line change
@@ -934,72 +934,82 @@ fn materialize_safe_required_file_symlinks(archive_dir: &Path) -> Result<()> {
934934
})?;
935935

936936
for rel_path in REQUIRED_SITE_FILES {
937-
let path = archive_dir.join(rel_path);
938-
let metadata = match std::fs::symlink_metadata(&path) {
939-
Ok(metadata) => metadata,
940-
Err(err) if err.kind() == std::io::ErrorKind::NotFound => continue,
941-
Err(err) => {
942-
return Err(err).with_context(|| {
943-
format!(
944-
"Failed to inspect required site file {} before key mutation",
945-
path.display()
946-
)
947-
});
948-
}
949-
};
950-
if !metadata.file_type().is_symlink() {
951-
continue;
952-
}
937+
materialize_safe_required_file_symlink(archive_dir, &canonical_archive_dir, rel_path)?;
938+
}
953939

954-
let canonical_target = path.canonicalize().with_context(|| {
955-
format!(
956-
"Failed to resolve symlinked required site file {} before key mutation",
957-
path.display()
958-
)
959-
})?;
960-
if !canonical_target.starts_with(&canonical_archive_dir) {
961-
bail!(
962-
"Refusing to materialize required site file symlink outside archive root: {}",
963-
path.display()
964-
);
965-
}
940+
Ok(())
941+
}
966942

967-
let target_metadata = std::fs::metadata(&canonical_target).with_context(|| {
968-
format!(
969-
"Failed to inspect symlink target {} before key mutation",
970-
canonical_target.display()
971-
)
972-
})?;
973-
if !target_metadata.file_type().is_file() {
974-
bail!(
975-
"Refusing to materialize required site file symlink that does not point to a regular file: {}",
976-
path.display()
977-
);
943+
fn materialize_safe_required_file_symlink(
944+
archive_dir: &Path,
945+
canonical_archive_dir: &Path,
946+
rel_path: &str,
947+
) -> Result<()> {
948+
let path = archive_dir.join(rel_path);
949+
let metadata = match std::fs::symlink_metadata(&path) {
950+
Ok(metadata) => metadata,
951+
Err(err) if err.kind() == std::io::ErrorKind::NotFound => return Ok(()),
952+
Err(err) => {
953+
return Err(err).with_context(|| {
954+
format!(
955+
"Failed to inspect required site file {} before key mutation",
956+
path.display()
957+
)
958+
});
978959
}
960+
};
961+
if !metadata.file_type().is_symlink() {
962+
return Ok(());
963+
}
964+
965+
let canonical_target = path.canonicalize().with_context(|| {
966+
format!(
967+
"Failed to resolve symlinked required site file {} before key mutation",
968+
path.display()
969+
)
970+
})?;
971+
if !canonical_target.starts_with(canonical_archive_dir) {
972+
bail!(
973+
"Refusing to materialize required site file symlink outside archive root: {}",
974+
path.display()
975+
);
976+
}
979977

980-
let temp_path = unique_atomic_sidecar_path(&path, "materialize", "site-file");
981-
std::fs::copy(&canonical_target, &temp_path).with_context(|| {
978+
let target_metadata = std::fs::metadata(&canonical_target).with_context(|| {
979+
format!(
980+
"Failed to inspect symlink target {} before key mutation",
981+
canonical_target.display()
982+
)
983+
})?;
984+
if !target_metadata.file_type().is_file() {
985+
bail!(
986+
"Refusing to materialize required site file symlink that does not point to a regular file: {}",
987+
path.display()
988+
);
989+
}
990+
991+
let temp_path = unique_atomic_sidecar_path(&path, "materialize", "site-file");
992+
std::fs::copy(&canonical_target, &temp_path).with_context(|| {
993+
format!(
994+
"Failed copying symlink target {} into staged required site file {}",
995+
canonical_target.display(),
996+
temp_path.display()
997+
)
998+
})?;
999+
File::open(&temp_path)
1000+
.and_then(|file| file.sync_all())
1001+
.with_context(|| {
9821002
format!(
983-
"Failed copying symlink target {} into staged required site file {}",
984-
canonical_target.display(),
1003+
"Failed syncing materialized required site file {}",
9851004
temp_path.display()
9861005
)
9871006
})?;
988-
File::open(&temp_path)
989-
.and_then(|file| file.sync_all())
990-
.with_context(|| {
991-
format!(
992-
"Failed syncing materialized required site file {}",
993-
temp_path.display()
994-
)
995-
})?;
996-
replace_file_from_temp(&temp_path, &path).with_context(|| {
997-
format!(
998-
"Failed materializing required site file symlink {} before key mutation",
999-
path.display()
1000-
)
1001-
})?;
1002-
}
1007+
replace_file_from_temp(&temp_path, &path).with_context(|| {
1008+
format!(
1009+
"Failed materializing required site file symlink {} before key mutation",
1010+
path.display()
1011+
)
1012+
})?;
10031013

10041014
Ok(())
10051015
}
@@ -1886,34 +1896,40 @@ mod tests {
18861896

18871897
#[test]
18881898
#[cfg(unix)]
1889-
fn test_key_add_password_materializes_in_tree_symlinked_required_asset() {
1899+
fn test_key_add_password_materializes_in_tree_symlinked_required_asset() -> Result<()> {
18901900
let (_temp_dir, archive_dir) = setup_test_archive();
1891-
let site_dir = super::super::resolve_site_dir(&archive_dir).unwrap();
1901+
let site_dir = super::super::resolve_site_dir(&archive_dir)?;
18921902
replace_viewer_with_in_tree_symlink(&site_dir);
18931903

1894-
key_add_password(&archive_dir, "test-password", "new-password").unwrap();
1904+
key_add_password(&archive_dir, "test-password", "new-password")?;
18951905

1896-
assert_eq!(verify_bundle(&archive_dir, false).unwrap().status, "valid");
1897-
let viewer_metadata = std::fs::symlink_metadata(site_dir.join("viewer.js")).unwrap();
1898-
assert!(viewer_metadata.file_type().is_file());
1899-
assert!(!viewer_metadata.file_type().is_symlink());
1906+
anyhow::ensure!(verify_bundle(&archive_dir, false)?.status == "valid");
1907+
let viewer_metadata = std::fs::symlink_metadata(site_dir.join("viewer.js"))?;
1908+
anyhow::ensure!(viewer_metadata.file_type().is_file());
1909+
anyhow::ensure!(!viewer_metadata.file_type().is_symlink());
1910+
Ok(())
19001911
}
19011912

19021913
#[test]
19031914
#[cfg(unix)]
1904-
fn test_key_add_password_wrong_password_preserves_in_tree_symlinked_required_asset() {
1915+
fn test_key_add_password_wrong_password_preserves_in_tree_symlinked_required_asset()
1916+
-> Result<()> {
19051917
let (_temp_dir, archive_dir) = setup_test_archive();
1906-
let site_dir = super::super::resolve_site_dir(&archive_dir).unwrap();
1918+
let site_dir = super::super::resolve_site_dir(&archive_dir)?;
19071919
replace_viewer_with_in_tree_symlink(&site_dir);
19081920

1909-
let err = key_add_password(&archive_dir, "wrong-password", "new-password").unwrap_err();
1921+
let err = match key_add_password(&archive_dir, "wrong-password", "new-password") {
1922+
Ok(_) => bail!("wrong password unexpectedly added a key slot"),
1923+
Err(err) => err,
1924+
};
19101925

1911-
assert!(
1926+
anyhow::ensure!(
19121927
err.to_string().contains("Invalid password"),
19131928
"unexpected error: {err:#}"
19141929
);
1915-
let viewer_metadata = std::fs::symlink_metadata(site_dir.join("viewer.js")).unwrap();
1916-
assert!(viewer_metadata.file_type().is_symlink());
1930+
let viewer_metadata = std::fs::symlink_metadata(site_dir.join("viewer.js"))?;
1931+
anyhow::ensure!(viewer_metadata.file_type().is_symlink());
1932+
Ok(())
19171933
}
19181934

19191935
#[test]

0 commit comments

Comments
 (0)