Repository: pyrossh/rust-embed (language: rust)

Issue:
Directory traversal attack allowed when running in debug mode
```rust
#[derive(rust_embed::RustEmbed)]
#[folder = "src/"]
struct Asset;

fn main() {
    let d = Asset::get("../../../etc/passwd").unwrap().data;
    println!("{}", String::from_utf8_lossy(&d));
}
```

This code will (assuming you have the correct number of `../`s), print out the contents of your `/etc/passwd`.


Discussion / hints:
Thanks for the report. I'll open a PR ASAP.

Fix patch:
diff --git a/impl/src/lib.rs b/impl/src/lib.rs
index 5235d57..dd1ff14 100644
--- a/impl/src/lib.rs
+++ b/impl/src/lib.rs
@@ -98,6 +98,9 @@ fn dynamic(ident: &syn::Ident, folder_path: String, prefix: Option<&str>, includ
     const excludes: &[&str] = &[#(#excludes),*];
   };
 
+  let canonical_folder_path = Path::new(&folder_path).canonicalize().expect("folder path must resolve to an absolute path");
+  let canonical_folder_path = canonical_folder_path.to_str().expect("absolute folder path must be valid unicode");
+
   quote! {
       #[cfg(debug_assertions)]
       impl #ident {
@@ -111,8 +114,15 @@ fn dynamic(ident: &syn::Ident, folder_path: String, prefix: Option<&str>, includ
               let rel_file_path = file_path.replace("\\", "/");
               let file_path = std::path::Path::new(#folder_path).join(&rel_file_path);
 
+              // Make sure the path requested does not escape the folder path
+              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 None;
+              }
+
               if rust_embed::utils::is_path_included(&rel_file_path, includes, excludes) {
-                rust_embed::utils::read_file_from_fs(&file_path).ok()
+                rust_embed::utils::read_file_from_fs(&canonical_file_path).ok()
               } else {
                 None
               }


Test patch:
diff --git a/tests/path_traversal_attack.rs b/tests/path_traversal_attack.rs
new file mode 100644
index 0000000..43e4e8b
--- /dev/null
+++ b/tests/path_traversal_attack.rs
@@ -0,0 +1,13 @@
+use rust_embed::RustEmbed;
+
+#[derive(RustEmbed)]
+#[folder = "examples/public/"]
+struct Assets;
+
+/// Prevent attempts to access files 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]
+fn path_traversal_attack_fails() {
+  assert!(Assets::get("../basic.rs").is_none());
+}