// CWE-94: eval() or new Function() in extern JS blocks enables code injection.

///|
priv struct Cwe094Visitor {
  findings : Array[Finding]
  file : String
  source : String
  severity : Severity
  mut current_loc : @basic.Location?
}

///|
pub fn cwe094_info() -> RuleInfo {
  {
    id: { cwe: 94, variant: "eval-extern" },
    name: "Code Injection via eval in Extern JS",
    description: "eval() or new Function() in extern JS/JavaScript blocks enables arbitrary code execution.",
    default_severity: Error,
  }
}

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

///|
impl @syntax.IterVisitor for Cwe094Visitor with fn visit_Impl_TopFuncDef(
  env,
  fun_decl~,
  decl_body~,
  where_clause~,
  loc~,
) {
  env.current_loc = Some(loc)
  @syntax.IterVisitor::base(env).visit_Impl_TopFuncDef(
    fun_decl~,
    decl_body~,
    where_clause~,
    loc~,
  )
  env.current_loc = None
}

///|
impl @syntax.IterVisitor for Cwe094Visitor with fn visit_FuncStubs_Embedded(
  env,
  language~,
  code~,
) {
  let is_js = match language {
    Some(lang) =>
      lang.contains("js") || lang.contains("javascript") || lang.contains("JS")
    None => true
  }
  if is_js {
    let code_str = match code {
      CodeString(s) => s
      CodeMultilineString(lines) => {
        let buf = StringBuilder::new()
        lines.each(fn(line) {
          buf.write_string(line)
          buf.write_char('\n')
        })
        buf.to_string()
      }
    }
    if code_str.contains("eval(") ||
      code_str.contains("new Function(") ||
      code_str.contains("new Function (") {
      let loc = match env.current_loc {
        Some(l) => l
        None =>
          {
            start: { fname: env.file, lnum: 0, bol: 0, cnum: 0 },
            end: { fname: env.file, lnum: 0, bol: 0, cnum: 0 },
          }
      }
      env.findings.push(
        make_finding(
          { cwe: 94, variant: "eval-extern" },
          env.severity,
          High,
          "eval() or new Function() in extern JS block enables code injection. Consider safer alternatives.",
          env.file,
          env.source,
          loc,
        ),
      )
    }
  }
  @syntax.IterVisitor::base(env).visit_FuncStubs_Embedded(language~, code~)
}