use codelore_lib::external::sarif_parse::{
ExternalFinding, parse_sarif_with_engines, self_hash_fingerprint,
};
use codelore_lib::test_support::sarif_fixture;
fn parse_findings(raw: &str) -> Vec<ExternalFinding> {
parse_sarif_with_engines(raw)
.expect("SARIF document should parse")
.0
}
#[test]
fn semgrep_dialect_parses_correctly() {
let raw = sarif_fixture("semgrep.sarif.json");
let findings = parse_findings(&raw);
let expected = vec![
ExternalFinding {
engine: "semgrep".into(),
engine_version: "1.70.0".into(),
rule_id: "python.flask.security.injection.tainted-sql-string".into(),
path: "src/db.py".into(),
start_line: Some(42),
end_line: Some(44),
level: "error".into(), fingerprint: "abc123def456".into(),
message: "User-controlled data in a SQL statement".into(),
},
ExternalFinding {
engine: "semgrep".into(),
engine_version: "1.70.0".into(),
rule_id: "python.flask.security.injection.tainted-sql-string".into(),
path: "src/api.py".into(), start_line: Some(17),
end_line: None,
level: "error".into(),
fingerprint: "deadbeef0011".into(),
message: "Another tainted SQL string".into(),
},
];
assert_eq!(findings, expected, "semgrep findings mismatch");
}
#[test]
fn clippy_dialect_uses_self_hash_fingerprint() {
let raw = sarif_fixture("clippy.sarif.json");
let findings = parse_findings(&raw);
assert_eq!(findings.len(), 2, "expected 2 clippy findings");
let f1 = &findings[0];
assert_eq!(f1.engine, "clippy");
assert_eq!(f1.engine_version, "0.1.80");
assert_eq!(f1.rule_id, "clippy::unwrap_used");
assert_eq!(f1.path, "src/main.rs");
assert_eq!(f1.start_line, Some(88));
assert_eq!(f1.end_line, Some(88));
assert_eq!(f1.level, "warning");
let expected_fp1 =
self_hash_fingerprint("clippy", "clippy::unwrap_used", "src/main.rs", Some(88));
assert_eq!(
f1.fingerprint, expected_fp1,
"result 1 fingerprint should be self-hash"
);
let f2 = &findings[1];
assert_eq!(f2.rule_id, "clippy::cognitive_complexity");
assert_eq!(f2.path, "src/parser.rs");
assert_eq!(f2.start_line, Some(120));
assert_eq!(f2.end_line, None);
let expected_fp2 = self_hash_fingerprint(
"clippy",
"clippy::cognitive_complexity",
"src/parser.rs",
Some(120),
);
assert_eq!(
f2.fingerprint, expected_fp2,
"result 2 fingerprint should be self-hash"
);
}
#[test]
fn codeql_dialect_resolves_rule_index_and_strips_file_uri() {
let raw = sarif_fixture("codeql.sarif.json");
let findings = parse_findings(&raw);
assert_eq!(findings.len(), 2, "expected 2 codeql findings");
let f1 = &findings[0];
assert_eq!(f1.engine, "CodeQL");
assert_eq!(f1.engine_version, "2.17.0");
assert_eq!(f1.rule_id, "java/sql-injection");
assert_eq!(
f1.path,
"/home/runner/work/repo/src/main/java/com/example/Dao.java"
);
assert_eq!(f1.start_line, Some(55));
assert_eq!(f1.end_line, Some(58));
assert_eq!(f1.level, "error"); assert_eq!(f1.fingerprint, "1a2b3c4d5e6f7890");
let f2 = &findings[1];
assert_eq!(f2.rule_id, "java/xss"); assert_eq!(
f2.path,
"/home/runner/work/repo/src/main/java/com/example/Controller.java"
);
assert_eq!(f2.start_line, Some(30));
assert_eq!(f2.end_line, None);
assert_eq!(f2.level, "warning");
assert_eq!(f2.fingerprint, "aabbccddeeff0011");
}
#[test]
fn non_sarif_json_returns_err() {
let not_sarif = r#"{"kind": "SomeOtherTool", "results": []}"#;
let err = parse_sarif_with_engines(not_sarif);
assert!(err.is_err(), "non-SARIF JSON should return Err");
}
#[test]
fn invalid_json_returns_err() {
let err = parse_sarif_with_engines("not json at all {{{");
assert!(err.is_err(), "invalid JSON should return Err");
}
#[test]
fn malformed_result_inside_valid_doc_is_skipped() {
let raw = r#"{
"version": "2.1.0",
"runs": [{
"tool": { "driver": { "name": "test-scanner", "version": "1.0" } },
"results": [
{
"ruleId": "test/bad",
"message": { "text": "no locations here" }
},
{
"ruleId": "test/good",
"level": "warning",
"message": { "text": "valid finding" },
"locations": [{
"physicalLocation": {
"artifactLocation": { "uri": "src/good.rs" },
"region": { "startLine": 10 }
}
}]
}
]
}]
}"#;
let findings = parse_findings(raw);
assert_eq!(
findings.len(),
1,
"expected exactly 1 finding (malformed skipped)"
);
assert_eq!(findings[0].rule_id, "test/good");
assert_eq!(findings[0].path, "src/good.rs");
assert_eq!(findings[0].start_line, Some(10));
let expected_fp = self_hash_fingerprint("test-scanner", "test/good", "src/good.rs", Some(10));
assert_eq!(findings[0].fingerprint, expected_fp);
}
#[test]
fn finding_with_no_region_is_valid() {
let raw = r#"{
"version": "2.1.0",
"runs": [{
"tool": { "driver": { "name": "scanner", "version": "0.1" } },
"results": [{
"ruleId": "check/no-region",
"level": "note",
"message": { "text": "no region" },
"locations": [{
"physicalLocation": {
"artifactLocation": { "uri": "src/lib.rs" }
}
}]
}]
}]
}"#;
let findings = parse_findings(raw);
assert_eq!(findings.len(), 1);
assert_eq!(findings[0].start_line, None);
assert_eq!(findings[0].end_line, None);
assert_eq!(findings[0].level, "note");
}
#[test]
fn parse_sarif_engines_includes_zero_result_runs() {
let raw = r#"{
"version": "2.1.0",
"runs": [{
"tool": { "driver": { "name": "semgrep", "version": "1.0" } },
"results": []
}]
}"#;
let (findings, engines) = parse_sarif_with_engines(raw).expect("parse");
assert!(findings.is_empty());
assert_eq!(engines, vec!["semgrep"]);
}
#[test]
fn parse_sarif_engines_dedupes_and_rejects_non_sarif() {
let two_runs_same_engine = r#"{
"version": "2.1.0",
"runs": [
{ "tool": { "driver": { "name": "clippy" } }, "results": [] },
{ "tool": { "driver": { "name": "clippy" } }, "results": [] }
]
}"#;
let (_findings, engines) = parse_sarif_with_engines(two_runs_same_engine).expect("engines");
assert_eq!(engines, vec!["clippy"]);
assert!(parse_sarif_with_engines(r#"{"version":"2.1.0"}"#).is_err());
}
fn doc_with_both_fingerprints() -> String {
r#"{
"version": "2.1.0",
"runs": [{
"tool": { "driver": { "name": "semgrep", "rules": [] } },
"results": [{
"ruleId": "python.lang.security.audit.exec-detected",
"message": { "text": "exec detected" },
"fingerprints": { "matchBasedId/v1": "AUTHORITATIVE-match-based-id" },
"partialFingerprints": { "primaryLocationLineHash": "PARTIAL-line-hash" },
"locations": [{
"physicalLocation": {
"artifactLocation": { "uri": "src/app.py" },
"region": { "startLine": 12 }
}
}]
}]
}]
}"#
.to_string()
}
#[test]
fn authoritative_fingerprints_win_over_partial_ones() {
let findings = parse_findings(&doc_with_both_fingerprints());
assert_eq!(findings.len(), 1, "fixture defines exactly one result");
assert_eq!(
findings[0].fingerprint, "AUTHORITATIVE-match-based-id",
"`fingerprints` is the tool's authoritative identity and must win over \
`partialFingerprints`; taking the line hash makes a moved finding read as new"
);
}
#[test]
fn partial_fingerprints_are_still_used_when_alone() {
let doc = doc_with_both_fingerprints().replace(
r#""fingerprints": { "matchBasedId/v1": "AUTHORITATIVE-match-based-id" },"#,
"",
);
let findings = parse_findings(&doc);
assert_eq!(findings.len(), 1);
assert_eq!(
findings[0].fingerprint, "PARTIAL-line-hash",
"with no authoritative fingerprint, the partial one is still the best available"
);
}
fn doc_with_results(results_json: &str) -> String {
format!(
r#"{{
"version": "2.1.0",
"runs": [{{
"tool": {{ "driver": {{ "name": "semgrep", "rules": [] }} }},
"results": {results_json}
}}]
}}"#
)
}
#[test]
fn malformed_results_is_an_error_not_an_empty_scan() {
for malformed in [r#"{ "not": "an array" }"#, r#""a string""#, "42", "true"] {
let err = parse_sarif_with_engines(&doc_with_results(malformed))
.expect_err("a non-array `results` must not parse as a clean scan");
let msg = format!("{err}");
assert!(
msg.contains("results"),
"the error must name the offending field, got: {msg}"
);
}
}
#[test]
fn absent_results_is_a_legitimate_clean_scan() {
let doc = r#"{
"version": "2.1.0",
"runs": [{ "tool": { "driver": { "name": "semgrep", "rules": [] } } }]
}"#;
let (findings, engines) =
parse_sarif_with_engines(doc).expect("a run with no `results` is valid SARIF");
assert!(findings.is_empty(), "no results means no findings");
assert_eq!(
engines,
vec!["semgrep".to_string()],
"the engine must still be registered so a clean re-scan drops its fixed findings"
);
}
#[test]
fn empty_results_array_is_a_legitimate_clean_scan() {
let (findings, engines) =
parse_sarif_with_engines(&doc_with_results("[]")).expect("empty results is valid");
assert!(findings.is_empty());
assert_eq!(engines, vec!["semgrep".to_string()]);
}