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.

Revisions

  1. maple3142 revised this gist Apr 6, 2018. 2 changed files with 2 additions and 2 deletions.
    2 changes: 1 addition & 1 deletion onbeforescriptexecute.js
    Original file line number Diff line number Diff line change
    @@ -42,7 +42,7 @@
    if (arguments[0].toLowerCase() === 'beforescriptexecute') addBeforeScriptExecuteListener(arguments[1])
    else addev.apply(null, arguments)
    }
    window.removeEventListener = (ev, ...args) => {
    window.removeEventListener = function() {
    if (arguments[0].toLowerCase() === 'beforescriptexecute') removeBeforeScriptExecuteListener(arguments[1])
    else rmev.apply(null, arguments)
    }
    2 changes: 1 addition & 1 deletion onbeforescriptexecute.min.js
    Original file line number Diff line number Diff line change
    @@ -1 +1 @@
    !function(){function e(e,t){this.script=e,this.target=t,this._cancel=!1,this._replace=null,this._stop=!1}e.prototype.preventDefault=function(){this._cancel=!0},e.prototype.stopPropagation=function(){this._stop=!0},e.prototype.replacePayload=function(e){this._replace=e};var t=[],n=window.addEventListener.bind(window),o=window.removeEventListener.bind(window);window.addEventListener=function(){'beforescriptexecute'===arguments[0].toLowerCase()?function(e){if(!e instanceof Function)throw new Error('Event handler must be a function.');t.push(e)}(arguments[1]):n.apply(null,arguments)},window.removeEventListener=((e,...n)=>{'beforescriptexecute'===arguments[0].toLowerCase()?function(e){for(var n=t.length;n--;)t[n]===e&&t.splice(n,1)}(arguments[1]):o.apply(null,arguments)});var r=function(n,o){var r=new e(n,o);if(window.onbeforescriptexecute instanceof Function)try{window.onbeforescriptexecute(r)}catch(e){console.error(e)}for(var i=0;i<t.length&&!r._stop;i++)try{t[i](r)}catch(e){console.error(e)}return r};new MutationObserver(e=>{for(var t=0;t<e.length;t++)for(var n=0;n<e[t].addedNodes.length;n++){var o=e[t].addedNodes[n];if('SCRIPT'===o.tagName){var i=r(o,e[t].target);i._cancel?o.remove():'string'==typeof i._replace&&(o.textContent=i._replace)}}}).observe(document,{childList:!0,subtree:!0})}()
    !function(){function e(e,t){this.script=e,this.target=t,this._cancel=!1,this._replace=null,this._stop=!1}e.prototype.preventDefault=function(){this._cancel=!0},e.prototype.stopPropagation=function(){this._stop=!0},e.prototype.replacePayload=function(e){this._replace=e};var t=[],n=window.addEventListener.bind(window),o=window.removeEventListener.bind(window);window.addEventListener=function(){"beforescriptexecute"===arguments[0].toLowerCase()?function(e){if(!e instanceof Function)throw new Error("Event handler must be a function.");t.push(e)}(arguments[1]):n.apply(null,arguments)},window.removeEventListener=function(){"beforescriptexecute"===arguments[0].toLowerCase()?function(e){for(var n=t.length;n--;)t[n]===e&&t.splice(n,1)}(arguments[1]):o.apply(null,arguments)};var r=function(n,o){var r=new e(n,o);if(window.onbeforescriptexecute instanceof Function)try{window.onbeforescriptexecute(r)}catch(e){console.error(e)}for(var i=0;i<t.length&&!r._stop;i++)try{t[i](r)}catch(e){console.error(e)}return r};new MutationObserver(e=>{for(var t=0;t<e.length;t++)for(var n=0;n<e[t].addedNodes.length;n++){var o=e[t].addedNodes[n];if("SCRIPT"===o.tagName){var i=r(o,e[t].target);i._cancel?o.remove():"string"==typeof i._replace&&(o.textContent=i._replace)}}}).observe(document,{childList:!0,subtree:!0})}();
  2. maple3142 revised this gist Apr 6, 2018. 2 changed files with 38 additions and 40 deletions.
    77 changes: 37 additions & 40 deletions onbeforescriptexecute.js
    Original file line number Diff line number Diff line change
    @@ -1,57 +1,54 @@
    // 'beforescriptexecute' event
    // 'beforescriptexecute' event [es5]
    // original version: https://gist.github.com/jspenguin2017/cd568a50128c71e515738413cd09a890

    {
    ;(function() {
    ;('use strict')
    const Event = class Event {
    constructor(script, target) {
    this.script = script
    this.target = target
    function Event(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
    }
    this._cancel = false
    this._replace = null
    this._stop = false
    }
    Event.prototype.preventDefault = function() {
    this._cancel = true
    }
    Event.prototype.stopPropagation = function() {
    this._stop = true
    }
    Event.prototype.replacePayload = function(payload) {
    this._replace = payload
    }

    let callbacks = []
    let addBeforeScriptExecuteListener = f => {
    var callbacks = []
    var addBeforeScriptExecuteListener = function(f) {
    if (!f instanceof Function) {
    throw new Error('Event handler must be a function.')
    }
    callbacks.push(f)
    }
    let removeBeforeScriptExecuteListener = f => {
    let i = callbacks.length
    var removeBeforeScriptExecuteListener = function(f) {
    var i = callbacks.length
    while (i--) {
    if (callbacks[i] === f) {
    callbacks.splice(i, 1)
    }
    }
    }
    let addev = window.addEventListener.bind(window)
    let rmev = window.removeEventListener.bind(window)
    window.addEventListener = (ev, ...args) => {
    if (ev.toLowerCase() === 'beforescriptexecute') addBeforeScriptExecuteListener(args[0])
    else addev(ev, ...args)
    var addev = window.addEventListener.bind(window)
    var rmev = window.removeEventListener.bind(window)
    window.addEventListener = function() {
    if (arguments[0].toLowerCase() === 'beforescriptexecute') addBeforeScriptExecuteListener(arguments[1])
    else addev.apply(null, arguments)
    }
    window.removeEventListener = (ev, ...args) => {
    if (ev.toLowerCase() === 'beforescriptexecute') removeBeforeScriptExecuteListener(args[0])
    else rmev(ev, ...args)
    if (arguments[0].toLowerCase() === 'beforescriptexecute') removeBeforeScriptExecuteListener(arguments[1])
    else rmev.apply(null, arguments)
    }

    const dispatch = (script, target) => {
    const e = new Event(script, target)
    var dispatch = function(script, target) {
    var e = new Event(script, target)

    if (window.onbeforescriptexecute instanceof Function) {
    try {
    @@ -61,7 +58,7 @@
    }
    }

    for (let i = 0; i < callbacks.length; i++) {
    for (var i = 0; i < callbacks.length; i++) {
    if (e._stop) {
    break
    }
    @@ -74,12 +71,12 @@

    return e
    }
    const observer = new MutationObserver(mutations => {
    for (let i = 0; i < mutations.length; i++) {
    for (let j = 0; j < mutations[i].addedNodes.length; j++) {
    const el = mutations[i].addedNodes[j]
    var observer = new MutationObserver(mutations => {
    for (var i = 0; i < mutations.length; i++) {
    for (var j = 0; j < mutations[i].addedNodes.length; j++) {
    var el = mutations[i].addedNodes[j]
    if (el.tagName !== 'SCRIPT') continue
    const e = dispatch(el, mutations[i].target)
    var e = dispatch(el, mutations[i].target)
    if (e._cancel) {
    el.remove()
    } else if (typeof e._replace === 'string') {
    @@ -92,4 +89,4 @@
    childList: true,
    subtree: true
    })
    }
    })()
    1 change: 1 addition & 0 deletions onbeforescriptexecute.min.js
    Original file line number Diff line number Diff line change
    @@ -0,0 +1 @@
    !function(){function e(e,t){this.script=e,this.target=t,this._cancel=!1,this._replace=null,this._stop=!1}e.prototype.preventDefault=function(){this._cancel=!0},e.prototype.stopPropagation=function(){this._stop=!0},e.prototype.replacePayload=function(e){this._replace=e};var t=[],n=window.addEventListener.bind(window),o=window.removeEventListener.bind(window);window.addEventListener=function(){'beforescriptexecute'===arguments[0].toLowerCase()?function(e){if(!e instanceof Function)throw new Error('Event handler must be a function.');t.push(e)}(arguments[1]):n.apply(null,arguments)},window.removeEventListener=((e,...n)=>{'beforescriptexecute'===arguments[0].toLowerCase()?function(e){for(var n=t.length;n--;)t[n]===e&&t.splice(n,1)}(arguments[1]):o.apply(null,arguments)});var r=function(n,o){var r=new e(n,o);if(window.onbeforescriptexecute instanceof Function)try{window.onbeforescriptexecute(r)}catch(e){console.error(e)}for(var i=0;i<t.length&&!r._stop;i++)try{t[i](r)}catch(e){console.error(e)}return r};new MutationObserver(e=>{for(var t=0;t<e.length;t++)for(var n=0;n<e[t].addedNodes.length;n++){var o=e[t].addedNodes[n];if('SCRIPT'===o.tagName){var i=r(o,e[t].target);i._cancel?o.remove():'string'==typeof i._replace&&(o.textContent=i._replace)}}}).observe(document,{childList:!0,subtree:!0})}()
  3. maple3142 revised this gist Apr 6, 2018. 2 changed files with 5 additions and 49 deletions.
    26 changes: 1 addition & 25 deletions onbeforescriptexecute.js
    Original file line number Diff line number Diff line change
    @@ -1,4 +1,4 @@
    // polyfill of 'beforescriptexecute' event
    // 'beforescriptexecute' event
    // original version: https://gist.github.com/jspenguin2017/cd568a50128c71e515738413cd09a890

    {
    @@ -92,28 +92,4 @@
    childList: true,
    subtree: true
    })

    const create = document.createElement.bind(document)
    document.createElement = t => {
    if (t.toLowerCase() !== 'script') return create(t)
    const s = create('script')
    for (const key of ['textContent', 'innerHTML', 'src']) {
    let value = ''
    Object.defineProperty(s, key, {
    get() {
    return value
    },
    set(t) {
    value = t
    const e = dispatch(s, s)
    if (e._cancel) {
    s.remove()
    } else if (typeof e._replace === 'string' && key !== 'src') {
    Reflect.set(s, key, e._replace)
    }
    }
    })
    }
    return s
    }
    }
    28 changes: 4 additions & 24 deletions test.html
    Original file line number Diff line number Diff line change
    @@ -10,13 +10,13 @@
    <script src="onbeforescriptexecute.js"></script>
    <script>
    window.addEventListener('beforescriptexecute',e => {
    if (e.script.textContent.includes('alert') && !e.script.textContent.includes('document')) {
    if (e.script.textContent.includes('alert')) {
    e.preventDefault()
    }
    if (e.script.innerHTML.includes('hello')) {
    e.replacePayload(e.script.innerHTML.replace('hello', 'world'))
    else if (e.script.innerHTML.includes('hello')) {
    e.replacePayload(e.script.innerHTML.replace(/hello/g, 'world'))
    }
    if (e.script.src.includes('xxx')) {
    else if (e.script.src.includes('xxx')) {
    e.preventDefault()
    }
    })
    @@ -29,27 +29,7 @@
    // will execute, but 'hello' will be convert to 'world'
    console.log('hello')
    </script>
    <script>
    // will execute, but the dynamically added script won't
    const el=document.createElement('script')
    el.textContent=`
    alert('1')
    `
    document.body.appendChild(el)
    </script>
    <!-- won't execute -->
    <script src="xxx.js"></script>
    <script>
    const el2=document.createElement('script')
    el2.src='xxx.js'
    document.body.appendChild(el2)
    </script>
    <script>
    const el3=document.createElement('script')
    el3.innerHTML=`
    alert(789)
    `
    document.body.appendChild(el3)
    </script>
    </body>
    </html>
  4. maple3142 revised this gist Apr 6, 2018. 1 changed file with 1 addition and 1 deletion.
    2 changes: 1 addition & 1 deletion onbeforescriptexecute.js
    Original file line number Diff line number Diff line change
    @@ -106,7 +106,7 @@
    set(t) {
    value = t
    const e = dispatch(s, s)
    if (!e._cancel) {
    if (e._cancel) {
    s.remove()
    } else if (typeof e._replace === 'string' && key !== 'src') {
    Reflect.set(s, key, e._replace)
  5. maple3142 revised this gist Apr 6, 2018. 2 changed files with 15 additions and 5 deletions.
    16 changes: 13 additions & 3 deletions onbeforescriptexecute.js
    Original file line number Diff line number Diff line change
    @@ -3,7 +3,7 @@

    {
    ;('use strict')
    const Event = class {
    const Event = class Event {
    constructor(script, target) {
    this.script = script
    this.target = target
    @@ -25,20 +25,30 @@
    }

    let callbacks = []
    window.addBeforeScriptExecuteListener = f => {
    let addBeforeScriptExecuteListener = f => {
    if (!f instanceof Function) {
    throw new Error('Event handler must be a function.')
    }
    callbacks.push(f)
    }
    window.removeBeforeScriptExecuteListener = f => {
    let removeBeforeScriptExecuteListener = f => {
    let i = callbacks.length
    while (i--) {
    if (callbacks[i] === f) {
    callbacks.splice(i, 1)
    }
    }
    }
    let addev = window.addEventListener.bind(window)
    let rmev = window.removeEventListener.bind(window)
    window.addEventListener = (ev, ...args) => {
    if (ev.toLowerCase() === 'beforescriptexecute') addBeforeScriptExecuteListener(args[0])
    else addev(ev, ...args)
    }
    window.removeEventListener = (ev, ...args) => {
    if (ev.toLowerCase() === 'beforescriptexecute') removeBeforeScriptExecuteListener(args[0])
    else rmev(ev, ...args)
    }

    const dispatch = (script, target) => {
    const e = new Event(script, target)
    4 changes: 2 additions & 2 deletions test.html
    Original file line number Diff line number Diff line change
    @@ -9,7 +9,7 @@
    <body>
    <script src="onbeforescriptexecute.js"></script>
    <script>
    window.onbeforescriptexecute = e => {
    window.addEventListener('beforescriptexecute',e => {
    if (e.script.textContent.includes('alert') && !e.script.textContent.includes('document')) {
    e.preventDefault()
    }
    @@ -19,7 +19,7 @@
    if (e.script.src.includes('xxx')) {
    e.preventDefault()
    }
    }
    })
    </script>
    <script>
    // won't execute
  6. maple3142 revised this gist Apr 6, 2018. 2 changed files with 23 additions and 13 deletions.
    24 changes: 11 additions & 13 deletions onbeforescriptexecute.js
    Original file line number Diff line number Diff line change
    @@ -41,10 +41,6 @@
    }

    const dispatch = (script, target) => {
    if (script.tagName !== 'SCRIPT') {
    return
    }

    const e = new Event(script, target)

    if (window.onbeforescriptexecute instanceof Function) {
    @@ -66,17 +62,19 @@
    }
    }

    if (e._cancel) {
    script.remove()
    } else if (typeof e._replace === 'string') {
    script.textContent = e._replace
    }
    return e
    }
    const observer = new MutationObserver(mutations => {
    for (let i = 0; i < mutations.length; i++) {
    for (let j = 0; j < mutations[i].addedNodes.length; j++) {
    dispatch(mutations[i].addedNodes[j], mutations[i].target)
    const el = mutations[i].addedNodes[j]
    if (el.tagName !== 'SCRIPT') continue
    const e = dispatch(el, mutations[i].target)
    if (e._cancel) {
    el.remove()
    } else if (typeof e._replace === 'string') {
    el.textContent = e._replace
    }
    }
    }
    })
    @@ -89,7 +87,7 @@
    document.createElement = t => {
    if (t.toLowerCase() !== 'script') return create(t)
    const s = create('script')
    ;['textContent', 'innerHTML', 'src'].forEach(key => {
    for (const key of ['textContent', 'innerHTML', 'src']) {
    let value = ''
    Object.defineProperty(s, key, {
    get() {
    @@ -99,13 +97,13 @@
    value = t
    const e = dispatch(s, s)
    if (!e._cancel) {
    Reflect.set(s, key, t)
    s.remove()
    } else if (typeof e._replace === 'string' && key !== 'src') {
    Reflect.set(s, key, e._replace)
    }
    }
    })
    })
    }
    return s
    }
    }
    12 changes: 12 additions & 0 deletions test.html
    Original file line number Diff line number Diff line change
    @@ -39,5 +39,17 @@
    </script>
    <!-- won't execute -->
    <script src="xxx.js"></script>
    <script>
    const el2=document.createElement('script')
    el2.src='xxx.js'
    document.body.appendChild(el2)
    </script>
    <script>
    const el3=document.createElement('script')
    el3.innerHTML=`
    alert(789)
    `
    document.body.appendChild(el3)
    </script>
    </body>
    </html>
  7. maple3142 revised this gist Apr 6, 2018. No changes.
  8. maple3142 revised this gist Apr 6, 2018. No changes.
  9. maple3142 revised this gist Apr 6, 2018. 2 changed files with 3 additions and 2 deletions.
    4 changes: 2 additions & 2 deletions test.html
    Original file line number Diff line number Diff line change
    @@ -16,7 +16,7 @@
    if (e.script.innerHTML.includes('hello')) {
    e.replacePayload(e.script.innerHTML.replace('hello', 'world'))
    }
    if (e.script.src.includes('alert')) {
    if (e.script.src.includes('xxx')) {
    e.preventDefault()
    }
    }
    @@ -38,6 +38,6 @@
    document.body.appendChild(el)
    </script>
    <!-- won't execute -->
    <script src="alert.js"></script>
    <script src="xxx.js"></script>
    </body>
    </html>
    1 change: 1 addition & 0 deletions xxx.js
    Original file line number Diff line number Diff line change
    @@ -0,0 +1 @@
    alert('C8763')
  10. maple3142 revised this gist Apr 6, 2018. 1 changed file with 4 additions and 4 deletions.
    8 changes: 4 additions & 4 deletions onbeforescriptexecute.js
    Original file line number Diff line number Diff line change
    @@ -2,7 +2,7 @@
    // original version: https://gist.github.com/jspenguin2017/cd568a50128c71e515738413cd09a890

    {
    'use strict'
    ;('use strict')
    const Event = class {
    constructor(script, target) {
    this.script = script
    @@ -85,10 +85,10 @@
    subtree: true
    })

    document._ce = document.createElement
    const create = document.createElement.bind(document)
    document.createElement = t => {
    if (t.toLowerCase() !== 'script') return document._ce(t)
    const s = document._ce('script')
    if (t.toLowerCase() !== 'script') return create(t)
    const s = create('script')
    ;['textContent', 'innerHTML', 'src'].forEach(key => {
    let value = ''
    Object.defineProperty(s, key, {
  11. maple3142 revised this gist Apr 6, 2018. 2 changed files with 37 additions and 33 deletions.
    55 changes: 24 additions & 31 deletions onbeforescriptexecute.js
    Original file line number Diff line number Diff line change
    @@ -1,9 +1,8 @@
    // polyfill of 'beforescriptexecute' event
    // original version: https://gist.github.com/jspenguin2017/cd568a50128c71e515738413cd09a890
    // add support for dynamic added script (only for textContent)

    {
    ;('use strict')
    'use strict'
    const Event = class {
    constructor(script, target) {
    this.script = script
    @@ -86,33 +85,27 @@
    subtree: true
    })

    Object.defineProperty(HTMLScriptElement.prototype, 'textContent', {
    get() {
    return this._ct || this.innerHTML
    },
    set(t) {
    this._ct = t
    const e = dispatch(this, this)
    if (!e._cancel) {
    this.innerHTML = t
    } else if (typeof e._replace === 'string') {
    this.innerHTML = e._replace
    }
    }
    })

    // Object.defineProperty(HTMLScriptElement.prototype, 'innerHTML', {
    // get() {
    // return this._ct || this.textContent
    // },
    // set(t) {
    // this._ct = t
    // const e = dispatch(this, this)
    // if (!e._cancel) {
    // this.textContent = t
    // } else if (typeof e._replace === 'string') {
    // this.textContent = e._replace
    // }
    // }
    // })
    document._ce = document.createElement
    document.createElement = t => {
    if (t.toLowerCase() !== 'script') return document._ce(t)
    const s = document._ce('script')
    ;['textContent', 'innerHTML', 'src'].forEach(key => {
    let value = ''
    Object.defineProperty(s, key, {
    get() {
    return value
    },
    set(t) {
    value = t
    const e = dispatch(s, s)
    if (!e._cancel) {
    Reflect.set(s, key, t)
    } else if (typeof e._replace === 'string' && key !== 'src') {
    Reflect.set(s, key, e._replace)
    }
    }
    })
    })
    return s
    }
    }
    15 changes: 13 additions & 2 deletions test.html
    Original file line number Diff line number Diff line change
    @@ -9,24 +9,35 @@
    <body>
    <script src="onbeforescriptexecute.js"></script>
    <script>
    window.onbeforescriptexecute=e=>{
    if(e.script.textContent.includes(`alert`)&&!e.script.textContent.includes('document')){
    window.onbeforescriptexecute = e => {
    if (e.script.textContent.includes('alert') && !e.script.textContent.includes('document')) {
    e.preventDefault()
    }
    if (e.script.innerHTML.includes('hello')) {
    e.replacePayload(e.script.innerHTML.replace('hello', 'world'))
    }
    if (e.script.src.includes('alert')) {
    e.preventDefault()
    }
    }
    </script>
    <script>
    // won't execute
    alert('hello')
    </script>
    <script>
    // will execute, but 'hello' will be convert to 'world'
    console.log('hello')
    </script>
    <script>
    // will execute, but the dynamically added script won't
    const el=document.createElement('script')
    el.textContent=`
    alert('1')
    `
    document.body.appendChild(el)
    </script>
    <!-- won't execute -->
    <script src="alert.js"></script>
    </body>
    </html>
  12. maple3142 renamed this gist Apr 6, 2018. 1 changed file with 0 additions and 0 deletions.
    File renamed without changes.
  13. maple3142 revised this gist Apr 6, 2018. 3 changed files with 150 additions and 123 deletions.
    32 changes: 32 additions & 0 deletions index.html
    Original file line number Diff line number Diff line change
    @@ -0,0 +1,32 @@
    <!DOCTYPE html>
    <html lang="en">
    <head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <meta http-equiv="X-UA-Compatible" content="ie=edge">
    <title>Document</title>
    </head>
    <body>
    <script src="onbeforescriptexecute.js"></script>
    <script>
    window.onbeforescriptexecute=e=>{
    if(e.script.textContent.includes(`alert`)&&!e.script.textContent.includes('document')){
    e.preventDefault()
    }
    }
    </script>
    <script>
    alert('hello')
    </script>
    <script>
    console.log('hello')
    </script>
    <script>
    const el=document.createElement('script')
    el.textContent=`
    alert('1')
    `
    document.body.appendChild(el)
    </script>
    </body>
    </html>
    123 changes: 0 additions & 123 deletions onbeforescriptexecute.html
    Original file line number Diff line number Diff line change
    @@ -1,123 +0,0 @@
    <!DOCTYPE html>
    <html>
    <head>
    <meta charset="utf-8" />
    <script>
    {
    "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 (!f instanceof 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 (window.onbeforescriptexecute instanceof Function) {
    try {
    window.onbeforescriptexecute(e);
    } catch (err) {
    console.error(err);
    }
    }

    for (let i = 0; i < callbacks.length; i++) {
    if (e._stop) {
    break;
    }
    try {
    callbacks[i](e);
    } catch (err) {
    console.error(err);
    }
    }

    if (e._cancel) {
    script.remove();
    } else if (typeof e._replace === "string") {
    script.textContent = e._replace;
    }
    };
    const observer = new MutationObserver((mutations) => {
    for (let i = 0; i < mutations.length; i++) {
    for (let j = 0; j < mutations[i].addedNodes.length; j++) {
    dispatch(mutations[i].addedNodes[j], mutations[i].target);
    }
    }
    });
    observer.observe(document, {
    childList: true,
    subtree: true,
    });
    }

    //Only works for "hard coded" inline scripts, dynamically inserted scripts will execute before it can be canceled
    //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 61 - Yes - Yes
    //Edge 15 - Yes - Yes
    //Firefox 55 - NO - Yes

    //Here are some examples
    "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")) {
    e.replacePayload("console.log(2);");
    }
    };
    </script>
    </head>
    <body>
    <script>alert(1);</script>
    <script>console.log(1);</script>
    </body>
    </html>
    118 changes: 118 additions & 0 deletions onbeforescriptexecute.js
    Original file line number Diff line number Diff line change
    @@ -0,0 +1,118 @@
    // polyfill of 'beforescriptexecute' event
    // original version: https://gist.github.com/jspenguin2017/cd568a50128c71e515738413cd09a890
    // add support for dynamic added script (only for textContent)

    {
    ;('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 (!f instanceof 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 (window.onbeforescriptexecute instanceof Function) {
    try {
    window.onbeforescriptexecute(e)
    } catch (err) {
    console.error(err)
    }
    }

    for (let i = 0; i < callbacks.length; i++) {
    if (e._stop) {
    break
    }
    try {
    callbacks[i](e)
    } catch (err) {
    console.error(err)
    }
    }

    if (e._cancel) {
    script.remove()
    } else if (typeof e._replace === 'string') {
    script.textContent = e._replace
    }
    return e
    }
    const observer = new MutationObserver(mutations => {
    for (let i = 0; i < mutations.length; i++) {
    for (let j = 0; j < mutations[i].addedNodes.length; j++) {
    dispatch(mutations[i].addedNodes[j], mutations[i].target)
    }
    }
    })
    observer.observe(document, {
    childList: true,
    subtree: true
    })

    Object.defineProperty(HTMLScriptElement.prototype, 'textContent', {
    get() {
    return this._ct || this.innerHTML
    },
    set(t) {
    this._ct = t
    const e = dispatch(this, this)
    if (!e._cancel) {
    this.innerHTML = t
    } else if (typeof e._replace === 'string') {
    this.innerHTML = e._replace
    }
    }
    })

    // Object.defineProperty(HTMLScriptElement.prototype, 'innerHTML', {
    // get() {
    // return this._ct || this.textContent
    // },
    // set(t) {
    // this._ct = t
    // const e = dispatch(this, this)
    // if (!e._cancel) {
    // this.textContent = t
    // } else if (typeof e._replace === 'string') {
    // this.textContent = e._replace
    // }
    // }
    // })
    }
  14. @jspenguin2017 jspenguin2017 revised this gist Sep 11, 2017. 1 changed file with 1 addition and 1 deletion.
    2 changes: 1 addition & 1 deletion onbeforescriptexecute.html
    Original file line number Diff line number Diff line change
    @@ -28,7 +28,7 @@

    let callbacks = [];
    window.addBeforeScriptExecuteListener = (f) => {
    if (!window.onbeforescriptexecute instanceof Function) {
    if (!f instanceof Function) {
    throw new Error("Event handler must be a function.");
    }
    callbacks.push(f);
  15. @jspenguin2017 jspenguin2017 revised this gist Sep 11, 2017. 1 changed file with 20 additions and 2 deletions.
    22 changes: 20 additions & 2 deletions onbeforescriptexecute.html
    Original file line number Diff line number Diff line change
    @@ -1,6 +1,7 @@
    <!DOCTYPE html>
    <html>
    <head>
    <meta charset="utf-8" />
    <script>
    {
    "use strict";
    @@ -27,6 +28,9 @@

    let callbacks = [];
    window.addBeforeScriptExecuteListener = (f) => {
    if (!window.onbeforescriptexecute instanceof Function) {
    throw new Error("Event handler must be a function.");
    }
    callbacks.push(f);
    };
    window.removeBeforeScriptExecuteListener = (f) => {
    @@ -46,14 +50,22 @@
    const e = new Event(script, target);

    if (window.onbeforescriptexecute instanceof Function) {
    window.onbeforescriptexecute(e);
    try {
    window.onbeforescriptexecute(e);
    } catch (err) {
    console.error(err);
    }
    }

    for (let i = 0; i < callbacks.length; i++) {
    if (e._stop) {
    break;
    }
    callbacks[i](e);
    try {
    callbacks[i](e);
    } catch (err) {
    console.error(err);
    }
    }

    if (e._cancel) {
    @@ -80,6 +92,12 @@
    //dynamically inserted scripts
    //Also textContent is not always set properly, especially when the script is big

    //Compatibility:
    //Browser - Cancel Script - Change Script
    //Chrome 61 - Yes - Yes
    //Edge 15 - Yes - Yes
    //Firefox 55 - NO - Yes

    //Here are some examples
    "use strict";
    window.onbeforescriptexecute = (e) => {
  16. @jspenguin2017 jspenguin2017 revised this gist Sep 11, 2017. 3 changed files with 105 additions and 175 deletions.
    105 changes: 105 additions & 0 deletions onbeforescriptexecute.html
    Original file line number Diff line number Diff line change
    @@ -0,0 +1,105 @@
    <!DOCTYPE html>
    <html>
    <head>
    <script>
    {
    "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) => {
    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 (window.onbeforescriptexecute instanceof Function) {
    window.onbeforescriptexecute(e);
    }

    for (let i = 0; i < callbacks.length; i++) {
    if (e._stop) {
    break;
    }
    callbacks[i](e);
    }

    if (e._cancel) {
    script.remove();
    } else if (typeof e._replace === "string") {
    script.textContent = e._replace;
    }
    };
    const observer = new MutationObserver((mutations) => {
    for (let i = 0; i < mutations.length; i++) {
    for (let j = 0; j < mutations[i].addedNodes.length; j++) {
    dispatch(mutations[i].addedNodes[j], mutations[i].target);
    }
    }
    });
    observer.observe(document, {
    childList: true,
    subtree: true,
    });
    }

    //Only works for "hard coded" inline scripts, dynamically inserted scripts will execute before it can be canceled
    //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

    //Here are some examples
    "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")) {
    e.replacePayload("console.log(2);");
    }
    };
    </script>
    </head>
    <body>
    <script>alert(1);</script>
    <script>console.log(1);</script>
    </body>
    </html>
    86 changes: 0 additions & 86 deletions onbeforescriptexecute.js
    Original file line number Diff line number Diff line change
    @@ -1,86 +0,0 @@
    (() => {
    "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
    }
    89 changes: 0 additions & 89 deletions test.html
    Original file line number Diff line number Diff line change
    @@ -1,89 +0,0 @@
    <!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>
  17. @jspenguin2017 jspenguin2017 revised this gist Jul 30, 2017. 1 changed file with 2 additions and 1 deletion.
    3 changes: 2 additions & 1 deletion onbeforescriptexecute.js
    Original file line number Diff line number Diff line change
    @@ -81,5 +81,6 @@ if (Math.random() > 100) {
    }
    };
    //Only work for "hard coded" inline scripts (see test.html), dynamically inserted scripts will execute before I can cancel it
    //Also textContent is not always set properly when the script is large
    //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
    }
  18. @jspenguin2017 jspenguin2017 renamed this gist Jul 30, 2017. 1 changed file with 0 additions and 0 deletions.
  19. @jspenguin2017 jspenguin2017 created this gist Jul 30, 2017.
    85 changes: 85 additions & 0 deletions onbeforescriptexecute-polyfill.js
    Original file line number Diff line number Diff line change
    @@ -0,0 +1,85 @@
    (() => {
    "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
    //Also textContent is not always set properly when the script is large
    }
    89 changes: 89 additions & 0 deletions test.html
    Original file line number Diff line number Diff line change
    @@ -0,0 +1,89 @@
    <!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>