#![forbid(unsafe_code)]
use auditable_extract::raw_auditable_data;
#[cfg(feature = "serde")]
use auditable_serde::VersionInfo;
use miniz_oxide::inflate::decompress_to_vec_zlib_with_limit;
use std::fs::File;
use std::io::{BufRead, BufReader, Read};
use std::path::Path;
mod error;
pub use crate::error::*;
#[cfg(feature = "serde")]
pub fn audit_info_from_file(path: &Path, limits: Limits) -> Result<VersionInfo, Error> {
Ok(serde_json::from_str(&json_from_file(path, limits)?)?)
}
pub fn json_from_file(path: &Path, limits: Limits) -> Result<String, Error> {
let file = File::open(path)?;
let mut reader = BufReader::new(file);
json_from_reader(&mut reader, limits)
}
#[cfg(feature = "serde")]
pub fn audit_info_from_reader<T: BufRead>(
reader: &mut T,
limits: Limits,
) -> Result<VersionInfo, Error> {
Ok(serde_json::from_str(&json_from_reader(reader, limits)?)?)
}
pub fn json_from_reader<T: BufRead>(reader: &mut T, limits: Limits) -> Result<String, Error> {
let compressed_data = get_compressed_audit_data(reader, limits)?;
let decompressed_data =
decompress_to_vec_zlib_with_limit(&compressed_data, limits.decompressed_json_size)
.map_err(DecompressError::from_miniz)?;
Ok(String::from_utf8(decompressed_data)?)
}
fn get_compressed_audit_data<T: BufRead>(reader: &mut T, limits: Limits) -> Result<Vec<u8>, Error> {
let incremented_limit = u64::saturating_add(limits.input_file_size as u64, 1);
let mut f = reader.take(incremented_limit);
let mut input_binary = Vec::new();
f.read_to_end(&mut input_binary)?;
if input_binary.len() as u64 == incremented_limit {
Err(Error::InputLimitExceeded)?
}
let compressed_audit_data = raw_auditable_data(&input_binary)?;
if compressed_audit_data.len() > limits.decompressed_json_size {
Err(Error::OutputLimitExceeded)?;
}
Ok(compressed_audit_data.to_owned())
}
#[cfg(feature = "serde")]
pub fn audit_info_from_slice(
input_binary: &[u8],
decompressed_json_size_limit: usize,
) -> Result<VersionInfo, Error> {
Ok(serde_json::from_str(&json_from_slice(
input_binary,
decompressed_json_size_limit,
)?)?)
}
pub fn json_from_slice(
input_binary: &[u8],
decompressed_json_size_limit: usize,
) -> Result<String, Error> {
let compressed_audit_data = raw_auditable_data(input_binary)?;
if compressed_audit_data.len() > decompressed_json_size_limit {
Err(Error::OutputLimitExceeded)?;
}
let decompressed_data =
decompress_to_vec_zlib_with_limit(compressed_audit_data, decompressed_json_size_limit)
.map_err(DecompressError::from_miniz)?;
Ok(String::from_utf8(decompressed_data)?)
}
#[derive(Copy, Clone, Eq, PartialEq, Hash)]
pub struct Limits {
pub input_file_size: usize,
pub decompressed_json_size: usize,
}
impl Default for Limits {
fn default() -> Self {
Self {
input_file_size: 1024 * 1024 * 1024, decompressed_json_size: 1024 * 1024 * 8, }
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn input_file_limits() {
let limits = Limits {
input_file_size: 128,
decompressed_json_size: 99999,
};
let fake_data = vec![0; 1024];
let mut reader = std::io::Cursor::new(fake_data);
let result = get_compressed_audit_data(&mut reader, limits);
assert!(result.is_err());
assert!(result
.unwrap_err()
.to_string()
.contains("The input file is too large"));
}
}