// 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 (