use crate::archive::{Archive, OpenOptions};
use crate::error::PithosError;
use crate::format::limits::DeserializationLimits;
use crate::source::MemorySource;
use std::io::Cursor;
use std::sync::Arc;
#[derive(Clone, Copy, Debug, Eq, PartialEq)]
pub enum FuzzErrorCategory {
Archive,
Authentication,
Deserialization,
Limit,
Source,
}
#[derive(Clone, Copy, Debug, Eq, PartialEq)]
pub enum FuzzOutcome {
Accepted,
Rejected(FuzzErrorCategory),
}
pub fn decode_header(data: &[u8]) -> FuzzOutcome {
match crate::format::header::decode_header(&mut Cursor::new(data)) {
Ok(_) => FuzzOutcome::Accepted,
Err(_) => FuzzOutcome::Rejected(FuzzErrorCategory::Deserialization),
}
}
pub fn decode_directory(data: &[u8]) -> FuzzOutcome {
let limits = DeserializationLimits::default();
let mut remaining_block_references = limits.max_block_references;
let mut blocks: indexmap::IndexMap<[u8; 32], crate::format::block::BlockIndexEntry> =
indexmap::IndexMap::new();
match crate::archive::decode_validated_directory(
data,
crate::format::header::FormatVersion::CURRENT,
&limits,
&mut remaining_block_references,
&mut blocks,
) {
Ok(_) => FuzzOutcome::Accepted,
Err(error) => FuzzOutcome::Rejected(classify_archive_error(&error)),
}
}
pub fn validate_archive(data: &[u8]) -> FuzzOutcome {
match Archive::open(
MemorySource::new(Arc::<[u8]>::from(data)),
OpenOptions::default(),
) {
Ok(_) => FuzzOutcome::Accepted,
Err(error) => FuzzOutcome::Rejected(classify_archive_error(&error)),
}
}
pub fn decode_crypt4gh_header(data: &[u8]) -> FuzzOutcome {
match crate::adapters::crypt4gh::Crypt4GHHeader::try_from(data) {
Ok(_) => FuzzOutcome::Accepted,
Err(_) => FuzzOutcome::Rejected(FuzzErrorCategory::Deserialization),
}
}
fn classify_archive_error(error: &PithosError) -> FuzzErrorCategory {
match error {
PithosError::Crypt(_) => FuzzErrorCategory::Authentication,
PithosError::Deserialization(_) => FuzzErrorCategory::Deserialization,
PithosError::LimitExceeded { .. } | PithosError::AllocationFailed { .. } => {
FuzzErrorCategory::Limit
}
PithosError::Source(_) => FuzzErrorCategory::Source,
_ => FuzzErrorCategory::Archive,
}
}
#[cfg(test)]
mod tests {
use super::*;
const MINIMAL_DIRECTORY: [u8; 25] = [
0x50, 0x49, 0x54, 0x48, 0x4f, 0x53, 0x44, 0x52, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x19, 0xb1, 0x67, 0x40, 0x81,
];
#[test]
fn directory_fuzz_boundary_validates_complete_framing() {
assert_eq!(decode_directory(&MINIMAL_DIRECTORY), FuzzOutcome::Accepted);
let mut corrupt_crc = MINIMAL_DIRECTORY;
*corrupt_crc.last_mut().unwrap() ^= 1;
assert!(matches!(
decode_directory(&corrupt_crc),
FuzzOutcome::Rejected(FuzzErrorCategory::Archive)
));
assert!(matches!(
decode_directory(&MINIMAL_DIRECTORY[..MINIMAL_DIRECTORY.len() - 12]),
FuzzOutcome::Rejected(FuzzErrorCategory::Archive)
));
}
}