summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorHermes Agent <hermes@localhost>2026-08-12 05:12:40 +0000
committerHermes Agent <hermes@localhost>2026-08-12 05:12:40 +0000
commit3a47e14cbe6e15de0758c8bd4233a8d84ff80aa8 (patch)
tree3822eca08cfd2201e3c532187054bce75109f1c2
parent251509d734a635f8e6448cef0925d881d4f5f184 (diff)
Reject archive outputs inside rootfs
-rw-r--r--src/archive.rs19
-rw-r--r--tests/archive.rs17
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");