chore: base fuzz and micro-benchmark testing for static files module (#310)
it just introduces fuzzing and micro-benchmark tests for the static files module. As a way to continue enhancing the performance and security of SWS in general. It is expected to be improved over time including other modules as well.
Diff
benches/Cargo.toml | 5 +++++
benches/static_files.rs | 29 +++++++++++++++++++++++++++++
fuzz/.gitignore | 6 ++++++
fuzz/Cargo.toml | 22 ++++++++++++++++++++++
fuzz/src/static_files.rs | 17 +++++++++++++++++
src/static_files.rs | 12 ++++++++++++
6 files changed, 91 insertions(+)
@@ -13,3 +13,8 @@ hyper = { version = "0.14", features = ["stream", "http1", "http2", "tcp", "serv
name = "control_headers"
path = "control_headers.rs"
harness = false
[[bench]]
name = "static_files"
path = "static_files.rs"
harness = false
@@ -0,0 +1,29 @@
use criterion::{criterion_group, criterion_main, BenchmarkId, Criterion};
use static_web_server::static_files;
#[derive(Debug)]
struct Inputs<'a> {
base_path: &'a std::path::Path,
uri_path: &'a str,
}
impl std::fmt::Display for Inputs<'_> {
fn fmt(&self, fmt: &mut std::fmt::Formatter<'_>) -> Result<(), std::fmt::Error> {
write!(fmt, "{:?}", self)
}
}
fn sanitize_path_benchmark(c: &mut Criterion) {
let base_path = std::path::Path::new("root/../");
let uri_path: &str = "../assets/../../.../image.jpg";
let inputs = Inputs {
base_path,
uri_path,
};
c.bench_with_input(BenchmarkId::new("path_inputs", &inputs), &inputs, |b, s| {
b.iter(|| static_files::sanitize_path(s.base_path, s.uri_path))
});
}
criterion_group!(static_files_bench, sanitize_path_benchmark);
criterion_main!(static_files_bench);
@@ -0,0 +1,6 @@
target
corpus
artifacts
coverage
Cargo.lock
crash*
@@ -0,0 +1,22 @@
[package]
name = "static-web-server-fuzz"
version = "0.0.0"
publish = false
edition = "2021"
rust-version = "1.70.0"
[package.metadata]
cargo-fuzz = true
[dependencies]
arbitrary = { version = "1", features = ["derive"] }
libfuzzer-sys = { version = "0.4", features = ["arbitrary-derive"] }
static-web-server = { path = ".." }
hyper = { version = "0.14", features = ["stream", "http1", "http2", "tcp", "server"] }
[profile.release]
debug = 1
[[bin]]
name = "static_files"
path = "src/static_files.rs"
@@ -0,0 +1,17 @@
#![no_main]
use libfuzzer_sys::arbitrary::Arbitrary;
use libfuzzer_sys::fuzz_target;
use static_web_server::static_files;
#[derive(Debug, Arbitrary)]
struct RequestPath {
base: Vec<u8>,
uri: Vec<u8>,
}
fuzz_target!(|input: RequestPath| {
let uri = unsafe { std::str::from_utf8_unchecked(&input.uri[..]) };
let base = unsafe { std::str::from_utf8_unchecked(&input.base[..]) };
let _ = static_files::sanitize_path(std::path::Path::new(base), uri);
});
@@ -751,6 +751,18 @@ mod tests {
sanitize_path(base_dir, "/../foo.html").unwrap(),
root_dir().join("foo.html"),
);
assert_eq!(
sanitize_path(base_dir, "/../W�foo.html").unwrap(),
root_dir().join("W�foo.html"),
);
assert_eq!(
sanitize_path(base_dir, "/%EF%BF%BD/../bar.html").unwrap(),
root_dir().join("�/bar.html"),
);
assert_eq!(
sanitize_path(base_dir, "àí/é%20/öüñ").unwrap(),
root_dir().join("àí/é /öüñ"),
);
#[cfg(unix)]
let expected_path = root_dir().join("C:\\/foo.html");