#![deny(unsafe_code)]
#![warn(missing_docs)]
pub mod classify;
pub mod extract;
pub mod from_file;
pub mod regex_classifier;
pub mod yara_classifier;
pub mod yara_scanner;
#[derive(Debug, Clone)]
pub struct ClassifiedString {
pub value: String,
pub physical_offset: u64,
pub encoding: StringEncoding,
pub categories: Vec<(StringCategory, f32)>,
}
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub enum StringEncoding {
Ascii,
Utf8,
Utf16Le,
}
#[derive(Debug, Clone, PartialEq)]
pub enum StringCategory {
Url,
IpV4,
IpV6,
Email,
UnixPath,
WindowsPath,
RegistryKey,
DomainName,
CryptoAddress,
PrivateKey,
Base64Blob,
ShellCommand,
YaraMatch(String),
}
#[derive(Debug, thiserror::Error)]
pub enum Error {
#[error("I/O error: {0}")]
Io(#[from] std::io::Error),
#[error("format error: {0}")]
Format(#[from] memf_format::Error),
#[error("YARA error: {0}")]
Yara(String),
}
pub type Result<T> = std::result::Result<T, Error>;
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn classified_string_basic() {
let cs = ClassifiedString {
value: "https://example.com".into(),
physical_offset: 0x1234,
encoding: StringEncoding::Ascii,
categories: vec![(StringCategory::Url, 0.95)],
};
assert_eq!(cs.value, "https://example.com");
assert_eq!(cs.physical_offset, 0x1234);
assert_eq!(cs.categories.len(), 1);
}
#[test]
fn error_from_io() {
let io_err = std::io::Error::new(std::io::ErrorKind::NotFound, "missing");
let err: Error = Error::from(io_err);
assert!(matches!(err, Error::Io(_)));
assert!(err.to_string().contains("missing"));
}
#[test]
fn error_from_format() {
let fmt_err = memf_format::Error::UnknownFormat;
let err: Error = Error::from(fmt_err);
assert!(matches!(err, Error::Format(_)));
assert!(err.to_string().contains("unknown dump format"));
}
#[test]
fn error_yara_display() {
let err = Error::Yara("compilation failed".into());
assert!(err.to_string().contains("compilation failed"));
}
#[test]
fn string_encoding_variants() {
assert_ne!(StringEncoding::Ascii, StringEncoding::Utf16Le);
assert_ne!(StringEncoding::Ascii, StringEncoding::Utf8);
assert_ne!(StringEncoding::Utf8, StringEncoding::Utf16Le);
}
#[test]
fn string_category_equality() {
assert_eq!(StringCategory::Url, StringCategory::Url);
assert_ne!(StringCategory::Url, StringCategory::Email);
assert_eq!(
StringCategory::YaraMatch("test".into()),
StringCategory::YaraMatch("test".into())
);
assert_ne!(
StringCategory::YaraMatch("a".into()),
StringCategory::YaraMatch("b".into())
);
}
}