DOM XSS, Prototype Pollution and Client-Side Gadgets
Sources and sinks that never reach the server, CSP bypass, DOM clobbering, client-side path traversal and prototype pollution on both sides of the wire.
Client-side bugs are gadget problems. The injection point is rarely the interesting part; what the page already contains decides whether it executes.
Server-Side Prototype Pollution
An ordinary-looking JSON object may alter properties inherited by future objects when merge logic reaches __proto__ or constructor.prototype. Prototype pollution in Node.js appears when controlled properties reach the global prototype or another shared prototype. Impact depends on gadgets. Pollution becomes RCE, auth bypass or SSRF only when later code consumes the inherited property unsafely. I first prove pollution with a harmless property, then search for gadgets in application code or known libraries. Object.create(null), schema validation and safe merge logic reduce exposure. Blocking only proto may leave constructor/prototype paths.
1
2
3
4
5
{
"__proto__": {
"polluted": "yes"
}
}
Client-Side Prototype Pollution
Polluting the browser prototype is only half the work. Impact appears when a gadget turns that property into DOM XSS or another dangerous action. Common sources include query strings, fragments and JSON. Libraries parse and merge data before the application consumes the object. A gadget is code that reads an inherited property and forwards it to a sink such as innerHTML, script.src, eval or sensitive configuration. I use a canary property to prove pollution and then search loaded code for optional property reads. A scanner that finds accepted __proto__ without a gadget proves the primitive, not XSS. The report should separate both stages.
1
2
3
4
/?__proto__[polluted]=yes
// console
({}).polluted
DOM XSS
Some XSS never appears in any server response because the payload is created and executed entirely inside the browser. DOM XSS connects a controllable source such as location, postMessage or storage to an executable JavaScript sink. innerHTML, document.write, eval, Function and javascript URLs are classic sinks, while frameworks add their own wrappers. I trace the value through DevTools or DOM Invader, confirm transformations and prove execution with a minimal payload. Server-side encoding does not fix data that never passed through the server. The fix belongs in client code and in the sink selection.
1
2
const name = new URLSearchParams(location.search).get('name');
document.querySelector('#out').innerHTML = name;
CSP Bypass
CSP does not need unsafe-inline to be bypassed. Trust in the wrong script path, nonce or gadget can be enough. Content Security Policy restricts script sources and execution styles. Nonces and hashes are stronger than broad host allowlists. JSONP, framework gadgets, attacker-controlled JavaScript on trusted hosts and nonce reuse may create bypasses depending on policy. I parse the policy, identify the effective script-src and test each trusted origin for a real path to attacker-controlled JavaScript. CSP is defense in depth against XSS. A bypassable policy does not create XSS by itself and the underlying XSS remains the primary bug.
1
Content-Security-Policy: script-src 'nonce-r4nd0m' 'strict-dynamic' https:; object-src 'none'
DOM Clobbering
HTML without JavaScript can change the object application code reads and push an unexpected value into a dangerous sink. DOM clobbering uses elements with id or name attributes to create properties on window, document or forms. Code expecting a JavaScript object may receive a DOM element and later use href, src or another controlled string in a sensitive path. I look for implicit globals and fallbacks such as window.config || {} and test harmless markup before moving toward an executable sink. A sanitizer that strips scripts may still allow the ids and names required for clobbering. Defense needs to consider the DOM namespace.
1
<a id=config name=url href='https://example.test/payload.js'></a>
Client-Side Path Traversal
Path traversal also exists on the client when JavaScript uses controlled input to build the application’s next request. Client-side path traversal appears when a controllable path component is concatenated into fetch, axios or another request builder. The browser sends application cookies and headers to the final endpoint according to origin and code behavior. This may reach routes not exposed through the UI. I identify the source, observe the final URL in DevTools and test traversal or encoding while staying inside the authorized origin. Impact depends on method, body and credentials of the generated request. An internal GET and an authenticated DELETE create very different risk.
1
fetch('/api/files/' + userControlledName)