pub mod cascade;
pub mod commands;
mod generators;
pub mod scoring;
use std::path::PathBuf;
use serde::Serialize;
use crate::checks::{CheckReport, CoreLayer, LayerResult};
use crate::operation::MetadataParams;
use crate::state::SystemState;
#[derive(Debug, Clone, PartialEq, Eq, Hash, Serialize)]
pub struct Fix {
pub layer: CoreLayer,
pub action: FixAction,
pub impact: u8,
pub description: String,
}
#[derive(Debug, Clone, PartialEq, Eq, Hash, Serialize)]
#[non_exhaustive]
pub enum FixAction {
Chmod {
path: PathBuf,
mode_change: String,
},
Chown {
path: PathBuf,
owner: Option<u32>,
group: Option<u32>,
},
SetAcl {
path: PathBuf,
entry: String,
},
Remount {
mountpoint: PathBuf,
options: String,
},
Chattr {
path: PathBuf,
flags: String,
},
GrantCap {
path: Option<PathBuf>,
capability: String,
},
}
#[derive(Debug, Clone, PartialEq, Eq, Serialize)]
pub struct FixPlan {
pub fixes: Vec<Fix>,
pub warnings: Vec<String>,
}
#[must_use]
pub fn generate_fixes(
report: &CheckReport,
state: &SystemState,
params: &MetadataParams,
) -> FixPlan {
let mut fixes = Vec::new();
let mut warnings = Vec::new();
collect_mount_fixes(report, state, &mut fixes, &mut warnings);
collect_fsflags_fixes(report, state, &mut fixes, &mut warnings);
collect_traversal_fixes(report, state, &mut fixes, &mut warnings);
collect_dac_fixes(report, state, &mut fixes, &mut warnings);
collect_acl_fixes(report, state, &mut fixes, &mut warnings);
collect_metadata_fixes(report, state, params, &mut fixes, &mut warnings);
cascade::simulate_cascade(fixes, warnings, state)
}
fn collect_mount_fixes(
report: &CheckReport,
state: &SystemState,
fixes: &mut Vec<Fix>,
warnings: &mut Vec<String>,
) {
if let LayerResult::Fail { .. } = &report.core_results[CoreLayer::Mount] {
let new_fixes = generators::mount_fixes(state);
for fix in &new_fixes {
if scoring::needs_warning(fix.impact) {
warnings.push(format!("{}: affects entire filesystem", fix.description));
}
}
fixes.extend(new_fixes);
}
}
fn collect_fsflags_fixes(
report: &CheckReport,
state: &SystemState,
fixes: &mut Vec<Fix>,
warnings: &mut Vec<String>,
) {
if let LayerResult::Fail { .. } = &report.core_results[CoreLayer::FsFlags] {
let new_fixes = generators::fsflags_fixes(state);
for fix in &new_fixes {
if scoring::needs_warning(fix.impact) {
warnings.push(format!(
"{}: removes filesystem protection",
fix.description
));
}
}
fixes.extend(new_fixes);
}
}
fn collect_traversal_fixes(
report: &CheckReport,
state: &SystemState,
fixes: &mut Vec<Fix>,
_warnings: &mut [String],
) {
if let LayerResult::Fail {
component_index: Some(idx),
..
} = &report.core_results[CoreLayer::Traversal]
{
fixes.extend(generators::traversal_fixes(state, *idx));
}
}
fn collect_dac_fixes(
report: &CheckReport,
state: &SystemState,
fixes: &mut Vec<Fix>,
_warnings: &mut [String],
) {
if let LayerResult::Fail { .. } = &report.core_results[CoreLayer::Dac] {
fixes.extend(generators::dac_fixes(state));
}
}
fn collect_acl_fixes(
report: &CheckReport,
state: &SystemState,
fixes: &mut Vec<Fix>,
_warnings: &mut [String],
) {
if let LayerResult::Fail { .. } = &report.core_results[CoreLayer::Acl] {
fixes.extend(generators::acl_fixes(state));
}
}
fn collect_metadata_fixes(
report: &CheckReport,
state: &SystemState,
params: &MetadataParams,
fixes: &mut Vec<Fix>,
warnings: &mut Vec<String>,
) {
if let LayerResult::Fail { .. } = &report.core_results[CoreLayer::Metadata] {
let new_fixes = generators::metadata_fixes(state, params);
for fix in &new_fixes {
if scoring::needs_warning(fix.impact) {
warnings.push(format!(
"{}: high-privilege capability grant",
fix.description
));
}
}
fixes.extend(new_fixes);
}
}
#[cfg(test)]
#[path = "mod_tests.rs"]
mod tests;
#[cfg(test)]
#[path = "mod_scenario_tests.rs"]
mod scenario_tests;
#[cfg(test)]
#[path = "generators_tests.rs"]
mod generators_tests;
#[cfg(test)]
#[path = "generators_dac_tests.rs"]
mod generators_dac_tests;
#[cfg(test)]
#[path = "generators_metadata_tests.rs"]
mod generators_metadata_tests;