Skip to content

Commit 3d95d74

Browse files
committed
chore: setup fuzzer
1 parent 7ee42e0 commit 3d95d74

File tree

5 files changed

+168
-129
lines changed

5 files changed

+168
-129
lines changed

fuzz/.gitignore

+4
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,4 @@
1+
target
2+
corpus
3+
artifacts
4+
coverage

fuzz/Cargo.toml

+25
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,25 @@
1+
[package]
2+
name = "nodejs-semver-fuzz"
3+
version = "0.0.0"
4+
publish = false
5+
edition = "2021"
6+
7+
[package.metadata]
8+
cargo-fuzz = true
9+
10+
[dependencies]
11+
libfuzzer-sys = "0.4"
12+
nodejs-semver = { path = "../" }
13+
14+
# Prevent this from interfering with workspaces
15+
[workspace]
16+
members = ["."]
17+
18+
[profile.release]
19+
debug = 1
20+
21+
[[bin]]
22+
name = "semver"
23+
path = "fuzz_targets/semver.rs"
24+
test = false
25+
doc = false

fuzz/README.md

+5
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
# Development
2+
3+
```sh
4+
cargo +nightly fuzz run semver -- -only_ascii=1 -max_total_time=60 -max_len=30
5+
```

fuzz/fuzz_targets/semver.rs

+12
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,12 @@
1+
#![no_main]
2+
3+
use nodejs_semver::{Version, Range};
4+
5+
libfuzzer_sys::fuzz_target!(|data: &[u8]| {
6+
if let Ok(s) = std::str::from_utf8(data) {
7+
if s.chars().all(|s| !s.is_control()) {
8+
let _ = Version::parse(&s);
9+
let _ = Range::parse(&s);
10+
}
11+
}
12+
});

src/range.rs

+122-129
Original file line numberDiff line numberDiff line change
@@ -667,139 +667,132 @@ fn primitive(input: &str) -> IResult<&str, Option<BoundSet>, SemverParseError<&s
667667
use Operation::*;
668668

