1pub mod command;
2pub mod common;
3pub mod dependency;
4pub mod dockerfile;
5pub mod error;
6pub mod hook;
7#[macro_use]
8pub mod macros;
9pub mod manifest;
10pub mod mcp;
11pub mod plugin;
12pub mod rules_dir;
13pub mod skill;
14pub mod subagent;
15pub mod walker;
16
17use crate::error::{AuditError, Result};
18use crate::rules::Finding;
19pub use error::{ScanError, ScanResult};
20use std::path::Path;
21use tracing::{debug, trace};
22
23pub use command::CommandScanner;
24pub use common::ScannerConfig;
25pub use dependency::DependencyScanner;
26pub use dockerfile::DockerScanner;
27pub use hook::HookScanner;
28pub use manifest::{ManifestScanner, scan_manifest_directory};
29pub use mcp::McpScanner;
30pub use plugin::PluginScanner;
31pub use rules_dir::RulesDirScanner;
32pub use skill::{FrontmatterParser, SkillFileFilter, SkillScanner};
33pub use subagent::SubagentScanner;
34pub use walker::{DirectoryWalker, WalkConfig};
35
36pub trait Scanner {
38 fn scan_file(&self, path: &Path) -> Result<Vec<Finding>>;
39 fn scan_directory(&self, dir: &Path) -> Result<Vec<Finding>>;
40
41 fn scan_path(&self, path: &Path) -> Result<Vec<Finding>> {
42 trace!(path = %path.display(), "Scanning path");
43
44 if !path.exists() {
45 debug!(path = %path.display(), "Path not found");
46 return Err(AuditError::FileNotFound(path.display().to_string()));
47 }
48
49 if path.is_file() {
50 trace!(path = %path.display(), "Scanning as file");
51 return self.scan_file(path);
52 }
53
54 if !path.is_dir() {
55 debug!(path = %path.display(), "Path is not a directory");
56 return Err(AuditError::NotADirectory(path.display().to_string()));
57 }
58
59 trace!(path = %path.display(), "Scanning as directory");
60 self.scan_directory(path)
61 }
62}
63
64pub trait ContentScanner: Scanner {
70 fn config(&self) -> &ScannerConfig;
72
73 fn scan_content(&self, content: &str, file_path: &str) -> Result<Vec<Finding>> {
79 Ok(self.config().check_content(content, file_path))
80 }
81}