~repos /rust-embed

#rust#proc-macro#http

git clone https://pyrossh.dev/repos/rust-embed.git

rust macro which loads files into the rust binary at compile time during release and loads the file from the fs during dev.


51ee3fa6 Peter John

4 years ago
Merge pull request #160 from pyros2097/fix/path-traversal-attack
Files changed (2) hide show
  1. impl/src/lib.rs +11 -1
  2. tests/path_traversal_attack.rs +13 -0
impl/src/lib.rs CHANGED
@@ -98,6 +98,9 @@ fn dynamic(ident: &syn::Ident, folder_path: String, prefix: Option<&str>, includ
98
98
  const excludes: &[&str] = &[#(#excludes),*];
99
99
  };
100
100
 
101
+ let canonical_folder_path = Path::new(&folder_path).canonicalize().expect("folder path must resolve to an absolute path");
102
+ let canonical_folder_path = canonical_folder_path.to_str().expect("absolute folder path must be valid unicode");
103
+
101
104
  quote! {
102
105
  #[cfg(debug_assertions)]
103
106
  impl #ident {
@@ -111,8 +114,15 @@ fn dynamic(ident: &syn::Ident, folder_path: String, prefix: Option<&str>, includ
111
114
  let rel_file_path = file_path.replace("\\", "/");
112
115
  let file_path = std::path::Path::new(#folder_path).join(&rel_file_path);
113
116
 
117
+ // Make sure the path requested does not escape the folder path
118
+ let canonical_file_path = file_path.canonicalize().ok()?;
119
+ if !canonical_file_path.starts_with(#canonical_folder_path) {
120
+ // Tried to request a path that is not in the embedded folder
121
+ return None;
122
+ }
123
+
114
124
  if rust_embed::utils::is_path_included(&rel_file_path, includes, excludes) {
115
- rust_embed::utils::read_file_from_fs(&file_path).ok()
125
+ rust_embed::utils::read_file_from_fs(&canonical_file_path).ok()
116
126
  } else {
117
127
  None
118
128
  }
tests/path_traversal_attack.rs ADDED
@@ -0,0 +1,13 @@
1
+ use rust_embed::RustEmbed;
2
+
3
+ #[derive(RustEmbed)]
4
+ #[folder = "examples/public/"]
5
+ struct Assets;
6
+
7
+ /// Prevent attempts to access files outside of the embedded folder.
8
+ /// This is mainly a concern when running in debug mode, since that loads from
9
+ /// the file system at runtime.
10
+ #[test]
11
+ fn path_traversal_attack_fails() {
12
+ assert!(Assets::get("../basic.rs").is_none());
13
+ }