mirror of
https://github.com/askama-rs/askama.git
synced 2025-09-28 05:21:14 +00:00
parser: add fuzzer
This commit is contained in:
parent
241c60143e
commit
b62d558c21
6
.github/workflows/rust.yml
vendored
6
.github/workflows/rust.yml
vendored
@ -37,6 +37,7 @@ jobs:
|
||||
package: [
|
||||
rinja, rinja_actix, rinja_axum, rinja_derive, rinja_derive_standalone,
|
||||
rinja_parser, rinja_rocket, rinja_warp, testing, examples/actix-web-app,
|
||||
fuzz_parser
|
||||
]
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
@ -65,7 +66,8 @@ jobs:
|
||||
set -eu
|
||||
for PKG in \
|
||||
rinja rinja_actix rinja_axum rinja_derive rinja_derive_standalone \
|
||||
rinja_parser rinja_rocket rinja_warp testing examples/actix-web-app
|
||||
rinja_parser rinja_rocket rinja_warp testing examples/actix-web-app \
|
||||
fuzz_parser
|
||||
do
|
||||
cd "$PKG"
|
||||
echo "Testing: $PKG"
|
||||
@ -79,7 +81,7 @@ jobs:
|
||||
- uses: actions/checkout@v4
|
||||
- uses: dtolnay/rust-toolchain@master
|
||||
with:
|
||||
toolchain: "1.71.0"
|
||||
toolchain: "1.71.1"
|
||||
- run: cargo check --lib -p rinja --all-features
|
||||
|
||||
Audit:
|
||||
|
19
fuzz_parser/Cargo.toml
Normal file
19
fuzz_parser/Cargo.toml
Normal file
@ -0,0 +1,19 @@
|
||||
[package]
|
||||
name = "fuzz_parser"
|
||||
version = "0.1.0"
|
||||
edition = "2021"
|
||||
license = "MIT OR Apache-2.0"
|
||||
publish = false
|
||||
|
||||
[dependencies]
|
||||
rinja_parser = { path = "../rinja_parser" }
|
||||
|
||||
arbitrary = { version = "1.3.2", features = ["derive"] }
|
||||
pretty-error-debug = "0.3.0"
|
||||
thiserror = "1.0.63"
|
||||
|
||||
[workspace]
|
||||
members = [".", "fuzz"]
|
||||
|
||||
[profile.release]
|
||||
debug = 1
|
29
fuzz_parser/README.md
Normal file
29
fuzz_parser/README.md
Normal file
@ -0,0 +1,29 @@
|
||||
# Rinja Parser Fuzzer
|
||||
|
||||
First install `cargo-fuzz` and rust-nightly (once):
|
||||
|
||||
```sh
|
||||
cargo install cargo-fuzz
|
||||
rustup install nightly
|
||||
```
|
||||
|
||||
Then execute in this folder:
|
||||
|
||||
```sh
|
||||
RUST_BACKTRACE=1 nice cargo +nightly fuzz run fuzz
|
||||
```
|
||||
|
||||
The execution won't stop, but continue until you kill it with ctrl+c.
|
||||
Or until it finds a panic.
|
||||
If the execution found a panic, then a file with the input scenario is written, e.g.
|
||||
`fuzz/artifacts/fuzz/crash-4184…`.
|
||||
To get more information about the failed scenario, run or debug this command with the given path:
|
||||
|
||||
```sh
|
||||
cargo run -- fuzz/artifacts/fuzz/crash-4184…
|
||||
```
|
||||
|
||||
Find more information about fuzzing here:
|
||||
|
||||
* `cargo fuzz help run`
|
||||
* <https://rust-fuzz.github.io/book/cargo-fuzz.html>
|
2
fuzz_parser/fuzz/.gitignore
vendored
Normal file
2
fuzz_parser/fuzz/.gitignore
vendored
Normal file
@ -0,0 +1,2 @@
|
||||
/artifacts/
|
||||
/corpus/
|
14
fuzz_parser/fuzz/Cargo.toml
Normal file
14
fuzz_parser/fuzz/Cargo.toml
Normal file
@ -0,0 +1,14 @@
|
||||
[package]
|
||||
name = "fuzz"
|
||||
version = "0.1.0"
|
||||
edition = "2021"
|
||||
license = "MIT OR Apache-2.0"
|
||||
publish = false
|
||||
|
||||
[package.metadata]
|
||||
cargo-fuzz = true
|
||||
|
||||
[dependencies]
|
||||
fuzz_parser = { path = ".." }
|
||||
|
||||
libfuzzer-sys = "0.4.7"
|
7
fuzz_parser/fuzz/src/main.rs
Normal file
7
fuzz_parser/fuzz/src/main.rs
Normal file
@ -0,0 +1,7 @@
|
||||
#![no_main]
|
||||
|
||||
libfuzzer_sys::fuzz_target!(|data: &[u8]| {
|
||||
if let Ok(scenario) = fuzz_parser::Scenario::new(data) {
|
||||
let _ = scenario.run();
|
||||
}
|
||||
});
|
47
fuzz_parser/src/lib.rs
Normal file
47
fuzz_parser/src/lib.rs
Normal file
@ -0,0 +1,47 @@
|
||||
use arbitrary::{Arbitrary, Unstructured};
|
||||
use rinja_parser::{Ast, Syntax};
|
||||
|
||||
#[derive(Debug, Default)]
|
||||
pub struct Scenario<'a> {
|
||||
syntax: Syntax<'a>,
|
||||
src: &'a str,
|
||||
}
|
||||
|
||||
impl<'a> Scenario<'a> {
|
||||
pub fn new(data: &'a [u8]) -> Result<Self, arbitrary::Error> {
|
||||
let mut data = Unstructured::new(data);
|
||||
|
||||
let syntax = ArbitrarySyntax::arbitrary(&mut data)?;
|
||||
let _syntax = syntax.as_syntax();
|
||||
// FIXME: related issue: <https://github.com/rinja-rs/rinja/issues/138>
|
||||
let syntax = Syntax::default();
|
||||
|
||||
let src = <&str>::arbitrary_take_rest(data)?;
|
||||
|
||||
Ok(Self { syntax, src })
|
||||
}
|
||||
|
||||
pub fn run(&self) -> Result<(), rinja_parser::ParseError> {
|
||||
let Scenario { syntax, src } = self;
|
||||
Ast::from_str(src, None, syntax)?;
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Arbitrary, Default)]
|
||||
struct ArbitrarySyntax<'a>(Option<[Option<&'a str>; 6]>);
|
||||
|
||||
impl<'a> ArbitrarySyntax<'a> {
|
||||
fn as_syntax(&self) -> Syntax<'a> {
|
||||
let default = Syntax::default();
|
||||
let values = self.0.unwrap_or_default();
|
||||
Syntax {
|
||||
block_start: values[0].unwrap_or(default.block_start),
|
||||
block_end: values[1].unwrap_or(default.block_end),
|
||||
expr_start: values[2].unwrap_or(default.expr_start),
|
||||
expr_end: values[3].unwrap_or(default.expr_end),
|
||||
comment_start: values[4].unwrap_or(default.comment_start),
|
||||
comment_end: values[5].unwrap_or(default.comment_end),
|
||||
}
|
||||
}
|
||||
}
|
50
fuzz_parser/src/main.rs
Normal file
50
fuzz_parser/src/main.rs
Normal file
@ -0,0 +1,50 @@
|
||||
use std::env::args_os;
|
||||
use std::fs::OpenOptions;
|
||||
use std::io::Read;
|
||||
use std::path::{Path, PathBuf};
|
||||
|
||||
use fuzz_parser::Scenario;
|
||||
|
||||
fn main() -> Result<(), Error> {
|
||||
let mut args = args_os().fuse();
|
||||
let exe = args.next().map(PathBuf::from);
|
||||
let path = args.next().map(PathBuf::from);
|
||||
let empty = args.next().map(|_| ());
|
||||
|
||||
let (Some(path), None) = (path, empty) else {
|
||||
return Err(Error::Usage(exe));
|
||||
};
|
||||
|
||||
let mut data = Vec::new();
|
||||
match OpenOptions::new().read(true).open(Path::new(&path)) {
|
||||
Ok(mut f) => {
|
||||
f.read_to_end(&mut data)
|
||||
.map_err(|err| Error::Read(err, path))?;
|
||||
}
|
||||
Err(err) => return Err(Error::Open(err, path)),
|
||||
};
|
||||
|
||||
let scenario = &Scenario::new(&data).map_err(Error::Build)?;
|
||||
eprintln!("{scenario:#?}");
|
||||
scenario.run().map_err(Error::Run)?;
|
||||
println!("Success.");
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
#[derive(thiserror::Error, pretty_error_debug::Debug)]
|
||||
enum Error {
|
||||
#[error(
|
||||
"wrong arguments supplied\nUsage: {} <path>",
|
||||
.0.as_deref().unwrap_or(Path::new("fuzz_parser")).display(),
|
||||
)]
|
||||
Usage(Option<PathBuf>),
|
||||
#[error("could not open input file {}", .1.display())]
|
||||
Open(#[source] std::io::Error, PathBuf),
|
||||
#[error("could not read opened input file {}", .1.display())]
|
||||
Read(#[source] std::io::Error, PathBuf),
|
||||
#[error("could not build scenario")]
|
||||
Build(#[source] arbitrary::Error),
|
||||
#[error("could not run scenario")]
|
||||
Run(#[source] rinja_parser::ParseError),
|
||||
}
|
Loading…
x
Reference in New Issue
Block a user