Improve CSP clobbering (commit lost last time)

- Get csp setting asynchronously
 - Case insensitively match content-security-policy header value
 - Parse csp correctly
 - Simplify code

(When I rebased last time I lost the content of this commit somehow...)
This commit is contained in:
Colin Caine 2018-05-17 21:38:22 +01:00
parent d28fc7f0f6
commit 8f67be2d17
4 changed files with 75 additions and 42 deletions

6
package-lock.json generated
View file

@ -2011,6 +2011,10 @@
"integrity": "sha1-ojD2T1aDEOFJgAmUB5DsmVRbyn4=",
"dev": true
},
"csp-serdes": {
"version": "github:cmcaine/csp-serdes#6c6fe34dbd138855e5c26f331b094e9a75358a64",
"from": "github:cmcaine/csp-serdes"
},
"css": {
"version": "2.2.1",
"resolved": "https://registry.npmjs.org/css/-/css-2.2.1.tgz",
@ -9376,7 +9380,7 @@
}
},
"web-ext-types": {
"version": "github:kelseasy/web-ext-types#5f6e888318984c185413f8943d2616915e2af88f",
"version": "github:kelseasy/web-ext-types#53d82dcea599d34a23b5f4aa6a9c616613c7adc2",
"from": "github:kelseasy/web-ext-types",
"dev": true
},

View file

@ -5,6 +5,7 @@
"dependencies": {
"@types/css": "0.0.31",
"@types/nearley": "^2.11.0",
"csp-serdes": "github:cmcaine/csp-serdes",
"css": "^2.2.1",
"fuse.js": "^3.2.0",
"mark.js": "^8.11.1",

View file

@ -55,28 +55,35 @@ import * as webext from "./lib/webext"
l: prom => prom.then(console.log).catch(console.error),
})
// {{{ Clobber CSP
// This should be removed once https://bugzilla.mozilla.org/show_bug.cgi?id=1267027 is fixed
let cspListener
if (config.get("csp") == "clobber") {
cspListener = browser.webRequest.onHeadersReceived.addListener(
request.addurltocsp,
function addCSPListener() {
browser.webRequest.onHeadersReceived.addListener(
request.clobberCSP,
{ urls: ["<all_urls>"], types: ["main_frame"] },
["blocking", "responseHeaders"],
)
}
function removeCSPListener() {
browser.webRequest.onHeadersReceived.removeListener(request.clobberCSP)
}
config.getAsync("csp").then(csp => csp === "clobber" && addCSPListener())
browser.storage.onChanged.addListener((changes, areaname) => {
if (config.get("csp") == "clobber") {
cspListener = browser.webRequest.onHeadersReceived.addListener(
request.addurltocsp,
{ urls: ["<all_urls>"], types: ["main_frame"] },
["blocking", "responseHeaders"],
)
} else {
// This doesn't work. :(
// browser.webRequest.onHeadersReceived.removeListener(cspListener)
if ("userconfig" in changes) {
if (changes.userconfig.newValue.csp === "clobber") {
addCSPListener()
} else {
removeCSPListener()
}
}
})
// }}}
// Prevent Tridactyl from being updated while it is running in the hope of fixing #290
browser.runtime.onUpdateAvailable.addListener(_ => {})

View file

@ -1,32 +1,53 @@
import * as config from "./config"
import * as csp from "csp-serdes"
export function addurltocsp(response) {
let headers = response["responseHeaders"]
let cspind = headers.findIndex(
header => header.name == "Content-Security-Policy",
)
// if it's found
if (cspind > -1) {
// Split the csp header up so we can manage it individually.
let csparr = [headers[cspind]["value"].split("; ")][0]
for (let i = 0; i < csparr.length; i++) {
// Add 'unsafe-inline' as a directive since we use it
if (csparr[i].indexOf("style-src") > -1) {
if (csparr[i].indexOf("'self'") > -1) {
csparr[i] = csparr[i].replace(
"'self'",
"'self' 'unsafe-inline'",
)
}
}
// Remove the element if it's a sandbox directive
if (csparr[i] === "sandbox") {
csparr.splice(i, 1)
}
}
// Join the header up after clobberin'
headers[cspind]["value"] = csparr.join("; ")
class DefaultMap extends Map {
constructor(private defaultFactory, ...args) {
super(...args)
}
get(key) {
let ans = super.get(key)
if (ans === undefined) {
ans = this.defaultFactory(key)
super.set(key, ans)
}
return ans
}
}
/**
* Reduce CSP safety to permit tridactyl to run correctly
*
* style-src needs 'unsafe-inline' (hinting styles) and 'self' (mode indicator hiding)
* script-src needs 'unsafe-eval' (event hijacking)
* - but that's pretty dangerous, so maybe we shouldn't just clobber it?
* sandbox must not be set
*
* This only needs to happen because of a Firefox bug and we should stop doing
* it when they fix the bug.
*/
export function clobberCSP(response) {
const headers = response["responseHeaders"]
const cspHeader = headers.find(
header => header.name.toLowerCase() === "content-security-policy",
)
if (cspHeader !== undefined) {
const policy = new DefaultMap(
() => new Set(),
csp.parse(cspHeader.value),
)
policy.delete("sandbox")
policy
.get("style-src")
.add("'unsafe-inline'")
.add("'self'")
// policy.get("script-src").add("'unsafe-eval'")
// Replace old CSP
cspHeader.value = csp.serialize(policy)
return { responseHeaders: headers }
} else {
return {}
}
return { responseHeaders: headers }
}