// Baseline file support: generate and filter known findings.
///|
pub fn generate_baseline(findings : Array[Finding]) -> String {
let buf = StringBuilder::new()
buf.write_string("[\n")
for i, f in findings {
if i > 0 {
buf.write_string(",\n")
}
buf.write_string(" {\n")
buf.write_string(" \"rule_id\": \"")
buf.write_string(escape_json_str(rule_id_str(f.rule_id)))
buf.write_string("\",\n")
buf.write_string(" \"fingerprint\": \"")
buf.write_string(escape_json_str(f.fingerprint))
buf.write_string("\",\n")
buf.write_string(" \"file\": \"")
buf.write_string(escape_json_str(f.file))
buf.write_string("\",\n")
buf.write_string(" \"line\": ")
buf.write_string(f.line.to_string())
buf.write_string(",\n")
buf.write_string(" \"message\": \"")
buf.write_string(escape_json_str(f.message))
buf.write_string("\"\n")
buf.write_string(" }")
}
buf.write_string("\n]\n")
buf.to_string()
}
///|
pub fn load_baseline(path : String) -> Map[String, Bool] {
let keys : Map[String, Bool] = Map([])
let content = @fs.read_file_to_string(path) catch { _ => return keys }
let json = @json.parse(content) catch { _ => return keys }
match json {
Array(arr) =>
for item in arr {
match item {
Object(obj) => {
let rule_id = match obj.get("rule_id") {
Some(String(s)) => s
_ => ""
}
let fingerprint = match obj.get("fingerprint") {
Some(String(s)) => s
_ => ""
}
if rule_id.length() > 0 && fingerprint.length() > 0 {
keys[rule_id + ":" + fingerprint] = true
}
}
_ => ()
}
}
_ => ()
}
keys
}
///|
pub fn filter_baseline(
findings : Array[Finding],
baseline : Map[String, Bool],
) -> Array[Finding] {
if baseline.length() == 0 {
return findings
}
findings.filter(fn(f) {
let key = rule_id_str(f.rule_id) + ":" + f.fingerprint
!baseline.contains(key)
})
}
///|
fn escape_json_str(s : String) -> String {
let buf = StringBuilder::new()
for i = 0; i < s.length(); i = i + 1 {
let ch = s[i]
if ch == char16('"') {
buf.write_string("\\\"")
} else if ch == char16('\\') {
buf.write_string("\\\\")
} else if ch == char16('\n') {
buf.write_string("\\n")
} else if ch == char16('\r') {
buf.write_string("\\r")
} else if ch == char16('\t') {
buf.write_string("\\t")
} else {
buf.write_string(ch.to_int().unsafe_to_char().to_string())
}
}
buf.to_string()
}