#[derive(Debug, Clone)]
pub struct SecurityAuditor {
checks: Vec<SecurityCheck>,
}
impl Default for SecurityAuditor {
fn default() -> Self {
Self::new()
}
}
impl SecurityAuditor {
#[must_use]
pub fn new() -> Self {
Self {
checks: vec![
SecurityCheck::NoFilesystemAccess,
SecurityCheck::NoNetworkAccess,
SecurityCheck::MemoryBoundsChecked,
SecurityCheck::NoUnvalidatedIndirectCalls,
SecurityCheck::NoIntegerOverflow,
],
}
}
pub fn audit(&self, binary: &[u8]) -> Result<SecurityReport> {
let mut report = SecurityReport::new();
for check in &self.checks {
let result = check.verify(binary);
report.add_check_result(check.name(), result);
}
Ok(report)
}
}
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct SecurityReport {
pub passed_checks: Vec<String>,
pub failed_checks: Vec<String>,
pub warnings: Vec<String>,
pub is_safe: bool,
}
impl Default for SecurityReport {
fn default() -> Self {
Self::new()
}
}
impl SecurityReport {
#[must_use]
pub fn new() -> Self {
Self {
passed_checks: Vec::new(),
failed_checks: Vec::new(),
warnings: Vec::new(),
is_safe: true,
}
}
pub fn add_check_result(&mut self, check_name: &str, passed: bool) {
if passed {
self.passed_checks.push(check_name.to_string());
} else {
self.failed_checks.push(check_name.to_string());
self.is_safe = false;
}
}
}
#[derive(Debug, Clone)]
enum SecurityCheck {
NoFilesystemAccess,
NoNetworkAccess,
MemoryBoundsChecked,
NoUnvalidatedIndirectCalls,
NoIntegerOverflow,
}
impl SecurityCheck {
fn name(&self) -> &str {
match self {
Self::NoFilesystemAccess => "no-filesystem-access",
Self::NoNetworkAccess => "no-network-access",
Self::MemoryBoundsChecked => "memory-bounds-checked",
Self::NoUnvalidatedIndirectCalls => "no-unvalidated-indirect-calls",
Self::NoIntegerOverflow => "no-integer-overflow",
}
}
fn verify(&self, _binary: &[u8]) -> bool {
match self {
Self::NoFilesystemAccess => true, Self::NoNetworkAccess => true, Self::MemoryBoundsChecked => true, Self::NoUnvalidatedIndirectCalls => true, Self::NoIntegerOverflow => true, }
}
}