| 1 | #![no_main] |
| 2 | use libfuzzer_sys::fuzz_target; |
| 3 | use onestore::{ExGuid, ObjectData, PropertySets, RevisionIndex, Store, Value, create_section}; |
| 4 | fuzz_target!(|input: &[u8]| { |
| 5 | let count = input.first().copied().unwrap_or(0) as usize % 32; |
| 6 | let entries: Vec<_> = (0..count) |
| 7 | .map(|i| (format!("section-{i}.one"), [i as u8 + 1; 16])) |
| 8 | .collect(); |
| 9 | let sections: Vec<_> = entries |
| 10 | .iter() |
| 11 | .map(|(name, id)| (name.as_str(), *id)) |
| 12 | .collect(); |
| 13 | let toc = onestore::create_table_of_contents("Open Notebook.onetoc2", &sections).unwrap(); |
| 14 | let store = Store::parse(&toc).unwrap(); |
| 15 | assert!(store.checksum_mismatches.is_empty()); |
| 16 | let index = RevisionIndex::parse(&store).unwrap(); |
| 17 | index.validate_current().unwrap(); |
| 18 | let revision = index |
| 19 | .resolve( |
| 20 | index.root, |
| 21 | index.spaces[&index.root].labels[&(ExGuid::default(), 1)], |
| 22 | ) |
| 23 | .unwrap(); |
| 24 | assert_eq!( |
| 25 | revision.objects[&revision.roots[&1]] |
| 26 | .references() |
| 27 | .unwrap() |
| 28 | .objects |
| 29 | .len(), |
| 30 | count |
| 31 | ); |
| 32 | let Ok(text) = std::str::from_utf8(input) else { |
| 33 | return; |
| 34 | }; |
| 35 | let result = create_section("synthetic.one", text, "Fuzz author"); |
| 36 | if text.contains(['\0', '\n']) { |
| 37 | assert!(result.is_err()); |
| 38 | return; |
| 39 | } |
| 40 | let bytes = result.unwrap(); |
| 41 | let store = Store::parse(&bytes).unwrap(); |
| 42 | assert!(store.checksum_mismatches.is_empty()); |
| 43 | let index = RevisionIndex::parse(&store).unwrap(); |
| 44 | index.validate_current().unwrap(); |
| 45 | let expected: Vec<_> = text |
| 46 | .encode_utf16() |
| 47 | .chain([0]) |
| 48 | .flat_map(u16::to_le_bytes) |
| 49 | .collect(); |
| 50 | let mut found = 0; |
| 51 | for (osid, space) in &index.spaces { |
| 52 | let revision = index |
| 53 | .resolve(*osid, space.labels[&(ExGuid::default(), 1)]) |
| 54 | .unwrap(); |
| 55 | for object in revision |
| 56 | .objects |
| 57 | .values() |
| 58 | .filter(|object| object.jcid == 0x6000e) |
| 59 | { |
| 60 | let ObjectData::Properties(data) = object.data else { |
| 61 | panic!() |
| 62 | }; |
| 63 | assert!( |
| 64 | PropertySets::parse(data).unwrap().sets[0] |
| 65 | .iter() |
| 66 | .any(|p| p.id == 0x1c001c22 && p.value == Value::Bytes(&expected)) |
| 67 | ); |
| 68 | found += 1; |
| 69 | } |
| 70 | } |
| 71 | assert_eq!(found, 1); |
| 72 | }); |