Compare commits
3 commits
d58e46d65e
...
984724a465
Author | SHA1 | Date | |
---|---|---|---|
984724a465 | |||
4ad6f45379 | |||
607d58df84 |
3 changed files with 240 additions and 38 deletions
|
@ -31,6 +31,7 @@ thiserror = "1.0"
|
|||
|
||||
# fs
|
||||
tempfile = "3.10"
|
||||
path-clean = "1.0"
|
||||
|
||||
# error handling
|
||||
derive_more = { version = "1.0.0-beta.6", features = ["from", "display"] }
|
||||
|
|
|
@ -57,12 +57,25 @@ impl super::FileSystemLike for RealFileSystem {
|
|||
|
||||
impl RealFileSystem {
|
||||
fn validate(&self, path: &Path) -> super::Result<()> {
|
||||
if !path.starts_with(&self.base) {
|
||||
let canon = self.clean_path(path)?;
|
||||
if !canon.starts_with(&self.base) {
|
||||
return Err(super::Error::PathTraversal {
|
||||
base: self.base.clone(),
|
||||
path: path.to_path_buf(),
|
||||
path: canon.to_path_buf(),
|
||||
});
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn clean_path(&self, path: &Path) -> super::Result<PathBuf> {
|
||||
// let path = path.as_ref();
|
||||
use path_clean::PathClean;
|
||||
let abs_path = if path.is_absolute() {
|
||||
path.to_path_buf()
|
||||
} else {
|
||||
std::env::current_dir()?.join(path)
|
||||
}
|
||||
.clean();
|
||||
Ok(abs_path)
|
||||
}
|
||||
}
|
||||
|
|
260
src/tests/fs.rs
260
src/tests/fs.rs
|
@ -1,55 +1,243 @@
|
|||
use std::path::PathBuf;
|
||||
use assert2::let_assert;
|
||||
|
||||
use crate::fs;
|
||||
|
||||
type TestResult = Result<(), crate::fs::Error>;
|
||||
type TestResult = Result<(), fs::Error>;
|
||||
|
||||
#[test]
|
||||
fn write_read_file_exists() -> TestResult {
|
||||
let fs = fs::temp()?;
|
||||
let pathbuf: PathBuf = fs.path_of("foo".into())?;
|
||||
mod path_of {
|
||||
use super::*;
|
||||
#[test]
|
||||
fn validate_fails_on_path_traversal() -> TestResult {
|
||||
let fs = fs::temp()?;
|
||||
|
||||
fs.file_write(&pathbuf, "content")?;
|
||||
let c = fs.file_read_to_string(&pathbuf)?;
|
||||
assert_eq!(c, "content");
|
||||
let_assert!(Err(fs::Error::PathTraversal { base, path: _path }) = fs.path_of("..".into()));
|
||||
assert_eq!(base, fs.base());
|
||||
|
||||
let exists = fs.path_exists(&pathbuf)?;
|
||||
assert!(exists);
|
||||
|
||||
let is_file = fs.path_is_file(&pathbuf)?;
|
||||
assert!(is_file);
|
||||
|
||||
Ok(())
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn create_dir_should_create_a_dir() -> TestResult {
|
||||
let fs = fs::temp()?;
|
||||
let pathbuf = fs.path_of("subdir".into())?;
|
||||
mod file {
|
||||
use super::*;
|
||||
#[test]
|
||||
/// Write to a file, read it, verify it exists, is a file and has the expected contents
|
||||
fn write_read_file_exists() -> TestResult {
|
||||
let fs = fs::temp()?;
|
||||
let pathbuf = fs.base().join("foo");
|
||||
|
||||
fs.dir_create(&pathbuf)?;
|
||||
let_assert!(Ok(_) = fs.file_write(&pathbuf, "content"));
|
||||
let_assert!(
|
||||
Ok(c) = fs.file_read_to_string(&pathbuf),
|
||||
"file_read_to_string"
|
||||
);
|
||||
assert_eq!(c, "content");
|
||||
|
||||
let exists = fs.path_exists(&pathbuf)?;
|
||||
assert!(exists);
|
||||
let_assert!(Ok(exists) = fs.path_exists(&pathbuf));
|
||||
assert!(exists);
|
||||
|
||||
let is_dir = fs.path_is_dir(&pathbuf)?;
|
||||
assert!(is_dir);
|
||||
let_assert!(Ok(is_file) = fs.path_is_file(&pathbuf));
|
||||
assert!(is_file);
|
||||
|
||||
Ok(())
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn create_dir_all_should_create_a_dir() -> TestResult {
|
||||
let fs = fs::temp()?;
|
||||
let pathbuf = fs.base().join("subdir").join("child");
|
||||
mod dir_create {
|
||||
use super::*;
|
||||
#[test]
|
||||
fn should_create_a_dir() -> TestResult {
|
||||
let fs = fs::temp()?;
|
||||
let pathbuf = fs.base().join("subdir");
|
||||
|
||||
fs.dir_create_all(&pathbuf)?;
|
||||
let_assert!(Ok(_) = fs.dir_create(&pathbuf));
|
||||
|
||||
let exists = fs.path_exists(&pathbuf)?;
|
||||
assert!(exists, "path exists");
|
||||
let_assert!(Ok(exists) = fs.path_exists(&pathbuf));
|
||||
assert!(exists);
|
||||
|
||||
let is_dir = fs.path_is_dir(&pathbuf)?;
|
||||
assert!(is_dir, "path is a directory");
|
||||
let_assert!(Ok(is_dir) = fs.path_is_dir(&pathbuf));
|
||||
assert!(is_dir);
|
||||
|
||||
Ok(())
|
||||
Ok(())
|
||||
}
|
||||
#[test]
|
||||
fn should_fail_on_path_traversal() -> TestResult {
|
||||
let fs = fs::temp()?;
|
||||
let path = fs.base().join("..").join("foo");
|
||||
let_assert!(
|
||||
Err(fs::Error::PathTraversal {
|
||||
base: _base,
|
||||
path: _path
|
||||
}) = fs.dir_create(&path)
|
||||
);
|
||||
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
|
||||
mod dir_create_all {
|
||||
use super::*;
|
||||
|
||||
#[test]
|
||||
fn should_create_a_dir() -> TestResult {
|
||||
let fs = fs::temp()?;
|
||||
let pathbuf = fs.base().join("subdir").join("child");
|
||||
|
||||
let_assert!(Ok(_) = fs.dir_create_all(&pathbuf));
|
||||
|
||||
let_assert!(Ok(exists) = fs.path_exists(&pathbuf));
|
||||
assert!(exists, "path exists");
|
||||
|
||||
let_assert!(Ok(is_dir) = fs.path_is_dir(&pathbuf));
|
||||
assert!(is_dir, "path is a directory");
|
||||
|
||||
Ok(())
|
||||
}
|
||||
#[test]
|
||||
fn should_fail_on_path_traversal() -> TestResult {
|
||||
let fs = fs::temp()?;
|
||||
let path = fs.base().join("..").join("foo");
|
||||
let_assert!(
|
||||
Err(fs::Error::PathTraversal {
|
||||
base: _base,
|
||||
path: _path
|
||||
}) = fs.dir_create_all(&path)
|
||||
);
|
||||
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
|
||||
mod path_exists {
|
||||
use super::*;
|
||||
#[test]
|
||||
fn should_be_true_when_it_exists() -> TestResult {
|
||||
let fs = fs::temp()?;
|
||||
let path = fs.base().join("foo");
|
||||
let_assert!(Ok(_) = fs.file_write(&path, "bar"));
|
||||
let_assert!(Ok(exists) = fs.path_exists(&path));
|
||||
assert!(exists);
|
||||
|
||||
Ok(())
|
||||
}
|
||||
#[test]
|
||||
fn should_be_false_when_it_does_not_exist() -> TestResult {
|
||||
let fs = fs::temp()?;
|
||||
let path = fs.base().join("foo");
|
||||
let_assert!(Ok(exists) = fs.path_exists(&path));
|
||||
assert!(!exists);
|
||||
|
||||
Ok(())
|
||||
}
|
||||
#[test]
|
||||
fn should_fail_on_path_traversal() -> TestResult {
|
||||
let fs = fs::temp()?;
|
||||
let path = fs.base().join("..").join("foo");
|
||||
let_assert!(
|
||||
Err(fs::Error::PathTraversal {
|
||||
base: _base,
|
||||
path: _path
|
||||
}) = fs.path_exists(&path)
|
||||
);
|
||||
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
|
||||
mod path_is_dir {
|
||||
use super::*;
|
||||
#[test]
|
||||
fn should_be_true_when_is_a_dir() -> TestResult {
|
||||
let fs = fs::temp()?;
|
||||
let path = fs.base().join("foo");
|
||||
let_assert!(Ok(_) = fs.dir_create(&path));
|
||||
let_assert!(Ok(is_dir) = fs.path_is_dir(&path));
|
||||
assert!(is_dir);
|
||||
|
||||
Ok(())
|
||||
}
|
||||
#[test]
|
||||
fn should_be_false_when_is_a_file() -> TestResult {
|
||||
let fs = fs::temp()?;
|
||||
let path = fs.base().join("foo");
|
||||
let_assert!(Ok(_) = fs.file_write(&path, "bar"));
|
||||
let_assert!(Ok(is_dir) = fs.path_is_dir(&path));
|
||||
assert!(!is_dir);
|
||||
|
||||
Ok(())
|
||||
}
|
||||
#[test]
|
||||
#[ignore]
|
||||
fn should_be_false_when_is_a_link() -> TestResult {
|
||||
let fs = fs::temp()?;
|
||||
let path = fs.base().join("foo");
|
||||
// TODO create a link
|
||||
// let_assert!(Ok(_) = fs.file_write(&path, "bar"));
|
||||
let_assert!(Ok(is_dir) = fs.path_is_dir(&path));
|
||||
assert!(!is_dir);
|
||||
|
||||
Ok(())
|
||||
}
|
||||
#[test]
|
||||
fn should_fail_on_path_traversal() -> TestResult {
|
||||
let fs = fs::temp()?;
|
||||
let path = fs.base().join("..").join("foo");
|
||||
let_assert!(
|
||||
Err(fs::Error::PathTraversal {
|
||||
base: _base,
|
||||
path: _path
|
||||
}) = fs.path_is_dir(&path)
|
||||
);
|
||||
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
|
||||
mod path_is_file {
|
||||
use super::*;
|
||||
#[test]
|
||||
fn should_be_true_when_is_a_file() -> TestResult {
|
||||
let fs = fs::temp()?;
|
||||
let path = fs.base().join("foo");
|
||||
let_assert!(Ok(_) = fs.file_write(&path, "bar"));
|
||||
|
||||
let_assert!(Ok(is_file) = fs.path_is_file(&path));
|
||||
assert!(is_file);
|
||||
|
||||
Ok(())
|
||||
}
|
||||
#[test]
|
||||
fn should_be_false_when_is_a_dir() -> TestResult {
|
||||
let fs = fs::temp()?;
|
||||
let path = fs.base().join("foo");
|
||||
let_assert!(Ok(_) = fs.dir_create(&path));
|
||||
let_assert!(Ok(is_file) = fs.path_is_file(&path));
|
||||
assert!(!is_file);
|
||||
|
||||
Ok(())
|
||||
}
|
||||
#[test]
|
||||
#[ignore]
|
||||
fn should_be_false_when_is_a_link() -> TestResult {
|
||||
let fs = fs::temp()?;
|
||||
let path = fs.base().join("foo");
|
||||
// TODO create a link
|
||||
// let_assert!(Ok(_) = fs.file_write(&path, "bar"));
|
||||
let_assert!(Ok(is_file) = fs.path_is_file(&path));
|
||||
assert!(!is_file);
|
||||
|
||||
Ok(())
|
||||
}
|
||||
#[test]
|
||||
fn should_fail_on_path_traversal() -> TestResult {
|
||||
let fs = fs::temp()?;
|
||||
let path = fs.base().join("..").join("foo");
|
||||
let_assert!(
|
||||
Err(fs::Error::PathTraversal {
|
||||
base: _base,
|
||||
path: _path
|
||||
}) = fs.path_is_file(&path)
|
||||
);
|
||||
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
|
|
Loading…
Reference in a new issue