-
Notifications
You must be signed in to change notification settings - Fork 3
/
onbeforescriptexecute.html
146 lines (132 loc) · 4.72 KB
/
onbeforescriptexecute.html
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8" />
<meta name="author" content="jspenguin2017" />
<script>
// Library code, licensed under MIT
(() => {
"use strict";
const Event = class {
constructor(script, target) {
this.script = script;
this.target = target;
this._cancel = false;
this._replace = null;
this._stop = false;
}
preventDefault() {
this._cancel = true;
}
stopPropagation() {
this._stop = true;
}
replacePayload(payload) {
this._replace = payload;
}
};
let callbacks = [];
window.addBeforeScriptExecuteListener = (f) => {
if (typeof f !== "function") {
throw new Error("Event handler must be a function.");
}
callbacks.push(f);
};
window.removeBeforeScriptExecuteListener = (f) => {
let i = callbacks.length;
while (i--) {
if (callbacks[i] === f) {
callbacks.splice(i, 1);
}
}
};
const dispatch = (script, target) => {
if (script.tagName !== "SCRIPT") {
return;
}
const e = new Event(script, target);
if (typeof window.onbeforescriptexecute === "function") {
try {
window.onbeforescriptexecute(e);
} catch (err) {
console.error(err);
}
}
for (const func of callbacks) {
if (e._stop) {
break;
}
try {
func(e);
} catch (err) {
console.error(err);
}
}
if (e._cancel) {
script.textContent = "";
script.remove();
} else if (typeof e._replace === "string") {
script.textContent = e._replace;
}
};
const observer = new MutationObserver((mutations) => {
for (const m of mutations) {
for (const n of m.addedNodes) {
dispatch(n, m.target);
}
}
});
observer.observe(document, {
childList: true,
subtree: true,
});
})();
// Only works for hard coded scripts, dynamically inserted scripts
// will execute before it can be cancelled
//
// You can patch `Element.prototype.prepend`,
// `Element.prototype.append`, and related functions to interfere with
// dynamically inserted scripts
//
// Also, textContent is not always set properly, especially when the
// script is big
// Compatibility:
//
// Browser - Cancel Script - Change Script
// Chrome 67 - Yes - Yes
// Edge 41 - Yes - Yes
// Firefox 60 - Partially - Yes
//
// Only inline scripts can be cancelled on Firefox
// Example code, licensed under CC0-1.0
(() => {
"use strict";
window.onbeforescriptexecute = (e) => {
// You should check if textContent exists as this property is
// buggy sometimes
if (!e.script.textContent) {
return;
}
// Prevent execution of a script
if (e.script.textContent.includes("alert")) {
e.preventDefault();
}
// Change the code that runs
if (e.script.textContent.includes("console.log")) {
// Original payload is e.script.textContent, you can
// manipulate it however you want, just pass the final
// payload to e.replacePayload when you are done
e.replacePayload("console.log(2);");
// Later event handlers can override your payload, you
// can call e.stopPropagation to make sure the current
// payload is applied
}
};
})();
</script>
</head>
<body>
<script>alert(1);</script>
<script>console.log(1);</script>
</body>
</html>