window.CONFIG with markup
The app reads an optional global: const url = (window.CONFIG && window.CONFIG.url) || '/api/default'.
Inject HTML below (it is attached to the page so the browser's named access is live, but
<script> and on* handlers are stripped — clobbering needs neither).
Compare the vulnerable reader against the hardened one.
window.CONFIG resolves toInject markup to inspect…
loadScript(url) target—
form.submit
A control named submit shadows the form's submit() method. Inject the field,
then see how typeof form.submit flips from function to
object — calling it would throw. The fix borrows the real method.
form.submit—
HTMLFormElement.prototype.submit—
<a id="foo"> → window.foo // the element
<a id="x"><a id="x"> → window.x // HTMLCollection
<form id="f">
<input name="y"> → f.y // the control
<input name="length"> → f.length // clobbers the property
Defense:
document.getElementById('foo') // Element | null, never a collection
el instanceof HTMLScriptElement // verify the type you expect
HTMLFormElement.prototype.submit.call(f)// borrow the real method
DOMPurify.sanitize(html, { // remove the named-access surface
SANITIZE_NAMED_PROPS: true, FORBID_ATTR: ['id','name'],
})