From 82038a8ee12ee7f5bad0dce8c55e29358eb7412d Mon Sep 17 00:00:00 2001 From: cijiugechu Date: Fri, 19 Jan 2024 17:59:26 +0800 Subject: [PATCH] chore: setup fuzzer --- fuzz/.gitignore | 4 + fuzz/Cargo.toml | 25 ++++ fuzz/README.md | 5 + fuzz/fuzz_targets/semver.rs | 12 ++ src/range.rs | 251 ++++++++++++++++++------------------ 5 files changed, 168 insertions(+), 129 deletions(-) create mode 100644 fuzz/.gitignore create mode 100644 fuzz/Cargo.toml create mode 100644 fuzz/README.md create mode 100644 fuzz/fuzz_targets/semver.rs diff --git a/fuzz/.gitignore b/fuzz/.gitignore new file mode 100644 index 0000000..1a45eee --- /dev/null +++ b/fuzz/.gitignore @@ -0,0 +1,4 @@ +target +corpus +artifacts +coverage diff --git a/fuzz/Cargo.toml b/fuzz/Cargo.toml new file mode 100644 index 0000000..d35ea2e --- /dev/null +++ b/fuzz/Cargo.toml @@ -0,0 +1,25 @@ +[package] +name = "nodejs-semver-fuzz" +version = "0.0.0" +publish = false +edition = "2021" + +[package.metadata] +cargo-fuzz = true + +[dependencies] +libfuzzer-sys = "0.4" +nodejs-semver = { path = "../" } + +# Prevent this from interfering with workspaces +[workspace] +members = ["."] + +[profile.release] +debug = 1 + +[[bin]] +name = "semver" +path = "fuzz_targets/semver.rs" +test = false +doc = false \ No newline at end of file diff --git a/fuzz/README.md b/fuzz/README.md new file mode 100644 index 0000000..f970b33 --- /dev/null +++ b/fuzz/README.md @@ -0,0 +1,5 @@ +# Development + +```sh +cargo +nightly fuzz run semver -- -only_ascii=1 -max_total_time=60 -max_len=30 +``` \ No newline at end of file diff --git a/fuzz/fuzz_targets/semver.rs b/fuzz/fuzz_targets/semver.rs new file mode 100644 index 0000000..bd82ac8 --- /dev/null +++ b/fuzz/fuzz_targets/semver.rs @@ -0,0 +1,12 @@ +#![no_main] + +use nodejs_semver::{Version, Range}; + +libfuzzer_sys::fuzz_target!(|data: &[u8]| { + if let Ok(s) = std::str::from_utf8(data) { + if s.chars().all(|s| !s.is_control()) { + let _ = Version::parse(&s); + let _ = Range::parse(&s); + } + } +}); diff --git a/src/range.rs b/src/range.rs index 6c87d97..42b1b87 100644 --- a/src/range.rs +++ b/src/range.rs @@ -667,139 +667,132 @@ fn primitive(input: &str) -> IResult<&str, Option, SemverParseError<&s use Operation::*; Parser::map( - (operation, preceded(space0, partial_version)), - |parsed| { - match parsed { - (GreaterThanEquals, partial) => { - BoundSet::at_least(Predicate::Including(partial.into())) - } - ( - GreaterThan, - Partial { - major: Some(major), - minor: Some(minor), - patch: None, - .. - }, - ) => BoundSet::at_least(Predicate::Including((major, minor + 1, 0).into())), - ( - GreaterThan, - Partial { - major: Some(major), - minor: None, - patch: None, - .. - }, - ) => BoundSet::at_least(Predicate::Including((major + 1, 0, 0).into())), - (GreaterThan, partial) => BoundSet::at_least(Predicate::Excluding(partial.into())), - ( - LessThan, - Partial { - major: Some(major), - minor: Some(minor), - patch: None, - .. - }, - ) => BoundSet::at_most(Predicate::Excluding((major, minor, 0, 0).into())), - ( - LessThan, - Partial { - major, - minor, - patch, - pre_release, - build, - .. - }, - ) => BoundSet::at_most(Predicate::Excluding(Version { - major: major.unwrap_or(0), - minor: minor.unwrap_or(0), - patch: patch.unwrap_or(0), - build, - pre_release, - })), - ( - LessThanEquals, - Partial { - major, - minor: None, - patch: None, - .. - }, - ) => BoundSet::at_most(Predicate::Including( - (major.unwrap_or(0), MAX_SAFE_INTEGER, MAX_SAFE_INTEGER).into(), - )), - ( - LessThanEquals, - Partial { - major, - minor, - patch: None, - .. - }, - ) => BoundSet::at_most(Predicate::Including( - (major.unwrap_or(0), minor.unwrap_or(0), MAX_SAFE_INTEGER).into(), - )), - (LessThanEquals, partial) => { - BoundSet::at_most(Predicate::Including(partial.into())) - } - ( - Exact, - Partial { - major: Some(major), - minor: Some(minor), - patch: Some(patch), - pre_release, - .. - }, - ) => BoundSet::exact( Version { + (operation, preceded(space0, partial_version)), + |parsed| match parsed { + (GreaterThanEquals, partial) => { + BoundSet::at_least(Predicate::Including(partial.into())) + } + ( + GreaterThan, + Partial { + major: Some(major), + minor: Some(minor), + patch: None, + .. + }, + ) => BoundSet::at_least(Predicate::Including((major, minor + 1, 0).into())), + ( + GreaterThan, + Partial { + major: Some(major), + minor: None, + patch: None, + .. + }, + ) => BoundSet::at_least(Predicate::Including((major + 1, 0, 0).into())), + (GreaterThan, partial) => BoundSet::at_least(Predicate::Excluding(partial.into())), + ( + LessThan, + Partial { + major: Some(major), + minor: Some(minor), + patch: None, + .. + }, + ) => BoundSet::at_most(Predicate::Excluding((major, minor, 0, 0).into())), + ( + LessThan, + Partial { major, minor, patch, pre_release, + build, + .. + }, + ) => BoundSet::at_most(Predicate::Excluding(Version { + major: major.unwrap_or(0), + minor: minor.unwrap_or(0), + patch: patch.unwrap_or(0), + build, + pre_release, + })), + ( + LessThanEquals, + Partial { + major, + minor: None, + patch: None, + .. + }, + ) => BoundSet::at_most(Predicate::Including( + (major.unwrap_or(0), MAX_SAFE_INTEGER, MAX_SAFE_INTEGER).into(), + )), + ( + LessThanEquals, + Partial { + major, + minor, + patch: None, + .. + }, + ) => BoundSet::at_most(Predicate::Including( + (major.unwrap_or(0), minor.unwrap_or(0), MAX_SAFE_INTEGER).into(), + )), + (LessThanEquals, partial) => BoundSet::at_most(Predicate::Including(partial.into())), + ( + Exact, + Partial { + major: Some(major), + minor: Some(minor), + patch: Some(patch), + pre_release, + .. + }, + ) => BoundSet::exact(Version { + major, + minor, + patch, + pre_release, + build: vec![], + }), + ( + Exact, + Partial { + major: Some(major), + minor: Some(minor), + .. + }, + ) => BoundSet::new( + Bound::Lower(Predicate::Including((major, minor, 0).into())), + Bound::Upper(Predicate::Excluding(Version { + major, + minor: minor + 1, + patch: 0, + pre_release: vec![Identifier::Numeric(0)], build: vec![], - }), - ( - Exact, - Partial { - major: Some(major), - minor: Some(minor), - .. - }, - ) => BoundSet::new( - Bound::Lower(Predicate::Including( - (major, minor, 0).into(), - )), - Bound::Upper(Predicate::Excluding(Version { - major, - minor: minor + 1, - patch: 0, - pre_release: vec![Identifier::Numeric(0)], - build: vec![], - })), - ), - ( - Exact, - Partial { - major: Some(major), - .. - }, - ) => BoundSet::new( - Bound::Lower(Predicate::Including( - (major, 0, 0).into(), - )), - Bound::Upper(Predicate::Excluding(Version { - major: major + 1, - minor: 0, - patch: 0, - pre_release: vec![Identifier::Numeric(0)], - build: vec![], - })), - ), - _ => unreachable!("Failed to parse operation. This should not happen and should be reported as a bug, while parsing {}", input), - } - }, - ).context("operation range (ex: >= 1.2.3)").parse_next(input) + })), + ), + ( + Exact, + Partial { + major: Some(major), .. + }, + ) => BoundSet::new( + Bound::Lower(Predicate::Including((major, 0, 0).into())), + Bound::Upper(Predicate::Excluding(Version { + major: major + 1, + minor: 0, + patch: 0, + pre_release: vec![Identifier::Numeric(0)], + build: vec![], + })), + ), + _ => None, + }, + ) + .context("operation range (ex: >= 1.2.3)") + .parse_next(input) } fn operation(input: &str) -> IResult<&str, Operation, SemverParseError<&str>> { @@ -996,7 +989,7 @@ fn tilde(input: &str) -> IResult<&str, Option, SemverParseError<&str>> Bound::Lower(Predicate::Including((major, 0, 0).into())), Bound::Upper(Predicate::Excluding((major + 1, 0, 0, 0).into())), ), - _ => unreachable!("This should not have parsed: {}", input), + _ => None, }) .context("tilde version range (ex: ~1.2.3)") .parse_next(input)