Code for https://chrome.google.com/webstore/detail/ohcfioimjbmanibdlkhbcndkbdibpkpg
Isolated Scripts
// Copyright (c) 2017 The Chromium Authors. All rights reserved. | |
// Use of this source code is governed by a BSD-style license that can be | |
// found in the LICENSE file. | |
var secrets = {}; | |
// 1. If response header "Isolated-Script" is "true" and type is "script" | |
// 1.1. Cancel request, and inject response as content script | |
chrome.webRequest.onHeadersReceived.addListener( | |
function(info) { | |
var scriptUrl = new URL(info.url); | |
if ((info.responseHeaders||[]).find( | |
pair=>pair.name.match(/^Isolated-Script$/i) && pair.value=="true") | |
) { | |
fetch(scriptUrl.href, {redirect: "error", credentials: "include"}) | |
.then(response=> | |
response.status < 300 | |
&& response.headers.get('content-type').match(/^(text|application)\/javascript(;.*)?$/) | |
? response.text() | |
: 'console.error("bad script response")') | |
.then(code=>chrome.tabs.executeScript(info.tabId, { | |
code: wrapCode( | |
code, | |
secrets[scriptUrl.origin] = String(secrets[scriptUrl.origin] || Math.random())), | |
frameId: info.frameId | |
})); | |
chrome.tabs.executeScript(info.tabId, { | |
file: "secrethtml.js", | |
frameId: info.frameId | |
}); | |
return {redirectUrl: 'data:text/javascript,console.log("loading script as isolate");'}; | |
} | |
}, | |
// filters | |
{ | |
urls: ["*://*/*"], | |
types: ["script"] | |
}, | |
// extraInfoSpec | |
["blocking", "responseHeaders"]); | |
// 1.2. Replace fetch and XMLHttpRequest.send to add "Isolated-Script-Secret: SECRET" to every request made. | |
function wrapCode(code, secret) { | |
var wrapper = function(self, secret, code){ | |
console.log('running isolate script'); | |
self.fetch = new Proxy(self.fetch, { | |
apply: function(fetch, window, args) { | |
args[1] = args[1] || {}; | |
args[1].headers = args[1].headers || {}; | |
args[1].headers["Isolated-Script-Secret"] = secret; | |
return fetch.apply(window, args); | |
}}); | |
self.XMLHttpRequest.prototype.send = new Proxy(self.XMLHttpRequest.prototype.send, { | |
apply: function(send, xhr, args) { | |
xhr.addRequestHeader("Isolated-Script-Secret", secret); | |
return xhr.send.apply(xhr, args); | |
} | |
}); | |
code(); | |
}; | |
var code = "function(){" + code + "}"; | |
return "(" + wrapper + ")(self," + JSON.stringify(secret) + "," + code + ")"; | |
} | |
// 2. If request header "Isolated-Script-Secret" exists | |
// 2.1 Remove from every request, but remember it's value. | |
// 3. If request header "Cookie" contains cookie prefixed "__isolatedScript-" and request header "Isolated-Script-Secret" didn't contain secret. | |
// 3.1. Remove cookie from request. | |
chrome.webRequest.onBeforeSendHeaders.addListener( | |
function(info){ | |
var scriptUrl = new URL(info.url); | |
var requestHeaders = []; | |
var claimedSecret, cookieIndex, dirtyCookies, cleanCookies, cookies; | |
(info.requestHeaders||[]).forEach(function(header, index){ | |
if (header.name.match(/^Isolated-Script-Secret$/i)) { | |
claimedSecret = header.value; | |
} else | |
if (header.name.match(/^Cookie$/i)) { | |
cookieIndex = index; | |
dirtyCookies = header.value.split(';'); | |
cleanCookies = dirtyCookies.filter(function(cookie) { | |
return !cookie.match(/^\s*__isolatedScript-/); | |
}); | |
} else { | |
requestHeaders.push(header); | |
} | |
}); | |
if (dirtyCookies) { | |
if (claimedSecret && claimedSecret == secrets[scriptUrl.origin]) { | |
cookies = dirtyCookies.join(';'); | |
} else { | |
cookies = cleanCookies.join(';'); | |
} | |
requestHeaders.push({name: "Cookie", value: cookies}); | |
} | |
return {requestHeaders: requestHeaders}; | |
}, | |
// filters | |
{ | |
urls: ["*://*/*"] | |
}, | |
// extraInfoSpec | |
["blocking", "requestHeaders"]); |
{ | |
"name": "Isolate Scripts Demo", | |
"version": "0.1.1", | |
"description": "Demo for Isolate Scripts", | |
"permissions": [ | |
"webRequest", | |
"webRequestBlocking", | |
"https://isolate-scripts-demo.appspot.com/*" | |
], | |
"background": { | |
"scripts": ["background.js"] | |
}, | |
"manifest_version": 2 | |
} |
// Copyright (c) 2017 The Chromium Authors. All rights reserved. | |
// Use of this source code is governed by a BSD-style license that can be | |
// found in the LICENSE file. | |
var bootstrap = function(self) { | |
self.onmessage = function(m) { | |
document.open(); | |
document.write(m.data.html); | |
document.close(); | |
var eventPort = m.ports[0]; | |
for(var x in document.body) { | |
if (x.match(/^on/)) { | |
document.body[x] = function(event) { | |
var clone = {}; | |
for (var n in event) { | |
try { | |
var serialized = JSON.stringify(event[n]); | |
if (serialized == "{}" || serialized == "[]" || serialized == "null") { | |
continue; | |
} | |
clone[n] = JSON.parse(serialized); | |
} catch (e) {} | |
}; | |
eventPort.postMessage(clone); | |
}; | |
} | |
} | |
document.documentElement.style.cssText = "padding: 0; margin: 0; position: fixed; top: 0; left: 0; right: 0; bottom: 0; height: 100%: width: 100%;"; | |
document.body.style.cssText = m.data.style; | |
}; | |
} | |
Object.defineProperty(Element.prototype, 'innerHTML', { | |
set: function(html){ | |
if (html) { | |
var element = this; | |
var style = window.getComputedStyle(this, null).cssText; | |
var shadow = this.attachShadow({mode: 'closed'}); | |
var iframe = document.createElement('iframe'); | |
iframe.srcdoc = "<script>(" + bootstrap + ")(self)</script>"; | |
iframe.sandbox = "allow-forms allow-scripts"; | |
iframe.style.cssText = "height: 100%; width: 100%; padding: 0; margin: 0; border: 0;"; | |
var mc = new MessageChannel(); | |
mc.port1.onmessage = function(e) { | |
// Fake the event | |
var fakeEvent; | |
if (!fakeEvent && e.data.type.match(/^mouse|click/)) { | |
try { | |
fakeEvent = new MouseEvent(e.data.type, e.data); | |
} catch(e) {} | |
} | |
if (!fakeEvent) { | |
try { | |
fakeEvent = new UIEvent(e.data.type, e.data); | |
} catch(e) {} | |
} | |
if (!fakeEvent) { | |
try { | |
fakeEvent = new Event(e.data.type, e.data); | |
} catch(e) {} | |
} | |
if (!fakeEvent) { | |
try { | |
fakeEvent = new CustomEvent(e.data.type, e.data); | |
} catch(e) {} | |
} | |
if (fakeEvent) { | |
element.dispatchEvent(fakeEvent); | |
} | |
}; | |
iframe.onload = function() { | |
iframe.onload = null; | |
iframe.contentWindow.postMessage({ | |
html: html, | |
style: style | |
}, '*', [mc.port2]); | |
}; | |
shadow.appendChild(iframe); | |
} | |
} | |
}); |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment