// CWE-79: inner_html() with non-constant argument is an XSS sink.
// Only activated when rabbita package is imported.

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

///|
pub fn cwe079_inner_html_info() -> RuleInfo {
  {
    id: { cwe: 79, variant: "inner-html" },
    name: "XSS via inner_html with Dynamic Content",
    description: "inner_html() injects raw HTML. Non-constant arguments may contain user-controlled content enabling XSS.",
    default_severity: Error,
  }
}

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

///|
impl @syntax.IterVisitor for Cwe079InnerHtmlVisitor with fn visit_Expr_DotApply(
  env,
  self~,
  method_name~,
  args~,
  return_self~,
  loc~,
) {
  if method_name.name == "inner_html" {
    let first_arg_is_safe = match args {
      More({ value: Constant(c=String(_), ..), .. }, ..) => true
      More({ value: Ident(id~, ..), .. }, ..) => is_constant_ident(id.name)
      _ => false
    }
    if !first_arg_is_safe {
      env.findings.push(
        make_finding(
          { cwe: 79, variant: "inner-html" },
          env.severity,
          Medium,
          "inner_html() with non-constant argument is an XSS sink. User-controlled content will be injected as raw HTML.",
          env.file,
          env.source,
          loc,
        ),
      )
    }
  }
  @syntax.IterVisitor::base(env).visit_Expr_DotApply(
    self~,
    method_name~,
    args~,
    return_self~,
    loc~,
  )
}