---
name: dom-vulnerability-static-analysis
description: Static code analysis for DOM-based vulnerabilities in client-side JavaScript. USE WHEN performing pre-commit reviews, auditing large codebases without dynamic execution, triaging minified code for XSS issues, verifying sanitization routines, analyzing source-to-sink data flows, OR reviewing JavaScript files for potential DOM XSS. Covers source identification, data flow tracing, sink detection, sanitization assessment, postMessage handler review, framework-specific constructs, and automated pattern scanning with AST-based tools.
---

# DomVulnerabilityStaticAnalysis

Static code analysis framework for identifying DOM-based vulnerabilities in client-side JavaScript without dynamic execution.

## Overview

DOM-based vulnerabilities occur when untrusted data flows from a source to a sink entirely on the client. Static analysis involves examining the source code to identify these flows without executing the page. According to Intigriti's guide, hunting DOM-based XSS via static code analysis requires manually reviewing JavaScript files, searching for DOM sources, and tracing references forward through the code. Mozilla's research stresses that to avoid DOM XSS bugs we need to inspect patterns where a string is parsed into HTML, such as assignments to innerHTML or outerHTML, calls to insertAdjacentHTML(), and document.write().

## When to Use This Skill

Activate this skill when:

* You must audit JavaScript for DOM-based vulnerabilities but cannot run the code in a browser (e.g., during pre-commit reviews or reviewing minified code)
* You need to triage large codebases for potential XSS issues and create high-signal leads for further manual inspection
* You want to verify whether existing sanitisation routines correctly protect assignments to DOM sinks
* You are performing automated security scanning at scale
* You need to analyze minified or obfuscated JavaScript files

## Static Analysis Methodology

The static analysis process can be broken down into the following steps. Be systematic and record all findings for follow-up testing.

### 1. Identify Attacker-Controlled Sources

Begin by listing all the JavaScript constructs that can carry user input:

* **URL components:** `window.location`, `location.search`, `location.hash`, `location.pathname`, and related properties
* **Document referrer:** `document.referrer`
* **Cookies and storage:** `document.cookie`, `localStorage`, `sessionStorage`
* **Window name:** `window.name`
* **Post messages:** `event.data` in message handlers
* **Third-party inputs:** Data returned from APIs, query parameters passed into functions, and imported JSON or YAML files

