dom-vulnerability-detection · git:20260504.2a64374 · 2026-05-04 · sha256 f75295bd1352c7d5

dom-vulnerability-detection git:20260504.2a64374B

Immutable. This exact content is served forever at /api/v1/blob/f75295bd1352c7d5.

---
name: dom-vulnerability-detection
description: DOM-based XSS and client-side vulnerability detection for code review and dynamic analysis. USE WHEN auditing JavaScript code, reviewing client-side security, analyzing DOM manipulation, testing postMessage handlers, checking URL validation, investigating client-side template injection (AngularJS, Vue.js, htmx), OR identifying attacker-controlled sources and dangerous sinks. Covers source tracing, sink identification, sanitization validation, browser quirks (DOM clobbering, mutation XSS), and framework-specific vulnerabilities.
---

# DynamicDomVulnerabilityDetection

Comprehensive framework for identifying DOM-based cross-site scripting (XSS) and related client-side vulnerabilities during code review and dynamic analysis.

## Overview

DOM-based vulnerabilities occur when JavaScript reads data from an attacker-controlled source (for example, the URL or a cross-origin message) and sends it to a sink that interprets it as HTML or JavaScript. This skill summarizes the critical concepts and patterns needed to detect and exploit these issues, based on research across multiple security resources.

## When to Use This Skill

Activate this skill whenever you are asked to:
* Review JavaScript or HTML for potential cross-site scripting vulnerabilities
* Assess whether user-controlled data can reach dangerous DOM APIs (innerHTML, document.write, eval, etc.)
* Audit custom safe-URL helpers or regex filters for URL or input validation
* Investigate cross-window messaging (postMessage) logic for origin and reference checks
* Test single-page applications or front-end frameworks (AngularJS, Vue.js, htmx, etc.) for client-side template injection (CSTI)

## Step-by-Step Analysis Framework

### 1. Identify Attacker-Controlled Sources

Look for any of the following sources of input. These can carry malicious payloads if not properly sanitised:

* **Location:** `window.location`, `location.search`, `location.hash`, and related properties. Path and fragment data may be used without encoding.
* **Referrer:** `document.referrer` can contain untrusted data.
* **Cookies and storage:** Values from `document.cookie`, `localStorage`, etc., may originate from another subdomain or previous XSS.
* **Window name:** `window.name` is cross-origin writable and readable.
* **Messages:** Data passed via `postMessage` (the `e.data` property) is arbitrary and untrusted.

Use search tools (Ctrl+Shift+F in DevTools or grep) to find these sources in JavaScript files.

### 2. Trace the Data Flow

After locating a source, trace each assignment or transformation until you reach a sink. Keep track of variable names as they are reassigned. Use breakpoints in DevTools to inspect variable values at runtime.

### 3. Locate Dangerous Sinks

Common DOM sinks include:

* **HTML injection:** Assignments to `.innerHTML`, `.outerHTML`, `.insertAdjacentHTML`, calls to `document.write()`, `document.open()` ... `.write()` `.close()`, or jQuery's `.html()` and `.append()`.
* **URL redirects:** Assigning unvalidated strings to `location`, `location.href`, `location.assign()`, `window.open()`, or setting attribute values such as `element.src` or `element.href`.
* **Code execution:** Calls to `eval()`, `Function()`, `setTimeout()` or `setInterval()` with string arguments. jQuery's `$()` selector and other library helpers may implicitly call `.innerHTML`.
* **Template expressions:** AngularJS attributes like `ng-init` and Vue.js interpolation (`{{ ... }}`) interpret input as code.

If user-controlled data flows into any of these sinks, test for XSS by injecting a harmless payload such as `<img src onerror=alert(1)>` for HTML sinks, or `alert(1)` for JavaScript sinks. Remember that `<script>` tags do not execute when added via `.innerHTML`; use event handlers on `<img>` or `<iframe>` instead.

### 4. Evaluate Sanitisation and Filters

When filters or helpers are used, verify their correctness:

* **Regex validation:** Ensure regular expressions anchor the start of the string (`^`) as well as the end (`$`). A trailing `$` alone allows attackers to prepend arbitrary HTML. Avoid patterns like `/([a-zA-Z0-9]+|\s)+$/` which only constrain the end of the string.
* **Safe URL functions:** Be cautious of helpers that use `new URL(userInput, location)` and compare `.origin` against `window.location.origin`. If an attacker omits `//` in a URL (`https:example.com`), the relative parser may treat the URL as a local path, whereas the absolute parser will resolve to `https://example.com`. Parse the URL only once and validate both protocol and host.
* **Type coercion:** Do not trust objects simply because they are not strings. Passing an array with a malicious `toString()` to a URL helper can produce `javascript:` schemes. Always check the type explicitly.

