Skip to content

Commit a757803

Browse files
committed
fix: ToNormalPathComponent now also validates the component if it's not a Path.
Previously it was possible for strings or BString/BStr to contain non-normal components, now there is much more validation.
1 parent 0b244a7 commit a757803

File tree

2 files changed

+33
-7
lines changed

2 files changed

+33
-7
lines changed

gix-fs/src/stack.rs

+20-7
Original file line numberDiff line numberDiff line change
@@ -50,30 +50,43 @@ fn component_to_os_str<'a>(
5050

5151
impl ToNormalPathComponents for &BStr {
5252
fn to_normal_path_components(&self) -> impl Iterator<Item = Result<&OsStr, to_normal_path_components::Error>> {
53-
self.split(|b| *b == b'/').filter_map(bytes_component_to_os_str)
53+
self.split(|b| *b == b'/')
54+
.filter_map(|c| bytes_component_to_os_str(c, self))
5455
}
5556
}
5657

5758
impl ToNormalPathComponents for &str {
5859
fn to_normal_path_components(&self) -> impl Iterator<Item = Result<&OsStr, to_normal_path_components::Error>> {
59-
self.split('/').filter_map(|c| bytes_component_to_os_str(c.as_bytes()))
60+
self.split('/')
61+
.filter_map(|c| bytes_component_to_os_str(c.as_bytes(), (*self).into()))
6062
}
6163
}
6264

6365
impl ToNormalPathComponents for &BString {
6466
fn to_normal_path_components(&self) -> impl Iterator<Item = Result<&OsStr, to_normal_path_components::Error>> {
65-
self.split(|b| *b == b'/').filter_map(bytes_component_to_os_str)
67+
self.split(|b| *b == b'/')
68+
.filter_map(|c| bytes_component_to_os_str(c, self.as_bstr()))
6669
}
6770
}
6871

69-
fn bytes_component_to_os_str(component: &[u8]) -> Option<Result<&OsStr, to_normal_path_components::Error>> {
72+
fn bytes_component_to_os_str<'a>(
73+
component: &'a [u8],
74+
path: &BStr,
75+
) -> Option<Result<&'a OsStr, to_normal_path_components::Error>> {
7076
if component.is_empty() {
7177
return None;
7278
}
73-
gix_path::try_from_byte_slice(component.as_bstr())
79+
let component = match gix_path::try_from_byte_slice(component.as_bstr())
7480
.map_err(|_| to_normal_path_components::Error::IllegalUtf8)
75-
.map(Path::as_os_str)
76-
.into()
81+
{
82+
Ok(c) => c,
83+
Err(err) => return Some(Err(err)),
84+
};
85+
let component = component.components().next()?;
86+
Some(component_to_os_str(
87+
component,
88+
gix_path::try_from_byte_slice(path.as_ref()).ok()?,
89+
))
7790
}
7891

7992
/// Access

gix-fs/tests/fs/stack.rs

+13
Original file line numberDiff line numberDiff line change
@@ -246,6 +246,19 @@ fn absolute_paths_are_invalid() -> crate::Result {
246246
r#"Input path "/" contains relative or absolute components"#,
247247
"a leading slash is always considered absolute"
248248
);
249+
s.make_relative_path_current("/", &mut r)?;
250+
assert_eq!(
251+
s.current(),
252+
s.root(),
253+
"as string this is a no-op as it's just split by /"
254+
);
255+
256+
let err = s.make_relative_path_current("../breakout", &mut r).unwrap_err();
257+
assert_eq!(
258+
err.to_string(),
259+
r#"Input path "../breakout" contains relative or absolute components"#,
260+
"otherwise breakout attempts are detected"
261+
);
249262
s.make_relative_path_current(p("a/"), &mut r)?;
250263
assert_eq!(
251264
s.current(),

0 commit comments

Comments
 (0)