Last active
June 4, 2026 17:43
-
-
Save webstrand/a89942e806ff7c49148c389f8a3a10a4 to your computer and use it in GitHub Desktop.
Render the javascript environment inimical to continued execution
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| (() => { | |
| 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