use aptu_core::security::scanner::SecurityScanner;
use std::fmt::Write;
const HARDCODED_SECRETS_FIXTURE: &str =
include_str!("../../../tests/security_fixtures/vulnerable/hardcoded_secrets.rs");
const SQL_INJECTION_FIXTURE: &str =
include_str!("../../../tests/security_fixtures/vulnerable/sql_injection.rs");
const SAFE_PATTERNS_FIXTURE: &str =
include_str!("../../../tests/security_fixtures/safe/safe_patterns.rs");
fn create_test_diff(content: &str, filename: &str) -> String {
let mut diff_content = String::new();
for line in content.lines() {
let _ = writeln!(diff_content, "+{line}");
}
format!(
r#"diff --git a/{filename} b/{filename}
index 0000000..1111111 100644
--- a/{filename}
+++ b/{filename}
@@ -0,0 +1,{line_count} @@
{diff_content}"#,
line_count = content.lines().count(),
)
}
#[test]
fn test_hardcoded_secrets_detection() {
let scanner = SecurityScanner::new();
let diff = create_test_diff(HARDCODED_SECRETS_FIXTURE, "test.rs");
let findings = scanner.scan_diff(&diff);
let api_key_findings: Vec<_> = findings
.iter()
.filter(|f| f.pattern_id == "hardcoded-api-key")
.collect();
assert!(
!api_key_findings.is_empty(),
"Should detect hardcoded-api-key pattern in fixture. Findings: {findings:#?}"
);
let password_findings: Vec<_> = findings
.iter()
.filter(|f| f.pattern_id == "hardcoded-password")
.collect();
assert!(
!password_findings.is_empty(),
"Should detect hardcoded-password pattern in fixture. Findings: {findings:#?}"
);
}
#[test]
fn test_sql_injection_detection() {
let scanner = SecurityScanner::new();
let diff = create_test_diff(SQL_INJECTION_FIXTURE, "test.rs");
let findings = scanner.scan_diff(&diff);
let concat_findings: Vec<_> = findings
.iter()
.filter(|f| f.pattern_id == "sql-injection-concat")
.collect();
assert!(
!concat_findings.is_empty(),
"Should detect sql-injection-concat pattern in fixture. Findings: {findings:#?}"
);
let format_findings: Vec<_> = findings
.iter()
.filter(|f| f.pattern_id == "sql-injection-format")
.collect();
assert!(
!format_findings.is_empty(),
"Should detect sql-injection-format pattern in fixture. Findings: {findings:#?}"
);
let cmd_findings: Vec<_> = findings
.iter()
.filter(|f| f.pattern_id == "command-injection")
.collect();
assert!(
!cmd_findings.is_empty(),
"Should detect command-injection pattern in fixture. Findings: {findings:#?}"
);
let md5_findings: Vec<_> = findings
.iter()
.filter(|f| f.pattern_id == "weak-crypto-md5")
.collect();
assert!(
!md5_findings.is_empty(),
"Should detect weak-crypto-md5 pattern in fixture. Findings: {findings:#?}"
);
let sha1_findings: Vec<_> = findings
.iter()
.filter(|f| f.pattern_id == "weak-crypto-sha1")
.collect();
assert!(
!sha1_findings.is_empty(),
"Should detect weak-crypto-sha1 pattern in fixture. Findings: {findings:#?}"
);
}
#[test]
fn test_safe_patterns_no_findings() {
let scanner = SecurityScanner::new();
let diff = create_test_diff(SAFE_PATTERNS_FIXTURE, "test.rs");
let findings = scanner.scan_diff(&diff);
assert!(
findings.is_empty(),
"Safe fixture should produce zero findings, but got: {findings:#?}"
);
}
#[test]
fn test_multi_line_vulnerability_not_detected() {
let scanner = SecurityScanner::new();
let multi_line_code = r#"
fn vulnerable_query(id: &str) -> String {
let query = "SELECT * FROM users WHERE id = "
+ id;
query
}
"#;
let diff = create_test_diff(multi_line_code, "test.rs");
let findings = scanner.scan_diff(&diff);
assert!(
findings.is_empty(),
"Known limitation: SecurityScanner does not detect multi-line SQL injection \
where source and sink are on different lines. This test documents the limitation."
);
}