#[derive(Debug, Clone, Copy, PartialEq, Eq)]
#[non_exhaustive]
#[cfg_attr(feature = "serde", derive(serde::Serialize))]
pub struct ForensicArtifactsRef {
pub artifact_id: &'static str,
pub fa_name: &'static str,
pub source_type: &'static str,
}
pub static FA_TABLE: &[ForensicArtifactsRef] = &[
ForensicArtifactsRef {
artifact_id: "prefetch_dir",
fa_name: "WindowsPrefetch",
source_type: "FILE",
},
ForensicArtifactsRef {
artifact_id: "evtx_security",
fa_name: "WindowsEventLogs",
source_type: "FILE",
},
ForensicArtifactsRef {
artifact_id: "evtx_system",
fa_name: "WindowsEventLogs",
source_type: "FILE",
},
ForensicArtifactsRef {
artifact_id: "amcache_app_file",
fa_name: "WindowsAmcache",
source_type: "FILE",
},
ForensicArtifactsRef {
artifact_id: "shimcache",
fa_name: "WindowsAppCompatCache",
source_type: "REGISTRY_KEY",
},
ForensicArtifactsRef {
artifact_id: "userassist_exe",
fa_name: "WindowsUserAssist",
source_type: "REGISTRY_KEY",
},
ForensicArtifactsRef {
artifact_id: "lnk_files",
fa_name: "WindowsRecentFiles",
source_type: "FILE",
},
ForensicArtifactsRef {
artifact_id: "mft_file",
fa_name: "WindowsNTFSMFT",
source_type: "FILE",
},
ForensicArtifactsRef {
artifact_id: "ntds_dit",
fa_name: "WindowsNTDSDatabase",
source_type: "FILE",
},
ForensicArtifactsRef {
artifact_id: "sam_users",
fa_name: "WindowsSAMDatabase",
source_type: "FILE",
},
ForensicArtifactsRef {
artifact_id: "run_key_hklm",
fa_name: "WindowsRunKeys",
source_type: "REGISTRY_KEY",
},
ForensicArtifactsRef {
artifact_id: "scheduled_tasks_dir",
fa_name: "WindowsScheduledTasks",
source_type: "FILE",
},
ForensicArtifactsRef {
artifact_id: "linux_bash_history",
fa_name: "LinuxShellHistoryFile",
source_type: "FILE",
},
ForensicArtifactsRef {
artifact_id: "linux_passwd",
fa_name: "UnixPasswd",
source_type: "FILE",
},
ForensicArtifactsRef {
artifact_id: "linux_auth_log",
fa_name: "LinuxAuthLog",
source_type: "FILE",
},
ForensicArtifactsRef {
artifact_id: "macos_launch_agents_user",
fa_name: "MacOSLaunchAgentsPlistFiles",
source_type: "FILE",
},
ForensicArtifactsRef {
artifact_id: "macos_launch_daemons",
fa_name: "MacOSLaunchDaemonsPlistFiles",
source_type: "FILE",
},
];
pub fn fa_ref_for(artifact_id: &str) -> Option<&'static ForensicArtifactsRef> {
FA_TABLE.iter().find(|r| r.artifact_id == artifact_id)
}
pub fn to_fa_yaml(artifact_id: &str) -> Option<String> {
use crate::catalog::CATALOG;
let fa_ref = fa_ref_for(artifact_id)?;
let desc = CATALOG.by_id(artifact_id)?;
let path = desc
.file_path
.map(str::to_string)
.unwrap_or_else(|| desc.key_path.to_string());
Some(format!(
"name: {fa_name}\ndoc: {doc}.\nsources:\n- type: {source_type}\n attributes:\n paths:\n - '{path}'\nlabels:\n- forensic_artifact\nurls:\n- https://forensicartifacts.com/\n",
fa_name = fa_ref.fa_name,
doc = desc.name,
source_type = fa_ref.source_type,
path = path,
))
}
pub fn mapped_artifact_ids() -> Vec<&'static str> {
FA_TABLE.iter().map(|r| r.artifact_id).collect()
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn table_nonempty() {
assert!(!FA_TABLE.is_empty());
}
#[test]
fn prefetch_has_fa_ref() {
let r = fa_ref_for("prefetch_dir").expect("prefetch_dir should have FA ref");
assert_eq!(r.fa_name, "WindowsPrefetch");
}
#[test]
fn unknown_returns_none() {
assert!(fa_ref_for("nonexistent").is_none());
}
#[test]
fn to_fa_yaml_produces_valid_structure() {
let yaml = to_fa_yaml("prefetch_dir").expect("should produce YAML");
assert!(yaml.contains("name:"), "YAML should contain name field");
assert!(
yaml.contains("doc:") || yaml.contains("sources:"),
"YAML should have doc or sources"
);
}
#[test]
fn mapped_ids_nonempty() {
let ids = mapped_artifact_ids();
assert!(ids.len() >= 10);
}
#[test]
fn all_fa_artifact_ids_valid() {
use crate::catalog::CATALOG;
let catalog_ids: std::collections::HashSet<&str> =
CATALOG.list().iter().map(|d| d.id).collect();
for r in FA_TABLE {
assert!(
catalog_ids.contains(r.artifact_id),
"Unknown artifact_id: {}",
r.artifact_id
);
}
}
}