diff options
| author | Hermes Agent <hermes@localhost> | 2026-08-12 05:12:40 +0000 |
|---|---|---|
| committer | Hermes Agent <hermes@localhost> | 2026-08-12 05:12:40 +0000 |
| commit | 3a47e14cbe6e15de0758c8bd4233a8d84ff80aa8 (patch) | |
| tree | 3822eca08cfd2201e3c532187054bce75109f1c2 | |
| parent | 251509d734a635f8e6448cef0925d881d4f5f184 (diff) | |
Reject archive outputs inside rootfs
| -rw-r--r-- | src/archive.rs | 19 | ||||
| -rw-r--r-- | tests/archive.rs | 17 |
2 files changed, 36 insertions, 0 deletions
diff --git a/src/archive.rs b/src/archive.rs index 9a899bf..ad424c4 100644 --- a/src/archive.rs +++ b/src/archive.rs @@ -26,6 +26,7 @@ impl NativeTarWriter { } let output = output.as_ref(); + reject_output_inside_rootfs(rootfs, output)?; let temporary = temporary_output_path(output)?; let result = (|| { let file = OpenOptions::new() @@ -53,6 +54,24 @@ impl NativeTarWriter { } } +fn reject_output_inside_rootfs(rootfs: &Path, output: &Path) -> Result<()> { + let rootfs = rootfs + .canonicalize() + .with_context(|| format!("canonicalize rootfs {}", rootfs.display()))?; + let output_parent = output.parent().unwrap_or_else(|| Path::new(".")); + let output_parent = output_parent + .canonicalize() + .with_context(|| format!("canonicalize archive output parent {}", output_parent.display()))?; + if output_parent.starts_with(&rootfs) { + bail!( + "archive output must not be inside rootfs: {} is below {}", + output.display(), + rootfs.display() + ); + } + Ok(()) +} + fn temporary_output_path(output: &Path) -> Result<PathBuf> { let name = output .file_name() diff --git a/tests/archive.rs b/tests/archive.rs index da3e66a..6f803ef 100644 --- a/tests/archive.rs +++ b/tests/archive.rs @@ -108,6 +108,23 @@ fn rejects_a_missing_rootfs() { } #[test] +fn rejects_an_archive_output_inside_the_rootfs() { + let fixture = tempdir().expect("fixture directory"); + let rootfs = fixture.path().join("rootfs"); + fs::create_dir_all(rootfs.join("etc")).expect("create rootfs"); + fs::write(rootfs.join("etc/controller.conf"), "controller=true\n").expect("write rootfs file"); + let artifact = rootfs.join("controller.tar"); + + let error = NativeTarWriter::new() + .write(&rootfs, &artifact) + .expect_err("archive output inside rootfs must be rejected"); + + assert!(error.to_string().contains("archive output must not be inside rootfs")); + assert!(!artifact.exists()); + assert!(!rootfs.join("controller.tar.partial").exists()); +} + +#[test] fn removes_partial_output_when_an_unsupported_entry_stops_packaging() { let fixture = tempdir().expect("fixture directory"); let rootfs = fixture.path().join("rootfs"); |