// CWE-79: cmark render()/from_doc()/renderer() with explicit safe=false allows raw HTML/script injection.
// render() defaults to safe=true since cmark update; only explicit safe=false is unsafe.
// from_doc()/renderer()/xhtml_renderer() require safe~ explicitly.

///|
priv struct Cwe079CmarkVisitor {
  findings : Array[Finding]
  file : String
  source : String
  severity : Severity
}

///|
pub fn cwe079_cmark_info() -> RuleInfo {
  {
    id: { cwe: 79, variant: "cmark-unsafe" },
    name: "Explicit Unsafe HTML Rendering in cmark",
    description: "cmark render()/from_doc()/renderer() called with safe=false, allowing raw HTML and javascript: URLs in output.",
    default_severity: Error,
  }
}

///|
pub fn run_cwe079_cmark(
  impls : @list.List[@syntax.Impl],
  file : String,
  source : String,
  findings : Array[Finding],
  severity : Severity,
) -> Unit {
  let visitor : Cwe079CmarkVisitor = { findings, file, source, severity }
  impls.each(fn(impl_) { @syntax.IterVisitor::visit_Impl(visitor, impl_) })
}

///|
fn is_cmark_render_func(func : @syntax.Expr) -> Bool {
  match func {
    Ident(id~, ..) =>
      match id.name {
        Ident(name="render")
        | Ident(name="from_doc")
        | Ident(name="renderer")
        | Ident(name="xhtml_renderer") => true
        Dot(id="render", ..)
        | Dot(id="from_doc", ..)
        | Dot(id="renderer", ..)
        | Dot(id="xhtml_renderer", ..) => true
        _ => false
      }
    _ => false
  }
}

///|
impl @syntax.IterVisitor for Cwe079CmarkVisitor with fn visit_Expr_Apply(
  env,
  func~,
  args~,
  loc~,
) {
  if is_cmark_render_func(func) {
    let has_safe_false = has_labelled_bool_arg(args, "safe", false)
    if has_safe_false {
      env.findings.push(
        make_finding(
          { cwe: 79, variant: "cmark-unsafe" },
          env.severity,
          High,
          "cmark called with safe=false. Raw HTML and javascript: URLs pass through to output. Remove safe=false or set safe=true.",
          env.file,
          env.source,
          loc,
        ),
      )
    }
  }
  @syntax.IterVisitor::base(env).visit_Expr_Apply(func~, args~, loc~)
}