1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
|
/*
* 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.
*/
/*globals Cu, is, ok*/
"use strict";
const { ManifestObtainer } = ChromeUtils.importESModule(
"resource://gre/modules/ManifestObtainer.sys.mjs"
);
const path = "/tests/dom/security/test/csp/";
const testFile = `${path}file_web_manifest.html`;
const remoteFile = `${path}file_web_manifest_remote.html`;
const httpsManifest = `${path}file_web_manifest_https.html`;
const server = `${path}file_testserver.sjs`;
const defaultURL = new URL(`http://example.org${server}`);
const secureURL = new URL(`https://example.com:443${server}`);
// Enable web manifest processing.
Services.prefs.setBoolPref("dom.manifest.enabled", true);
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() {
const url = new URL(defaultURL);
url.searchParams.append("file", testFile);
url.searchParams.append("csp", "default-src 'none'");
return url.href;
},
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() {
const url = new URL(defaultURL);
url.searchParams.append("file", testFile);
url.searchParams.append("csp", "default-src mochi.test:8888");
return url.href;
},
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() {
const url = new URL(defaultURL);
url.searchParams.append("file", testFile);
url.searchParams.append("csp", "default-src 'self'");
return url.href;
},
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() {
const url = new URL(defaultURL);
url.searchParams.append("file", remoteFile);
url.searchParams.append("csp", "default-src http://mochi.test:8888");
return url.href;
},
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() {
const url = new URL(defaultURL);
url.searchParams.append("file", remoteFile);
url.searchParams.append("csp", "default-src 'none'");
return url.href;
},
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() {
const url = new URL(defaultURL);
url.searchParams.append("file", testFile);
url.searchParams.append("csp", "manifest-src 'self'");
return url.href;
},
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() {
const url = new URL(defaultURL);
url.searchParams.append("file", testFile);
url.searchParams.append("csp", "manifest-src http://example.org");
return url.href;
},
run(manifest) {
is(manifest.name, "loaded", this.expected);
},
},
{
expected: "CSP manifest-src allows mochi.test:8888",
get tabURL() {
const url = new URL(defaultURL);
url.searchParams.append("file", remoteFile);
url.searchParams.append("cors", "*");
url.searchParams.append(
"csp",
"default-src *; manifest-src http://mochi.test:8888"
);
return url.href;
},
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() {
const url = new URL(defaultURL);
url.searchParams.append("file", testFile);
url.searchParams.append("csp", "manifest-src mochi.test:8888");
return url.href;
},
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() {
const url = new URL(defaultURL);
url.searchParams.append("file", remoteFile);
url.searchParams.append("csp", "manifest-src 'self'");
return url.href;
},
run(topic) {
is(topic, "csp-on-violate-policy", this.expected);
},
},
// CSP allows fetching over TLS from example.org, so manifest should load.
{
expected: "CSP manifest-src allows example.com over TLS",
get tabURL() {
// secureURL loads https://example.com:443
// and gets manifest from https://example.org:443
const url = new URL(secureURL);
url.searchParams.append("file", httpsManifest);
url.searchParams.append("cors", "*");
url.searchParams.append("csp", "manifest-src https://example.com:443");
return url.href;
},
run(manifest) {
is(manifest.name, "loaded", this.expected);
},
},
];
//jscs:disable
add_task(async function () {
//jscs:enable
const testPromises = tests.map(test => {
const tabOptions = {
gBrowser,
url: test.tabURL,
skipAnimation: true,
};
return BrowserTestUtils.withNewTab(tabOptions, browser =>
testObtainingManifest(browser, test)
);
});
await Promise.all(testPromises);
});
async function testObtainingManifest(aBrowser, aTest) {
const waitForObserver = waitForNetObserver(aBrowser, aTest);
// Expect an exception (from promise rejection) if there a content policy
// that is violated.
try {
const manifest = await ManifestObtainer.browserObtainManifest(aBrowser);
aTest.run(manifest);
} catch (e) {
const wasBlocked = e.message.includes(
"NetworkError when attempting to fetch resource"
);
ok(
wasBlocked,
`Expected promise rejection obtaining ${aTest.tabURL}: ${e.message}`
);
} finally {
await waitForObserver;
}
}
// Helper object used to observe policy violations when blocking is expected.
function waitForNetObserver(aBrowser, aTest) {
// We don't need to wait for violation, so just resolve
if (!aTest.expected.includes("block")) {
return Promise.resolve();
}
return ContentTask.spawn(aBrowser, [], () => {
return new Promise(resolve => {
function observe(subject, topic) {
Services.obs.removeObserver(observe, "csp-on-violate-policy");
resolve();
}
Services.obs.addObserver(observe, "csp-on-violate-policy");
});
}).then(() => aTest.run("csp-on-violate-policy"));
}
|