plugins/native/znippy-plugin-git/fuzz/src/write_seeds.rs
//! Write the fuzz corpus seeds for `read_paths`: one valid input per read path
//! (prefixed with its selector byte) and every hostile fixture under each path.
use std::path::PathBuf;
use std::sync::Arc;
use znippy_common::arrow::array::{BinaryBuilder, RecordBatch, StringBuilder, UInt64Builder};
use znippy_common::arrow::datatypes::{DataType, Field, Schema};
use znippy_common::arrow::ipc::writer::StreamWriter;
use znippy_plugin_git::exploded_arrow::{ExplodePolicy, ExplodedArchive};
use znippy_plugin_git::index_layout::{ObjectIndex, OneTableFourColumns, synthetic_entries};
use znippy_plugin_git::{CommitNode, GitObjectKind, reach_schema};
fn stream(batch: &RecordBatch) -> Vec<u8> {
let mut out = Vec::new();
let mut w = StreamWriter::try_new(&mut out, &batch.schema()).unwrap();
w.write(batch).unwrap();
w.finish().unwrap();
out
}
fn main() {
let dir = PathBuf::from(std::env::args().nth(1).expect("corpus dir"));
std::fs::create_dir_all(&dir).unwrap();
let mut seeds: Vec<(u8, Vec<u8>)> = Vec::new();
let nodes = vec![
CommitNode {
oid: "a".repeat(40),
parents: vec![],
tree: Some("t".repeat(40)),
committer_time: Some(1),
generation: 1,
},
CommitNode {
oid: "b".repeat(40),
parents: vec!["a".repeat(40)],
tree: None,
committer_time: None,
generation: 2,
},
];
seeds.push((0, znippy_plugin_git::graph::encode_graph(&nodes).unwrap()));
let mut o = StringBuilder::new();
let mut c = UInt64Builder::new();
let mut b = BinaryBuilder::new();
o.append_value("a".repeat(40));
c.append_value(0);
b.append_value([58u8, 48, 0, 0, 0, 0, 0, 0]);
let batch = RecordBatch::try_new(
reach_schema(),
vec![
Arc::new(o.finish()),
Arc::new(c.finish()),
Arc::new(b.finish()),
],
)
.unwrap();
seeds.push((1, stream(&batch)));
let schema = Arc::new(Schema::new(vec![Field::new("name", DataType::Utf8, false)]));
let mut log = Vec::new();
for n in ["refs/heads/main", "refs/heads/x"] {
let mut s = StringBuilder::new();
s.append_value(n);
log.extend(stream(
&RecordBatch::try_new(schema.clone(), vec![Arc::new(s.finish())]).unwrap(),
));
}
seeds.push((2, log));
let mut j = znippy_plugin_git::archive_write::encode_journal_schema().unwrap();
j.extend(znippy_plugin_git::archive_write::encode_journal_row(0, 100).unwrap());
j.extend(znippy_plugin_git::archive_write::encode_journal_row(100, 7).unwrap());
seeds.push((3, j));
let p = std::env::temp_dir().join(format!("plugin-git-seed-{}.arrow", std::process::id()));
let _ = std::fs::remove_file(&p);
{
let t = ExplodedArchive::open_with_policy(&p, ExplodePolicy::Full).unwrap();
t.explode_at(
&[1; 20],
GitObjectKind::Commit,
Some(0o100644),
Some("a/b"),
b"tree x\n",
)
.unwrap();
t.explode(&[2; 20], GitObjectKind::Blob, &[b'z'; 3000])
.unwrap();
t.flush().unwrap();
}
seeds.push((4, std::fs::read(&p).unwrap()));
let _ = std::fs::remove_file(&p);
let ix = OneTableFourColumns::build(&synthetic_entries(3, 20, 7)).unwrap();
seeds.push((5, ix.persisted_bytes().unwrap().to_vec()));
let fixtures =
PathBuf::from(env!("CARGO_MANIFEST_DIR")).join("../../../../tests/fixtures/hostile-ipc");
let mut hostile = Vec::new();
for e in std::fs::read_dir(fixtures).unwrap() {
hostile.push(std::fs::read(e.unwrap().path()).unwrap());
}
let mut n = 0;
for (sel, bytes) in &seeds {
let mut v = vec![*sel];
v.extend(bytes);
std::fs::write(dir.join(format!("valid-{sel}")), v).unwrap();
n += 1;
}
for (i, h) in hostile.iter().enumerate() {
for sel in 0..6u8 {
let mut v = vec![sel];
v.extend(h);
std::fs::write(dir.join(format!("hostile-{i}-{sel}")), v).unwrap();
n += 1;
}
}
println!("{n} seeds in {}", dir.display());
}