use std::io::{Cursor, Write}; use zip::write::SimpleFileOptions; use zip::AesMode; use super::ReportContext; pub(super) fn build_zip( ctx: &ReportContext, password: &str, html: &str, pdf: &[u8], ) -> Result, zip::result::ZipError> { let buf = Cursor::new(Vec::new()); let mut zip = zip::ZipWriter::new(buf); let options = SimpleFileOptions::default() .compression_method(zip::CompressionMethod::Deflated) .with_aes_encryption(AesMode::Aes256, password); // report.pdf (primary) zip.start_file("report.pdf", options)?; zip.write_all(pdf)?; // report.html (fallback) zip.start_file("report.html", options)?; zip.write_all(html.as_bytes())?; // findings.json let findings_json = serde_json::to_string_pretty(&ctx.findings).unwrap_or_else(|_| "[]".to_string()); zip.start_file("findings.json", options)?; zip.write_all(findings_json.as_bytes())?; // attack-chain.json let chain_json = serde_json::to_string_pretty(&ctx.attack_chain).unwrap_or_else(|_| "[]".to_string()); zip.start_file("attack-chain.json", options)?; zip.write_all(chain_json.as_bytes())?; let cursor = zip.finish()?; Ok(cursor.into_inner()) }