Bug 1582115: Exempt pdf.js from being subject to CSP from page. r=Gijs

Differential Revision: https://phabricator.services.mozilla.com/D74614
This commit is contained in:
Christoph Kerschbaumer 2020-05-12 08:51:08 +00:00
parent edbe69232a
commit 2c610ecc1f
5 changed files with 89 additions and 8 deletions

View File

@ -76,8 +76,16 @@ bool subjectToCSP(nsIURI* aURI, nsContentPolicyType aContentType) {
contentType == nsIContentPolicy::TYPE_STYLESHEET ||
contentType == nsIContentPolicy::TYPE_DTD ||
contentType == nsIContentPolicy::TYPE_XBL;
if (aURI->SchemeIs("resource") && !isImgOrStyleOrDTDorXBL) {
return true;
if (aURI->SchemeIs("resource")) {
nsAutoCString uriSpec;
aURI->GetSpec(uriSpec);
// Exempt pdf.js from being subject to a page's CSP.
if (StringBeginsWith(uriSpec, NS_LITERAL_CSTRING("resource://pdf.js/"))) {
return false;
}
if (!isImgOrStyleOrDTDorXBL) {
return true;
}
}
if (aURI->SchemeIs("chrome") && !isImgOrStyleOrDTDorXBL) {
return true;
@ -237,9 +245,9 @@ CSPService::AsyncOnChannelRedirect(nsIChannel* oldChannel,
// context. In turn, we do not have an event target for policy violations.
// Enforce the CSP check in the content process where we have that info.
// We allow redirect checks to run for document loads via
// DocumentLoadListener, since these are fully supported and we don't expose
// the redirects to the content process. We can't do this for all request
// types yet because we don't serialize nsICSPEventListener.
// DocumentLoadListener, since these are fully supported and we don't
// expose the redirects to the content process. We can't do this for all
// request types yet because we don't serialize nsICSPEventListener.
if (parentChannel && !docListener) {
return NS_OK;
}
@ -323,9 +331,9 @@ nsresult CSPService::ConsultCSPForRedirect(nsIURI* aOriginalURI,
bool isPreload = nsContentUtils::IsPreloadType(policyType);
/* On redirect, if the content policy is a preload type, rejecting the preload
* results in the load silently failing, so we convert preloads to the actual
* type. See Bug 1219453.
/* On redirect, if the content policy is a preload type, rejecting the
* preload results in the load silently failing, so we convert preloads to
* the actual type. See Bug 1219453.
*/
policyType =
nsContentUtils::InternalContentPolicyTypeToExternalOrWorker(policyType);

View File

@ -17,3 +17,7 @@ support-files =
support-files =
file_csp_meta_uir.html
[browser_manifest-src-override-default-src.js]
[browser_pdfjs_not_subject_to_csp.js]
support-files =
dummy.pdf
file_pdfjs_not_subject_to_csp.html

View File

@ -0,0 +1,48 @@
"use strict";
const TEST_PATH = getRootDirectory(gTestPath).replace(
"chrome://mochitests/content",
"https://example.com"
);
add_task(async function() {
await SpecialPowers.pushPrefEnv({
set: [["pdfjs.eventBusDispatchToDOM", true]],
});
await BrowserTestUtils.withNewTab(
TEST_PATH + "file_pdfjs_not_subject_to_csp.html",
async function(browser) {
let pdfPromise = BrowserTestUtils.waitForContentEvent(
browser,
"documentloaded",
false,
null,
true
);
await ContentTask.spawn(browser, {}, async function() {
let pdfButton = content.document.getElementById("pdfButton");
pdfButton.click();
});
await pdfPromise;
await ContentTask.spawn(browser, {}, async function() {
let pdfFrame = content.document.getElementById("pdfFrame");
// 1) Sanity that we have loaded the PDF using a blob
ok(pdfFrame.src.startsWith("blob:"), "it's a blob URL");
// 2) Ensure that the PDF has actually loaded
ok(
pdfFrame.contentDocument.querySelector("div#viewer"),
"document content has viewer UI"
);
// 3) Ensure we have the correct CSP attached
let cspJSON = pdfFrame.contentDocument.cspJSON;
ok(cspJSON.includes("script-src"), "found script-src directive");
ok(cspJSON.includes("allowPDF"), "found script-src nonce value");
});
}
);
});

Binary file not shown.

View File

@ -0,0 +1,21 @@
<html>
<head>
<meta http-equiv="Content-Security-Policy" content="script-src 'self' 'nonce-allowPDF'">
</head>
<body>
<iframe id="pdfFrame"></iframe>
<br/>
<button id="pdfButton">click to load pdf</button>
<script nonce="allowPDF">
async function loadPDFIntoIframe() {
let response = await fetch("dummy.pdf");
let blob = await response.blob();
var blobUrl = URL.createObjectURL(blob);
var pdfFrame = document.getElementById("pdfFrame");
pdfFrame.src = blobUrl;
}
let pdfButton = document.getElementById("pdfButton");
pdfButton.addEventListener("click", loadPDFIntoIframe);
</script>
</body>
</html>