669669
Parser::map(
670-
(operation, preceded(space0, partial_version)),
671-
|parsed| {
672-
match parsed {
673-
(GreaterThanEquals, partial) => {
674-
BoundSet::at_least(Predicate::Including(partial.into()))
675-
}
676-
(
677-
GreaterThan,
678-
Partial {
679-
major: Some(major),
680-
minor: Some(minor),
681-
patch: None,
682-
..
683-
},
684-
) => BoundSet::at_least(Predicate::Including((major, minor + 1, 0).into())),
685-
(
686-
GreaterThan,
687-
Partial {
688-
major: Some(major),
689-
minor: None,
690-
patch: None,
691-
..
692-
},
693-
) => BoundSet::at_least(Predicate::Including((major + 1, 0, 0).into())),
694-
(GreaterThan, partial) => BoundSet::at_least(Predicate::Excluding(partial.into())),
695-
(
696-
LessThan,
697-
Partial {
698-
major: Some(major),
699-
minor: Some(minor),
700-
patch: None,
701-
..
702-
},
703-
) => BoundSet::at_most(Predicate::Excluding((major, minor, 0, 0).into())),
704-
(
705-
LessThan,
706-
Partial {
707-
major,
708-
minor,
709-
patch,
710-
pre_release,
711-
build,
712-
..
713-
},
714-
) => BoundSet::at_most(Predicate::Excluding(Version {
715-
major: major.unwrap_or(0),
716-
minor: minor.unwrap_or(0),
717-
patch: patch.unwrap_or(0),
718-
build,
719-
pre_release,
720-
})),
721-
(
722-
LessThanEquals,
723-
Partial {
724-
major,
725-
minor: None,
726-
patch: None,
727-
..
728-
},
729-
) => BoundSet::at_most(Predicate::Including(
730-
(major.unwrap_or(0), MAX_SAFE_INTEGER, MAX_SAFE_INTEGER).into(),
731-
)),
732-
(
733-
LessThanEquals,
734-
Partial {
735-
major,
736-
minor,
737-
patch: None,
738-
..
739-
},
740-
) => BoundSet::at_most(Predicate::Including(
741-
(major.unwrap_or(0), minor.unwrap_or(0), MAX_SAFE_INTEGER).into(),
742-
)),
743-
(LessThanEquals, partial) => {
744-
BoundSet::at_most(Predicate::Including(partial.into()))
745-
}
746-
(
747-
Exact,
748-
Partial {
749-
major: Some(major),
750-
minor: Some(minor),
751-
patch: Some(patch),
752-
pre_release,
753-
..
754-
},
755-
) => BoundSet::exact( Version {
670+
(operation, preceded(space0, partial_version)),
671+
|parsed| match parsed {
672+
(GreaterThanEquals, partial) => {
673+
BoundSet::at_least(Predicate::Including(partial.into()))
674+
}
675+
(
676+
GreaterThan,
677+
Partial {
678+
major: Some(major),
679+
minor: Some(minor),
680+
patch: None,
681+
..
682+
},
683+
) => BoundSet::at_least(Predicate::Including((major, minor + 1, 0).into())),
684+
(
685+
GreaterThan,
686+
Partial {
687+
major: Some(major),
688+
minor: None,
689+
patch: None,
690+
..
691+
},
692+
) => BoundSet::at_least(Predicate::Including((major + 1, 0, 0).into())),
693+
(GreaterThan, partial) => BoundSet::at_least(Predicate::Excluding(partial.into())),
694+
(
695+
LessThan,
696+
Partial {
697+
major: Some(major),
698+
minor: Some(minor),
699+
patch: None,
700+
..
701+
},
702+
) => BoundSet::at_most(Predicate::Excluding((major, minor, 0, 0).into())),
703+
(
704+
LessThan,
705+
Partial {
756706
major,
757707
minor,
758708
patch,
759709
pre_release,
710+
build,
711+
..
712+
},
713+
) => BoundSet::at_most(Predicate::Excluding(Version {
714+
major: major.unwrap_or(0),
715+
minor: minor.unwrap_or(0),
716+
patch: patch.unwrap_or(0),
717+
build,
718+
pre_release,
719+
})),
720+
(
721+
LessThanEquals,
722+
Partial {
723+
major,
724+
minor: None,
725+
patch: None,
726+
..
727+
},
728+
) => BoundSet::at_most(Predicate::Including(
729+
(major.unwrap_or(0), MAX_SAFE_INTEGER, MAX_SAFE_INTEGER).into(),
730+
)),
731+
(
732+
LessThanEquals,
733+
Partial {
734+
major,
735+
minor,
736+
patch: None,
737+
..
738+
},
739+
) => BoundSet::at_most(Predicate::Including(
740+
(major.unwrap_or(0), minor.unwrap_or(0), MAX_SAFE_INTEGER).into(),
741+
)),
742+
(LessThanEquals, partial) => BoundSet::at_most(Predicate::Including(partial.into())),
743+
(
744+
Exact,
745+
Partial {
746+
major: Some(major),
747+
minor: Some(minor),
748+
patch: Some(patch),
749+
pre_release,
750+
..
751+
},
752+
) => BoundSet::exact(Version {
753+
major,
754+
minor,
755+
patch,
756+
pre_release,
757+
build: vec![],
758+
}),
759+
(
760+
Exact,
761+
Partial {
762+
major: Some(major),
763+
minor: Some(minor),
764+
..
765+
},
766+
) => BoundSet::new(
767+
Bound::Lower(Predicate::Including((major, minor, 0).into())),
768+
Bound::Upper(Predicate::Excluding(Version {
769+
major,
770+
minor: minor + 1,
771+
patch: 0,
772+
pre_release: vec![Identifier::Numeric(0)],
760773
build: vec![],
761-
}),
762-
(
763-
Exact,
764-
Partial {
765-
major: Some(major),
766-
minor: Some(minor),
767-
..
768-
},
769-
) => BoundSet::new(
770-
Bound::Lower(Predicate::Including(
771-
(major, minor, 0).into(),
772-
)),
773-
Bound::Upper(Predicate::Excluding(Version {
774-
major,
775-
minor: minor + 1,
776-
patch: 0,
777-
pre_release: vec![Identifier::Numeric(0)],
778-
build: vec![],
779-
})),
780-
),
781-
(
782-
Exact,
783-
Partial {
784-
major: Some(major),
785-
..
786-
},
787-
) => BoundSet::new(
788-
Bound::Lower(Predicate::Including(
789-
(major, 0, 0).into(),
790-
)),
791-
Bound::Upper(Predicate::Excluding(Version {
792-
major: major + 1,
793-
minor: 0,
794-
patch: 0,
795-
pre_release: vec![Identifier::Numeric(0)],
796-
build: vec![],
797-
})),
798-
),
799-
_ => unreachable!("Failed to parse operation. This should not happen and should be reported as a bug, while parsing {}", input),
800-
}
801-
},
802-
).context("operation range (ex: >= 1.2.3)").parse_next(input)
774+
})),
775+
),
776+
(
777+
Exact,
778+
Partial {
779+
major: Some(major), ..
780+
},
781+
) => BoundSet::new(
782+
Bound::Lower(Predicate::Including((major, 0, 0).into())),
783+
Bound::Upper(Predicate::Excluding(Version {
784+
major: major + 1,
785+
minor: 0,
786+
patch: 0,
787+
pre_release: vec![Identifier::Numeric(0)],
788+
build: vec![],
789+
})),
790+
),
791+
_ => None,
792+
},
793+
)
794+
.context("operation range (ex: >= 1.2.3)")
795+
.parse_next(input)
803796
}
804797

805798
fn operation(input: &str) -> IResult<&str, Operation, SemverParseError<&str>> {
@@ -996,7 +989,7 @@ fn tilde(input: &str) -> IResult<&str, Option<BoundSet>, SemverParseError<&str>>
996989
Bound::Lower(Predicate::Including((major, 0, 0).into())),
997990
Bound::Upper(Predicate::Excluding((major + 1, 0, 0, 0).into())),
998991
),
999-
_ => unreachable!("This should not have parsed: {}", input),
992+
_ => None,
1000993
})
1001994
.context("tilde version range (ex: ~1.2.3)")
1002995
.parse_next(input)

0 commit comments

Comments
 (0)