Fix all clippy lints and pass CI checks

- Box large enum variants in EventData to reduce memory footprint
- Add Default trait implementations for types with new() methods
- Replace or_insert_with(Vec::new) with or_default()
- Convert vec init+push patterns to vec! macro
- Fix field reassignment with default initialization
- Convert match to if for simple equality checks
- Remove unused Backend type parameters from TUI draw functions
- Apply rustfmt formatting

All tests passing (24 total). Zero clippy warnings. Ready for CI/CD.
This commit is contained in:
pandaadir05
2025-11-20 15:13:16 +02:00
parent 27dffbd108
commit 655585d9ef
7 changed files with 187 additions and 166 deletions

View File

@@ -75,11 +75,11 @@ pub enum EventSeverity {
#[derive(Debug, Clone, Serialize, Deserialize)]
pub enum EventData {
Detection(DetectionEventData),
ThreatIntel(ThreatIntelEventData),
Detection(Box<DetectionEventData>),
ThreatIntel(Box<ThreatIntelEventData>),
Evasion(EvasionEventData),
System(SystemEventData),
Alert(AlertEventData),
Alert(Box<AlertEventData>),
Performance(PerformanceEventData),
}
@@ -559,11 +559,11 @@ impl EventStreamingSystem {
ThreatLevel::Suspicious => EventSeverity::Medium,
ThreatLevel::Malicious => EventSeverity::High,
},
data: EventData::Detection(DetectionEventData {
data: EventData::Detection(Box::new(DetectionEventData {
detection_result: detection,
analysis_duration: Duration::from_millis(100), // Would be actual duration
confidence_threshold: 0.7,
}),
})),
correlation_id: None,
tags: vec!["process-injection".to_string(), "detection".to_string()],
};

View File

