mirror of
https://github.com/rustfs/rustfs.git
synced 2026-08-19 19:16:17 +00:00
test(property): expand parser invariants (#3534)
* test(property): harden parser invariants for #3522 * test(property): satisfy clippy for #3522
This commit is contained in:
@@ -770,6 +770,11 @@ where
|
||||
let decoded_path = percent_decode_str(&path_str)
|
||||
.decode_utf8()
|
||||
.map_err(|_| FsError::GeneralFailure)?;
|
||||
|
||||
if decoded_path.chars().any(char::is_control) {
|
||||
return Err(FsError::GeneralFailure);
|
||||
}
|
||||
|
||||
let cleaned_path = path::clean(&decoded_path);
|
||||
let (bucket, object) = path::path_to_bucket_object(&cleaned_path);
|
||||
|
||||
@@ -777,6 +782,10 @@ where
|
||||
return Ok((String::new(), None));
|
||||
}
|
||||
|
||||
if object.contains(path::GLOBAL_DIR_SUFFIX) {
|
||||
return Err(FsError::GeneralFailure);
|
||||
}
|
||||
|
||||
let key = if object.is_empty() { None } else { Some(object) };
|
||||
Ok((bucket, key))
|
||||
}
|
||||
@@ -1689,6 +1698,7 @@ mod tests {
|
||||
use dav_server::davpath::DavPath;
|
||||
use dav_server::fs::FsError;
|
||||
use futures_util::StreamExt;
|
||||
use percent_encoding::{NON_ALPHANUMERIC, utf8_percent_encode};
|
||||
use rustfs_credentials::Credentials;
|
||||
use rustfs_policy::auth::UserIdentity;
|
||||
use s3s::dto::*;
|
||||
@@ -2186,6 +2196,58 @@ mod tests {
|
||||
assert_eq!(key.as_deref(), Some("file with spaces.txt"));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn parse_path_rejects_control_bytes_after_decode() {
|
||||
let driver = driver();
|
||||
let path = DavPath::new("/bucket/report%0Aname.txt").expect("path should parse");
|
||||
|
||||
let err = driver.parse_path(&path).expect_err("control bytes should be rejected");
|
||||
|
||||
assert_eq!(err, FsError::GeneralFailure);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn parse_path_rejects_internal_directory_marker() {
|
||||
let driver = driver();
|
||||
let path = DavPath::new("/bucket/__XLDIR__").expect("path should parse");
|
||||
|
||||
let err = driver
|
||||
.parse_path(&path)
|
||||
.expect_err("internal directory marker should be rejected");
|
||||
|
||||
assert_eq!(err, FsError::GeneralFailure);
|
||||
}
|
||||
|
||||
proptest::proptest! {
|
||||
#[test]
|
||||
fn parse_path_never_leaks_control_bytes_or_traversal_in_ok_output(
|
||||
input in proptest::prelude::any::<String>(),
|
||||
) {
|
||||
let driver = driver();
|
||||
let encoded = utf8_percent_encode(&input, NON_ALPHANUMERIC).to_string();
|
||||
let Ok(path) = DavPath::new(&format!("/{encoded}")) else {
|
||||
return Ok(());
|
||||
};
|
||||
|
||||
match driver.parse_path(&path) {
|
||||
Err(err) => {
|
||||
proptest::prop_assert_eq!(err, FsError::GeneralFailure);
|
||||
}
|
||||
Ok((bucket, key)) => {
|
||||
proptest::prop_assert!(!bucket.contains('/'));
|
||||
proptest::prop_assert!(!bucket.chars().any(char::is_control));
|
||||
|
||||
if let Some(k) = key.as_deref() {
|
||||
proptest::prop_assert!(!k.chars().any(char::is_control));
|
||||
proptest::prop_assert!(!k.starts_with('/'));
|
||||
proptest::prop_assert!(!k.split('/').any(|segment| segment == ".."));
|
||||
proptest::prop_assert!(!k.contains(rustfs_utils::path::GLOBAL_DIR_SUFFIX));
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[tokio::test]
|
||||
async fn directory_rename_returns_error_when_delete_fails_after_successful_copy() {
|
||||
let (driver, storage) = recording_driver(
|
||||
|
||||
Reference in New Issue
Block a user