Skip to content

Commit 5afd46a

Browse files
committed
feat(bump): working on next release
1 parent 9fae045 commit 5afd46a

10 files changed

Lines changed: 1765 additions & 486 deletions

‎src/findings.rs‎

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -120,7 +120,7 @@ impl std::fmt::Display for IssueCategory {
120120
}
121121
}
122122

123-
#[derive(Debug, Clone, Default, Serialize, Deserialize)]
123+
#[derive(Debug, Clone, Default, PartialEq, Serialize, Deserialize)]
124124
pub struct SecurityIssue {
125125
pub category: IssueCategory,
126126
#[serde(default)]
@@ -133,7 +133,7 @@ pub struct SecurityIssue {
133133
pub custom_tags: Vec<String>,
134134
}
135135

136-
#[derive(Debug, Clone, Serialize, Deserialize)]
136+
#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
137137
pub struct VulnerabilityFinding {
138138
pub id: String,
139139
pub title: String,

‎src/phase/ai_aggregation.rs‎

Lines changed: 0 additions & 99 deletions
Original file line numberDiff line numberDiff line change
@@ -102,102 +102,3 @@ impl ScanPhase for AiAggregationPhase {
102102
true
103103
}
104104
}
105-
106-
#[cfg(test)]
107-
mod tests {
108-
use super::*;
109-
use crate::config::ScannerConfig;
110-
use crate::findings::Severity;
111-
use crate::scanner::Scanner;
112-
use tempfile::TempDir;
113-
114-
fn create_test_scanner() -> (Scanner, TempDir) {
115-
let temp_dir = TempDir::new().unwrap();
116-
let config = ScannerConfig::default();
117-
let scanner = Scanner::new(config, temp_dir.path().to_path_buf(), false);
118-
(scanner, temp_dir)
119-
}
120-
121-
#[test]
122-
fn test_ai_aggregation_phase_creation() {
123-
let phase = AiAggregationPhase;
124-
assert_eq!(phase.name(), "AiAggregation");
125-
assert_eq!(phase.order(), 10);
126-
}
127-
128-
#[test]
129-
fn test_is_enabled_always_true() {
130-
let (scanner, _temp) = create_test_scanner();
131-
let analyzed_files = Vec::new();
132-
let ctx = PhaseContext {
133-
scanner: Box::leak(Box::new(scanner)),
134-
analyzed_files: Box::leak(Box::new(analyzed_files)),
135-
};
136-
let phase = AiAggregationPhase;
137-
assert!(phase.is_enabled(&ctx));
138-
}
139-
140-
#[tokio::test]
141-
async fn test_execute_with_empty_findings() {
142-
let (scanner, _temp) = create_test_scanner();
143-
let analyzed_files = Vec::new();
144-
let mut ctx = PhaseContext {
145-
scanner: Box::leak(Box::new(scanner)),
146-
analyzed_files: Box::leak(Box::new(analyzed_files)),
147-
};
148-
let phase = AiAggregationPhase;
149-
let result = phase.execute(&mut ctx).await;
150-
assert!(result.is_ok());
151-
let findings = result.unwrap();
152-
assert!(findings.is_empty());
153-
}
154-
155-
#[tokio::test]
156-
async fn test_execute_without_api_key() {
157-
let (scanner, _temp) = create_test_scanner();
158-
scanner.state.send_modify(|s| {
159-
s.findings.push(VulnerabilityFinding {
160-
id: "test-1".to_string(),
161-
title: "Test vulnerability".to_string(),
162-
description: "Test description".to_string(),
163-
severity: Severity::High,
164-
confidence_score: 0.5,
165-
cwe_id: Some("CWE-79".to_string()),
166-
file_path: "test.c".to_string(),
167-
line_number: Some(10),
168-
code_snippet: None,
169-
diff_hunk: None,
170-
recommendation: None,
171-
code_location: None,
172-
already_reported: false,
173-
sources: vec!["test".to_string()],
174-
commit_reference: None,
175-
ticket_reference: None,
176-
priority_score: None,
177-
cross_file_references: None,
178-
verification_status: None,
179-
verification_notes: None,
180-
verification_error: None,
181-
agent_evidence_path: None,
182-
security_issue: None,
183-
poc_code: None,
184-
mitigation_code: None,
185-
poc_format: None,
186-
llm_model: None,
187-
agent_mode: false,
188-
statement_range: None,
189-
triage_verdict: None,
190-
});
191-
});
192-
let analyzed_files = Vec::new();
193-
let mut ctx = PhaseContext {
194-
scanner: Box::leak(Box::new(scanner)),
195-
analyzed_files: Box::leak(Box::new(analyzed_files)),
196-
};
197-
let phase = AiAggregationPhase;
198-
let result = phase.execute(&mut ctx).await;
199-
assert!(result.is_ok());
200-
let findings = result.unwrap();
201-
assert_eq!(findings.len(), 1);
202-
}
203-
}

‎src/phase/git_analysis.rs‎

Lines changed: 0 additions & 99 deletions
Original file line numberDiff line numberDiff line change
@@ -129,102 +129,3 @@ impl ScanPhase for GitAnalysisPhase {
129129
true
130130
}
131131
}
132-
133-
#[cfg(test)]
134-
mod tests {
135-
use super::*;
136-
use crate::config::ScannerConfig;
137-
use crate::findings::{Severity, VulnerabilityFinding};
138-
use crate::scanner::Scanner;
139-
use tempfile::TempDir;
140-
141-
fn create_test_scanner() -> (Scanner, TempDir) {
142-
let temp_dir = TempDir::new().unwrap();
143-
let config = ScannerConfig::default();
144-
let scanner = Scanner::new(config, temp_dir.path().to_path_buf(), false);
145-
(scanner, temp_dir)
146-
}
147-
148-
#[test]
149-
fn test_git_analysis_phase_creation() {
150-
let phase = GitAnalysisPhase;
151-
assert_eq!(phase.name(), "GitAnalysis");
152-
assert_eq!(phase.order(), 7);
153-
}
154-
155-
#[test]
156-
fn test_is_enabled_always_true() {
157-
let (scanner, _temp) = create_test_scanner();
158-
let analyzed_files = Vec::new();
159-
let ctx = PhaseContext {
160-
scanner: Box::leak(Box::new(scanner)),
161-
analyzed_files: Box::leak(Box::new(analyzed_files)),
162-
};
163-
let phase = GitAnalysisPhase;
164-
assert!(phase.is_enabled(&ctx));
165-
}
166-
167-
#[tokio::test]
168-
async fn test_execute_with_empty_findings() {
169-
let (scanner, _temp) = create_test_scanner();
170-
let analyzed_files = Vec::new();
171-
let mut ctx = PhaseContext {
172-
scanner: Box::leak(Box::new(scanner)),
173-
analyzed_files: Box::leak(Box::new(analyzed_files)),
174-
};
175-
let phase = GitAnalysisPhase;
176-
let result = phase.execute(&mut ctx).await;
177-
assert!(result.is_ok());
178-
let findings = result.unwrap();
179-
assert!(findings.is_empty());
180-
}
181-
182-
#[tokio::test]
183-
async fn test_execute_processes_findings() {
184-
let (scanner, _temp) = create_test_scanner();
185-
scanner.state.send_modify(|s| {
186-
s.findings.push(VulnerabilityFinding {
187-
id: "test-1".to_string(),
188-
title: "Test vulnerability".to_string(),
189-
description: "Test description".to_string(),
190-
severity: Severity::High,
191-
confidence_score: 0.5,
192-
cwe_id: Some("CWE-79".to_string()),
193-
file_path: "test.c".to_string(),
194-
line_number: Some(10),
195-
code_snippet: None,
196-
diff_hunk: None,
197-
recommendation: None,
198-
code_location: None,
199-
already_reported: false,
200-
sources: vec!["test".to_string()],
201-
commit_reference: None,
202-
ticket_reference: None,
203-
priority_score: None,
204-
cross_file_references: None,
205-
verification_status: None,
206-
verification_notes: None,
207-
verification_error: None,
208-
agent_evidence_path: None,
209-
security_issue: None,
210-
poc_code: None,
211-
mitigation_code: None,
212-
poc_format: None,
213-
llm_model: None,
214-
agent_mode: false,
215-
statement_range: None,
216-
triage_verdict: None,
217-
});
218-
});
219-
let analyzed_files = Vec::new();
220-
let mut ctx = PhaseContext {
221-
scanner: Box::leak(Box::new(scanner)),
222-
analyzed_files: Box::leak(Box::new(analyzed_files)),
223-
};
224-
let phase = GitAnalysisPhase;
225-
let result = phase.execute(&mut ctx).await;
226-
assert!(result.is_ok());
227-
let findings = result.unwrap();
228-
assert_eq!(findings.len(), 1);
229-
}
230-
}

