feat: add comprehensive fuzz testing suite with 11 targets - #52
Open
sunsided wants to merge 1 commit into
Open
Conversation
- parse_str: main Gedcom::new + parse_data on UTF-8 text - parse_bytes: GedcomBuilder with encoding detection and size limit - stream_parser: streaming parser via BufRead - detect_encoding: encoding detection + ANSEL decoder exercise - detect_version: version line scanner - escape_at_roundtrip: property-based @ escaping roundtrip - name_parse: NameType::parse for name string parsing - date_qualifier: DateQualifier::parse (ABT, BEF, AFT, etc.) - writer_roundtrip: parse → write → re-parse idempotence - encode_decode_roundtrip: encode_to_bytes → decode roundtrip - gedzip_read: ZIP reader + full GEDCOM parse Includes seed corpus from test fixtures, GEDCOM dictionary file, fuzz README with invocations, and .cargo/config.toml aliases
There was a problem hiding this comment.
Pull request overview
Adds a cargo-fuzz harness to stress-test ged_io’s parsing/encoding/writing pipelines with multiple fuzz targets plus supporting docs/config.
Changes:
- Introduces 11 new libFuzzer fuzz targets covering parsing, streaming parsing, encoding detection, writer roundtrips, and GEDZIP reading.
- Adds fuzzing infrastructure (
fuzz/crate, seed dictionary, README) and Cargo aliases for common fuzz invocations. - Updates lockfiles (both
fuzz/Cargo.lockand the repository rootCargo.lock).
Reviewed changes
Copilot reviewed 16 out of 18 changed files in this pull request and generated 6 comments.
Show a summary per file
| File | Description |
|---|---|
| fuzz/fuzz_targets/writer_roundtrip.rs | Roundtrip parse → write → parse fuzz target |
| fuzz/fuzz_targets/stream_parser.rs | Streaming parser fuzz target via BufRead |
| fuzz/fuzz_targets/parse_str.rs | UTF-8 &str parser fuzz target |
| fuzz/fuzz_targets/parse_bytes.rs | Byte pipeline fuzz target via GedcomBuilder |
| fuzz/fuzz_targets/name_parse.rs | NameType::parse fuzz target |
| fuzz/fuzz_targets/gedzip_read.rs | GEDZIP reader fuzz target |
| fuzz/fuzz_targets/escape_at_roundtrip.rs | Property-style fuzz target for @ escaping |
| fuzz/fuzz_targets/encode_decode_roundtrip.rs | Encode/decode roundtrip fuzz target per encoding |
| fuzz/fuzz_targets/detect_version.rs | Version detection fuzz target |
| fuzz/fuzz_targets/detect_encoding.rs | Encoding detection + decode fuzz target |
| fuzz/fuzz_targets/date_qualifier.rs | DateQualifier::parse fuzz target |
| fuzz/dict/gedcom.dict | Fuzzing dictionary tokens for GEDCOM inputs |
| fuzz/README.md | Fuzzing documentation and invocation examples |
| fuzz/Cargo.toml | cargo-fuzz crate manifest and target registration |
| fuzz/Cargo.lock | Lockfile for fuzz crate dependencies |
| fuzz/.gitignore | Ignores fuzz artifacts/corpus/coverage |
| .cargo/config.toml | Cargo aliases for running fuzz targets |
| Cargo.lock | Root lockfile dependency updates |
💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.
Comment on lines
+19
to
+25
| name = "fuzz_target_1" | ||
| path = "fuzz_targets/fuzz_target_1.rs" | ||
| test = false | ||
| doc = false | ||
| bench = false | ||
|
|
||
| [[bin]] |
| }; | ||
| let escaped = ged_io::util::escape_at_signs(&input.s, input.is_gedcom_7); | ||
| let unescaped = ged_io::util::unescape_at_signs(&escaped, input.is_gedcom_7); | ||
| if !input.is_gedcom_7 || !input.s[1..].contains('@') { |
Comment on lines
+2
to
+3
| # run with "cargo +nightly fuzz-..." | ||
| fuzz-all = "fuzz run parse_str -- -dict=fuzz/dict/gedcom.dict -max_len=65536" |
Comment on lines
+5
to
+12
| fuzz_target!(|data: &[u8]| { | ||
| if data.is_empty() { | ||
| return; | ||
| } | ||
| let _ = ged_io::detect_encoding(data); | ||
| let _ = ged_io::decode_gedcom_bytes(data); | ||
| for enc in [ | ||
| ged_io::GedcomEncoding::Utf8, |
| use libfuzzer_sys::fuzz_target; | ||
|
|
||
| fuzz_target!(|data: &[u8]| { | ||
| if let Ok(s) = std::str::from_utf8(data) { |
Comment on lines
+28
to
+30
| if let Ok(mut g2) = ged_io::Gedcom::new(output.chars()) { | ||
| let _ = g2.parse_data(); | ||
| } |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Hey hey. I figured that parsing is a good candidate for fuzz testing, so I added the following suites for cargo-fuzz to the project:
parse_str: mainGedcom::new+parse_dataon UTF-8 textparse_bytes:GedcomBuilderwith encoding detection and size limitstream_parser: streaming parser viaBufReaddetect_encoding: encoding detection + ANSEL decoder exercisedetect_version: version line scannerescape_at_roundtrip: property-based @ escaping roundtripname_parse:NameType::parsefor name string parsingdate_qualifier:DateQualifier::parse(ABT, BEF, AFT, etc.)writer_roundtrip: parse → write → re-parse idempotenceencode_decode_roundtrip: encode_to_bytes → decode roundtripgedzip_read: ZIP reader + full GEDCOM parseIt includes a seed corpus from test fixtures, GEDCOM dictionary file, fuzz README with invocations, and .cargo/config.toml aliases.
The sad reality? Nothing is found, the parser was already perfect. 🤡 No hard feelings at all if this is closed as-is.
How to run
To run the main parser test, this should work: