JavaScript Unsafe DOM Manipulation Checker
Cross-Site Scripting (XSS) vulnerabilities often occur through unsafe DOM manipulation methods. This checker detects potentially dangerous DOM operations that could lead to XSS attacks.
Step 1: Writing the test file
First, let's create a test file that covers various unsafe DOM manipulation patterns. Create .globstar/js_unsafe_dom.test.js
:
javascript
function test_dangerous_dom_operations() {
const userInput = getUserInput();
const element = document.getElementById('content');
// These should be flagged
// <expect-error>
element.innerHTML = userInput;
// <expect-error>
element.innerHTML = "<div>" + userInput + "</div>";
// <expect-error>
element.insertAdjacentHTML('beforeend', `${userInput}`);
// These are safe and should not be flagged
// Safe - using textContent
element.textContent = userInput;
// Safe - using createElement
const div = document.createElement('div');
div.textContent = userInput;
element.appendChild(div);
// Safe - using static HTML
element.innerHTML = '<div>Static content</div>';
}
function test_edge_cases() {
const element = document.querySelector('.content');
// Should not flag property access
const currentHTML = element.innerHTML;
// Should not flag non-HTML string concatenation
const message = "Hello, " + username;
// Should not flag commented code
// element.innerHTML = userInput;
}
// Helper function to simulate user input
function getUserInput() {
return "user provided content";
}
Our test file:
- Includes common unsafe DOM manipulation patterns
- Shows safe alternatives using proper DOM methods
- Covers edge cases to avoid false positives
- Uses
<expect-error>
to mark lines that should trigger the checker
Step 2: Writing the checker
Now that we have our test file ready, let's create the checker in .globstar/js_unsafe_dom.yml
:
yaml
language: javascript
name: js_unsafe_dom
message: "Possible XSS vulnerability: Use textContent, createElement, or proper HTML sanitization instead of innerHTML."
category: security
severity: critical
pattern: >
[
(assignment_expression
left: (member_expression
property: (property_identifier) @prop)
right: [
(identifier)
(binary_expression)
(template_string)
]
(#eq? @prop "innerHTML")) @js_unsafe_dom
(call_expression
function: (member_expression
property: (property_identifier) @method)
arguments: (arguments
[
(identifier)
(template_string)
(binary_expression)
])
(#eq? @method "insertAdjacentHTML")) @js_unsafe_dom
]
filters:
- pattern-inside: (function_declaration)
exclude:
- "test/**"
- "*_test.js"
- "tests/**"
- "__tests__/**"
description: |
Cross-Site Scripting (XSS) vulnerabilities can occur when using unsafe DOM manipulation
methods with unsanitized input. Methods like innerHTML and insertAdjacentHTML can
execute arbitrary JavaScript if they contain malicious content.
Let's break down how this checker matches our test cases:
First Pattern Block
(assignment_expression left: (member_expression property: (property_identifier) @prop) right: [...])
This matches assignments to innerHTML, checking for various types of potentially unsafe values.
Second Pattern Block
(call_expression function: (member_expression property: (property_identifier) @method) arguments: (arguments ...))
This matches calls to insertAdjacentHTML with potentially unsafe arguments.
Testing the checker
Run the checker against your test file:
bash
globstar test