diff --git a/examples/public/symlinks/main.js b/examples/public/symlinks/main.js new file mode 120000 index 0000000..05fdb15 --- /dev/null +++ b/examples/public/symlinks/main.js @@ -0,0 +1 @@ +../main.js \ No newline at end of file diff --git a/impl/src/lib.rs b/impl/src/lib.rs index 114456b..cedb14b 100644 --- a/impl/src/lib.rs +++ b/impl/src/lib.rs @@ -141,7 +141,16 @@ fn dynamic(ident: &syn::Ident, folder_path: String, prefix: Option<&str>, includ let canonical_file_path = file_path.canonicalize().ok()?; if !canonical_file_path.starts_with(#canonical_folder_path) { // Tried to request a path that is not in the embedded folder - return ::std::option::Option::None; + + // TODO: Currently it allows "path_traversal_attack" for the symlink files + // For it to be working properly we need to get absolute path first + // and check that instead if it starts with `canonical_folder_path` + // https://doc.rust-lang.org/std/path/fn.absolute.html (currently nightly) + // Should be allowed only if it was a symlink + let metadata = ::std::fs::symlink_metadata(file_path.as_path()).ok()?; + if !metadata.is_symlink() { + return ::std::option::Option::None; + } } if rust_embed::utils::is_path_included(&rel_file_path, INCLUDES, EXCLUDES) { diff --git a/tests/include_exclude.rs b/tests/include_exclude.rs index e49ca89..a0dd99d 100644 --- a/tests/include_exclude.rs +++ b/tests/include_exclude.rs @@ -9,7 +9,8 @@ fn get_works() { assert!(AllAssets::get("index.html").is_some(), "index.html should exist"); assert!(AllAssets::get("gg.html").is_none(), "gg.html should not exist"); assert!(AllAssets::get("images/llama.png").is_some(), "llama.png should exist"); - assert_eq!(AllAssets::iter().count(), 6); + assert!(AllAssets::get("symlinks/main.js").is_some(), "main.js should exist"); + assert_eq!(AllAssets::iter().count(), 7); } #[derive(RustEmbed)] @@ -36,8 +37,9 @@ struct ExcludeSomeAssets; fn excluding_some_assets_works() { assert!(ExcludeSomeAssets::get("index.html").is_none(), "index.html should not exist"); assert!(ExcludeSomeAssets::get("main.js").is_some(), "main.js should exist"); + assert!(ExcludeSomeAssets::get("symlinks/main.js").is_some(), "main.js symlink should exist"); assert!(ExcludeSomeAssets::get("images/llama.png").is_none(), "llama.png should not exist"); - assert_eq!(ExcludeSomeAssets::iter().count(), 2); + assert_eq!(ExcludeSomeAssets::iter().count(), 3); } #[derive(RustEmbed)] @@ -52,3 +54,15 @@ fn exclude_has_higher_priority() { assert!(ExcludePriorityAssets::get("images/llama.png").is_some(), "llama.png should exist"); assert_eq!(ExcludePriorityAssets::iter().count(), 2); } + +#[derive(RustEmbed)] +#[folder = "examples/public/symlinks"] +#[include = "main.js"] +struct IncludeSymlink; + +#[test] +fn include_symlink() { + assert_eq!(IncludeSymlink::iter().count(), 1); + assert_eq!(IncludeSymlink::iter().next(), Some(std::borrow::Cow::Borrowed("main.js"))); + assert!(IncludeSymlink::get("main.js").is_some()) +} diff --git a/tests/interpolated_path.rs b/tests/interpolated_path.rs index 0df2366..e446eef 100644 --- a/tests/interpolated_path.rs +++ b/tests/interpolated_path.rs @@ -19,7 +19,7 @@ fn iter_works() { assert!(Asset::get(file.as_ref()).is_some()); num_files += 1; } - assert_eq!(num_files, 6); + assert_eq!(num_files, 7); } #[test] @@ -32,6 +32,6 @@ fn trait_works_generic_helper() { assert!(E::get(file.as_ref()).is_some()); num_files += 1; } - assert_eq!(num_files, 6); + assert_eq!(num_files, 7); assert!(E::get("gg.html").is_none(), "gg.html should not exist"); } diff --git a/tests/lib.rs b/tests/lib.rs index a4636aa..b3d6433 100644 --- a/tests/lib.rs +++ b/tests/lib.rs @@ -28,7 +28,7 @@ fn iter_works() { assert!(Asset::get(file.as_ref()).is_some()); num_files += 1; } - assert_eq!(num_files, 6); + assert_eq!(num_files, 7); } #[test] @@ -41,6 +41,6 @@ fn trait_works_generic_helper() { assert!(E::get(file.as_ref()).is_some()); num_files += 1; } - assert_eq!(num_files, 6); + assert_eq!(num_files, 7); assert!(E::get("gg.html").is_none(), "gg.html should not exist"); } diff --git a/tests/path_traversal_attack.rs b/tests/path_traversal_attack.rs index 43e4e8b..63e9bee 100644 --- a/tests/path_traversal_attack.rs +++ b/tests/path_traversal_attack.rs @@ -11,3 +11,17 @@ struct Assets; fn path_traversal_attack_fails() { assert!(Assets::get("../basic.rs").is_none()); } + +#[derive(RustEmbed)] +#[folder = "examples/axum-spa/"] +struct AxumAssets; + +// TODO: +/// Prevent attempts to access symlinks outside of the embedded folder. +/// This is mainly a concern when running in debug mode, since that loads from +/// the file system at runtime. +#[test] +#[ignore = "see https://github.com/pyrossh/rust-embed/pull/235"] +fn path_traversal_attack_symlink_fails() { + assert!(Assets::get("../public/symlinks/main.js").is_none()); +}