2015-06-02 22:42:19 +03:00
|
|
|
/*
|
|
|
|
* Description of the tests:
|
|
|
|
* These tests check for conformance to the CSP spec as they relate to Web Manifests.
|
|
|
|
*
|
|
|
|
* In particular, the tests check that default-src and manifest-src directives are
|
|
|
|
* are respected by the ManifestObtainer.
|
|
|
|
*/
|
2016-03-16 18:07:00 +03:00
|
|
|
/*globals Cu, is, ok*/
|
2015-06-02 22:42:19 +03:00
|
|
|
'use strict';
|
|
|
|
requestLongerTimeout(10); // e10s tests take time.
|
2015-07-17 04:45:59 +03:00
|
|
|
const {
|
|
|
|
ManifestObtainer
|
2015-07-30 18:56:12 +03:00
|
|
|
} = Cu.import('resource://gre/modules/ManifestObtainer.jsm', {});
|
2015-06-09 02:21:50 +03:00
|
|
|
const path = '/tests/dom/security/test/csp/';
|
|
|
|
const testFile = `file=${path}file_web_manifest.html`;
|
|
|
|
const remoteFile = `file=${path}file_web_manifest_remote.html`;
|
2015-07-17 04:45:59 +03:00
|
|
|
const httpsManifest = `file=${path}file_web_manifest_https.html`;
|
2015-06-09 02:21:50 +03:00
|
|
|
const server = 'file_testserver.sjs';
|
2015-06-02 22:42:19 +03:00
|
|
|
const defaultURL = `http://example.org${path}${server}`;
|
2016-03-16 18:07:00 +03:00
|
|
|
const secureURL = `https://example.com:443${path}${server}`;
|
2015-06-02 22:42:19 +03:00
|
|
|
const tests = [
|
|
|
|
// CSP block everything, so trying to load a manifest
|
|
|
|
// will result in a policy violation.
|
|
|
|
{
|
|
|
|
expected: `default-src 'none' blocks fetching manifest.`,
|
|
|
|
get tabURL() {
|
|
|
|
let queryParts = [
|
|
|
|
`csp=default-src 'none'`,
|
|
|
|
testFile
|
|
|
|
];
|
|
|
|
return `${defaultURL}?${queryParts.join('&')}`;
|
|
|
|
},
|
|
|
|
run(topic) {
|
|
|
|
is(topic, 'csp-on-violate-policy', this.expected);
|
|
|
|
}
|
|
|
|
},
|
|
|
|
// CSP allows fetching only from mochi.test:8888,
|
|
|
|
// so trying to load a manifest from same origin
|
|
|
|
// triggers a CSP violation.
|
|
|
|
{
|
|
|
|
expected: `default-src mochi.test:8888 blocks manifest fetching.`,
|
|
|
|
get tabURL() {
|
|
|
|
let queryParts = [
|
|
|
|
`csp=default-src mochi.test:8888`,
|
|
|
|
testFile
|
|
|
|
];
|
|
|
|
return `${defaultURL}?${queryParts.join('&')}`;
|
|
|
|
},
|
|
|
|
run(topic) {
|
|
|
|
is(topic, 'csp-on-violate-policy', this.expected);
|
|
|
|
}
|
|
|
|
},
|
|
|
|
// CSP restricts fetching to 'self', so allowing the manifest
|
|
|
|
// to load. The name of the manifest is then checked.
|
|
|
|
{
|
|
|
|
expected: `CSP default-src 'self' allows fetch of manifest.`,
|
|
|
|
get tabURL() {
|
|
|
|
let queryParts = [
|
|
|
|
`csp=default-src 'self'`,
|
|
|
|
testFile
|
|
|
|
];
|
|
|
|
return `${defaultURL}?${queryParts.join('&')}`;
|
|
|
|
},
|
|
|
|
run(manifest) {
|
|
|
|
is(manifest.name, 'loaded', this.expected);
|
|
|
|
}
|
|
|
|
},
|
|
|
|
// CSP only allows fetching from mochi.test:8888 and remoteFile
|
|
|
|
// requests a manifest from that origin, so manifest should load.
|
|
|
|
{
|
|
|
|
expected: 'CSP default-src mochi.test:8888 allows fetching manifest.',
|
|
|
|
get tabURL() {
|
|
|
|
let queryParts = [
|
|
|
|
`csp=default-src http://mochi.test:8888`,
|
|
|
|
remoteFile
|
|
|
|
];
|
|
|
|
return `${defaultURL}?${queryParts.join('&')}`;
|
|
|
|
},
|
|
|
|
run(manifest) {
|
|
|
|
is(manifest.name, 'loaded', this.expected);
|
|
|
|
}
|
|
|
|
},
|
|
|
|
// default-src blocks everything, so any attempt to
|
|
|
|
// fetch a manifest from another origin will trigger a
|
|
|
|
// policy violation.
|
|
|
|
{
|
|
|
|
expected: `default-src 'none' blocks mochi.test:8888`,
|
|
|
|
get tabURL() {
|
|
|
|
let queryParts = [
|
|
|
|
`csp=default-src 'none'`,
|
|
|
|
remoteFile
|
|
|
|
];
|
|
|
|
return `${defaultURL}?${queryParts.join('&')}`;
|
|
|
|
},
|
|
|
|
run(topic) {
|
|
|
|
is(topic, 'csp-on-violate-policy', this.expected);
|
|
|
|
}
|
|
|
|
},
|
|
|
|
// CSP allows fetching from self, so manifest should load.
|
|
|
|
{
|
|
|
|
expected: `CSP manifest-src allows self`,
|
|
|
|
get tabURL() {
|
|
|
|
let queryParts = [
|
2016-03-16 18:07:00 +03:00
|
|
|
`csp=manifest-src 'self'`,
|
2015-06-02 22:42:19 +03:00
|
|
|
testFile
|
|
|
|
];
|
|
|
|
return `${defaultURL}?${queryParts.join('&')}`;
|
|
|
|
},
|
|
|
|
run(manifest) {
|
|
|
|
is(manifest.name, 'loaded', this.expected);
|
|
|
|
}
|
|
|
|
},
|
|
|
|
// CSP allows fetching from example.org, so manifest should load.
|
|
|
|
{
|
|
|
|
expected: `CSP manifest-src allows http://example.org`,
|
|
|
|
get tabURL() {
|
|
|
|
let queryParts = [
|
2016-03-16 18:07:00 +03:00
|
|
|
`csp=manifest-src http://example.org`,
|
2015-06-02 22:42:19 +03:00
|
|
|
testFile
|
|
|
|
];
|
|
|
|
return `${defaultURL}?${queryParts.join('&')}`;
|
|
|
|
},
|
|
|
|
run(manifest) {
|
|
|
|
is(manifest.name, 'loaded', this.expected);
|
|
|
|
}
|
|
|
|
},
|
|
|
|
{
|
|
|
|
expected: `CSP manifest-src allows mochi.test:8888`,
|
|
|
|
get tabURL() {
|
|
|
|
let queryParts = [
|
2016-03-16 18:07:00 +03:00
|
|
|
`cors=*`,
|
2015-06-02 22:42:19 +03:00
|
|
|
`csp=default-src *; manifest-src http://mochi.test:8888`,
|
|
|
|
remoteFile
|
|
|
|
];
|
|
|
|
return `${defaultURL}?${queryParts.join('&')}`;
|
|
|
|
},
|
|
|
|
run(manifest) {
|
|
|
|
is(manifest.name, 'loaded', this.expected);
|
|
|
|
}
|
|
|
|
},
|
|
|
|
// CSP restricts fetching to mochi.test:8888, but the test
|
|
|
|
// file is at example.org. Hence, a policy violation is
|
|
|
|
// triggered.
|
|
|
|
{
|
|
|
|
expected: `CSP blocks manifest fetching from example.org.`,
|
|
|
|
get tabURL() {
|
|
|
|
let queryParts = [
|
|
|
|
`csp=manifest-src mochi.test:8888`,
|
|
|
|
testFile
|
|
|
|
];
|
|
|
|
return `${defaultURL}?${queryParts.join('&')}`;
|
|
|
|
},
|
|
|
|
run(topic) {
|
|
|
|
is(topic, 'csp-on-violate-policy', this.expected);
|
|
|
|
}
|
|
|
|
},
|
|
|
|
// CSP is set to only allow manifest to be loaded from same origin,
|
|
|
|
// but the remote file attempts to load from a different origin. Thus
|
|
|
|
// this causes a CSP violation.
|
|
|
|
{
|
|
|
|
expected: `CSP manifest-src 'self' blocks cross-origin fetch.`,
|
|
|
|
get tabURL() {
|
|
|
|
let queryParts = [
|
|
|
|
`csp=manifest-src 'self'`,
|
|
|
|
remoteFile
|
|
|
|
];
|
|
|
|
return `${defaultURL}?${queryParts.join('&')}`;
|
|
|
|
},
|
|
|
|
run(topic) {
|
|
|
|
is(topic, 'csp-on-violate-policy', this.expected);
|
|
|
|
}
|
2016-03-16 18:07:00 +03:00
|
|
|
},
|
|
|
|
// CSP allows fetching over TLS from example.org, so manifest should load.
|
|
|
|
{
|
|
|
|
expected: `CSP manifest-src allows example.com over TLS`,
|
|
|
|
get tabURL() {
|
|
|
|
let queryParts = [
|
|
|
|
'cors=*',
|
|
|
|
'csp=manifest-src https://example.com:443',
|
|
|
|
httpsManifest
|
|
|
|
];
|
|
|
|
// secureURL loads https://example.com:443
|
|
|
|
// and gets manifest from https://example.org:443
|
|
|
|
return `${secureURL}?${queryParts.join('&')}`;
|
|
|
|
},
|
|
|
|
run(manifest) {
|
|
|
|
is(manifest.name, 'loaded', this.expected);
|
|
|
|
}
|
|
|
|
},
|
2015-06-02 22:42:19 +03:00
|
|
|
];
|
|
|
|
//jscs:disable
|
|
|
|
add_task(function* () {
|
|
|
|
//jscs:enable
|
2016-03-16 18:07:00 +03:00
|
|
|
var testPromises = tests.map(
|
|
|
|
(test) => ([test, {gBrowser, url: test.tabURL, skipAnimation: true}])
|
|
|
|
).map(
|
|
|
|
([test, tabOptions]) => BrowserTestUtils.withNewTab(tabOptions, (browser) => testObtainingManifest(browser, test))
|
2015-06-02 22:42:19 +03:00
|
|
|
);
|
2016-03-16 18:07:00 +03:00
|
|
|
yield Promise.all(testPromises);
|
|
|
|
});
|
2015-06-02 22:42:19 +03:00
|
|
|
|
2016-03-16 18:07:00 +03:00
|
|
|
function* testObtainingManifest(aBrowser, aTest) {
|
|
|
|
const expectsBlocked = aTest.expected.includes('block');
|
|
|
|
const observer = (expectsBlocked) ? createNetObserver(aTest) : null;
|
|
|
|
// Expect an exception (from promise rejection) if there a content policy
|
|
|
|
// that is violated.
|
|
|
|
try {
|
|
|
|
const manifest = yield ManifestObtainer.browserObtainManifest(aBrowser);
|
|
|
|
aTest.run(manifest);
|
|
|
|
} catch (e) {
|
|
|
|
const wasBlocked = e.message.includes('blocked the loading of a resource');
|
|
|
|
ok(wasBlocked,`Expected promise rejection obtaining ${aTest.tabURL}: ${e.message}`);
|
|
|
|
if (observer) {
|
|
|
|
yield observer.untilFinished;
|
2015-06-02 22:42:19 +03:00
|
|
|
return;
|
|
|
|
}
|
2016-03-16 18:07:00 +03:00
|
|
|
throw e;
|
2015-06-02 22:42:19 +03:00
|
|
|
}
|
2016-03-16 18:07:00 +03:00
|
|
|
}
|
2015-06-02 22:42:19 +03:00
|
|
|
|
2016-03-16 18:07:00 +03:00
|
|
|
// Helper object used to observe policy violations. It waits 1 seconds
|
2015-06-02 22:42:19 +03:00
|
|
|
// for a response, and then times out causing its associated test to fail.
|
2016-03-16 18:07:00 +03:00
|
|
|
function createNetObserver(test) {
|
2015-06-02 22:42:19 +03:00
|
|
|
let finishedTest;
|
|
|
|
let success = false;
|
2016-03-16 18:07:00 +03:00
|
|
|
const finished = new Promise((resolver) => {
|
2015-06-02 22:42:19 +03:00
|
|
|
finishedTest = resolver;
|
2016-03-16 18:07:00 +03:00
|
|
|
});
|
|
|
|
const timeoutId = setTimeout(() => {
|
2015-06-02 22:42:19 +03:00
|
|
|
if (!success) {
|
|
|
|
test.run('This test timed out.');
|
|
|
|
finishedTest();
|
|
|
|
}
|
|
|
|
}, 1000);
|
2016-03-16 18:07:00 +03:00
|
|
|
var observer = {
|
|
|
|
get untilFinished(){
|
|
|
|
return finished;
|
|
|
|
},
|
|
|
|
observe(subject, topic) {
|
|
|
|
SpecialPowers.removeObserver(observer, 'csp-on-violate-policy');
|
|
|
|
test.run(topic);
|
|
|
|
finishedTest();
|
|
|
|
clearTimeout(timeoutId);
|
|
|
|
success = true;
|
|
|
|
},
|
|
|
|
};
|
|
|
|
SpecialPowers.addObserver(observer, 'csp-on-violate-policy', false);
|
|
|
|
return observer;
|
2015-06-02 22:42:19 +03:00
|
|
|
}
|