### 5. Examine Browser Quirks

Be aware of subtle DOM behaviours that can be abused:

* **DOM clobbering:** HTML elements become global variables on `window` via their `id` attribute. An attacker can inject an element like `<img id="CONFIG_SRC" data-url="https://attacker.com/poc.js">` so that `window.CONFIG_SRC.dataset.url` points to a malicious script. Avoid using `window.<id>` to reference elements and use `document.getElementById()` instead.
* **Race conditions:** When asynchronous functions like `requestIdleCallback()` load dynamic scripts, an attacker may manipulate elements between the check and the use. Ensure checks occur immediately before inserting the script and do not rely on timed order.
* **Mutation XSS:** Some sanitizers (e.g. DOMPurify) can be bypassed when browsers re-interpret mutated HTML. Test payloads like `<svg><g onload=alert(1)></g></svg>` or unusual Unicode characters.

### 6. Investigate postMessage Handlers

Cross-window communication via `window.postMessage()` is a common source of client-side vulnerabilities. Follow these steps:

1. **Enumerate handlers:** Search for `addEventListener('message', ...)` or assignments to `onmessage`. Use browser extensions like postMessage-tracker to log messages.
2. **Verify origin checks:** A handler must strictly compare `e.origin` to an exact, whitelisted origin or to `window.location.origin`. Avoid `startsWith()` or `endsWith()` checks; subdomain tricks like `https://example.com.attacker.com` or `anythingexample.com` bypass them. Never compare to `window.origin`; this property can be `'null'` and is easily spoofed.
3. **Inspect event.source:** Do not trust window references alone; attackers can hijack nested iframes or produce messages with `e.source=null` by deleting the iframe after sending the message. Always verify both the origin and the expected window reference.
4. **Sanitise message data:** Do not call functions based on `e.data`. Attackers can craft structured clone objects (arrays with additional properties) so that `window[data.func](data)` or similar patterns will invoke `setTimeout`, `constructor.constructor`, or other built-in executors. Accept only primitive types or explicitly parse JSON to discard prototype properties.
5. **Beware of null origins:** Sandboxed iframes (`<iframe sandbox="... allow-scripts">`) produce messages with `e.origin === 'null'`. Treat these as untrusted even when `window.origin` is also `'null'`.
6. **Check ordering in comparisons:** When using helper functions like `isSameOrigin`, ensure you compare the trusted origin against the untrusted one in the correct order. Passing arguments in reverse may inadvertently accept `'null'` origins.

### 7. Test Front-End Frameworks

Client-side template injection (CSTI) occurs when frameworks interpret user input as template expressions:

* **AngularJS (v1.x):** Attributes like `ng-init` and interpolation `{{constructor.constructor('alert(1)')()}}` execute arbitrary code when within an element that has `ng-app` or `data-ng-app`. Injection is also possible via `data-ng-init` and class-based syntax such as `class="ng-init:constructor.constructor('alert(1)')()"`. Newer Angular versions (v2+) are not vulnerable in this way.
* **Vue.js:** Within a Vue instance, payloads like `{{this.constructor.constructor('alert(1)')()}}` may execute. Vue also allows event handlers via attributes like `v-on` or `@click`.
* **htmx:** Attributes such as `hx-on="error:alert(1)"` or malformed attribute names (`hx-trigger="x[1)}),alert(2)//]"`) can break into evaluation.

When reviewing code using a framework, search for places where user input is inserted directly into templates or directive attributes without proper escaping. Test injecting `{{7*7}}` or other harmless expressions to see if the result is evaluated.

### 8. Perform Dynamic Testing

1. **Inject markers:** Modify the URL (query string, hash) or other sources with a unique token (e.g. `XSS_TEST`) and observe whether it appears in the DOM. Use DevTools search (Ctrl+F) to locate the token.
2. **Trigger events:** If the code uses the hash, load the page normally and then modify `window.location.hash` via JavaScript or by changing the URL.
3. **Set breakpoints:** Use DevTools to set breakpoints at points where sources are read or sinks are called. Step through the code to inspect variable values.
4. **Try payloads:** Replace the marker with payloads appropriate for the sink. For HTML sinks, `<img src=x onerror=alert(1)>` is a universal payload. For location assignments, use `javascript:alert(1)`. For eval or setTimeout, use plain `alert(1)` or craft arrays with properties as described above.
5. **Observe browser differences:** Some browsers encode `location.search` and `location.hash` automatically. Test in multiple browsers if a payload fails.

