trash_forensic/lib.rs
1//! Forensic anomaly analysis for **trash / deleted-file artifacts**, layered on
2//! the [`trash_core`] readers. Each platform's analyzer lives in its own module,
3//! gated behind a same-named Cargo feature (all enabled by default):
4//!
5//! | Module | Feature | Scheme | Artifact |
6//! |---|---|---|---|
7//! | [`windows`] | `windows` | `RECYCLEBIN-*` | Recycle Bin `$I`/`$R` |
8//! | [`linux`] | `linux` | `TRASH-*` | freedesktop.org / XDG `.trashinfo` |
9//!
10//! Every analyzer inspects a parsed reader record + its pairing and reports
11//! anomalies as canonical [`forensicnomicon::report::Finding`]s, so trash
12//! findings aggregate alongside every other `SecurityRonin` analyzer. Findings
13//! are observations, never legal conclusions: the analyst concludes.
14//!
15//! ```no_run
16//! # #[cfg(feature = "windows")]
17//! # fn demo(dir: &std::path::Path) -> std::io::Result<()> {
18//! use trash_core::{parse_index, scan_pairs};
19//! use trash_forensic::audit_pair;
20//! for pair in scan_pairs(dir)? {
21//! let bytes = std::fs::read(&pair.index_path)?;
22//! if let Ok(index) = parse_index(&bytes) {
23//! for finding in audit_pair(&index, &pair) {
24//! println!("[{:?}] {} — {}", finding.severity, finding.code, finding.note);
25//! }
26//! }
27//! }
28//! # Ok(())
29//! # }
30//! ```
31
32#![cfg_attr(test, allow(clippy::unwrap_used, clippy::expect_used))]
33
34#[cfg(feature = "windows")]
35pub mod windows;
36
37#[cfg(feature = "linux")]
38pub mod linux;
39
40#[cfg(feature = "macos")]
41pub mod macos;
42
43#[cfg(feature = "android")]
44pub mod android;
45
46#[cfg(feature = "ios")]
47pub mod ios;
48
49#[cfg(feature = "windows")]
50pub use windows::{audit_pair, AnomalyKind};
51
52#[cfg(feature = "linux")]
53pub use linux::{audit_entry, TrashAnomaly};
54
55#[cfg(feature = "macos")]
56pub use macos::{audit_put_back, DsStoreAnomaly};
57
58#[cfg(feature = "android")]
59pub use android::{audit_trashed_name, TrashedNameAnomaly};
60
61#[cfg(feature = "ios")]
62pub use ios::{audit_trashed_asset, IosAssetAnomaly};
63
64/// Analyzer name, recorded on every finding's [`forensicnomicon::report::Source`]
65/// for reproducibility, shared across the per-OS analyzers.
66pub const ANALYZER: &str = "trash-forensic";
67
68/// Whether a stored path contains a parent-directory (`..`) component, treating
69/// both Windows (`\`) and POSIX (`/`) separators. Matches `..` only as a whole
70/// path component, so a filename like `my..notes.txt` is not flagged. Shared by
71/// every platform analyzer (path traversal is a cross-platform concealment tell).
72#[cfg(any(feature = "windows", feature = "linux", feature = "macos"))]
73pub(crate) fn has_path_traversal(path: &str) -> bool {
74 path.split(['\\', '/']).any(|component| component == "..")
75}