Skip to content

Instantly share code, notes, and snippets.

Show Gist options
  • Select an option

  • Save maple3142/c6593f0aff180bc0e4be5d549259da2c to your computer and use it in GitHub Desktop.

Select an option

Save maple3142/c6593f0aff180bc0e4be5d549259da2c to your computer and use it in GitHub Desktop.
polyfill of 'beforescriptexecute' event
(() => {
"use strict";
const BeforeScriptExecuteEvent = class {
constructor(script, target) {
this.script = script;
this.target = target;
this._cancel = false;
this._replace = null;
this._stop = false;
}
preventScriptExecution() {
this._cancel = true;
}
replaceScriptPayload(payload) {
this._replace = payload;
}
stopPropagation() {
this._stop = true;
}
};
let callbacks = [];
window.addBeforeScriptExecuteListener = (f) => {
callbacks.push(f);
};
window.removeBeforeScriptExecuteListener = (f) => {
for (let i = 0; i < callbacks.length; i++) {
if (callbacks[i] === f) {
callbacks.splice(i, 1);
i--;
}
}
};
const filter = (node, target) => {
if (node.tagName === "SCRIPT") {
dispatchEvent(node, target);
}
};
const dispatchEvent = (script, target) => {
const event = new BeforeScriptExecuteEvent(script, target);
if (window.onbeforescriptexecute instanceof Function)
window.onbeforescriptexecute(event);
for (let i = 0; i < callbacks.length; i++) {
if (event._stop) break;
callbacks[i](event);
}
if (event._cancel)
script.remove();
else if (typeof event._replace === "string")
script.textContent = event._replace;
};
const observer = new MutationObserver((mutations) => {
for (let i = 0; i < mutations.length; i++) {
for (let j = 0; j < mutations[i].addedNodes.length; j++) {
filter(mutations[i].addedNodes[j], mutations[i].target);
}
}
});
observer.observe(document, {
childList: true,
subtree: true,
});
})();
if (Math.random() > 100) {
//Quick start:
window.onbeforescriptexecute = (event) => {
if (event.script.textContent && event.script.textContent.includes("alert")) {
console.log("Script blocked!");
event.preventScriptExecution();
}
};
//Only work for "hard coded" inline scripts (see test.html), dynamically inserted scripts will execute before I can cancel it
//I think you have to patch `eval`, `Element.prototype.append`, and related functions to interfere with dynamically inserted scripts
//Also textContent is not always set properly when the page is cached
}
<!DOCTYPE html>
<html>
<head>
<script>
(() => {
"use strict";
const BeforeScriptExecuteEvent = class {
constructor(script, target) {
this.script = script;
this.target = target;
this._cancel = false;
this._replace = null;
this._stop = false;
}
preventScriptExecution() {
this._cancel = true;
}
replaceScriptPayload(payload) {
this._replace = payload;
}
stopPropagation() {
this._stop = true;
}
};
let callbacks = [];
window.addBeforeScriptExecuteListener = (f) => {
callbacks.push(f);
};
window.removeBeforeScriptExecuteListener = (f) => {
for (let i = 0; i < callbacks.length; i++) {
if (callbacks[i] === f) {
callbacks.splice(i, 1);
i--;
}
}
};
const filter = (node, target) => {
if (node.tagName === "SCRIPT") {
dispatchEvent(node, target);
}
};
const dispatchEvent = (script, target) => {
const event = new BeforeScriptExecuteEvent(script, target);
if (window.onbeforescriptexecute instanceof Function)
window.onbeforescriptexecute(event);
for (let i = 0; i < callbacks.length; i++) {
if (event._stop) break;
callbacks[i](event);
}
if (event._cancel)
script.remove();
else if (typeof event._replace === "string")
script.textContent = event._replace;
};
const observer = new MutationObserver((mutations) => {
for (let i = 0; i < mutations.length; i++) {
for (let j = 0; j < mutations[i].addedNodes.length; j++) {
filter(mutations[i].addedNodes[j], mutations[i].target);
}
}
});
observer.observe(document, {
childList: true,
subtree: true,
});
})();
window.onbeforescriptexecute = (event) => {
if (event.script.textContent && event.script.textContent.includes("alert")) {
console.log("Script blocked!");
event.preventScriptExecution();
}
};
</script>
</head>
<body>
<script>alert(1);</script>
</body>
</html>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment