address newly introduced CSP issues by not inserting the hash allowed rule for {default,script}-src if there is a 'unsafe-line' rule in place, fixes #11
This commit is contained in:
parent
1732d8fa96
commit
0bd91f6f3f
20 changed files with 230 additions and 130 deletions
|
@ -1,5 +1,3 @@
|
||||||
/*jslint es6: true*/
|
|
||||||
/*global window*/
|
|
||||||
(function () {
|
(function () {
|
||||||
"use strict";
|
"use strict";
|
||||||
|
|
||||||
|
@ -156,8 +154,7 @@
|
||||||
// injected proxy code to the list of scripts that are allowed to
|
// injected proxy code to the list of scripts that are allowed to
|
||||||
// run in the page.
|
// run in the page.
|
||||||
const cspDynamicPolicyHeaders = details.responseHeaders
|
const cspDynamicPolicyHeaders = details.responseHeaders
|
||||||
.filter(httpHeadersLib.isHeaderCSP)
|
.filter(httpHeadersLib.isHeaderCSPScriptSrcWithOutUnsafeInline);
|
||||||
.filter(httpHeadersLib.isCSPHeaderSettingScriptSrc);
|
|
||||||
|
|
||||||
if (cspDynamicPolicyHeaders.length === 1) {
|
if (cspDynamicPolicyHeaders.length === 1) {
|
||||||
const [ignore, scriptHash] = proxyBlockLib.generateScriptPayload(
|
const [ignore, scriptHash] = proxyBlockLib.generateScriptPayload(
|
||||||
|
|
|
@ -1,8 +1,8 @@
|
||||||
/*jslint es6: true, this: true*/
|
|
||||||
/*global window, Vue*/
|
|
||||||
(function () {
|
(function () {
|
||||||
"use strict";
|
"use strict";
|
||||||
|
|
||||||
|
const Vue = window.Vue;
|
||||||
|
|
||||||
Vue.component("domain-rules", {
|
Vue.component("domain-rules", {
|
||||||
props: ["domainNames", "selectedDomain"],
|
props: ["domainNames", "selectedDomain"],
|
||||||
template: `
|
template: `
|
||||||
|
|
|
@ -1,8 +1,8 @@
|
||||||
/*jslint es6: true, this: true*/
|
|
||||||
/*global window, Vue*/
|
|
||||||
(function () {
|
(function () {
|
||||||
"use strict";
|
"use strict";
|
||||||
|
|
||||||
|
const Vue = window.Vue;
|
||||||
|
|
||||||
Vue.component("logging-settings", {
|
Vue.component("logging-settings", {
|
||||||
props: ["shouldLog"],
|
props: ["shouldLog"],
|
||||||
template: `
|
template: `
|
||||||
|
|
|
@ -1,9 +1,8 @@
|
||||||
/*jslint es6: true, this: true*/
|
|
||||||
/*global window, Vue*/
|
|
||||||
(function () {
|
(function () {
|
||||||
"use strict";
|
"use strict";
|
||||||
|
|
||||||
const standardsDefaults = window.WEB_API_MANAGER.defaults;
|
const standardsDefaults = window.WEB_API_MANAGER.defaults;
|
||||||
|
const Vue = window.Vue;
|
||||||
|
|
||||||
Vue.component("web-api-standards", {
|
Vue.component("web-api-standards", {
|
||||||
props: ["standards", "selectedStandards", "selectedDomain"],
|
props: ["standards", "selectedStandards", "selectedDomain"],
|
||||||
|
|
|
@ -1,5 +1,3 @@
|
||||||
/*jslint es6: true*/
|
|
||||||
/*global window, browser, chrome, Vue*/
|
|
||||||
(function () {
|
(function () {
|
||||||
"use strict";
|
"use strict";
|
||||||
|
|
||||||
|
@ -8,6 +6,7 @@
|
||||||
const standards = window.WEB_API_MANAGER.standards;
|
const standards = window.WEB_API_MANAGER.standards;
|
||||||
const {storageLib, stateLib} = window.WEB_API_MANAGER;
|
const {storageLib, stateLib} = window.WEB_API_MANAGER;
|
||||||
const defaultDomain = "(default)";
|
const defaultDomain = "(default)";
|
||||||
|
const Vue = window.Vue;
|
||||||
|
|
||||||
const state = stateLib.generateStateObject(defaultDomain, standards);
|
const state = stateLib.generateStateObject(defaultDomain, standards);
|
||||||
|
|
||||||
|
|
|
@ -13,28 +13,47 @@
|
||||||
const script = doc.createElement('script');
|
const script = doc.createElement('script');
|
||||||
const rootElm = doc.head || doc.documentElement;
|
const rootElm = doc.head || doc.documentElement;
|
||||||
|
|
||||||
const cookieValue = cookies2.get(standardsCookieName);
|
// First see if we can read the standards to block out of the cookie
|
||||||
|
// sent from the extension. If not, then try to read it out of local
|
||||||
|
// storage (which may be needed if there are multiple requests to the same
|
||||||
|
// domain that interleave so that the cookie is deleted by one request
|
||||||
|
// before it can be read out by the other).
|
||||||
|
let domainPref;
|
||||||
|
|
||||||
if (!cookieValue) {
|
try {
|
||||||
|
domainPref = cookies2.get(standardsCookieName);
|
||||||
|
cookies2.remove(standardsCookieName, {path: window.document.location.pathname});
|
||||||
|
} catch (e) {
|
||||||
|
// This can happen if an iframe tries to read the cookie created from
|
||||||
|
// a parent request without the allow-same-origin attribute.
|
||||||
|
}
|
||||||
|
|
||||||
|
// If we couldn't read the domain preferences out of the cookie, then
|
||||||
|
// see if we can read it out of localStorage.
|
||||||
|
if (!domainPref) {
|
||||||
|
if (window.localStorage) {
|
||||||
|
domainPref = window.localStorage[standardsCookieName];
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
// Otherwise, if we did read things out of the cookie, then store
|
||||||
|
// it in local storage, so that other requests to the same origin
|
||||||
|
// can read the blocking settings.
|
||||||
|
window.localStorage[standardsCookieName] = domainPref;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!domainPref) {
|
||||||
window.console.log(`Unable to find the Web API Manager settings for ${doc.location.href}`);
|
window.console.log(`Unable to find the Web API Manager settings for ${doc.location.href}`);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
cookies2.remove(standardsCookieName, {path: window.document.location.pathname});
|
const [standardsToBlock, shouldLog] = cookieEncodingLib.fromCookieValue(domainPref);
|
||||||
|
|
||||||
if (!cookieValue) {
|
const [scriptToInject, ignore] = proxyBlockLib.generateScriptPayload(
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
const [standardsToBlock, shouldLog] = cookieEncodingLib.fromCookieValue(cookieValue);
|
|
||||||
|
|
||||||
const [scriptToInject, scriptHash] = proxyBlockLib.generateScriptPayload(
|
|
||||||
standards,
|
standards,
|
||||||
standardsToBlock,
|
standardsToBlock,
|
||||||
shouldLog
|
shouldLog
|
||||||
);
|
);
|
||||||
|
|
||||||
script.appendChild(doc.createTextNode(scriptToInject));
|
script.appendChild(doc.createTextNode(scriptToInject));
|
||||||
script.integrity = "sha256-" + scriptHash;
|
|
||||||
rootElm.appendChild(script);
|
rootElm.appendChild(script);
|
||||||
}());
|
}());
|
||||||
|
|
|
@ -1,5 +1,3 @@
|
||||||
/*jslint es6: true, browser: true*/
|
|
||||||
/*global window*/
|
|
||||||
/**
|
/**
|
||||||
* This file defines default blocking rules for domains that haven't been
|
* This file defines default blocking rules for domains that haven't been
|
||||||
* overwritten, either by the extension user, or by a subscribed policy
|
* overwritten, either by the extension user, or by a subscribed policy
|
||||||
|
|
|
@ -1,5 +1,3 @@
|
||||||
/*jslint es6: true*/
|
|
||||||
/*global window*/
|
|
||||||
(function () {
|
(function () {
|
||||||
"use strict";
|
"use strict";
|
||||||
const defaultKey = "(default)";
|
const defaultKey = "(default)";
|
||||||
|
|
|
@ -1,90 +1,65 @@
|
||||||
/*jslint es6: true*/
|
|
||||||
/*global window*/
|
|
||||||
(function () {
|
(function () {
|
||||||
"use strict";
|
"use strict";
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Returns a boolean description of whether the given header
|
* Returns a boolean description of whether the given header
|
||||||
* (in the structure defined by the WebExtension WebRequest API)
|
* (in the structure defined by the WebExtension WebRequest API)
|
||||||
* is describing a Set-Cookie instruction.
|
* 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.
|
||||||
*
|
*
|
||||||
* @see https://developer.mozilla.org/en-US/Add-ons/WebExtensions/API/webRequest/HttpHeaders
|
* @see https://developer.mozilla.org/en-US/Add-ons/WebExtensions/API/webRequest/HttpHeaders
|
||||||
|
* @see https://w3c.github.io/webappsec-csp/
|
||||||
*
|
*
|
||||||
* @param object header
|
* @param object header
|
||||||
* An object describing a HTTP header
|
* An object describing a HTTP header
|
||||||
*
|
*
|
||||||
* @return boolean
|
* @return boolean
|
||||||
* true if the given object represents a Set-Cookie instruction, and false
|
* true if the given object depicts a CSP policy with the above stated
|
||||||
* in all other cases.
|
* properties, and false in all other cases.
|
||||||
*/
|
*/
|
||||||
const isSetCookie = function (header) {
|
const isHeaderCSPScriptSrcWithOutUnsafeInline = function (header) {
|
||||||
|
|
||||||
return (
|
if (!header ||
|
||||||
header &&
|
!header.name ||
|
||||||
header.name &&
|
header.name.toLowerCase().indexOf("content-security-policy") === -1) {
|
||||||
header.name.toLowerCase().indexOf("set-cookie") !== -1
|
return false;
|
||||||
);
|
}
|
||||||
};
|
|
||||||
|
|
||||||
const isNotHTTPOnlySetCookie = function (header) {
|
const cspInstruction = header.value;
|
||||||
|
let relevantRule;
|
||||||
|
|
||||||
return (
|
if (cspInstruction.indexOf("script-src ") !== -1) {
|
||||||
header &&
|
relevantRule = "script-src";
|
||||||
header.value &&
|
} else if (cspInstruction.indexOf("default-src ") !== -1) {
|
||||||
header.value.toLowerCase().indexOf("httponly") === -1
|
relevantRule = "default-src";
|
||||||
);
|
} else {
|
||||||
};
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
const scriptSrcInstructionPattern = new RegExp(relevantRule + " .*?(?:;|$)", "i");
|
||||||
* Returns a boolean description of whether the given header
|
const match = scriptSrcInstructionPattern.exec(cspInstruction);
|
||||||
* (in the structure defined by the WebExtension WebRequest API)
|
|
||||||
* is describing a Content-Security-Policy for a site.
|
|
||||||
*
|
|
||||||
* @see https://developer.mozilla.org/en-US/Add-ons/WebExtensions/API/webRequest/HttpHeaders
|
|
||||||
*
|
|
||||||
* @param object header
|
|
||||||
* An object describing a HTTP header
|
|
||||||
*
|
|
||||||
* @return boolean
|
|
||||||
* true if the given object represents a HTTP CSP header, and false
|
|
||||||
* in all other cases.
|
|
||||||
*/
|
|
||||||
const isHeaderCSP = function (header) {
|
|
||||||
|
|
||||||
return (
|
if (!match) {
|
||||||
header &&
|
return false;
|
||||||
header.name &&
|
}
|
||||||
header.name.toLowerCase().indexOf("content-security-policy") !== -1
|
|
||||||
);
|
|
||||||
};
|
|
||||||
|
|
||||||
/**
|
return match[0].indexOf("'unsafe-inline'") === -1;
|
||||||
* Returns a boolean description of whether the given header
|
|
||||||
* (in the structure defined by the WebExtension WebRequest API)
|
|
||||||
* is describing a strict dynamic Content-Security-Policy for a site.
|
|
||||||
*
|
|
||||||
* @see https://w3c.github.io/webappsec-csp/#strict-dynamic-usage
|
|
||||||
*
|
|
||||||
* @param object header
|
|
||||||
* An object describing a HTTP header
|
|
||||||
*
|
|
||||||
* @return boolean
|
|
||||||
* true if the given object is a CSP header that defines a
|
|
||||||
* "strict-dynamic" policy, and false in all other cases.
|
|
||||||
*/
|
|
||||||
const isCSPHeaderSettingScriptSrc = function (header) {
|
|
||||||
|
|
||||||
return (
|
|
||||||
header &&
|
|
||||||
header.value &&
|
|
||||||
header.value.indexOf("script-src") !== -1
|
|
||||||
);
|
|
||||||
};
|
};
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Returns a new CSP instruction, with source with the given hash
|
* Returns a new CSP instruction, with source with the given hash
|
||||||
* whitelisted.
|
* whitelisted.
|
||||||
*
|
*
|
||||||
|
* 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.
|
||||||
|
*
|
||||||
* @see https://w3c.github.io/webappsec-csp/#strict-dynamic-usage
|
* @see https://w3c.github.io/webappsec-csp/#strict-dynamic-usage
|
||||||
* @see https://w3c.github.io/webappsec-csp/#grammardef-hash-source
|
* @see https://w3c.github.io/webappsec-csp/#grammardef-hash-source
|
||||||
*
|
*
|
||||||
|
@ -96,28 +71,35 @@
|
||||||
*
|
*
|
||||||
* @return string|false
|
* @return string|false
|
||||||
* Returns false if the CSP instruction looks malformed (ie we
|
* Returns false if the CSP instruction looks malformed (ie we
|
||||||
* couldn't find a "script-src" tag), otherwise, a new valud
|
* couldn't find either a "script-src" or "default-src" section),
|
||||||
* CSP instruction with the given hash allowed.
|
* otherwise, a new value CSP instruction with the given hash allowed.
|
||||||
*/
|
*/
|
||||||
const createCSPInstructionWithHashAllowed = function (cspInstruction, scriptHash) {
|
const createCSPInstructionWithHashAllowed = function (cspInstruction, scriptHash) {
|
||||||
|
|
||||||
const indexOfScriptSrc = cspInstruction.indexOf("script-src ");
|
const indexOfScriptSrc = cspInstruction.indexOf("script-src ");
|
||||||
if (indexOfScriptSrc === -1) {
|
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 {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
const lengthOfRule = ruleToModify.length;
|
||||||
|
|
||||||
const preSrcScript = cspInstruction.substring(0, indexOfScriptSrc);
|
const preSrcRule = cspInstruction.substring(0, indexOfRuleStart);
|
||||||
const postScriptSrc = cspInstruction.substring(indexOfScriptSrc + 11);
|
const postSrcRule = cspInstruction.substring(indexOfRuleStart + lengthOfRule);
|
||||||
const newInstruction = preSrcScript + "script-src '" + scriptHash + "' " + postScriptSrc;
|
const newInstruction = preSrcRule + ruleToModify + " '" + scriptHash + "' " + postSrcRule;
|
||||||
|
|
||||||
return newInstruction;
|
return newInstruction;
|
||||||
};
|
};
|
||||||
|
|
||||||
window.WEB_API_MANAGER.httpHeadersLib = {
|
window.WEB_API_MANAGER.httpHeadersLib = {
|
||||||
isSetCookie,
|
isHeaderCSPScriptSrcWithOutUnsafeInline,
|
||||||
isNotHTTPOnlySetCookie,
|
|
||||||
isHeaderCSP,
|
|
||||||
isCSPHeaderSettingScriptSrc,
|
|
||||||
createCSPInstructionWithHashAllowed
|
createCSPInstructionWithHashAllowed
|
||||||
};
|
};
|
||||||
}());
|
}());
|
|
@ -1,4 +1,3 @@
|
||||||
/*global window*/
|
|
||||||
// Initial content script for the Web API manager extension, that creates
|
// Initial content script for the Web API manager extension, that creates
|
||||||
// the "namespace" we'll use for all the content scripts in the extension.
|
// the "namespace" we'll use for all the content scripts in the extension.
|
||||||
(function () {
|
(function () {
|
||||||
|
|
|
@ -1,5 +1,3 @@
|
||||||
/*jslint es6: true, for: true, bitwise: true*/
|
|
||||||
/*global window*/
|
|
||||||
// Functions to represent a selection of options out of a bigger set
|
// Functions to represent a selection of options out of a bigger set
|
||||||
// in a compact, base64 representation.
|
// in a compact, base64 representation.
|
||||||
//
|
//
|
||||||
|
|
|
@ -1,5 +1,4 @@
|
||||||
/*jslint es6: true, browser: true*/
|
/*globals sjcl*/
|
||||||
/*global window, sjcl*/
|
|
||||||
// This module generates JavaScript code for instrumenting the DOM
|
// This module generates JavaScript code for instrumenting the DOM
|
||||||
// to prevent pages from accessing Web API standards. This code
|
// to prevent pages from accessing Web API standards. This code
|
||||||
// is generated programatically so that both the background and content
|
// is generated programatically so that both the background and content
|
||||||
|
|
|
@ -1,5 +1,3 @@
|
||||||
/*jslint es6: true*/
|
|
||||||
/*global window*/
|
|
||||||
(function () {
|
(function () {
|
||||||
"use strict";
|
"use strict";
|
||||||
|
|
||||||
|
|
|
@ -1,7 +1,7 @@
|
||||||
{
|
{
|
||||||
"manifest_version": 2,
|
"manifest_version": 2,
|
||||||
"name": "WebAPI Manager",
|
"name": "WebAPI Manager",
|
||||||
"version": "0.9.2",
|
"version": "0.9.3",
|
||||||
"description": "Improves browser security and privacy by controlling page access to the Web API.",
|
"description": "Improves browser security and privacy by controlling page access to the Web API.",
|
||||||
"icons": {
|
"icons": {
|
||||||
"48": "images/uic-48.png",
|
"48": "images/uic-48.png",
|
||||||
|
@ -46,8 +46,8 @@
|
||||||
],
|
],
|
||||||
"background": {
|
"background": {
|
||||||
"scripts": [
|
"scripts": [
|
||||||
"lib/vendor/sjcl.js",
|
|
||||||
"lib/vendor/URI.js",
|
"lib/vendor/URI.js",
|
||||||
|
"lib/vendor/sjcl.js",
|
||||||
"lib/init.js",
|
"lib/init.js",
|
||||||
"lib/standards.js",
|
"lib/standards.js",
|
||||||
"lib/pack.js",
|
"lib/pack.js",
|
||||||
|
|
|
@ -1,6 +1,6 @@
|
||||||
{
|
{
|
||||||
"name": "web-api-manager",
|
"name": "web-api-manager",
|
||||||
"version": "0.9.2",
|
"version": "0.9.3",
|
||||||
"description": "Tools to generate Web API managing browser extensions for Firefox and Chrome.",
|
"description": "Tools to generate Web API managing browser extensions for Firefox and Chrome.",
|
||||||
"author": "Peter Snyder <psnyde2@uic.edu> (https://www.cs.uic.edu/~psnyder/)",
|
"author": "Peter Snyder <psnyde2@uic.edu> (https://www.cs.uic.edu/~psnyder/)",
|
||||||
"license": "GPL-3.0",
|
"license": "GPL-3.0",
|
||||||
|
|
|
@ -12,5 +12,9 @@ module.exports = {
|
||||||
google: {
|
google: {
|
||||||
username: "",
|
username: "",
|
||||||
password: ""
|
password: ""
|
||||||
|
},
|
||||||
|
facebook: {
|
||||||
|
username: "",
|
||||||
|
password: ""
|
||||||
}
|
}
|
||||||
};
|
};
|
|
@ -1,42 +1,28 @@
|
||||||
"use strict";
|
"use strict";
|
||||||
|
|
||||||
const http = require("http");
|
|
||||||
const utils = require("./lib/utils");
|
const utils = require("./lib/utils");
|
||||||
const injected = require("./lib/injected");
|
const injected = require("./lib/injected");
|
||||||
|
const testServer = require("./lib/server");
|
||||||
|
|
||||||
describe("Basic", function () {
|
describe("Basic", function () {
|
||||||
|
|
||||||
const testPort = 8989;
|
|
||||||
const testUrl = `http://localhost:${testPort}`;
|
|
||||||
const svgTestScript = injected.testSVGTestScript();
|
const svgTestScript = injected.testSVGTestScript();
|
||||||
|
|
||||||
let httpServer;
|
let httpServer;
|
||||||
|
let testUrl;
|
||||||
|
|
||||||
this.timeout = function () {
|
this.timeout = function () {
|
||||||
return 10000;
|
return 10000;
|
||||||
};
|
};
|
||||||
|
|
||||||
beforeEach(function () {
|
beforeEach(function () {
|
||||||
httpServer = http.createServer(function (req, res) {
|
const [server, url] = testServer.start();
|
||||||
const staticResponse = `<!DOCTYPE "html">
|
testUrl = url;
|
||||||
<html>
|
httpServer = server;
|
||||||
<head>
|
|
||||||
<title>Test Page</title>
|
|
||||||
</head>
|
|
||||||
<body>
|
|
||||||
<p>Test Content</p>
|
|
||||||
</body>
|
|
||||||
</html>
|
|
||||||
`;
|
|
||||||
res.writeHead(200, {"Content-Type": "text/html"});
|
|
||||||
res.write(staticResponse);
|
|
||||||
res.end();
|
|
||||||
});
|
|
||||||
httpServer.listen(8989);
|
|
||||||
});
|
});
|
||||||
|
|
||||||
afterEach(function () {
|
afterEach(function () {
|
||||||
httpServer.close();
|
testServer.stop(httpServer);
|
||||||
});
|
});
|
||||||
|
|
||||||
describe("blocking", function () {
|
describe("blocking", function () {
|
||||||
|
|
43
test/functional/csp.js
Normal file
43
test/functional/csp.js
Normal file
|
@ -0,0 +1,43 @@
|
||||||
|
"use strict";
|
||||||
|
|
||||||
|
const utils = require("./lib/utils");
|
||||||
|
const injected = require("./lib/injected");
|
||||||
|
const testServer = require("./lib/server");
|
||||||
|
|
||||||
|
describe("Content-Security-Protocol tests", function () {
|
||||||
|
|
||||||
|
describe("script-src", function () {
|
||||||
|
|
||||||
|
this.timeout = () => 20000;
|
||||||
|
|
||||||
|
it("default-src and script-src (from Pitchfork.com)", function (done) {
|
||||||
|
|
||||||
|
const [server, testUrl] = testServer.start(function (headers) {
|
||||||
|
// Add the CSP header to every request
|
||||||
|
headers['Content-Security-Protocol'] = "default-src https: data: 'unsafe-inline' 'unsafe-eval'; child-src https: data: blob:; connect-src https: data: blob:; font-src https: data:; img-src https: data: blob:; media-src https: data: blob:; object-src https:; script-src https: data: blob: 'unsafe-inline' 'unsafe-eval'; style-src https: 'unsafe-inline'; block-all-mixed-content; upgrade-insecure-requests; report-uri https://capture.condenastdigital.com/csp/pitchfork;";
|
||||||
|
});
|
||||||
|
|
||||||
|
const svgTestScript = injected.testSVGTestScript();
|
||||||
|
const standardsToBlock = utils.constants.svgBlockRule;
|
||||||
|
let driverReference;
|
||||||
|
|
||||||
|
utils.promiseGetDriver()
|
||||||
|
.then(function (driver) {
|
||||||
|
driverReference = driver;
|
||||||
|
return utils.promiseSetBlockingRules(driver, standardsToBlock);
|
||||||
|
})
|
||||||
|
.then(() => driverReference.get(testUrl))
|
||||||
|
.then(() => driverReference.executeAsyncScript(svgTestScript))
|
||||||
|
.then(function () {
|
||||||
|
driverReference.quit();
|
||||||
|
testServer.stop(server);
|
||||||
|
done();
|
||||||
|
})
|
||||||
|
.catch(function (e) {
|
||||||
|
driverReference.quit();
|
||||||
|
testServer.stop(server);
|
||||||
|
done(e);
|
||||||
|
});
|
||||||
|
});
|
||||||
|
});
|
||||||
|
});
|
39
test/functional/lib/server.js
Normal file
39
test/functional/lib/server.js
Normal file
|
@ -0,0 +1,39 @@
|
||||||
|
"use strict";
|
||||||
|
|
||||||
|
const http = require("http");
|
||||||
|
|
||||||
|
const testPort = 8989;
|
||||||
|
const testUrl = `http://localhost:${testPort}`;
|
||||||
|
|
||||||
|
const staticResponse = `<!DOCTYPE "html">
|
||||||
|
<html>
|
||||||
|
<head>
|
||||||
|
<title>Test Page</title>
|
||||||
|
</head>
|
||||||
|
<body>
|
||||||
|
<p>Test Content</p>
|
||||||
|
</body>
|
||||||
|
</html>
|
||||||
|
`;
|
||||||
|
|
||||||
|
module.exports.start = function (callback) {
|
||||||
|
|
||||||
|
const httpServer = http.createServer(function (req, res) {
|
||||||
|
|
||||||
|
let headers = {"Content-Type": "text/html; charset=utf-8"};
|
||||||
|
|
||||||
|
if (callback !== undefined) {
|
||||||
|
callback(headers);
|
||||||
|
}
|
||||||
|
|
||||||
|
res.writeHead(200, headers);
|
||||||
|
res.write(staticResponse);
|
||||||
|
res.end();
|
||||||
|
});
|
||||||
|
httpServer.listen(8989);
|
||||||
|
return [httpServer, testUrl];
|
||||||
|
};
|
||||||
|
|
||||||
|
module.exports.stop = function (server) {
|
||||||
|
server.close();
|
||||||
|
};
|
|
@ -92,6 +92,48 @@ describe("Logging into popular sites", function () {
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
|
describe("Facebook", function () {
|
||||||
|
this.timeout = function () {
|
||||||
|
return 20000;
|
||||||
|
};
|
||||||
|
|
||||||
|
it("Log in", function (done) {
|
||||||
|
|
||||||
|
const formValues = [
|
||||||
|
["email", testParams.facebook.username],
|
||||||
|
["pass", testParams.facebook.password]
|
||||||
|
];
|
||||||
|
|
||||||
|
let driverReference;
|
||||||
|
|
||||||
|
utils.promiseGetDriver()
|
||||||
|
.then(function (driver) {
|
||||||
|
driverReference = driver;
|
||||||
|
return driverReference.get("https://www.facebook.com/");
|
||||||
|
})
|
||||||
|
.then(function () {
|
||||||
|
return driverReference.wait(until.elementsLocated(
|
||||||
|
by.name("email")
|
||||||
|
), 5000);
|
||||||
|
})
|
||||||
|
.then(() => utils.promiseSetFormAndSubmit(driverReference, formValues))
|
||||||
|
.then(function () {
|
||||||
|
return driverReference.wait(until.elementLocated(
|
||||||
|
by.css("div[data-click='profile_icon']")
|
||||||
|
), 10000);
|
||||||
|
})
|
||||||
|
.then(function () {
|
||||||
|
driverReference.quit();
|
||||||
|
done();
|
||||||
|
})
|
||||||
|
.catch(function (e) {
|
||||||
|
driverReference.quit();
|
||||||
|
console.log(e);
|
||||||
|
done(new Error("Was not able to log in"));
|
||||||
|
});
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
describe("YouTube", function () {
|
describe("YouTube", function () {
|
||||||
|
|
||||||
this.timeout = function () {
|
this.timeout = function () {
|
||||||
|
@ -110,7 +152,7 @@ describe("Logging into popular sites", function () {
|
||||||
.then(function () {
|
.then(function () {
|
||||||
return driverReference.wait(until.elementsLocated(
|
return driverReference.wait(until.elementsLocated(
|
||||||
by.css("#buttons ytd-button-renderer a")
|
by.css("#buttons ytd-button-renderer a")
|
||||||
), 2000);
|
), 5000);
|
||||||
})
|
})
|
||||||
.then(anchors => anchors[anchors.length - 1].click())
|
.then(anchors => anchors[anchors.length - 1].click())
|
||||||
.then(() => utils.pause(2000))
|
.then(() => utils.pause(2000))
|
||||||
|
@ -132,7 +174,7 @@ describe("Logging into popular sites", function () {
|
||||||
.then(function () {
|
.then(function () {
|
||||||
return driverReference.wait(until.elementLocated(
|
return driverReference.wait(until.elementLocated(
|
||||||
by.css("ytd-app")
|
by.css("ytd-app")
|
||||||
), 40000);
|
), 10000);
|
||||||
})
|
})
|
||||||
.then(function () {
|
.then(function () {
|
||||||
driverReference.quit();
|
driverReference.quit();
|
||||||
|
|
Loading…
Add table
Reference in a new issue