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}