‎src/phase/llm_discovery.rs‎

Lines changed: 0 additions & 167 deletions
Original file line numberDiff line numberDiff line change
@@ -196,173 +196,6 @@ Respond with ONLY JSON:
196196
}
197197
}
198198

199-
#[cfg(test)]
200-
mod tests {
201-
use super::*;
202-
use crate::config::{LlmConfig, LlmPhaseConfig, LlmPhasesConfig, ScannerConfig};
203-
use crate::findings::{Severity, VulnerabilityFinding};
204-
use crate::scanner::Scanner;
205-
use tempfile::TempDir;
206-
207-
fn create_test_scanner() -> (Scanner, TempDir) {
208-
let temp_dir = TempDir::new().unwrap();
209-
let config = ScannerConfig::default();
210-
let scanner = Scanner::new(config, temp_dir.path().to_path_buf(), false);
211-
(scanner, temp_dir)
212-
}
213-
214-
fn create_test_scanner_with_llm() -> (Scanner, TempDir) {
215-
let temp_dir = TempDir::new().unwrap();
216-
let mut config = ScannerConfig::default();
217-
config.llm = LlmConfig {
218-
timeout_secs: 30,
219-
max_retries: 3,
220-
retry_backoff_ms: 2000,
221-
max_concurrent: 4,
222-
phases: LlmPhasesConfig {
223-
discovery: LlmPhaseConfig {
224-
base_url: "http://test.local".to_string(),
225-
api_key: Some("test-key".to_string()),
226-
model: "test-model".to_string(),
227-
models: vec![],
228-
timeout_secs: None,
229-
},
230-
..Default::default()
231-
},
232-
tgi: Default::default(),
233-
};
234-
let scanner = Scanner::new(config, temp_dir.path().to_path_buf(), false);
235-
(scanner, temp_dir)
236-
}
237-
238-
#[test]
239-
fn test_llm_discovery_phase_creation() {
240-
let phase = LlmDiscoveryPhase;
241-
assert_eq!(phase.name(), "LlmDiscovery");
242-
assert_eq!(phase.order(), 4);
243-
}
244-
245-
#[test]
246-
fn test_is_enabled_with_api_key() {
247-
let (scanner, _temp) = create_test_scanner_with_llm();
248-
let analyzed_files = Vec::new();
249-
let ctx = PhaseContext {
250-
scanner: Box::leak(Box::new(scanner)),
251-
analyzed_files: Box::leak(Box::new(analyzed_files)),
252-
};
253-
let phase = LlmDiscoveryPhase;
254-
assert!(phase.is_enabled(&ctx));
255-
}
256-
257-
#[test]
258-
fn test_is_disabled_without_api_key() {
259-
let (scanner, _temp) = create_test_scanner();
260-
let analyzed_files = Vec::new();
261-
let ctx = PhaseContext {
262-
scanner: Box::leak(Box::new(scanner)),
263-
analyzed_files: Box::leak(Box::new(analyzed_files)),
264-
};
265-
let phase = LlmDiscoveryPhase;
266-
assert!(!phase.is_enabled(&ctx));
267-
}
268-
269-
#[tokio::test]
270-
async fn test_execute_with_empty_findings() {
271-
let (scanner, _temp) = create_test_scanner_with_llm();
272-
let analyzed_files = Vec::new();
273-
let mut ctx = PhaseContext {
274-
scanner: Box::leak(Box::new(scanner)),
275-
analyzed_files: Box::leak(Box::new(analyzed_files)),
276-
};
277-
let phase = LlmDiscoveryPhase;
278-
let result = phase.execute(&mut ctx).await;
279-
assert!(result.is_ok());
280-
let findings = result.unwrap();
281-
assert!(findings.is_empty());
282-
}
283-
284-
#[tokio::test]
285-
async fn test_execute_without_api_key_returns_original() {
286-
let (scanner, _temp) = create_test_scanner();
287-
scanner.state.send_modify(|s| {
288-
s.findings.push(VulnerabilityFinding {
289-
id: "test-1".to_string(),
290-
title: "Test vulnerability".to_string(),
291-
description: "Test description".to_string(),
292-
severity: Severity::High,
293-
confidence_score: 0.5,
294-
cwe_id: Some("CWE-79".to_string()),
295-
file_path: "test.c".to_string(),
296-
line_number: Some(10),
297-
code_snippet: None,
298-
diff_hunk: None,
299-
recommendation: None,
300-
code_location: None,
301-
already_reported: false,
302-
sources: vec!["test".to_string()],
303-
commit_reference: None,
304-
ticket_reference: None,
305-
priority_score: None,
306-
cross_file_references: None,
307-
verification_status: None,
308-
verification_notes: None,
309-
verification_error: None,
310-
agent_evidence_path: None,
311-
security_issue: None,
312-
poc_code: None,
313-
mitigation_code: None,
314-
poc_format: None,
315-
llm_model: None,
316-
agent_mode: false,
317-
statement_range: None,
318-
triage_verdict: None,
319-
});
320-
});
321-
let analyzed_files = Vec::new();
322-
let mut ctx = PhaseContext {
323-
scanner: Box::leak(Box::new(scanner)),
324-
analyzed_files: Box::leak(Box::new(analyzed_files)),
325-
};
326-
let phase = LlmDiscoveryPhase;
327-
let result = phase.execute(&mut ctx).await;
328-
assert!(result.is_ok());
329-
let findings = result.unwrap();
330-
assert_eq!(findings.len(), 1);
331-
}
332-
333-
#[test]
334-
fn test_parse_llm_response_valid_json() {
335-
let content = r#"{"description": "Test desc", "fix_code": "test fix"}"#;
336-
let mut descriptions = Vec::new();
337-
let mut fixes = Vec::new();
338-
parse_llm_response(content, &mut descriptions, &mut fixes, "test-model");
339-
assert_eq!(descriptions.len(), 1);
340-
assert_eq!(descriptions[0], "Test desc");
341-
assert_eq!(fixes.len(), 1);
342-
assert_eq!(fixes[0], "test fix");
343-
}
344-
345-
#[test]
346-
fn test_parse_llm_response_invalid_json() {
347-
let content = "not valid json";
348-
let mut descriptions = Vec::new();
349-
let mut fixes = Vec::new();
350-
parse_llm_response(content, &mut descriptions, &mut fixes, "test-model");
351-
assert!(descriptions.is_empty());
352-
assert!(fixes.is_empty());
353-
}
354-
355-
#[test]
356-
fn test_parse_llm_response_partial_json() {
357-
let content = r#"{"description": "Only desc"}"#;
358-
let mut descriptions = Vec::new();
359-
let mut fixes = Vec::new();
360-
parse_llm_response(content, &mut descriptions, &mut fixes, "test-model");
361-
assert_eq!(descriptions.len(), 1);
362-
assert!(fixes.is_empty());
363-
}
364-
}
365-
366199
/// Helper to parse LLM JSON response and extract description/fix
367200
fn parse_llm_response(
368201
content: &str,

0 commit comments

Comments
 (0)