Search across the codebase for these properties. Use a case-sensitive search (grep, ripgrep, or your IDE's find-in-files) to build a list of occurrences. For minified or obfuscated files, consider using a beautifier to improve readability.

### 2. Trace Data Flow to Sinks

Once you locate a source, trace how the data moves through variables. Follow assignments, function arguments, and returns to determine where the value is eventually used. It may help to annotate the code with comments or to draw a simple flow diagram. Keep in mind that static analysis cannot determine the run-time value of a variable; treat any variable assigned from a source as untrusted until proven otherwise.

### 3. Locate Dangerous Sinks

Identify places where untrusted data is inserted into the DOM or executed as code. Key patterns to look for include:

| Sink Category | Examples |
|---------------|----------|
| **HTML injection** | Assignments to `.innerHTML` and `.outerHTML`, calls to `insertAdjacentHTML()`, `document.write()` and `document.writeln()`, jQuery's `.html()` and `.append()` methods |
| **Code execution** | `eval()`, `Function()`, `setTimeout()/setInterval()` when called with a string, or constructing `<script>` elements with dynamic src attributes |
| **URL redirection** | Assignments to `location`, `location.href`, `location.assign()`, `window.open()` and setting src/href attributes on `<a>`, `<iframe>`, `<img>`, etc. |
| **Template evaluation** | Framework-specific directives such as AngularJS `ng-init`, Vue `v-bind`/`v-on`, or htmx `hx-on` that interpolate expressions |

Write down each instance where data flows from an untrusted variable into one of these sinks. If the right side of an assignment is a literal string, it is usually safe. Otherwise, proceed to the next step.

### 4. Assess Sanitisation and Validation

Review any filtering or sanitisation logic applied to the data before it reaches a sink. Ask these questions:

* **Is a sanitizer used?** Functions like `DOMPurify.purify()` are recognised as safe in tooling like Mozilla's ESLint plugin. If the code calls a known sanitizer (e.g., `DOMPurify.sanitize()`, `escapeHTML()`), it is likely safe.
* **Are regex patterns robust?** Filters must anchor both the beginning and end of the string. Patterns such as `/([a-zA-Z0-9]+|\s)+$/` allow attackers to prepend malicious content. Ensure the pattern uses `^` and `$` to bound the entire string.
* **Do URL validators parse correctly?** Functions that call `new URL()` may misparse schemes if the input is malformed. Reject ambiguous formats like `https:example.com` and validate both protocol and host.
* **Are types enforced?** Static analysis cannot inspect run-time types, so treat any input that is not explicitly converted or validated as a potentially dangerous string. Arrays or objects may call custom `toString()` methods that return `javascript:` schemes. Confirm type checking is in place.

Document any sanitisation functions encountered. Where the filter logic appears insufficient or absent, flag the instance for further review.

**⚠️ Critical Warning:** If a function is called `escapeHTML` but it IS NOT a standard library function, you should review the function FIRST before flagging it as safe. A common pattern is developers naming their own validation functions which can often be bypassed.

* If this is the case, examine the function and understand how it could be bypassed and deem it weak or insecure.

### 5. Examine postMessage Handlers

Cross-origin messaging is handled via `window.postMessage()`. When reviewing static code:

1. **Locate handlers:** Search for `addEventListener('message', ...)` and assignments to `onmessage`.
2. **Check origin validation:** Ensure the code compares `event.origin` to a trusted, exact origin rather than using `startsWith()` or `includes()`. Static analysis should flag comparisons to `window.origin` or cases where no origin check is performed. Accept only messages from known domains.
3. **Validate message data:** Avoid executing functions based on `event.data`. Look for patterns like `window[data.func](data.payload)` and flag them. Data should be parsed (e.g., `JSON.parse()`) and validated against a strict schema before use.

### 6. Review Framework-Specific Constructs

Modern frameworks introduce their own templating and directive mechanisms. When reviewing code, search for:

* **AngularJS:** Look for `ng-init`, `ng-app`, or double curly `{{...}}` interpolation. If user input is bound inside these expressions without sanitisation, it may trigger client-side template injection.
* **Vue.js:** Look for `v-bind:`, `v-on:`, and mustache syntax. Binding untrusted data directly can lead to execution of functions such as `this.constructor.constructor('alert(1)')()`.
* **htmx:** Check attributes like `hx-on`, `hx-trigger`, and `hx-target`. Malformed attribute names or values may break parsing rules and execute injected code.

Flag any directive or binding where user data is inserted into template expressions or directive attributes.

### 7. Perform En-Masse Pattern Scanning

For large codebases, manual inspection is impractical. Use automated searches to highlight high-risk patterns:

1. **Regex scanning:** Search for assignments to known sinks using regular expressions (e.g., `\.innerHTML\s*=`, `\.outerHTML\s*=`, `insertAdjacentHTML\(`, `document\.write\(`). Tools like ripgrep can search recursively and provide line numbers. Once this is done, it is vital to trace and understand context to understand if the flow is accessible, how it is accessible and what sources can reach it.

2. **AST-based tools:** Consider using linting plugins or static analysis tools such as `eslint-plugin-no-unsanitized` (for JavaScript) which parse the Abstract Syntax Tree and differentiate between hardcoded literals and variables. These tools reduce false positives and highlight flows that require review.

3. **Source-to-sink analysis:** If your tooling supports taint analysis, run it to automatically trace untrusted sources to sinks. Treat any flagged path as a high priority for manual verification.

4. **Whitelisting safe patterns:** Maintain a list of approved sanitisation functions (e.g., `DOMPurify.purify`, `escapeHTML`) and configure your scanner to treat assignments wrapped in these functions as safe. Anything else should be flagged for human review. Remember, if a function is called `escapeHTML` but it IS NOT a standard library, you should review the function FIRST before flagging it as safe. A common pattern is developers naming their own functions to validate which can often be bypassed.
   * If this is the case, examine the function and understand how it could be bypassed and deem it weak or insecure.

Document the results in a spreadsheet or issue tracker. For each finding, record the file, line number, source variable, sink call, and any sanitiser observed. This helps prioritise follow-up audits.

### 8. Flag and Communicate Findings

Static analysis produces potential vulnerabilities, not definitive exploits. For each flagged instance:

* **Describe the source–sink flow:** Indicate the untrusted input and the sink it reaches. Example: "queryParam → innerHTML without sanitisation."
* **Assess sanitisation:** Note whether a recognised sanitiser is used or if a custom filter appears weak (e.g., regex missing anchors).
* **Prioritise by risk:** Highlight sinks that lead to code execution (`eval`, `Function`) and critical flows like client-side redirects. Provide examples of potential payloads to illustrate impact.
* **Recommend remediation:** Suggest encoding output, validating inputs, using frameworks' safe APIs (e.g., `textContent` instead of `innerHTML`), or introducing Trusted Types. Encourage developers to adopt automated linting to prevent regressions.

## High-Signal Patterns for Static Review

During static analysis, prioritise the following patterns as they often indicate vulnerabilities:

1. Assignments to `.innerHTML`/`.outerHTML` with a variable rather than a literal string
2. Calls to `document.write()`, `document.open().write()`, `append()` or `html()` where the argument is user-controlled
3. Construction of `<script>`, `<iframe>`, or `<img>` elements with dynamic src/href attributes derived from user input
4. Functions that build URLs by concatenating strings rather than using safe URL constructors, and then assign them to `location` or `window.open()`
5. Regex validations that do not anchor the start and end of the string
6. Origin checks in postMessage handlers that use `startsWith()` or compare to `window.origin` instead of explicit allowed origins
7. Conditional logic that branches on untrusted data and calls dynamic execution functions like `eval()`, `Function()`, or `setTimeout()`
8. Framework directives or template interpolations that embed variables without sanitisation

## Automated Scanning Tools

Consider using these tools to accelerate static analysis:

* **eslint-plugin-no-unsanitized** - Mozilla's ESLint plugin that detects unsafe DOM manipulation
* **Semgrep** - Pattern-based code scanner with DOM XSS rules
* **CodeQL** - GitHub's semantic code analysis engine with JavaScript security queries
* **ripgrep/grep** - For quick regex-based searches across codebases
* **js-beautify** - For deobfuscating and formatting minified JavaScript before analysis

## Analysis Workflow

Follow this systematic workflow for large codebases:

```
1. Setup
   ├─ Collect all JavaScript files
   ├─ Deobfuscate/beautify minified code
   └─ Set up linting tools (ESLint, Semgrep)

2. Automated Scan
   ├─ Run AST-based tools (eslint-plugin-no-unsanitized)
   ├─ Run pattern matchers (Semgrep/CodeQL)
   ├─ Search for source patterns (location.*, document.cookie, etc.)
   └─ Search for sink patterns (.innerHTML, eval, etc.)

3. Manual Triage
   ├─ Review each flagged instance
   ├─ Trace data flow from source to sink
   ├─ Assess sanitization effectiveness
   └─ Document findings with risk rating

4. Reporting
   ├─ Categorize by severity (Critical/High/Medium/Low)
   ├─ Provide proof-of-concept payloads
   ├─ Recommend specific remediation
   └─ Track fixes and retest
```

## Examples

**Example 1: Scan codebase for innerHTML assignments**
```
User: "Find all potentially unsafe innerHTML assignments in this repository"
→ Use ripgrep to search for \.innerHTML\s*= patterns
→ Filter out assignments with literal strings
→ Trace remaining assignments back to data sources
→ Flag flows from location.*, document.referrer, or event.data
→ Generate report with file paths, line numbers, and risk assessment
```

**Example 2: Audit custom sanitization function**
```
User: "Review this escapeHTML function to see if it's secure"
→ Read function implementation
→ Check if it uses proper encoding (HTML entity encoding)
→ Test against known bypass patterns (<img src=x onerror=alert(1)>)
→ Verify it handles all contexts (attributes, text nodes, URLs)
→ Report weaknesses and recommend using DOMPurify instead
```

**Example 3: Triage large JavaScript bundle**
```
User: "This 5MB minified bundle may have DOM XSS vulnerabilities"
→ Beautify the bundle using js-beautify
→ Run eslint-plugin-no-unsanitized across the formatted code
→ Search for high-risk patterns (eval, Function, innerHTML assignments)
→ Prioritize findings by examining source-to-sink flows
→ Create ranked list of vulnerabilities for manual verification
```

**Example 4: Analyze postMessage handler security**
```
User: "Check if these postMessage handlers are secure"
→ Search for addEventListener('message', ...) patterns
→ Extract each handler function
→ Verify presence of event.origin validation
→ Check if validation uses strict equality (===) with whitelisted origins
→ Look for dangerous patterns like window[event.data.func]()
→ Document insecure handlers with exploitation scenarios
```

## References

* [PortSwigger Web Security Academy – DOM-based XSS](https://portswigger.net/web-security/cross-site-scripting/dom-based)
* [Intigriti – Hunting DOM XSS via Static Code Analysis](https://blog.intigriti.com)
* [Mozilla – eslint-plugin-no-unsanitized](https://github.com/mozilla/eslint-plugin-no-unsanitized)
* [Practical CTF: Cross-Site Scripting and postMessage Exploitation](https://google.com)
* Youssef Sammouda – DOM XSS write-ups
* [Client-Side Security](https://book.jorianwoltjer.com/web/client-side)
* [XSS Deep Dive](https://book.jorianwoltjer.com/web/client-side/cross-site-scripting-xss)
* [WebSockets Security](https://book.jorianwoltjer.com/web/client-side/websockets)

---

**License:** CC-BY-4.0

This skill provides a comprehensive checklist for static analysis of client-side code. It complements dynamic testing by identifying flows worth exploring further when run-time analysis is possible.
