Skip to content

Instantly share code, notes, and snippets.

@webstrand
Last active June 4, 2026 17:43
Show Gist options
  • Select an option

  • Save webstrand/a89942e806ff7c49148c389f8a3a10a4 to your computer and use it in GitHub Desktop.

Select an option

Save webstrand/a89942e806ff7c49148c389f8a3a10a4 to your computer and use it in GitHub Desktop.
Render the javascript environment inimical to continued execution
(() => {
const uncurryThis = Function.prototype.bind.bind(Function.prototype.call);
const shift = uncurryThis(Array.prototype.shift);
const unshift = uncurryThis(Array.prototype.unshift);
const push = uncurryThis(Array.prototype.push);
const setHas = uncurryThis(Set.prototype.has);
const setAdd = uncurryThis(Set.prototype.add);
const entries = Object.entries;
const getOwnPropDesc = Object.getOwnPropertyDescriptors;
const defineProp = Object.defineProperty;
const getProto = Object.getPrototypeOf;
const prom = Promise;
const str = String;
const log = globalThis.console.log;
const schedule = window.setTimeout.bind(window);
const yieldOnce = () => new prom((res) => { log("poisoning"); schedule(res, 0); });
const get = (key) => { throw 'disabled' };
const deadDesc = { __proto__: null, get, configurable: false };
async function poison() {
const visited = new Set([globalThis, prom.prototype]);
const work = [globalThis];
//const workProv = ["globalThis"];
let target, i = 0;
while ((target = shift(work)) != null) {
//const prov = shift(workProv);
//log("working on ", prov);
if (++i % 200 === 0) await yieldOnce();
const t = typeof target;
if (t !== 'object' && t !== 'function') continue;
// snapshot own descriptors before poisoning, so captured child refs are
// unaffected by us poisoning this target's own props.
let descs;
try { descs = entries(getOwnPropDesc(target)); } catch (e) { /* exotic/cross-origin */
// still try to follow its prototype even if we can't read own props
try {
const p = getProto(target);
if (p != null && !setHas(visited, p)) {
setAdd(visited, p);
push(work, p);
//push(workProv, `${prov}.__proto__`);
}
} catch (e2) { }
continue;
}
try {
for (let j = 0; j < descs.length; j++) {
const { 0: key, 1: desc } = descs[j];
// poison this own property into a throwing getter
try {
defineProp(target, key, deadDesc);
} catch { }
// enqueue the value reference (data props only) to keep walking.
if (desc && 'value' in desc) {
const v = desc.value;
if (v != null && (typeof v === 'object' || typeof v === 'function') &&
!setHas(visited, v)) {
setAdd(visited, v);
unshift(work, v);
//unshift(workProv, `${prov}.${key}`)
}
}
}
}
catch { }
// descend into prototypes, too
try {
const proto = getProto(target);
if (proto != null && !setHas(visited, proto)) {
setAdd(visited, proto);
push(work, proto);
//push(workProv, `${prov}.__proto__`);
}
} catch { }
}
}
Object.defineProperty(globalThis, "poison", { value: poison });
})()
let log = globalThis.console.log; poison().then(() => log("poisoning DONE"), (e) => log("poisoning FAILED", e))
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment