diff --git a/rules/javascript/frontend_security.ron b/rules/javascript/frontend_security.ron
index c01afcd..4117a0b 100644
--- a/rules/javascript/frontend_security.ron
+++ b/rules/javascript/frontend_security.ron
@@ -45,6 +45,30 @@
file_types: Some((extensions: Some([".js", ".jsx", ".ts", ".tsx"]))),
tags: Some(["xss", "dom", "frontend", "cwe-79"])
),
+ (
+ id: Some("js-dom-xss-innerhtml-001"),
+ name: Some("DOM XSS via dynamic innerHTML/outerHTML assignment"),
+ category: Some("xss"),
+ mode: "search",
+ // The leading `^[^=;{]*` keeps a match anchored to the assignment that starts the
+ // matched node, so enclosing blocks are not reported instead of the sink line.
+ patterns: Some([
+ // Interpolated template literal: el.innerHTML = `${value}`
+ "regex:^[^=;{]*\\.(innerHTML|outerHTML)\\b[^=]*=[^=][\\s\\S]*\\$\\{",
+ // HTML markup concatenated with an expression:
+ // el.innerHTML = '' + value / el.innerHTML = value + ''
+ "regex:^[^=;{]*\\.(innerHTML|outerHTML)\\b[^=]*=[^=][\\s\\S]*(['\"`]\\s*\\+\\s*[A-Za-z_$]|[A-Za-z_$][\\w$]*\\s*\\+\\s*['\"`])",
+ // Non-literal value: el.innerHTML = value / render(value), el.innerHTML += value
+ "regex:^[^=;{]*\\.(innerHTML|outerHTML)\\b[^=]*=\\s*[A-Za-z_$]"
+ ]),
+ finding_type: Some("DOM XSS"),
+ severity: Some("High"),
+ confidence: Some("Medium"),
+ cwe_id: Some("cwe-79"),
+ description: Some("innerHTML/outerHTML is assigned a value the scanner cannot prove is static HTML, so attacker-controlled markup can execute; assign textContent or sanitize with DOMPurify instead"),
+ file_types: Some((extensions: Some([".js", ".jsx", ".ts", ".tsx"]))),
+ tags: Some(["xss", "dom", "frontend", "cwe-79"])
+ ),
(
id: Some("js-react-dangerously-set-inner-html-001"),
name: Some("React DOM XSS via dangerouslySetInnerHTML"),
diff --git a/src/scanner/utils.rs b/src/scanner/utils.rs
index 922ebc8..2e1c4b7 100644
--- a/src/scanner/utils.rs
+++ b/src/scanner/utils.rs
@@ -606,8 +606,16 @@ impl AstUtils {
}
fn check_html_sanitization(code: &str) -> bool {
- let html_sanitizers =
- ["DOMPurify.sanitize(", "validator.escape(", "xss(", "escapeHtml(", "encodeHTML("];
+ let html_sanitizers = [
+ "DOMPurify.sanitize(",
+ "validator.escape(",
+ "xss(",
+ "escapeHtml(",
+ "escapeHTML(",
+ "encodeHTML(",
+ "sanitizeHtml(",
+ "sanitizeHTML(",
+ ];
html_sanitizers.iter().any(|pat| code.contains(pat))
}