// Remediation advice engine.
// Provides fix suggestions, code patches, and security best practices
// for each detected vulnerability type.
///|
pub(all) struct Remediation {
rule_id : String
title : String
risk_summary : String
fix_description : String
code_before : String
code_after : String
references : Array[String]
owasp_category : String
}
///|
pub fn get_remediation(finding : Finding) -> Remediation {
let rule_id = rule_id_str(finding.rule_id)
if rule_id == "CWE-116/replace-escaping" {
remediation_cwe116()
} else if rule_id == "CWE-79/cmark-unsafe" {
remediation_cwe079_cmark()
} else if rule_id == "CWE-79/inner-html" {
remediation_cwe079_inner_html()
} else if rule_id == "CWE-94/eval-extern" {
remediation_cwe094()
} else if rule_id == "CWE-942/cors-credentials" {
remediation_cwe942()
} else if rule_id == "CWE-614/cookie-attrs" {
remediation_cwe614()
} else if rule_id == "CWE-770/no-body-limit" {
remediation_cwe770()
} else if rule_id == "CWE-346/ws-origin" {
remediation_cwe346()
} else if rule_id == "CWE-22/path-concat" {
remediation_cwe022()
} else {
remediation_generic(rule_id)
}
}
///|
pub fn get_all_remediations(findings : Array[Finding]) -> Array[Remediation] {
let seen : Map[String, Bool] = Map([])
let result : Array[Remediation] = []
for f in findings {
let id = rule_id_str(f.rule_id)
if !seen.contains(id) {
seen[id] = true
result.push(get_remediation(f))
}
}
result
}
///|
fn remediation_cwe116() -> Remediation {
{
rule_id: "CWE-116/replace-escaping",
title: "Use replace_all() Instead of replace() for HTML Escaping",
risk_summary: "MoonBit's String::replace() only replaces the first occurrence. Attackers can bypass HTML escaping by including multiple special characters.",
fix_description: "Replace all calls to .replace() with .replace_all() when escaping HTML metacharacters. Better yet, use a dedicated HTML escaping function that handles all characters in one pass.",
code_before: "fn escape_html(s : String) -> String {\n s.replace(old=\"<\", new=\"<\")\n .replace(old=\">\", new=\">\")\n .replace(old=\"&\", new=\"&\")\n}",
code_after: "fn escape_html(s : String) -> String {\n s.replace_all(old=\"&\", new=\"&\") // & first!\n .replace_all(old=\"<\", new=\"<\")\n .replace_all(old=\">\", new=\">\")\n .replace_all(old=\"\\\"\", new=\""\")\n .replace_all(old=\"'\", new=\"'\")\n}",
references: [
"https://cwe.mitre.org/data/definitions/116.html", "https://cheatsheetseries.owasp.org/cheatsheets/Cross_Site_Scripting_Prevention_Cheat_Sheet.html",
],
owasp_category: "A03:2021 - Injection",
}
}
///|
fn remediation_cwe079_cmark() -> Remediation {
{
rule_id: "CWE-79/cmark-unsafe",
title: "Enable Safe Mode for cmark HTML Rendering",
risk_summary: "cmark's render() defaults to safe=false, allowing raw HTML tags (