@@ -398,80 +398,78 @@ impl TestFramework {
/// Create detection engine unit tests
fn create_detection_engine_tests(&mut self) {
let mut test_cases = Vec::new();
// Test clean process detection
test_cases.push(TestCase {
name: "clean_process_detection".to_string(),
description: "Verify clean processes are not flagged".to_string(),
test_function: TestFunction::DetectionTest(DetectionTestParams {
process_data: ProcessTestData {
name: "notepad.exe".to_string(),
pid: 1234,
path: Some("C:\\Windows\\System32\\notepad.exe".to_string()),
thread_count: 1,
suspicious_indicators: Vec::new(),
},
memory_data: vec![MemoryTestData {
base_address: 0x400000,
size: 0x10000,
protection: MemoryProtection::ReadExecute,
contains_shellcode: false,
shellcode_pattern: None,
}],
thread_data: vec![ThreadTestData {
tid: 5678,
entry_point: 0x401000,
stack_base: 0x500000,
stack_size: 0x10000,
is_suspicious: false,
}],
injection_type: None,
}),
expected_result: ExpectedResult::ThreatLevel(ThreatLevel::Clean),
timeout: Duration::from_secs(5),
tags: vec!["unit".to_string(), "detection".to_string()],
});
// Test malicious process detection
test_cases.push(TestCase {
name: "malicious_process_detection".to_string(),
description: "Verify malicious processes are properly detected".to_string(),
test_function: TestFunction::DetectionTest(DetectionTestParams {
process_data: ProcessTestData {
name: "malware.exe".to_string(),
pid: 9999,
path: Some("C:\\Temp\\malware.exe".to_string()),
thread_count: 5,
suspicious_indicators: vec![
"High RWX memory usage".to_string(),
"Suspicious API calls".to_string(),
],
},
memory_data: vec![MemoryTestData {
base_address: 0x200000,
size: 0x1000,
protection: MemoryProtection::ReadWriteExecute,
contains_shellcode: true,
shellcode_pattern: Some(vec![0x90, 0x90, 0xEB, 0xFE]), // NOP NOP JMP -2
}],
thread_data: vec![ThreadTestData {
tid: 1111,
entry_point: 0x200000,
stack_base: 0x600000,
stack_size: 0x10000,
is_suspicious: true,
}],
injection_type: Some(InjectionTestType::ShellcodeInjection),
}),
expected_result: ExpectedResult::ThreatLevel(ThreatLevel::Malicious),
timeout: Duration::from_secs(10),
tags: vec![
"unit".to_string(),
"detection".to_string(),
"malware".to_string(),
],
});
let test_cases = vec![
TestCase {
name: "clean_process_detection".to_string(),
description: "Verify clean processes are not flagged".to_string(),
test_function: TestFunction::DetectionTest(DetectionTestParams {
process_data: ProcessTestData {
name: "notepad.exe".to_string(),
pid: 1234,
path: Some("C:\\Windows\\System32\\notepad.exe".to_string()),
thread_count: 1,
suspicious_indicators: Vec::new(),
},
memory_data: vec![MemoryTestData {
base_address: 0x400000,
size: 0x10000,
protection: MemoryProtection::ReadExecute,
contains_shellcode: false,
shellcode_pattern: None,
}],
thread_data: vec![ThreadTestData {
tid: 5678,
entry_point: 0x401000,
stack_base: 0x500000,
stack_size: 0x10000,
is_suspicious: false,
}],
injection_type: None,
}),
expected_result: ExpectedResult::ThreatLevel(ThreatLevel::Clean),
timeout: Duration::from_secs(5),
tags: vec!["unit".to_string(), "detection".to_string()],
},
// Test malicious process detection
TestCase {
name: "malicious_process_detection".to_string(),
description: "Verify malicious processes are properly detected".to_string(),
test_function: TestFunction::DetectionTest(DetectionTestParams {
process_data: ProcessTestData {
name: "malware.exe".to_string(),
pid: 9999,
path: Some("C:\\Temp\\malware.exe".to_string()),
thread_count: 5,
suspicious_indicators: vec![
"High RWX memory usage".to_string(),
"Suspicious API calls".to_string(),
],
},
memory_data: vec![MemoryTestData {
base_address: 0x200000,
size: 0x1000,
protection: MemoryProtection::ReadWriteExecute,
contains_shellcode: true,
shellcode_pattern: Some(vec![0x90, 0x90, 0xEB, 0xFE]), // NOP NOP JMP -2
}],
thread_data: vec![ThreadTestData {
tid: 1111,
entry_point: 0x200000,
stack_base: 0x600000,
stack_size: 0x10000,
is_suspicious: true,
}],
injection_type: Some(InjectionTestType::ShellcodeInjection),
}),
expected_result: ExpectedResult::ThreatLevel(ThreatLevel::Malicious),
timeout: Duration::from_secs(10),
tags: vec![
"unit".to_string(),
"detection".to_string(),
"malware".to_string(),
],
},
];
let test_suite = TestSuite {
name: "detection_engine_tests".to_string(),
@@ -487,10 +485,7 @@ impl TestFramework {
/// Create shellcode detection tests
fn create_shellcode_detection_tests(&mut self) {
let mut test_cases = Vec::new();
// Test common shellcode patterns
test_cases.push(TestCase {
let test_cases = vec![TestCase {
name: "common_shellcode_patterns".to_string(),
description: "Detect common shellcode patterns".to_string(),
test_function: TestFunction::DetectionTest(DetectionTestParams {
@@ -519,7 +514,7 @@ impl TestFramework {
expected_result: ExpectedResult::IndicatorPresent("Shellcode detected".to_string()),
timeout: Duration::from_secs(5),
tags: vec!["unit".to_string(), "shellcode".to_string()],
});
}];
let test_suite = TestSuite {
name: "shellcode_detection_tests".to_string(),
@@ -535,9 +530,7 @@ impl TestFramework {
/// Create process hollowing detection tests
fn create_process_hollowing_tests(&mut self) {
let mut test_cases = Vec::new();
test_cases.push(TestCase {
let test_cases = vec![TestCase {
name: "process_hollowing_detection".to_string(),
description: "Detect process hollowing techniques".to_string(),
test_function: TestFunction::DetectionTest(DetectionTestParams {
@@ -564,7 +557,7 @@ impl TestFramework {
expected_result: ExpectedResult::IndicatorPresent("Process hollowing".to_string()),
timeout: Duration::from_secs(10),
tags: vec!["unit".to_string(), "hollowing".to_string()],
});
}];
let test_suite = TestSuite {
name: "process_hollowing_tests".to_string(),
@@ -580,9 +573,7 @@ impl TestFramework {
/// Create evasion detection tests
fn create_evasion_detection_tests(&mut self) {
let mut test_cases = Vec::new();
test_cases.push(TestCase {
let test_cases = vec![TestCase {
name: "anti_debug_detection".to_string(),
description: "Detect anti-debugging techniques".to_string(),
test_function: TestFunction::DetectionTest(DetectionTestParams {
@@ -603,7 +594,7 @@ impl TestFramework {
expected_result: ExpectedResult::IndicatorPresent("Evasion technique".to_string()),
timeout: Duration::from_secs(15),
tags: vec!["unit".to_string(), "evasion".to_string()],
});
}];
let test_suite = TestSuite {
name: "evasion_detection_tests".to_string(),
@@ -625,39 +616,38 @@ impl TestFramework {
/// Create performance benchmark tests
fn create_performance_tests(&mut self) {
let mut benchmarks = Vec::new();
benchmarks.push(Benchmark {
name: "single_process_analysis".to_string(),
description: "Benchmark single process analysis performance".to_string(),
benchmark_function: BenchmarkFunction::ProcessAnalysis(ProcessAnalysisBenchmark {
process_count: 1,
complexity_level: ComplexityLevel::Moderate,
}),
warm_up_iterations: 10,
measurement_iterations: 100,
target_metrics: vec![
PerformanceMetric::ExecutionTime,
PerformanceMetric::MemoryUsage,
PerformanceMetric::CPUUtilization,
],
});
benchmarks.push(Benchmark {
name: "bulk_process_analysis".to_string(),
description: "Benchmark bulk process analysis performance".to_string(),
benchmark_function: BenchmarkFunction::ProcessAnalysis(ProcessAnalysisBenchmark {
process_count: 100,
complexity_level: ComplexityLevel::Simple,
}),
warm_up_iterations: 5,
measurement_iterations: 20,
target_metrics: vec![
PerformanceMetric::ThroughputRate,
PerformanceMetric::LatencyP95,
PerformanceMetric::MemoryUsage,
],
});
let benchmarks = vec![
Benchmark {
name: "single_process_analysis".to_string(),
description: "Benchmark single process analysis performance".to_string(),
benchmark_function: BenchmarkFunction::ProcessAnalysis(ProcessAnalysisBenchmark {
process_count: 1,
complexity_level: ComplexityLevel::Moderate,
}),
warm_up_iterations: 10,
measurement_iterations: 100,
target_metrics: vec![
PerformanceMetric::ExecutionTime,
PerformanceMetric::MemoryUsage,
PerformanceMetric::CPUUtilization,
],
},
Benchmark {
name: "bulk_process_analysis".to_string(),
description: "Benchmark bulk process analysis performance".to_string(),
benchmark_function: BenchmarkFunction::ProcessAnalysis(ProcessAnalysisBenchmark {
process_count: 100,
complexity_level: ComplexityLevel::Simple,
}),
warm_up_iterations: 5,
measurement_iterations: 20,
target_metrics: vec![
PerformanceMetric::ThroughputRate,
PerformanceMetric::LatencyP95,
PerformanceMetric::MemoryUsage,
],
},
];
let benchmark_suite = BenchmarkSuite {
name: "performance_benchmarks".to_string(),

View File

@@ -296,6 +296,12 @@ pub struct CertificateInfo {
pub valid_to: SystemTime,
}
impl Default for ThreatIntelligence {
fn default() -> Self {
Self::new()
}
}
impl ThreatIntelligence {
pub fn new() -> Self {
Self {
@@ -551,6 +557,12 @@ impl ThreatIntelligence {
}
}
impl Default for IocDatabase {
fn default() -> Self {
Self::new()
}
}
impl IocDatabase {
pub fn new() -> Self {
Self {
@@ -570,13 +582,13 @@ impl IocDatabase {
IocType::FileHash => {
self.hash_index
.entry(ioc.value.clone())
.or_insert_with(Vec::new)
.or_default()
.push(ioc.id.clone());
}
IocType::MemorySignature | IocType::BehaviorPattern => {
self.pattern_index
.entry(ioc.value.clone())
.or_insert_with(Vec::new)
.or_default()
.push(ioc.id.clone());
}
_ => {}
@@ -637,6 +649,12 @@ impl IocDatabase {
}
}
impl Default for AttributionEngine {
fn default() -> Self {
Self::new()
}
}
impl AttributionEngine {
pub fn new() -> Self {
Self {
@@ -725,6 +743,12 @@ impl AttributionEngine {
}
}
impl Default for SimilarityCalculator {
fn default() -> Self {
Self::new()
}
}
impl SimilarityCalculator {
pub fn new() -> Self {
Self {
@@ -735,6 +759,12 @@ impl SimilarityCalculator {
}
}
impl Default for ReputationCache {
fn default() -> Self {
Self::new()
}
}
impl ReputationCache {
pub fn new() -> Self {
Self {

View File

@@ -139,8 +139,10 @@ mod tests {
let config = DetectionConfig::default();
assert!(config.validate().is_ok());
let mut invalid_config = DetectionConfig::default();
invalid_config.confidence_threshold = 1.5; // Invalid
let mut invalid_config = DetectionConfig {
confidence_threshold: 1.5, // Invalid
..Default::default()
};
assert!(invalid_config.validate().is_err());
invalid_config.confidence_threshold = -0.1; // Invalid
@@ -165,8 +167,10 @@ mod tests {
#[test]
fn test_engine_with_custom_config() {
let mut config = DetectionConfig::default();
config.hook_detection = false;
let config = DetectionConfig {
hook_detection: false,
..Default::default()
};
let mut engine =
DetectionEngine::with_config(Some(config)).expect("Failed to create engine");

View File

@@ -379,19 +379,16 @@ impl App {
}
pub fn select_item(&mut self) {
match self.current_tab {
TabIndex::Processes => {
if let Some(i) = self.processes_state.selected() {
if let Some(process) = self.processes.get(i) {
self.selected_process = Some(process.clone());
self.add_log_message(format!(
"Selected process: {} (PID: {})",
process.name, process.pid
));
}
if self.current_tab == TabIndex::Processes {
if let Some(i) = self.processes_state.selected() {
if let Some(process) = self.processes.get(i) {
self.selected_process = Some(process.clone());
self.add_log_message(format!(
"Selected process: {} (PID: {})",
process.name, process.pid
));
}
}
_ => {}
}
}

View File

@@ -69,7 +69,7 @@ async fn run_app<B: Backend>(terminal: &mut Terminal<B>, app: Arc<Mutex<App>>) -
// Draw the UI
terminal.draw(|f| {
if let Ok(app) = app.try_lock() {
ui::draw::<CrosstermBackend<std::io::Stdout>>(f, &app);
ui::draw(f, &app);
}
})?;

View File

@@ -32,7 +32,7 @@ mod colors {
use colors::*;
pub fn draw<B: Backend>(f: &mut Frame, app: &App) {
pub fn draw(f: &mut Frame, app: &App) {
let size = f.size();
// Create main layout
@@ -46,23 +46,23 @@ pub fn draw<B: Backend>(f: &mut Frame, app: &App) {
.split(size);
// Draw header
draw_header::<B>(f, chunks[0], app);
draw_header(f, chunks[0], app);
// Draw main content based on selected tab
match app.current_tab {
TabIndex::Overview => draw_overview::<B>(f, chunks[1], app),
TabIndex::Processes => draw_processes::<B>(f, chunks[1], app),
TabIndex::Detections => draw_detections::<B>(f, chunks[1], app),
TabIndex::Memory => draw_memory::<B>(f, chunks[1], app),
TabIndex::Logs => draw_logs::<B>(f, chunks[1], app),
TabIndex::Overview => draw_overview(f, chunks[1], app),
TabIndex::Processes => draw_processes(f, chunks[1], app),
TabIndex::Detections => draw_detections(f, chunks[1], app),
TabIndex::Memory => draw_memory(f, chunks[1], app),
TabIndex::Logs => draw_logs(f, chunks[1], app),
TabIndex::ThreatIntel => {} // TODO: Implement threat intel view
}
// Draw footer
draw_footer::<B>(f, chunks[2], app);
draw_footer(f, chunks[2], app);
}
fn draw_header<B: Backend>(f: &mut Frame, area: Rect, app: &App) {
fn draw_header(f: &mut Frame, area: Rect, app: &App) {
let titles = app.get_tab_titles();
let tabs = Tabs::new(titles)
.block(
@@ -84,7 +84,7 @@ fn draw_header<B: Backend>(f: &mut Frame, area: Rect, app: &App) {
f.render_widget(tabs, area);
}
fn draw_footer<B: Backend>(f: &mut Frame, area: Rect, app: &App) {
fn draw_footer(f: &mut Frame, area: Rect, app: &App) {
let help_text = match app.current_tab {
TabIndex::Overview => {
"Up/Down: Navigate | Tab: Switch tabs | R: Refresh | C: Clear | Q: Quit"
@@ -112,7 +112,7 @@ fn draw_footer<B: Backend>(f: &mut Frame, area: Rect, app: &App) {
f.render_widget(footer, area);
}
fn draw_overview<B: Backend>(f: &mut Frame, area: Rect, app: &App) {
fn draw_overview(f: &mut Frame, area: Rect, app: &App) {
let chunks = Layout::default()
.direction(Direction::Vertical)
.constraints([
@@ -123,16 +123,16 @@ fn draw_overview<B: Backend>(f: &mut Frame, area: Rect, app: &App) {
.split(area);
// Statistics panel
draw_stats_panel::<B>(f, chunks[0], app);
draw_stats_panel(f, chunks[0], app);
// Threat level gauge
draw_threat_gauge::<B>(f, chunks[1], app);
draw_threat_gauge(f, chunks[1], app);
// Recent detections
draw_recent_detections::<B>(f, chunks[2], app);
draw_recent_detections(f, chunks[2], app);
}
fn draw_stats_panel<B: Backend>(f: &mut Frame, area: Rect, app: &App) {
fn draw_stats_panel(f: &mut Frame, area: Rect, app: &App) {
let stats_chunks = Layout::default()
.direction(Direction::Horizontal)
.constraints([
@@ -204,7 +204,7 @@ fn draw_stats_panel<B: Backend>(f: &mut Frame, area: Rect, app: &App) {
f.render_widget(perf_gauge, stats_chunks[3]);
}
fn draw_threat_gauge<B: Backend>(f: &mut Frame, area: Rect, app: &App) {
fn draw_threat_gauge(f: &mut Frame, area: Rect, app: &App) {
let threat_level = if app.stats.malicious_processes > 0 {
100
} else if app.stats.suspicious_processes > 0 {
@@ -239,7 +239,7 @@ fn draw_threat_gauge<B: Backend>(f: &mut Frame, area: Rect, app: &App) {
f.render_widget(threat_gauge, area);
}
fn draw_recent_detections<B: Backend>(f: &mut Frame, area: Rect, app: &App) {
fn draw_recent_detections(f: &mut Frame, area: Rect, app: &App) {
let items: Vec<ListItem> = app
.detections
.iter()
@@ -277,7 +277,7 @@ fn draw_recent_detections<B: Backend>(f: &mut Frame, area: Rect, app: &App) {
f.render_widget(list, area);
}
fn draw_processes<B: Backend>(f: &mut Frame, area: Rect, app: &App) {
fn draw_processes(f: &mut Frame, area: Rect, app: &App) {
let chunks = Layout::default()
.direction(Direction::Horizontal)
.constraints([Constraint::Percentage(70), Constraint::Percentage(30)])
@@ -334,10 +334,10 @@ fn draw_processes<B: Backend>(f: &mut Frame, area: Rect, app: &App) {
f.render_stateful_widget(table, chunks[0], &mut state);
// Process details panel
draw_process_details::<B>(f, chunks[1], app);
draw_process_details(f, chunks[1], app);
}
fn draw_process_details<B: Backend>(f: &mut Frame, area: Rect, app: &App) {
fn draw_process_details(f: &mut Frame, area: Rect, app: &App) {
let details = if let Some(ref process) = app.selected_process {
format!(
"PID: {}\nPPID: {}\nName: {}\nPath: {}\nThreads: {}",
@@ -364,7 +364,7 @@ fn draw_process_details<B: Backend>(f: &mut Frame, area: Rect, app: &App) {
f.render_widget(paragraph, area);
}
fn draw_detections<B: Backend>(f: &mut Frame, area: Rect, app: &App) {
fn draw_detections(f: &mut Frame, area: Rect, app: &App) {
let items: Vec<ListItem> = app
.detections
.iter()
@@ -420,7 +420,7 @@ fn draw_detections<B: Backend>(f: &mut Frame, area: Rect, app: &App) {
f.render_stateful_widget(list, area, &mut state);
}
fn draw_memory<B: Backend>(f: &mut Frame, area: Rect, app: &App) {
fn draw_memory(f: &mut Frame, area: Rect, app: &App) {
let chunks = Layout::default()
.direction(Direction::Vertical)
.constraints([Constraint::Length(8), Constraint::Min(0)])
@@ -459,7 +459,7 @@ fn draw_memory<B: Backend>(f: &mut Frame, area: Rect, app: &App) {
f.render_widget(memory_info, chunks[1]);
}
fn draw_logs<B: Backend>(f: &mut Frame, area: Rect, app: &App) {
fn draw_logs(f: &mut Frame, area: Rect, app: &App) {
let items: Vec<ListItem> = app
.logs
.iter()