From 33e80a2be4a9698697aa9df3a4828c5ffb9318a6 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ren=C3=A9=20Kijewski?= Date: Sun, 18 Aug 2024 05:37:35 +0200 Subject: [PATCH] fuzz: fuzz text filters --- fuzzing/README.md | 2 +- fuzzing/fuzz/Cargo.toml | 8 +++ fuzzing/fuzz/fuzz_targets/all.rs | 6 +- fuzzing/fuzz/fuzz_targets/filters.rs | 5 ++ fuzzing/fuzz/fuzz_targets/html.rs | 6 +- fuzzing/fuzz/fuzz_targets/parser.rs | 6 +- fuzzing/fuzz/src/all.rs | 1 + fuzzing/fuzz/src/filters.rs | 88 ++++++++++++++++++++++++++++ fuzzing/fuzz/src/lib.rs | 19 ++++++ 9 files changed, 125 insertions(+), 16 deletions(-) create mode 100644 fuzzing/fuzz/fuzz_targets/filters.rs create mode 100644 fuzzing/fuzz/src/filters.rs diff --git a/fuzzing/README.md b/fuzzing/README.md index 8a90adf9..b4b98d97 100644 --- a/fuzzing/README.md +++ b/fuzzing/README.md @@ -13,7 +13,7 @@ Then execute in this folder: RUST_BACKTRACE=1 nice cargo +nightly fuzz run ``` -`fuzz_target` is one out of `all`, `html` or `parser`. +`fuzz_target` is one out of `all`, `filters`, `html` or `parser`. The execution won't stop, but continue until you kill it with ctrl+c. Or until it finds a panic. diff --git a/fuzzing/fuzz/Cargo.toml b/fuzzing/fuzz/Cargo.toml index 2cd0dba5..ee54d506 100644 --- a/fuzzing/fuzz/Cargo.toml +++ b/fuzzing/fuzz/Cargo.toml @@ -9,11 +9,13 @@ publish = false cargo-fuzz = true [dependencies] +rinja = { path = "../../rinja" } rinja_parser = { path = "../../rinja_parser" } arbitrary = { version = "1.3.2", features = ["derive"] } html-escape = "0.2.13" libfuzzer-sys = "0.4.7" +thiserror = "1.0.63" [[bin]] name = "all" @@ -21,6 +23,12 @@ path = "fuzz_targets/all.rs" test = false doc = false +[[bin]] +name = "filters" +path = "fuzz_targets/filters.rs" +test = false +doc = false + [[bin]] name = "html" path = "fuzz_targets/html.rs" diff --git a/fuzzing/fuzz/fuzz_targets/all.rs b/fuzzing/fuzz/fuzz_targets/all.rs index 20fd1655..fc4c5868 100644 --- a/fuzzing/fuzz/fuzz_targets/all.rs +++ b/fuzzing/fuzz/fuzz_targets/all.rs @@ -1,9 +1,5 @@ #![no_main] -use fuzz::Scenario; - libfuzzer_sys::fuzz_target!(|data: &[u8]| { - if let Ok(scenario) = fuzz::all::Scenario::new(data) { - let _ = scenario.run(); - } + let _ = ::fuzz(data); }); diff --git a/fuzzing/fuzz/fuzz_targets/filters.rs b/fuzzing/fuzz/fuzz_targets/filters.rs new file mode 100644 index 00000000..99e6a8cd --- /dev/null +++ b/fuzzing/fuzz/fuzz_targets/filters.rs @@ -0,0 +1,5 @@ +#![no_main] + +libfuzzer_sys::fuzz_target!(|data: &[u8]| { + let _ = ::fuzz(data); +}); diff --git a/fuzzing/fuzz/fuzz_targets/html.rs b/fuzzing/fuzz/fuzz_targets/html.rs index 66a012fc..e66623b4 100644 --- a/fuzzing/fuzz/fuzz_targets/html.rs +++ b/fuzzing/fuzz/fuzz_targets/html.rs @@ -1,9 +1,5 @@ #![no_main] -use fuzz::Scenario; - libfuzzer_sys::fuzz_target!(|data: &[u8]| { - if let Ok(scenario) = fuzz::html::Scenario::new(data) { - let _ = scenario.run(); - } + let _ = ::fuzz(data); }); diff --git a/fuzzing/fuzz/fuzz_targets/parser.rs b/fuzzing/fuzz/fuzz_targets/parser.rs index 9a4e7a18..1c41dcee 100644 --- a/fuzzing/fuzz/fuzz_targets/parser.rs +++ b/fuzzing/fuzz/fuzz_targets/parser.rs @@ -1,9 +1,5 @@ #![no_main] -use fuzz::Scenario; - libfuzzer_sys::fuzz_target!(|data: &[u8]| { - if let Ok(scenario) = fuzz::parser::Scenario::new(data) { - let _ = scenario.run(); - } + let _ = ::fuzz(data); }); diff --git a/fuzzing/fuzz/src/all.rs b/fuzzing/fuzz/src/all.rs index d7826198..68708bb7 100644 --- a/fuzzing/fuzz/src/all.rs +++ b/fuzzing/fuzz/src/all.rs @@ -65,6 +65,7 @@ macro_rules! this_file { } this_file! { + crate::filters::Filters; crate::html::Html; crate::parser::Parser; } diff --git a/fuzzing/fuzz/src/filters.rs b/fuzzing/fuzz/src/filters.rs new file mode 100644 index 00000000..3ec37d62 --- /dev/null +++ b/fuzzing/fuzz/src/filters.rs @@ -0,0 +1,88 @@ +use arbitrary::{Arbitrary, Unstructured}; +use rinja::filters; + +#[derive(Arbitrary, Debug, Clone, Copy)] +pub enum Scenario<'a> { + Text(Text<'a>), +} + +impl<'a> super::Scenario<'a> for Scenario<'a> { + type RunError = rinja::Error; + + fn new(data: &'a [u8]) -> Result { + Self::arbitrary_take_rest(Unstructured::new(data)) + } + + fn run(&self) -> Result<(), Self::RunError> { + match *self { + Self::Text(text) => run_text(text), + } + } +} + +fn run_text(filter: Text<'_>) -> Result<(), rinja::Error> { + let Text { input, filter } = filter; + let _ = match filter { + TextFilter::Capitalize => filters::capitalize(input)?.to_string(), + TextFilter::Center(a) => filters::center(input, a)?.to_string(), + TextFilter::Indent(a) => filters::indent(input, a)?.to_string(), + TextFilter::Linebreaks => filters::linebreaks(input)?.to_string(), + TextFilter::LinebreaksBr => filters::linebreaksbr(input)?.to_string(), + TextFilter::Lowercase => filters::lowercase(input)?.to_string(), + TextFilter::ParagraphBreaks => filters::paragraphbreaks(input)?.to_string(), + TextFilter::Safe(e) => match e { + Escaper::Html => filters::safe(input, filters::Html)?.to_string(), + Escaper::Text => filters::safe(input, filters::Text)?.to_string(), + }, + TextFilter::Title => filters::title(input)?.to_string(), + TextFilter::Trim => filters::trim(input)?.to_string(), + TextFilter::Truncate(a) => filters::truncate(input, a)?.to_string(), + TextFilter::Uppercase => filters::uppercase(input)?.to_string(), + TextFilter::Urlencode => filters::urlencode(input)?.to_string(), + TextFilter::UrlencodeStrict => filters::urlencode_strict(input)?.to_string(), + }; + Ok(()) +} + +#[derive(Arbitrary, Debug, Clone, Copy)] +pub struct Text<'a> { + input: &'a str, + filter: TextFilter, +} + +#[derive(Arbitrary, Debug, Clone, Copy)] +enum TextFilter { + Capitalize, + Center(usize), + Indent(usize), + Linebreaks, + LinebreaksBr, + Lowercase, + ParagraphBreaks, + Safe(Escaper), + Title, + Trim, + Truncate(usize), + Uppercase, + Urlencode, + UrlencodeStrict, +} + +#[derive(Arbitrary, Debug, Clone, Copy)] +enum Escaper { + Html, + Text, +} + +// TODO: +// abs +// escape, +// filesizeformat +// fmt +// format +// into_f64 +// into_isize +// join +// json +// json_pretty +// wordcount diff --git a/fuzzing/fuzz/src/lib.rs b/fuzzing/fuzz/src/lib.rs index efcd1038..e68a1e2c 100644 --- a/fuzzing/fuzz/src/lib.rs +++ b/fuzzing/fuzz/src/lib.rs @@ -1,4 +1,5 @@ pub mod all; +pub mod filters; pub mod html; pub mod parser; @@ -7,6 +8,9 @@ use std::fmt; pub const TARGETS: &[(&str, TargetBuilder)] = &[ ("all", |data| NamedTarget::new::(data)), + ("filters", |data| { + NamedTarget::new::(data) + }), ("html", |data| NamedTarget::new::(data)), ("parser", |data| NamedTarget::new::(data)), ]; @@ -16,10 +20,25 @@ pub type TargetBuilder = for<'a> fn(&'a [u8]) -> Result, arbitra pub trait Scenario<'a>: fmt::Debug + Sized { type RunError: Error + Send + 'static; + fn fuzz(data: &'a [u8]) -> Result<(), FuzzError> { + Self::new(data) + .map_err(FuzzError::New)? + .run() + .map_err(FuzzError::Run) + } + fn new(data: &'a [u8]) -> Result; fn run(&self) -> Result<(), Self::RunError>; } +#[derive(Debug, thiserror::Error)] +pub enum FuzzError { + #[error("could not build scenario")] + New(#[source] arbitrary::Error), + #[error("could not run scenario")] + Run(#[source] RunError), +} + #[derive(Debug, Clone, Copy, Default)] pub struct DisplayTargets;