~repos /rust-embed

#rust#proc-macro#http

git clone https://pyrossh.dev/repos/rust-embed.git
Discussions: https://groups.google.com/g/rust-embed-devs

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


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
+ }