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