1#![no_main]
2//! Replaces a native text with arbitrary text: the edit either refuses it or appends a
3//! revision storing it, leaving the old header's view of the file intact.
4use libfuzzer_sys::fuzz_target;
5use onestore::{
6 ExGuid, RevisionIndex, Store,
7 document::{Document, Kind},
8 op::{Op, PageOp},
9};
10use std::sync::LazyLock;
11
12#[path = "../../crates/onestore/tests/support/ops.rs"]
13mod ops;
14
15const SOURCE: &[u8] =
16 include_bytes!("../../corpus/native/20260905-05/snapshots/02-text/notebook/synthetic.one");
17
18fn text(bytes: &[u8], target: Option<(ExGuid, ExGuid)>) -> Vec<(ExGuid, ExGuid, String)> {
19 let store = Store::parse(bytes).unwrap();
20 assert!(store.checksum_mismatches.is_empty());
21 let index = RevisionIndex::parse(&store).unwrap();
22 index.validate_current().unwrap();
23 let document = Document::parse(&index).unwrap();
24 let mut found = Vec::new();
25 for (sid, space) in &document.spaces {
26 let revision = &space.revisions[&space.contexts[&ExGuid::default()]];
27 for (oid, node) in &revision.nodes {
28 if let Kind::RichText { text, .. } = &node.kind
29 && target.is_none_or(|target| target == (*sid, *oid))
30 {
31 found.push((*sid, *oid, text.clone()));
32 }
33 }
34 }
35 found
36}
37
38static TARGET: LazyLock<(ExGuid, ExGuid)> = LazyLock::new(|| {
39 text(SOURCE, None)
40 .into_iter()
41 .find_map(|(sid, oid, text)| (text == "Fictitious plain text.").then_some((sid, oid)))
42 .expect("Missing native seed text")
43});
44
45fuzz_target!(|value: &[u8]| {
46 let (sid, oid) = *TARGET;
47 let value = String::from_utf8_lossy(value);
48 let op = PageOp::Text {
49 text: oid,
50 range: 0.."Fictitious plain text.".len() as u32,
51 with: value.to_string(),
52 };
53 let edited = ops::apply(SOURCE, "Fuzz", vec![Op::Page { space: sid, op }]);
54 if value.contains(['\0', '\n', '\u{fffc}']) {
55 assert!(edited.is_err());
56 return;
57 }
58 let bytes = edited.unwrap().image;
59 let mut before_commit = bytes.clone();
60 before_commit[..1024].copy_from_slice(&SOURCE[..1024]);
61 assert_eq!(
62 text(&before_commit, Some((sid, oid)))[0].2,
63 "Fictitious plain text."
64 );
65 assert_eq!(text(&bytes, Some((sid, oid)))[0].2, value);
66});