Skip to main content

zip_extensions/audit/handlers/
entry_view.rs

1use crate::audit::handlers::util;
2use std::io::{Read, Seek};
3use std::path::PathBuf;
4use zip::read::ZipFile;
5
6/// A lightweight, precomputed view over a ZIP entry used by analysis handlers.
7#[derive(Debug, Clone)]
8pub struct EntryView {
9    pub compressed_size: u64,
10    pub depth_hint: usize,
11    pub enclosed_name: PathBuf,
12    pub encrypted: bool,
13    pub has_abs: bool,
14    pub has_parent_components: bool,
15    pub invalid_utf8: bool,
16    pub name_raw: Vec<u8>,
17    pub ratio: f64,
18    pub symlink: bool,
19    pub symlink_target: Option<String>,
20    pub uncompressed_size: u64,
21    pub unix_mode: Option<u32>,
22}
23
24impl EntryView {
25    /// Processes a `ZipFile` and extracts relevant metadata and properties into an `EntryView` for
26    /// easier management and inspection. It performs operations like determining the entry name,
27    /// checking for invalid UTF-8 in the file name, computing compression ratios, and identifying
28    /// symbolic links.
29    pub fn from_entry<R: Read + Seek>(mut entry: ZipFile<R>) -> Self {
30        // Build EntryView with precomputed fields
31        let name_raw_slice: &[u8] = entry.name_raw();
32        let (name_raw, utf8_opt) = util::name_raw_and_utf8(name_raw_slice);
33        let invalid_utf8 = utf8_opt.is_none();
34        let enclosed_name = entry.mangled_name();
35
36        let has_abs = util::is_absolute_path_bytes(&name_raw);
37        let has_parent = util::has_parent_components_bytes(&name_raw);
38        let depth_hint = util::depth_hint_bytes(&name_raw);
39
40        let compressed_size = entry.compressed_size();
41        let uncompressed_size = entry.size();
42        let ratio = util::compression_ratio(compressed_size, uncompressed_size);
43
44        let encrypted = entry.encrypted();
45
46        let unix_mode = entry.unix_mode();
47        let symlink = util::is_symlink_unix_mode(unix_mode);
48        let mut symlink_target: Option<String> = None;
49        const SAFE_UNCOMPRESSED_SIZE_LIMIT: u64 = 8192;
50        if symlink && uncompressed_size <= SAFE_UNCOMPRESSED_SIZE_LIMIT {
51            let mut target = String::new();
52            let _ = entry.read_to_string(&mut target);
53            if !target.is_empty() {
54                symlink_target = Some(target);
55            }
56        }
57
58        let view = EntryView {
59            compressed_size,
60            depth_hint,
61            enclosed_name,
62            encrypted,
63            has_abs,
64            has_parent_components: has_parent,
65            invalid_utf8,
66            name_raw,
67            ratio,
68            symlink,
69            symlink_target,
70            uncompressed_size,
71            unix_mode,
72        };
73
74        view
75    }
76}