#![allow(dead_code)]
use clamav_sys::{
cl_scan_options, CL_SCAN_DEV_COLLECT_PERFORMANCE_INFO, CL_SCAN_DEV_COLLECT_SHA,
CL_SCAN_GENERAL_ALLMATCHES, CL_SCAN_GENERAL_COLLECT_METADATA, CL_SCAN_GENERAL_HEURISTICS,
CL_SCAN_GENERAL_HEURISTIC_PRECEDENCE, CL_SCAN_GENERAL_UNPRIVILEGED, CL_SCAN_HEURISTIC_BROKEN,
CL_SCAN_HEURISTIC_ENCRYPTED_ARCHIVE, CL_SCAN_HEURISTIC_ENCRYPTED_DOC,
CL_SCAN_HEURISTIC_EXCEEDS_MAX, CL_SCAN_HEURISTIC_MACROS, CL_SCAN_HEURISTIC_PARTITION_INTXN,
CL_SCAN_HEURISTIC_PHISHING_CLOAK, CL_SCAN_HEURISTIC_PHISHING_SSL_MISMATCH,
CL_SCAN_HEURISTIC_STRUCTURED, CL_SCAN_HEURISTIC_STRUCTURED_CC,
CL_SCAN_HEURISTIC_STRUCTURED_SSN_NORMAL, CL_SCAN_HEURISTIC_STRUCTURED_SSN_STRIPPED,
CL_SCAN_MAIL_PARTIAL_MESSAGE, CL_SCAN_PARSE_ARCHIVE, CL_SCAN_PARSE_ELF, CL_SCAN_PARSE_HTML,
CL_SCAN_PARSE_HWP3, CL_SCAN_PARSE_MAIL, CL_SCAN_PARSE_OLE2, CL_SCAN_PARSE_PDF,
CL_SCAN_PARSE_PE, CL_SCAN_PARSE_SWF, CL_SCAN_PARSE_XMLDOCS,
};
use bitflags::bitflags;
bitflags! {
#[repr(C)]
pub struct GeneralFlags: u32 {
const CL_SCAN_GENERAL_ALLMATCHES = CL_SCAN_GENERAL_ALLMATCHES;
const CL_SCAN_GENERAL_COLLECT_METADATA = CL_SCAN_GENERAL_COLLECT_METADATA;
const CL_SCAN_GENERAL_HEURISTICS = CL_SCAN_GENERAL_HEURISTICS;
const CL_SCAN_GENERAL_HEURISTIC_PRECEDENCE = CL_SCAN_GENERAL_HEURISTIC_PRECEDENCE;
const CL_SCAN_GENERAL_UNPRIVILEGED = CL_SCAN_GENERAL_UNPRIVILEGED;
}
}
bitflags! {
#[repr(C)]
pub struct ParseFlags : u32 {
const CL_SCAN_PARSE_ARCHIVE = CL_SCAN_PARSE_ARCHIVE;
const CL_SCAN_PARSE_ELF = CL_SCAN_PARSE_ELF;
const CL_SCAN_PARSE_PDF = CL_SCAN_PARSE_PDF;
const CL_SCAN_PARSE_SWF = CL_SCAN_PARSE_SWF;
const CL_SCAN_PARSE_HWP3 = CL_SCAN_PARSE_HWP3;
const CL_SCAN_PARSE_XMLDOCS = CL_SCAN_PARSE_XMLDOCS;
const CL_SCAN_PARSE_MAIL = CL_SCAN_PARSE_MAIL;
const CL_SCAN_PARSE_OLE2 = CL_SCAN_PARSE_OLE2;
const CL_SCAN_PARSE_HTML = CL_SCAN_PARSE_HTML;
const CL_SCAN_PARSE_PE = CL_SCAN_PARSE_PE;
}
}
bitflags! {
#[repr(C)]
pub struct HeuristicFlags : u32 {
const CL_SCAN_HEURISTIC_BROKEN = CL_SCAN_HEURISTIC_BROKEN;
const CL_SCAN_HEURISTIC_EXCEEDS_MAX = CL_SCAN_HEURISTIC_EXCEEDS_MAX;
const CL_SCAN_HEURISTIC_PHISHING_SSL_MISMATCH = CL_SCAN_HEURISTIC_PHISHING_SSL_MISMATCH;
const CL_SCAN_HEURISTIC_PHISHING_CLOAK = CL_SCAN_HEURISTIC_PHISHING_CLOAK;
const CL_SCAN_HEURISTIC_MACROS = CL_SCAN_HEURISTIC_MACROS;
const CL_SCAN_HEURISTIC_ENCRYPTED_ARCHIVE = CL_SCAN_HEURISTIC_ENCRYPTED_ARCHIVE;
const CL_SCAN_HEURISTIC_ENCRYPTED_DOC = CL_SCAN_HEURISTIC_ENCRYPTED_DOC;
const CL_SCAN_HEURISTIC_PARTITION_INTXN = CL_SCAN_HEURISTIC_PARTITION_INTXN;
const CL_SCAN_HEURISTIC_STRUCTURED = CL_SCAN_HEURISTIC_STRUCTURED;
const CL_SCAN_HEURISTIC_STRUCTURED_SSN_NORMAL = CL_SCAN_HEURISTIC_STRUCTURED_SSN_NORMAL;
const CL_SCAN_HEURISTIC_STRUCTURED_SSN_STRIPPED = CL_SCAN_HEURISTIC_STRUCTURED_SSN_STRIPPED;
const CL_SCAN_HEURISTIC_STRUCTURED_CC = CL_SCAN_HEURISTIC_STRUCTURED_CC;
}
}
bitflags! {
#[repr(C)]
pub struct MailFlags : u32 {
const CL_SCAN_MAIL_PARTIAL_MESSAGE = CL_SCAN_MAIL_PARTIAL_MESSAGE;
}
}
bitflags! {
#[repr(C)]
pub struct DevFlags : u32 {
const CL_SCAN_DEV_COLLECT_SHA = CL_SCAN_DEV_COLLECT_SHA;
const CL_SCAN_DEV_COLLECT_PERFORMANCE_INFO = CL_SCAN_DEV_COLLECT_PERFORMANCE_INFO;
}
}
#[derive(Default)]
pub struct ScanSettings {
pub settings: cl_scan_options,
}
impl ScanSettings {
#[must_use]
pub fn general(&self) -> GeneralFlags {
GeneralFlags::from_bits(self.settings.general).unwrap_or(GeneralFlags::empty())
}
pub fn set_general(&mut self, flags: &GeneralFlags) {
self.settings.general = flags.bits();
}
#[must_use]
pub fn parse(&self) -> ParseFlags {
ParseFlags::from_bits(self.settings.parse).unwrap_or(ParseFlags::empty())
}
pub fn set_parse(&mut self, flags: &ParseFlags) {
self.settings.parse = flags.bits();
}
#[must_use]
pub fn heuristic(&self) -> HeuristicFlags {
HeuristicFlags::from_bits(self.settings.heuristic).unwrap_or(HeuristicFlags::empty())
}
pub fn set_heuristic(&mut self, flags: &HeuristicFlags) {
self.settings.heuristic = flags.bits();
}
#[must_use]
pub fn mail(&self) -> MailFlags {
MailFlags::from_bits(self.settings.mail).unwrap_or(MailFlags::empty())
}
pub fn set_mail(&mut self, flags: &MailFlags) {
self.settings.mail = flags.bits();
}
#[must_use]
pub fn dev(&self) -> DevFlags {
DevFlags::from_bits(self.settings.dev).unwrap_or(DevFlags::empty())
}
pub fn set_dev(&mut self, flags: &DevFlags) {
self.settings.dev = flags.bits();
}
}
impl ToString for ScanSettings {
#[allow(clippy::too_many_lines)]
fn to_string(&self) -> String {
let mut flag_names = Vec::<String>::new();
let general_flags = vec![
(
GeneralFlags::CL_SCAN_GENERAL_ALLMATCHES,
"CL_SCAN_GENERAL_ALLMATCHES",
),
(
GeneralFlags::CL_SCAN_GENERAL_COLLECT_METADATA,
"CL_SCAN_GENERAL_COLLECT_METADATA",
),
(
GeneralFlags::CL_SCAN_GENERAL_HEURISTICS,
"CL_SCAN_GENERAL_HEURISTICS",
),
(
GeneralFlags::CL_SCAN_GENERAL_HEURISTIC_PRECEDENCE,
"CL_SCAN_GENERAL_HEURISTIC_PRECEDENCE",
),
(
GeneralFlags::CL_SCAN_GENERAL_UNPRIVILEGED,
"CL_SCAN_GENERAL_UNPRIVILEGED",
),
];
let parse_flags = vec![
(ParseFlags::CL_SCAN_PARSE_ARCHIVE, "CL_SCAN_PARSE_ARCHIVE"),
(ParseFlags::CL_SCAN_PARSE_ELF, "CL_SCAN_PARSE_ELF"),
(ParseFlags::CL_SCAN_PARSE_PDF, "CL_SCAN_PARSE_PDF"),
(ParseFlags::CL_SCAN_PARSE_SWF, "CL_SCAN_PARSE_SWF"),
(ParseFlags::CL_SCAN_PARSE_HWP3, "CL_SCAN_PARSE_HWP3"),
(ParseFlags::CL_SCAN_PARSE_XMLDOCS, "CL_SCAN_PARSE_XMLDOCS"),
(ParseFlags::CL_SCAN_PARSE_MAIL, "CL_SCAN_PARSE_MAIL"),
(ParseFlags::CL_SCAN_PARSE_OLE2, "CL_SCAN_PARSE_OLE2"),
(ParseFlags::CL_SCAN_PARSE_HTML, "CL_SCAN_PARSE_HTML"),
(ParseFlags::CL_SCAN_PARSE_PE, "CL_SCAN_PARSE_PE"),
];
let heuristic_flags = vec![
(
HeuristicFlags::CL_SCAN_HEURISTIC_BROKEN,
"CL_SCAN_HEURISTIC_BROKEN",
),
(
HeuristicFlags::CL_SCAN_HEURISTIC_EXCEEDS_MAX,
"CL_SCAN_HEURISTIC_EXCEEDS_MAX",
),
(
HeuristicFlags::CL_SCAN_HEURISTIC_PHISHING_SSL_MISMATCH,
"CL_SCAN_HEURISTIC_PHISHING_SSL_MISMATCH",
),
(
HeuristicFlags::CL_SCAN_HEURISTIC_PHISHING_CLOAK,
"CL_SCAN_HEURISTIC_PHISHING_CLOAK",
),
(
HeuristicFlags::CL_SCAN_HEURISTIC_MACROS,
"CL_SCAN_HEURISTIC_MACROS",
),
(
HeuristicFlags::CL_SCAN_HEURISTIC_ENCRYPTED_ARCHIVE,
"CL_SCAN_HEURISTIC_ENCRYPTED_ARCHIVE",
),
(
HeuristicFlags::CL_SCAN_HEURISTIC_ENCRYPTED_DOC,
"CL_SCAN_HEURISTIC_ENCRYPTED_DOC",
),
(
HeuristicFlags::CL_SCAN_HEURISTIC_PARTITION_INTXN,
"CL_SCAN_HEURISTIC_PARTITION_INTXN",
),
(
HeuristicFlags::CL_SCAN_HEURISTIC_STRUCTURED,
"CL_SCAN_HEURISTIC_STRUCTURED",
),
(
HeuristicFlags::CL_SCAN_HEURISTIC_STRUCTURED_SSN_NORMAL,
"CL_SCAN_HEURISTIC_STRUCTURED_SSN_NORMAL",
),
(
HeuristicFlags::CL_SCAN_HEURISTIC_STRUCTURED_SSN_STRIPPED,
"CL_SCAN_HEURISTIC_STRUCTURED_SSN_STRIPPED",
),
(
HeuristicFlags::CL_SCAN_HEURISTIC_STRUCTURED_CC,
"CL_SCAN_HEURISTIC_STRUCTURED_CC",
),
];
let mail_flags = vec![(
MailFlags::CL_SCAN_MAIL_PARTIAL_MESSAGE,
"CL_SCAN_MAIL_PARTIAL_MESSAGE",
)];
let dev_flags = vec![
(DevFlags::CL_SCAN_DEV_COLLECT_SHA, "CL_SCAN_DEV_COLLECT_SHA"),
(
DevFlags::CL_SCAN_DEV_COLLECT_PERFORMANCE_INFO,
"CL_SCAN_DEV_COLLECT_PERFORMANCE_INFO",
),
];
for (flag, name) in general_flags {
if self.general().contains(flag) {
flag_names.push(name.to_string());
}
}
for (flag, name) in parse_flags {
if self.parse().contains(flag) {
flag_names.push(name.to_string());
}
}
for (flag, name) in heuristic_flags {
if self.heuristic().contains(flag) {
flag_names.push(name.to_string());
}
}
for (flag, name) in mail_flags {
if self.mail().contains(flag) {
flag_names.push(name.to_string());
}
}
for (flag, name) in dev_flags {
if self.dev().contains(flag) {
flag_names.push(name.to_string());
}
}
flag_names.join(" ")
}
}
pub struct Builder {
current: cl_scan_options,
}
impl Builder {
#[must_use]
pub fn new() -> Self {
Builder {
current: cl_scan_options::default(),
}
}
#[must_use]
pub fn build(&self) -> ScanSettings {
ScanSettings {
settings: self.current,
}
}
pub fn clear(&mut self) -> &mut Self {
self.current.parse = 0;
self
}
pub fn enable_archive(&mut self) -> &mut Self {
self.current.parse |= CL_SCAN_PARSE_ARCHIVE;
self
}
pub fn enable_mail(&mut self) -> &mut Self {
self.current.parse |= CL_SCAN_PARSE_MAIL;
self
}
pub fn enable_ole2(&mut self) -> &mut Self {
self.current.parse |= CL_SCAN_PARSE_OLE2;
self
}
pub fn block_encrypted(&mut self) -> &mut Self {
self.current.heuristic |= CL_SCAN_HEURISTIC_ENCRYPTED_ARCHIVE;
self
}
pub fn enable_html(&mut self) -> &mut Self {
self.current.parse |= CL_SCAN_PARSE_HTML;
self
}
pub fn enable_pe(&mut self) -> &mut Self {
self.current.parse |= CL_SCAN_PARSE_PE;
self
}
pub fn block_broken_executables(&mut self) -> &mut Self {
self.current.heuristic |= CL_SCAN_HEURISTIC_BROKEN;
self
}
pub fn block_max_limit(&mut self) -> &mut Self {
self.current.heuristic |= CL_SCAN_HEURISTIC_EXCEEDS_MAX;
self
}
pub fn enable_phishing_blockssl(&mut self) -> &mut Self {
self.current.heuristic |= CL_SCAN_HEURISTIC_PHISHING_SSL_MISMATCH;
self
}
pub fn enable_phishing_blockcloak(&mut self) -> &mut Self {
self.current.heuristic |= CL_SCAN_HEURISTIC_PHISHING_CLOAK;
self
}
pub fn enable_elf(&mut self) -> &mut Self {
self.current.parse |= CL_SCAN_PARSE_ELF;
self
}
pub fn enable_pdf(&mut self) -> &mut Self {
self.current.parse |= CL_SCAN_PARSE_PDF;
self
}
pub fn enable_structured(&mut self) -> &mut Self {
self.current.heuristic |= CL_SCAN_HEURISTIC_STRUCTURED;
self
}
pub fn enable_structured_ssn_normal(&mut self) -> &mut Self {
self.current.heuristic |= CL_SCAN_HEURISTIC_STRUCTURED_SSN_NORMAL;
self
}
pub fn enable_structured_ssn_stripped(&mut self) -> &mut Self {
self.current.heuristic |= CL_SCAN_HEURISTIC_STRUCTURED_SSN_STRIPPED;
self
}
pub fn enable_partial_message(&mut self) -> &mut Self {
self.current.mail |= CL_SCAN_MAIL_PARTIAL_MESSAGE;
self
}
pub fn enable_heuristic_precedence(&mut self) -> &mut Self {
self.current.general |= CL_SCAN_GENERAL_HEURISTIC_PRECEDENCE;
self
}
pub fn block_macros(&mut self) -> &mut Self {
self.current.heuristic |= CL_SCAN_HEURISTIC_MACROS;
self
}
pub fn enable_swf(&mut self) -> &mut Self {
self.current.parse |= CL_SCAN_PARSE_SWF;
self
}
pub fn enable_xmldocs(&mut self) -> &mut Self {
self.current.parse |= CL_SCAN_PARSE_XMLDOCS;
self
}
pub fn enable_hwp3(&mut self) -> &mut Self {
self.current.parse |= CL_SCAN_PARSE_HWP3;
self
}
}
impl Default for Builder {
fn default() -> Self {
Self::new()
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn builder_defaults_to_standard_opts() {
let settings = Builder::new().build();
assert_eq!(settings.settings, clamav_sys::cl_scan_options::default());
}
#[test]
fn builder_clear_success() {
let settings = Builder::new().clear().build();
assert_eq!(settings.settings.general, 0);
assert_eq!(settings.settings.parse, 0);
assert_eq!(settings.settings.heuristic, 0);
assert_eq!(settings.settings.mail, 0);
assert_eq!(settings.settings.dev, 0);
}
#[test]
fn builder_just_pdf_success() {
let settings = Builder::new().clear().enable_pdf().build();
assert_eq!(settings.settings.parse, CL_SCAN_PARSE_PDF);
}
#[test]
fn builder_normal_files_success() {
let settings = Builder::new()
.clear()
.enable_pdf()
.enable_html()
.enable_pe()
.build();
assert_eq!(
settings.settings.parse,
CL_SCAN_PARSE_PDF | CL_SCAN_PARSE_HTML | CL_SCAN_PARSE_PE
);
}
#[test]
fn display_settings_standard_options_success() {
let string_settings = ScanSettings::default().to_string();
assert!(string_settings.contains("CL_SCAN_PARSE_ARCHIVE"));
assert!(string_settings.contains("CL_SCAN_PARSE_MAIL"));
assert!(string_settings.contains("CL_SCAN_PARSE_OLE2"));
assert!(string_settings.contains("CL_SCAN_PARSE_PDF"));
assert!(string_settings.contains("CL_SCAN_PARSE_HTML"));
assert!(string_settings.contains("CL_SCAN_PARSE_PE"));
assert!(string_settings.contains("CL_SCAN_PARSE_ELF"));
assert!(string_settings.contains("CL_SCAN_PARSE_SWF"));
assert!(string_settings.contains("CL_SCAN_PARSE_XMLDOCS"));
}
#[test]
fn settings_default_to_standard() {
let settings: ScanSettings = ScanSettings::default();
assert_eq!(settings.settings, cl_scan_options::default());
}
}