-
Notifications
You must be signed in to change notification settings - Fork 13.3k
std::PathBuff::join("/some/path") overrides the original path in the resulting PathBuf #59726
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Comments
It is documented: https://doc.rust-lang.org/std/path/struct.PathBuf.html#method.push ( |
Would it not be possible to add something such as |
@Proximyst You could use |
I’d also like to add that this has resulted in a security issue in tower-http and warp, and the latter isn’t fixed yet. |
I've implemented a crate to mitigate path traversal vulnerabilities: https://crates.io/crates/path_ratchet/ (it currently doesn't support |
I support the initiative to add |
Just preventing the rhs of
I agree that path traversal vulnerabilities are unnecessarily hard to prevent, but nothing proposed in this thread looks like a slam-dunk solution to me. Something closer to the |
I agree that
Also see cap-std for a Rust library that implements a similar pattern. Consider the following example, on Windows: use std::path::Path;
fn main() {
let base_path = Path::new("/static/serve/dir");
let mut final_path = base_path.to_path_buf();
let path_components = Path::new( "c:/a/b/c");
final_path.push(path_components);
println!("{:?}", final_path);
} Unsurprisingly, it results in use std::path::Path;
fn main() {
let base_path = Path::new("/static/serve/dir");
let mut final_path = base_path.to_path_buf();
let path_components = Path::new("c:/a/b/c");
assert!(!path_components.is_absolute(), "bad path");
final_path.push(path_components);
println!("{:?}", final_path);
} This will trigger the assert. But what about a non-absolute prefix path? use std::path::Path;
fn main() {
let base_path = Path::new("/static/serve/dir");
let mut final_path = base_path.to_path_buf();
let path_components = Path::new("c:a/b/c");
assert!(!path_components.is_absolute(), "bad path");
final_path.push(path_components);
println!("{:?}", final_path);
} Surprisingly (at least to me), this works, resulting in So how could this be fixed? Looking at the source code for use std::path::Path;
fn main() {
let base_path = Path::new("/static/serve/dir");
let mut final_path = base_path.to_path_buf();
let path_components = Path::new("c:a/b/c");
assert!(!path_components.is_absolute(), "bad path");
if matches!(
path_components.components().next(),
Some(std::path::Component::Prefix(_))
) {
panic!("bad path");
}
final_path.push(path_components);
println!("{:?}", final_path);
} This will reject the relative prefix path. I think the situation around Oops, like I used |
There are two different problems which shouldn't be conflated:
Both solutions have their use case.
@hanna-kruppe Do you mean something like
👍 This is the reason why sanitisation/validation is bad and the principal "Parse, don’t validate" should be applied. |
This issue is a bit all over the place now. I see at least three separate actionable ideas floating around that one could pursue:
|
When join's argument start's with "/" then join overrides any path present in &self making this code valid:
This is not documented and generates a lot of confusion.
The text was updated successfully, but these errors were encountered: