Commit bc808758 authored by jww@chromium.org's avatar jww@chromium.org

Isolated world injected inline styles should bypass main world CSP.

Inline styles that are added to a page from an isolated world should bypass the
main world's CSP, much like how scripts do. As an example, this is important for
extensions to make sure they can bypass the page's CSP when they inject a style
tag into the page.

R=mkwst@chromium.org
BUG=385246

Review URL: https://codereview.chromium.org/341443003

git-svn-id: svn://svn.chromium.org/blink/trunk@176461 bbb929c8-8fbe-4397-9dbb-9b2b20218538
parent e2ab53e6
CONSOLE MESSAGE: line 38: Injecting in main world: this should fail.
CONSOLE ERROR: Refused to apply inline style because it violates the following Content Security Policy directive: "default-src 'none'". Either the 'unsafe-inline' keyword, a hash ('sha256-...'), or a nonce ('nonce-...') is required to enable inline execution. Note also that 'style-src' was not explicitly set, so 'default-src' is used as a fallback.
CONSOLE MESSAGE: line 31: PASS: Style assignment in test 4 was not blocked by CSP.
CONSOLE MESSAGE: line 42: Injecting into isolated world without bypass: this should fail.
CONSOLE ERROR: Refused to apply inline style because it violates the following Content Security Policy directive: "default-src 'none'". Either the 'unsafe-inline' keyword, a hash ('sha256-...'), or a nonce ('nonce-...') is required to enable inline execution. Note also that 'style-src' was not explicitly set, so 'default-src' is used as a fallback.
CONSOLE MESSAGE: line 19: PASS: Style assignment in test 3 was not blocked by CSP.
CONSOLE MESSAGE: line 46: Starting to bypass main world's CSP: this should pass!
CONSOLE MESSAGE: line 12: PASS: Style assignment in test 2 was blocked by CSP.
CONSOLE MESSAGE: line 51: Injecting into main world again: this should fail.
CONSOLE ERROR: Refused to apply inline style because it violates the following Content Security Policy directive: "default-src 'none'". Either the 'unsafe-inline' keyword, a hash ('sha256-...'), or a nonce ('nonce-...') is required to enable inline execution. Note also that 'style-src' was not explicitly set, so 'default-src' is used as a fallback.
CONSOLE MESSAGE: line 31: PASS: Style assignment in test 1 was not blocked by CSP.
This test ensures that style applied in isolated worlds marked with their own Content Security Policy aren't affected by the page's content security policy. Extensions, for example, should be able to inject inline CSS (even though it's probably a bad idea to do so).
<!DOCTYPE html>
<html>
<head>
<meta http-equiv="Content-Security-Policy" content="default-src 'none'; script-src 'self' 'unsafe-eval'">
<script src="./resources/bypass-main-world-csp-for-inline-style.js"></script>
</head>
<body>
<p>
This test ensures that style applied in isolated worlds marked with
their own Content Security Policy aren't affected by the page's content
security policy. Extensions, for example, should be able to inject
inline CSS (even though it's probably a bad idea to do so).
</p>
</body>
</html>
if (window.testRunner) {
testRunner.dumpAsText();
testRunner.waitUntilDone();
}
tests = 4;
window.addEventListener("message", function(message) {
tests -= 1;
test();
}, false);
function test() {
function injectInlineStyle(shouldSucceed, tests) {
var id = 'div' + tests;
var div = document.createElement('div');
div.id = id;
document.body.appendChild(div);
var style = document.createElement('style');
style.innerText = '#' + id + ' { color: red; }';
document.body.appendChild(style);
var success = window.getComputedStyle(document.getElementById(id)).color === "rgb(255, 0, 0)";
if (shouldSucceed) {
if (success)
console.log("PASS: Style assignment in test " + tests + " was blocked by CSP.");
else
console.log("FAIL: Style assignment in test " + tests + " was not blocked by CSP.");
} else {
if (success)
console.log("FAIL: Style assignment in test " + tests + " was blocked by CSP.");
else
console.log("PASS: Style assignment in test " + tests + " was not blocked by CSP.");
}
window.postMessage("next", "*");
}
switch (tests) {
case 4:
console.log("Injecting in main world: this should fail.");
injectInlineStyle(false, tests);
break;
case 3:
console.log("Injecting into isolated world without bypass: this should fail.");
testRunner.evaluateScriptInIsolatedWorld(1, String(eval("injectInlineStyle")) + "\ninjectInlineStyle(false," + tests + ");");
break;
case 2:
console.log("Starting to bypass main world's CSP: this should pass!");
testRunner.setIsolatedWorldContentSecurityPolicy(1, 'style-src \'unsafe-inline\' *');
testRunner.evaluateScriptInIsolatedWorld(1, String(eval("injectInlineStyle")) + "\ninjectInlineStyle(true," + tests + ");");
break;
case 1:
console.log("Injecting into main world again: this should fail.");
injectInlineStyle(false, tests);
break;
case 0:
testRunner.setIsolatedWorldContentSecurityPolicy(1, '');
testRunner.notifyDone();
break;
}
}
document.addEventListener('DOMContentLoaded', test);
...@@ -21,6 +21,7 @@ ...@@ -21,6 +21,7 @@
#include "config.h" #include "config.h"
#include "core/dom/StyleElement.h" #include "core/dom/StyleElement.h"
#include "bindings/v8/ScriptController.h"
#include "core/css/MediaList.h" #include "core/css/MediaList.h"
#include "core/css/MediaQueryEvaluator.h" #include "core/css/MediaQueryEvaluator.h"
#include "core/css/StyleSheetContents.h" #include "core/css/StyleSheetContents.h"
...@@ -28,6 +29,7 @@ ...@@ -28,6 +29,7 @@
#include "core/dom/Element.h" #include "core/dom/Element.h"
#include "core/dom/ScriptableDocumentParser.h" #include "core/dom/ScriptableDocumentParser.h"
#include "core/dom/StyleEngine.h" #include "core/dom/StyleEngine.h"
#include "core/frame/LocalFrame.h"
#include "core/frame/csp/ContentSecurityPolicy.h" #include "core/frame/csp/ContentSecurityPolicy.h"
#include "core/html/HTMLStyleElement.h" #include "core/html/HTMLStyleElement.h"
#include "platform/TraceEvent.h" #include "platform/TraceEvent.h"
...@@ -147,9 +149,14 @@ void StyleElement::createSheet(Element* e, const String& text) ...@@ -147,9 +149,14 @@ void StyleElement::createSheet(Element* e, const String& text)
if (m_sheet) if (m_sheet)
clearSheet(e); clearSheet(e);
// Inline style added from an isolated world should bypass the main world's
// CSP just as an inline script would.
LocalFrame* frame = document.frame();
bool shouldBypassMainWorldContentSecurityPolicy = frame && frame->script().shouldBypassMainWorldContentSecurityPolicy();
// If type is empty or CSS, this is a CSS style sheet. // If type is empty or CSS, this is a CSS style sheet.
const AtomicString& type = this->type(); const AtomicString& type = this->type();
bool passesContentSecurityPolicyChecks = document.contentSecurityPolicy()->allowStyleHash(text) || document.contentSecurityPolicy()->allowStyleNonce(e->fastGetAttribute(HTMLNames::nonceAttr)) || document.contentSecurityPolicy()->allowInlineStyle(e->document().url(), m_startPosition.m_line); bool passesContentSecurityPolicyChecks = shouldBypassMainWorldContentSecurityPolicy || document.contentSecurityPolicy()->allowStyleHash(text) || document.contentSecurityPolicy()->allowStyleNonce(e->fastGetAttribute(HTMLNames::nonceAttr)) || document.contentSecurityPolicy()->allowInlineStyle(e->document().url(), m_startPosition.m_line);
if (isCSS(e, type) && passesContentSecurityPolicyChecks) { if (isCSS(e, type) && passesContentSecurityPolicyChecks) {
RefPtrWillBeRawPtr<MediaQuerySet> mediaQueries = MediaQuerySet::create(media()); RefPtrWillBeRawPtr<MediaQuerySet> mediaQueries = MediaQuerySet::create(media());
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment