2017-10-20 15:09:12 +00:00
|
|
|
(function () {
|
|
|
|
"use strict";
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Returns a boolean description of whether the given header
|
|
|
|
* (in the structure defined by the WebExtension WebRequest API)
|
2017-10-23 05:17:23 +00:00
|
|
|
* matches the following critera:
|
|
|
|
* 1. Is a content-security-policy instruction
|
|
|
|
* 2. Includes either a script-src or default-src rule, and
|
|
|
|
* 3. That rule _does not_ include an 'unsafe-inline' instruction.
|
|
|
|
*
|
|
|
|
* This function is used to determine whether we need to inject a hash
|
|
|
|
* of the injected proxyblocking code into the pages CSP policy, to white
|
|
|
|
* list our script.
|
2017-10-20 15:09:12 +00:00
|
|
|
*
|
|
|
|
* @see https://developer.mozilla.org/en-US/Add-ons/WebExtensions/API/webRequest/HttpHeaders
|
2017-10-23 05:17:23 +00:00
|
|
|
* @see https://w3c.github.io/webappsec-csp/
|
2017-10-20 15:09:12 +00:00
|
|
|
*
|
|
|
|
* @param object header
|
|
|
|
* An object describing a HTTP header
|
|
|
|
*
|
|
|
|
* @return boolean
|
2017-10-23 05:17:23 +00:00
|
|
|
* true if the given object depicts a CSP policy with the above stated
|
|
|
|
* properties, and false in all other cases.
|
2017-10-20 15:09:12 +00:00
|
|
|
*/
|
2017-10-23 05:17:23 +00:00
|
|
|
const isHeaderCSPScriptSrcWithOutUnsafeInline = function (header) {
|
2017-10-21 15:49:56 +00:00
|
|
|
|
2017-10-23 05:17:23 +00:00
|
|
|
if (!header ||
|
|
|
|
!header.name ||
|
|
|
|
header.name.toLowerCase().indexOf("content-security-policy") === -1) {
|
|
|
|
return false;
|
|
|
|
}
|
2017-10-20 15:09:12 +00:00
|
|
|
|
2017-10-23 05:17:23 +00:00
|
|
|
const cspInstruction = header.value;
|
|
|
|
let relevantRule;
|
2017-10-21 15:49:56 +00:00
|
|
|
|
2017-10-23 05:17:23 +00:00
|
|
|
if (cspInstruction.indexOf("script-src ") !== -1) {
|
|
|
|
relevantRule = "script-src";
|
|
|
|
} else if (cspInstruction.indexOf("default-src ") !== -1) {
|
|
|
|
relevantRule = "default-src";
|
|
|
|
} else {
|
|
|
|
return false;
|
|
|
|
}
|
2017-10-20 15:09:12 +00:00
|
|
|
|
2017-10-23 05:17:23 +00:00
|
|
|
const scriptSrcInstructionPattern = new RegExp(relevantRule + " .*?(?:;|$)", "i");
|
|
|
|
const match = scriptSrcInstructionPattern.exec(cspInstruction);
|
2017-10-20 15:09:12 +00:00
|
|
|
|
2017-10-23 05:17:23 +00:00
|
|
|
if (!match) {
|
|
|
|
return false;
|
|
|
|
}
|
2017-10-20 15:09:12 +00:00
|
|
|
|
2017-10-23 05:17:23 +00:00
|
|
|
return match[0].indexOf("'unsafe-inline'") === -1;
|
2017-10-20 15:09:12 +00:00
|
|
|
};
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Returns a new CSP instruction, with source with the given hash
|
|
|
|
* whitelisted.
|
2017-10-23 05:17:23 +00:00
|
|
|
*
|
|
|
|
* If the CSP instruction has a "script-src" rule, then the hash-value
|
|
|
|
* will be inserted there. Otherwise, it will be inserted in the
|
|
|
|
* default-src section.
|
2017-10-20 15:09:12 +00:00
|
|
|
*
|
|
|
|
* @see https://w3c.github.io/webappsec-csp/#strict-dynamic-usage
|
|
|
|
* @see https://w3c.github.io/webappsec-csp/#grammardef-hash-source
|
|
|
|
*
|
|
|
|
* @param string cspInstruction
|
|
|
|
* The value of a HTTP header defining a CSP instruction.
|
|
|
|
* @param string scriptHash
|
|
|
|
* A hash value, in the form of "sha256-<some hash>", that is a valid
|
|
|
|
* hash source description.
|
|
|
|
*
|
|
|
|
* @return string|false
|
|
|
|
* Returns false if the CSP instruction looks malformed (ie we
|
2017-10-23 05:17:23 +00:00
|
|
|
* couldn't find either a "script-src" or "default-src" section),
|
|
|
|
* otherwise, a new value CSP instruction with the given hash allowed.
|
2017-10-20 15:09:12 +00:00
|
|
|
*/
|
|
|
|
const createCSPInstructionWithHashAllowed = function (cspInstruction, scriptHash) {
|
|
|
|
|
|
|
|
const indexOfScriptSrc = cspInstruction.indexOf("script-src ");
|
2017-10-23 05:17:23 +00:00
|
|
|
const indexOfDefaultSrc = cspInstruction.indexOf("default-src ");
|
|
|
|
|
|
|
|
let ruleToModify, indexOfRuleStart;
|
|
|
|
if (indexOfScriptSrc !== -1) {
|
|
|
|
ruleToModify = "script-src";
|
|
|
|
indexOfRuleStart = indexOfScriptSrc;
|
|
|
|
} else if (indexOfDefaultSrc !== -1) {
|
|
|
|
ruleToModify = "default-src";
|
|
|
|
indexOfRuleStart = indexOfDefaultSrc;
|
|
|
|
} else {
|
2017-10-20 15:09:12 +00:00
|
|
|
return false;
|
|
|
|
}
|
2017-10-23 05:17:23 +00:00
|
|
|
const lengthOfRule = ruleToModify.length;
|
2017-10-20 15:09:12 +00:00
|
|
|
|
2017-10-23 05:17:23 +00:00
|
|
|
const preSrcRule = cspInstruction.substring(0, indexOfRuleStart);
|
|
|
|
const postSrcRule = cspInstruction.substring(indexOfRuleStart + lengthOfRule);
|
|
|
|
const newInstruction = preSrcRule + ruleToModify + " '" + scriptHash + "' " + postSrcRule;
|
2017-10-20 15:09:12 +00:00
|
|
|
|
|
|
|
return newInstruction;
|
|
|
|
};
|
|
|
|
|
|
|
|
window.WEB_API_MANAGER.httpHeadersLib = {
|
2017-10-23 05:17:23 +00:00
|
|
|
isHeaderCSPScriptSrcWithOutUnsafeInline,
|
2017-10-20 15:09:12 +00:00
|
|
|
createCSPInstructionWithHashAllowed
|
|
|
|
};
|
|
|
|
}());
|