## Sources and Sinks Reference

| Category | Examples |
|----------|----------|
| **Sources** | `location.search`, `location.hash`, `location.pathname`, `document.referrer`, `document.cookie`, `window.name`, `event.data` in message handlers |
| **HTML sinks** | `.innerHTML`, `.outerHTML`, `.insertAdjacentHTML()`, `document.write()`, `document.open()/write()/close()`, jQuery's `.html()`, `.append()`, `.attr()` when setting src or href |
| **Execution sinks** | `eval()`, `Function()`, `setTimeout()/setInterval()` with string input, assignment of `javascript:` URLs to location or `<a href>` attributes |
| **DOM manipulations** | Creating `<script>` elements with dynamic src, using `document.createElement('iframe')` with user-controlled src, adding dynamic `<link>` or `<style>` elements |
| **Framework sinks** | AngularJS `ng-init`, `ng-app` expressions; Vue `v-bind`, `v-on` directives; htmx `hx-on`, `hx-trigger` |

## High-Signal Patterns to Watch For

During code review, prioritise code segments matching these patterns:

1. Assignments like `element.innerHTML = userInput` or `document.write(userInput)`. Ask where `userInput` originates.
2. Creation of `<script>` tags where the src is derived from user data or a `data-` attribute (`window.CONFIG_SRC.dataset.url`).
3. Safe-URL checks comparing `new URL(url, location).origin` to `location.origin` without verifying the scheme and host.
4. Use of `match()` or `test()` on user strings with regexes lacking a `^` anchor. Example: `/([a-zA-Z0-9]+|\s)+$/`.
5. Calls to `postMessage(message, '*')` and handlers lacking strict `e.origin` checks or using `window.origin`.
6. Handlers executing dynamic functions (`window[data.func](data)`, `callbacks[category][name](data)`), or calling `eval(e.data)`.
7. Use of `window.<id>` variables (DOM clobbering) or global variables not explicitly declared.
8. Framework attributes binding user input directly into template expressions (`{{...}}`, `ng-init`, `v-html`).

## Defensive Recommendations

Advise developers and testers to adopt the following defences:

* **Encode and escape outputs:** Use appropriate context-sensitive encoding functions (HTML encode, attribute encode, JavaScript encode) before inserting data into the DOM.
* **Use Trusted Types:** Enforce Trusted Types to prevent assignment of unsafe strings to DOM sinks.
* **Validate URLs strictly:** Parse user URLs once, require explicit schemes (`http://`, `https://`), and check both host and scheme against an allow-list. Reject ambiguous formats like `https:example.com`.
* **Anchor regex filters:** Use `^` and `$` in patterns and avoid using alternations that can be bypassed by prepending malicious data.
* **Sanitise message data:** Use structured formats like JSON for messages, and always validate `e.origin`. Never trust `window.origin` or treat `'null'` as trusted.
* **Avoid global element IDs:** When referencing elements, use `document.getElementById()` or scoped variables instead of relying on the global window object.
* **Limit dynamic script loading:** Avoid injecting scripts based on untrusted data. If dynamic loading is necessary, apply a strict allow-list of accepted paths or hosts.
* **Keep sanitizers up to date:** Libraries like DOMPurify should be regularly updated, and you should test for known bypasses (mutation XSS) when filtering user input.

## Examples

**Example 1: Audit URL parameter handling**
```
User: "Review this JavaScript file for XSS vulnerabilities"
→ Search for location.search, location.hash usage
→ Trace data flow to innerHTML, document.write, or eval sinks
→ Identify missing sanitization and report findings
```

**Example 2: Test postMessage handler security**
```
User: "Check if this postMessage handler is secure"
→ Verify e.origin validation uses strict equality
→ Check for window.origin usage (vulnerable)
→ Test with null origin from sandboxed iframe
→ Report bypasses and recommend fixes
```

**Example 3: Analyze client-side template injection**
```
User: "This AngularJS app accepts user input in the URL"
→ Test for ng-init or {{}} injection points
→ Try payload: {{constructor.constructor('alert(1)')()}}
→ Verify if template expressions execute
→ Document CSTI vulnerability and context
```

## References

* [PortSwigger Web Security Academy – DOM-based XSS](https://portswigger.net/web-security/cross-site-scripting/dom-based)
* [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)