use super::report_common::sample_finding;
use crate::support::reporters::CsvReporter;
use keyhog_core::VerificationResult;
const AWS_REMEDIATION_CSV: &str = r#""{""action"":""Disable or delete the exposed IAM access key, then rotate any paired secret access key and session token."",""revoke_url"":""https://docs.aws.amazon.com/IAM/latest/UserGuide/id_credentials_access-keys.html#Using_ManagingAccessKeys"",""docs_url"":""https://docs.aws.amazon.com/IAM/latest/UserGuide/id_credentials_access-keys.html"",""revoke_command"":""aws iam update-access-key --access-key-id {{credential}} --status Inactive""}""#;
const AWS_METADATA_CSV: &str = r#""{""account_id"":""123456789012""}""#;
fn render(finding: &keyhog_core::VerifiedFinding) -> String {
let mut buf: Vec<u8> = Vec::new();
{
let mut reporter = CsvReporter::new(&mut buf).expect("new csv reporter");
reporter.report(finding).expect("report finding");
reporter.finish().expect("finish");
}
String::from_utf8(buf).expect("utf8 csv output")
}
#[test]
fn csv_emits_header_then_escaped_row() {
let out = render(&sample_finding());
let mut lines = out.lines();
assert_eq!(
lines.next().expect("header line"),
"detector_id,detector_name,service,severity,credential_redacted,credential_hash,companions_redacted,source,file_path,line,offset,commit,author,date,verification,confidence,entropy,remediation,metadata,additional_locations",
);
assert_eq!(
lines.next().expect("data row"),
format!("aws-access-key,\"AWS Key, \"\"prod\"\" <a&b>\",aws,high,AKIA...7XYA,deadbeef00000000000000000000000000000000000000000000000000000000,{{}},filesystem,config/app.env,12,5,,,,live,0.875,,{AWS_REMEDIATION_CSV},{AWS_METADATA_CSV},[]"),
);
assert!(
lines.next().is_none(),
"exactly one data row expected: {out:?}"
);
}
#[test]
fn csv_uses_canonical_structured_verification_tokens() {
for (verification, expected) in [
(VerificationResult::Live, "live"),
(VerificationResult::Revoked, "revoked"),
(VerificationResult::Dead, "dead"),
(VerificationResult::RateLimited, "rate_limited"),
(VerificationResult::Error("boom".to_string()), "error: boom"),
(VerificationResult::Unverifiable, "unverifiable"),
(VerificationResult::Skipped, "skipped"),
] {
let mut finding = sample_finding();
finding.verification = verification;
let out = render(&finding);
let row = out.lines().nth(1).expect("csv data row");
assert!(
row.ends_with(&format!(
",,,,{expected},0.875,,{AWS_REMEDIATION_CSV},{AWS_METADATA_CSV},[]"
)),
"CSV must use the canonical structured verification token: {out:?}"
);
}
}