Skip to main content

forensicnomicon_data/catalog/
mod.rs

1//! Forensic artifact catalog — assembled dataset + global [`CATALOG`].
2//!
3//! The catalog **engine** (descriptor [`types`], the [`ForensicCatalog`] lookup
4//! engine and its query methods, container/record parsing profiles, and in-core
5//! decoders) lives in the `forensicnomicon-core` crate. This module owns the
6//! fast-moving **data** — the ~6.5k assembled artifact descriptors — and wires
7//! them into the compile-time global [`CATALOG`].
8//!
9//! The whole engine surface is re-exported here, so existing
10//! `forensicnomicon::catalog::*` (and crate-internal `crate::catalog::*`) paths —
11//! including the `types` submodule the generated descriptors reference — resolve
12//! exactly as they did before the engine/data split.
13//!
14//! # Curated source corpus
15//!
16//! Artifact additions are researched from a maintained DFIR source corpus under
17//! `archive/sources/` (`catalog-directories.json`, `manual-sources.json`,
18//! `dfir-feeds.opml`, `source-inventory.json`). Prefer primary/vendor
19//! documentation and well-cited practitioner research; each
20//! [`ArtifactDescriptor::sources`] entry should still point to the specific
21//! authoritative references that justify the artifact.
22
23// Re-export the entire engine surface (types, the `types` module, ForensicCatalog,
24// parsing-profile free functions, decode helpers) from core. This makes every
25// `crate::catalog::*` and `crate::catalog::types::*` path used by the descriptor
26// dataset resolve unchanged.
27pub use forensicnomicon_core::catalog::*;
28
29mod descriptors;
30
31/// The global forensic artifact catalog containing all known artifact descriptors.
32///
33/// Maintainer note:
34/// New descriptors should be researched against the curated DFIR source corpus
35/// documented in this module header, then anchored with artifact-specific URLs in
36/// the descriptor's `sources` field. Archived source corpora are discovery input;
37/// they do not replace per-artifact attribution.
38pub static CATALOG: ForensicCatalog = ForensicCatalog::new(descriptors::CATALOG_ENTRIES);
39
40// ── Backward-compatible free functions ───────────────────────────────────────
41// Pre-split these lived here and queried the global CATALOG directly. They now
42// delegate to the equivalent engine methods on the global catalog instance, so
43// the public `forensicnomicon::catalog::*_for_artifact` paths keep working.
44
45/// Returns the outer-container parsing profile for a catalog artifact id.
46pub fn container_profile_for_artifact(id: &str) -> Option<&'static ContainerProfile> {
47    CATALOG.container_profile(id)
48}
49
50/// Returns the outer-container carving/signature guidance for a catalog artifact id.
51pub fn container_signature_for_artifact(id: &str) -> Option<&'static ContainerSignature> {
52    CATALOG.container_signature(id)
53}
54
55/// Returns record signatures associated with a catalog artifact id.
56pub fn record_signatures_for_artifact(id: &str) -> Vec<&'static RecordSignature> {
57    CATALOG.record_signatures(id)
58}
59
60// Re-export descriptor statics so crate-internal tests can reference them by name
61// (e.g. `USERASSIST_EXE`, `RUN_KEY_HKLM_RUN`). These are not part of the public
62// API -- consumers should use `CATALOG.by_id(...)` instead.
63#[cfg(test)]
64#[doc(hidden)]
65pub(crate) use descriptors::*;
66
67// ── Tests ─────────────────────────────────────────────────────────────────────
68#[cfg(test)]
69mod tests;
70
71#[cfg(test)]
72mod refactor_contract {
73
74    #[test]
75    fn artifact_facade_fns_delegate_to_catalog() {
76        for id in [
77            "userassist_exe",
78            "prefetch_file",
79            "run_key_hklm",
80            "not-a-real-id",
81        ] {
82            assert_eq!(
83                container_profile_for_artifact(id).is_some(),
84                CATALOG.container_profile(id).is_some(),
85            );
86            assert_eq!(
87                container_signature_for_artifact(id).is_some(),
88                CATALOG.container_signature(id).is_some(),
89            );
90            assert_eq!(
91                record_signatures_for_artifact(id).len(),
92                CATALOG.record_signatures(id).len(),
93            );
94        }
95    }
96    use super::*;
97
98    /// Verifies that the public API surface of the catalog module remains
99    /// accessible after the engine/data split. If this test compiles and passes,
100    /// the split has not broken any consumer-facing path.
101    #[test]
102    fn catalog_api_surface_intact() {
103        // CATALOG static and its query methods must be reachable.
104        let _ = CATALOG.by_id("userassist_exe");
105        let _ = CATALOG.for_triage();
106        let _ = CATALOG.by_mitre("T1547.001");
107        let _ = CATALOG.filter_by_keyword("prefetch");
108
109        // Key public types must be nameable without qualification issues.
110        let _: ArtifactLocation = ArtifactLocation::File;
111        let _: TriagePriority = TriagePriority::Critical;
112        let _: DataScope = DataScope::User;
113        let _: OsScope = OsScope::Win10Plus;
114        let _: HiveTarget = HiveTarget::NtUser;
115
116        // Free functions must be reachable.
117        let _ = all_container_profiles();
118        let _ = all_container_signatures();
119        let _ = all_parsing_profiles();
120        let _ = all_record_signatures();
121    }
122}