Skip to content

Commit 0d78db2

Browse files
committed
add validation for path components and tree-names
---- Note that this commit also streamlines obtaininig a relative path for a directory, which previously could panic.
1 parent f3d5a69 commit 0d78db2

File tree

4 files changed

+379
-51
lines changed

4 files changed

+379
-51
lines changed

gix-fs/src/stack.rs

Lines changed: 28 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
use std::path::{Path, PathBuf};
1+
use std::path::{Component, Path, PathBuf};
22

33
use crate::Stack;
44

@@ -22,20 +22,22 @@ impl Stack {
2222

2323
/// A delegate for use in a [`Stack`].
2424
pub trait Delegate {
25-
/// Called whenever we push a directory on top of the stack, after the fact.
25+
/// Called whenever we push a directory on top of the stack, and after the respective call to [`push()`](Self::push).
2626
///
27-
/// It is also called if the currently acted on path is a directory in itself.
28-
/// Use `stack.current()` to see the directory.
27+
/// It is only called if the currently acted on path is a directory in itself, which is determined by knowing
28+
/// that it's not the last component fo the path.
29+
/// Use [`Stack::current()`] to see the directory.
2930
fn push_directory(&mut self, stack: &Stack) -> std::io::Result<()>;
3031

31-
/// Called after any component was pushed, with the path available at `stack.current()`.
32+
/// Called after any component was pushed, with the path available at [`Stack::current()`].
3233
///
33-
/// `is_last_component` is true if the path is completely built.
34+
/// `is_last_component` is `true` if the path is completely built, which typically means it's not a directory.
3435
fn push(&mut self, is_last_component: bool, stack: &Stack) -> std::io::Result<()>;
3536

3637
/// Called right after a directory-component was popped off the stack.
3738
///
38-
/// Use it to pop information off internal data structures.
39+
/// Use it to pop information off internal data structures. Note that no equivalent call exists for popping
40+
/// the file-component.
3941
fn pop_directory(&mut self);
4042
}
4143

@@ -58,12 +60,17 @@ impl Stack {
5860
/// The full path to `relative` will be returned along with the data returned by `push_comp`.
5961
/// Note that this only works correctly for the delegate's `push_directory()` and `pop_directory()` methods if
6062
/// `relative` paths are terminal, so point to their designated file or directory.
63+
/// The path is also expected to be normalized, and should not contain extra separators, and must not contain `..`
64+
/// or have leading or trailing slashes (or additionally backslashes on Windows).
6165
pub fn make_relative_path_current(&mut self, relative: &Path, delegate: &mut dyn Delegate) -> std::io::Result<()> {
62-
debug_assert!(
63-
relative.is_relative(),
64-
"only index paths are handled correctly here, must be relative"
65-
);
66-
66+
if relative.as_os_str().is_empty() {
67+
return Err(std::io::Error::new(
68+
std::io::ErrorKind::Other,
69+
"empty inputs are not allowed",
70+
));
71+
}
72+
// TODO: prevent leading or trailing slashes, on Windows also backslashes.
73+
// prevent leading backslashes on Windows as they are strange
6774
if self.valid_components == 0 {
6875
delegate.push_directory(self)?;
6976
}
@@ -95,6 +102,15 @@ impl Stack {
95102
}
96103

97104
while let Some(comp) = components.next() {
105+
if !matches!(comp, Component::Normal(_)) {
106+
return Err(std::io::Error::new(
107+
std::io::ErrorKind::Other,
108+
format!(
109+
"Input path \"{}\" contains relative or absolute components",
110+
relative.display()
111+
),
112+
));
113+
}
98114
let is_last_component = components.peek().is_none();
99115
self.current_is_directory = !is_last_component;
100116
self.current.push(comp);

0 commit comments

Comments
 (0)