diff --git a/src/libs/safe-path/src/pinned_path_buf.rs b/src/libs/safe-path/src/pinned_path_buf.rs index e25b43769f..4310637df5 100644 --- a/src/libs/safe-path/src/pinned_path_buf.rs +++ b/src/libs/safe-path/src/pinned_path_buf.rs @@ -230,6 +230,7 @@ impl AsRef for PinnedPathBuf { #[cfg(test)] mod tests { use super::*; + use std::ffi::OsString; use std::fs::DirBuilder; use std::io::Write; use std::os::unix::fs::{symlink, MetadataExt}; @@ -396,5 +397,48 @@ mod tests { let path = path.open_child(OsStr::new("child")).unwrap(); let content = fs::read_to_string(&path).unwrap(); assert_eq!(&content, "test"); + + path.open_child(&OsString::from("__does_not_exist__")) + .unwrap_err(); + path.open_child(&OsString::from("test/a")).unwrap_err(); + } + + #[test] + fn test_prepare_path_component() { + assert!(PinnedPathBuf::prepare_path_component(&OsString::from("")).is_err()); + assert!(PinnedPathBuf::prepare_path_component(&OsString::from(".")).is_err()); + assert!(PinnedPathBuf::prepare_path_component(&OsString::from("..")).is_err()); + assert!(PinnedPathBuf::prepare_path_component(&OsString::from("/")).is_err()); + assert!(PinnedPathBuf::prepare_path_component(&OsString::from("//")).is_err()); + assert!(PinnedPathBuf::prepare_path_component(&OsString::from("a/b")).is_err()); + assert!(PinnedPathBuf::prepare_path_component(&OsString::from("./b")).is_err()); + assert!(PinnedPathBuf::prepare_path_component(&OsString::from("a/.")).is_err()); + assert!(PinnedPathBuf::prepare_path_component(&OsString::from("a/..")).is_err()); + assert!(PinnedPathBuf::prepare_path_component(&OsString::from("a/./")).is_err()); + assert!(PinnedPathBuf::prepare_path_component(&OsString::from("a/../")).is_err()); + assert!(PinnedPathBuf::prepare_path_component(&OsString::from("a/./a")).is_err()); + assert!(PinnedPathBuf::prepare_path_component(&OsString::from("a/../a")).is_err()); + + assert!(PinnedPathBuf::prepare_path_component(&OsString::from("a")).is_ok()); + assert!(PinnedPathBuf::prepare_path_component(&OsString::from("a.b")).is_ok()); + assert!(PinnedPathBuf::prepare_path_component(&OsString::from("a..b")).is_ok()); + } + + #[test] + fn test_target_fs_object_changed() { + let rootfs_dir = tempfile::tempdir().expect("failed to create tmpdir"); + let rootfs_path = rootfs_dir.path(); + let file = rootfs_path.join("child"); + fs::write(&file, "test").unwrap(); + + let path = PinnedPathBuf::from_path(&file).unwrap(); + let path3 = fs::read_link(path.as_path()).unwrap(); + assert_eq!(&path3, path.target()); + fs::rename(file, rootfs_path.join("child2")).unwrap(); + let path4 = fs::read_link(path.as_path()).unwrap(); + assert_ne!(&path4, path.target()); + fs::remove_file(rootfs_path.join("child2")).unwrap(); + let path5 = fs::read_link(path.as_path()).unwrap(); + assert_ne!(&path4, &path5); } } diff --git a/src/libs/safe-path/src/scoped_dir_builder.rs b/src/libs/safe-path/src/scoped_dir_builder.rs index 656da1c0f7..39ceac1076 100644 --- a/src/libs/safe-path/src/scoped_dir_builder.rs +++ b/src/libs/safe-path/src/scoped_dir_builder.rs @@ -253,9 +253,42 @@ mod tests { builder.create("../../.").unwrap(); builder.create("").unwrap(); builder.create_with_unscoped_path("/").unwrap(); - builder.create_with_unscoped_path(".").unwrap(); - builder.create_with_unscoped_path("..").unwrap(); - builder.create_with_unscoped_path("../../.").unwrap(); - builder.create_with_unscoped_path("").unwrap(); + builder.create_with_unscoped_path("/..").unwrap(); + builder.create_with_unscoped_path("/../.").unwrap(); + } + + #[test] + fn test_create_with_absolute_path() { + // create temporary directory to emulate container rootfs with symlink + let rootfs_dir = tempdir().expect("failed to create tmpdir"); + DirBuilder::new() + .create(rootfs_dir.path().join("b")) + .unwrap(); + symlink(rootfs_dir.path().join("b"), rootfs_dir.path().join("a")).unwrap(); + let rootfs_path = &rootfs_dir.path().join("a"); + + let mut builder = ScopedDirBuilder::new(&rootfs_path).unwrap(); + builder.create_with_unscoped_path("/").unwrap_err(); + builder + .create_with_unscoped_path(rootfs_path.join("../__xxxx___xxx__")) + .unwrap_err(); + builder + .create_with_unscoped_path(rootfs_path.join("c/d")) + .unwrap_err(); + + // Return `AlreadyExist` when recursive is false + builder.create_with_unscoped_path(&rootfs_path).unwrap_err(); + builder + .create_with_unscoped_path(rootfs_path.join(".")) + .unwrap_err(); + + builder.recursive(true); + builder.create_with_unscoped_path(&rootfs_path).unwrap(); + builder + .create_with_unscoped_path(rootfs_path.join(".")) + .unwrap(); + builder + .create_with_unscoped_path(rootfs_path.join("c/d")) + .unwrap(); } } diff --git a/src/libs/safe-path/src/scoped_path_resolver.rs b/src/libs/safe-path/src/scoped_path_resolver.rs index 8d61111d79..59b06bfe70 100644 --- a/src/libs/safe-path/src/scoped_path_resolver.rs +++ b/src/libs/safe-path/src/scoped_path_resolver.rs @@ -319,6 +319,97 @@ mod tests { // Detect symlink loop. fs::symlink("/endpoint_b", rootfs_path.join("endpoint_a")).unwrap(); fs::symlink("/endpoint_a", rootfs_path.join("endpoint_b")).unwrap(); + scoped_resolve(rootfs_path, "endpoint_a").unwrap_err(); + } + + #[test] + fn test_scoped_join() { + // create temporary directory to emulate container rootfs with symlink + let rootfs_dir = tempdir().expect("failed to create tmpdir"); + let rootfs_path = &rootfs_dir.path(); + + assert_eq!( + scoped_join(&rootfs_path, "a").unwrap(), + rootfs_path.join("a") + ); + assert_eq!( + scoped_join(&rootfs_path, "./a").unwrap(), + rootfs_path.join("a") + ); + assert_eq!( + scoped_join(&rootfs_path, "././a").unwrap(), + rootfs_path.join("a") + ); + assert_eq!( + scoped_join(&rootfs_path, "c/d/../../a").unwrap(), + rootfs_path.join("a") + ); + assert_eq!( + scoped_join(&rootfs_path, "c/d/../../../.././a").unwrap(), + rootfs_path.join("a") + ); + assert_eq!( + scoped_join(&rootfs_path, "../../a").unwrap(), + rootfs_path.join("a") + ); + assert_eq!( + scoped_join(&rootfs_path, "./../a").unwrap(), + rootfs_path.join("a") + ); + } + + #[test] + fn test_scoped_join_symlink() { + // create temporary directory to emulate container rootfs with symlink + let rootfs_dir = tempdir().expect("failed to create tmpdir"); + let rootfs_path = &rootfs_dir.path(); + DirBuilder::new() + .recursive(true) + .create(rootfs_dir.path().join("b/c")) + .unwrap(); + fs::symlink("b/c", rootfs_dir.path().join("a")).unwrap(); + + let target = rootfs_path.join("b/c"); + assert_eq!(scoped_join(&rootfs_path, "a").unwrap(), target); + assert_eq!(scoped_join(&rootfs_path, "./a").unwrap(), target); + assert_eq!(scoped_join(&rootfs_path, "././a").unwrap(), target); + assert_eq!(scoped_join(&rootfs_path, "b/c/../../a").unwrap(), target); + assert_eq!( + scoped_join(&rootfs_path, "b/c/../../../.././a").unwrap(), + target + ); + assert_eq!(scoped_join(&rootfs_path, "../../a").unwrap(), target); + assert_eq!(scoped_join(&rootfs_path, "./../a").unwrap(), target); + assert_eq!(scoped_join(&rootfs_path, "a/../../../a").unwrap(), target); + assert_eq!(scoped_join(&rootfs_path, "a/../../../b/c").unwrap(), target); + } + + #[test] + fn test_scoped_join_symlink_loop() { + // create temporary directory to emulate container rootfs with symlink + let rootfs_dir = tempdir().expect("failed to create tmpdir"); + let rootfs_path = &rootfs_dir.path(); + fs::symlink("/endpoint_b", rootfs_path.join("endpoint_a")).unwrap(); + fs::symlink("/endpoint_a", rootfs_path.join("endpoint_b")).unwrap(); scoped_join(rootfs_path, "endpoint_a").unwrap_err(); } + + #[test] + fn test_scoped_join_unicode_character() { + // create temporary directory to emulate container rootfs with symlink + let rootfs_dir = tempdir().expect("failed to create tmpdir"); + let rootfs_path = &rootfs_dir.path().canonicalize().unwrap(); + + let path = scoped_join(rootfs_path, "您好").unwrap(); + assert_eq!(path, rootfs_path.join("您好")); + + let path = scoped_join(rootfs_path, "../../../您好").unwrap(); + assert_eq!(path, rootfs_path.join("您好")); + + let path = scoped_join(rootfs_path, "。。/您好").unwrap(); + assert_eq!(path, rootfs_path.join("。。/您好")); + + let path = scoped_join(rootfs_path, "您好/../../test").unwrap(); + assert_eq!(path, rootfs_path.join("test")); + } }