| name | dom-clobbering-anti-pattern |
| description | Security anti-pattern for DOM Clobbering vulnerabilities (CWE-79 variant). Use when generating or reviewing code that accesses DOM elements by ID, uses global variables, or relies on document properties. Detects HTML injection that overwrites JavaScript globals. |
DOM Clobbering Anti-Pattern
Severity: Medium
Summary
DOM Clobbering overwrites global JavaScript variables via attacker-controlled HTML. Browsers auto-create global variables from id and name attributes. Enables logic bypasses, XSS, and security control evasion. Bypasses HTML sanitizers that allow id and name.
The Anti-Pattern
Application JavaScript relies on global variables that HTML injection overwrites. Code expects legitimate objects but receives DOM element references instead.
BAD Code Example
if (window.appConfig.isAdmin) {
showAdminPanel();
}
function submitForm() {
var form = document.getElementById('someForm');
form.submit();
}
GOOD Code Example
const myApp = {};
myApp.config = {
isAdmin: false
};
if (myApp.config.isAdmin) {
showAdminPanel();
}
function submitForm() {
var form = document.getElementById('someForm');
if (form instanceof HTMLFormElement) {
form.submit();
} else {
console.error("Error: 'someForm' is not a valid form element.");
}
}
Object.freeze(myApp.config);
React Example
BAD:
function AdminPanel() {
if (window.appConfig?.isAdmin) {
return <AdminControls />;
}
return <AccessDenied />;
}
GOOD:
import { useContext } from 'react';
import { ConfigContext } from './ConfigContext';
function AdminPanel() {
const config = useContext(ConfigContext);
if (config.isAdmin) {
return <AdminControls />;
}
return <AccessDenied />;
}
TypeScript Example
GOOD:
interface AppConfig {
isAdmin: boolean;
apiUrl: string;
}
const appConfig: AppConfig = {
isAdmin: false,
apiUrl: '/api'
};
function checkAdmin(): void {
if (appConfig.isAdmin) {
showAdminPanel();
}
}
function submitForm(formId: string): void {
const elem = document.getElementById(formId);
if (!(elem instanceof HTMLFormElement)) {
throw new TypeError(`Element ${formId} is not a form`);
}
elem.submit();
}
Detection
JavaScript Patterns:
- Global variable access:
window.config, document.userInfo
- Implicit globals:
config.isAdmin (no var/let/const declaration)
document.getElementById() without type validation
- Security checks on globals:
if (window.auth.isLoggedIn)
Search Patterns:
- Grep:
window\.[a-zA-Z]+\.|\bdocument\.[a-zA-Z]+\.|getElementById\(.*\)\.(?!tag|class)
- Look for:
if (window. or if (document.
- Check: Direct property access without instanceof check
HTML Sanitizer Review:
- DOMPurify: Check
FORBID_ATTR doesn't exclude id, name
- Sanitize-html: Review
allowedAttributes config
- HTML Purifier: Verify attribute whitelist
Manual Testing:
- Identify global variables: Check
window object in console
- Inject clobbering HTML:
<div id="globalVarName"></div>
- Verify override:
console.log(typeof window.globalVarName)
- Test security impact: Try bypassing checks
Prevention
Testing for DOM Clobbering
Manual Testing:
- Identify globals: Open browser console, type
window. and check autocomplete
- Test clobbering: Inject
<div id="targetGlobal"></div> via input fields
- Verify type change:
typeof window.targetGlobal should show object → object (Element)
- Test bypass: Check if security checks fail (admin access, auth bypass)
Automated Testing:
- Static Analysis: ESLint plugin
eslint-plugin-no-unsanitized
- Dynamic Testing: Burp Suite DOM Clobbering scanner
- Code Review: Search for
window. access patterns
- Type Checking: TypeScript strict mode catches many cases
Example Test:
describe('DOM Clobbering Protection', () => {
it('should prevent config object clobbering', () => {
const div = document.createElement('div');
div.id = 'appConfig';
document.body.appendChild(div);
expect(typeof myApp.config).toBe('object');
expect(myApp.config.isAdmin).toBe(false);
document.body.removeChild(div);
});
it('should validate form element types', () => {
const div = document.createElement('div');
div.id = 'loginForm';
document.body.appendChild(div);
expect( ()).();
});
});
Browser DevTools Check:
Object.keys(window).filter(key =>
typeof window[key] === 'object' &&
window[key] !== null &&
!window[key].toString().includes('[native code]')
);
Remediation Steps
- Identify global dependencies - Search for
window. and implicit globals
- Audit HTML sanitizer - Check if
id and name are allowed
- Create namespace - Move globals to module scope or namespace object
- Add type validation - Check
instanceof before using DOM elements
- Freeze critical objects - Use
Object.freeze() on config objects
- Update code - Replace
window.foo with myApp.foo
- Test protection - Verify clobbering attempts fail
- Enable TypeScript - Leverage type safety for additional protection
Related Security Patterns & Anti-Patterns
References