Bug 763879 - implement inline stylesheet blocking for CSP (r=dbaron)

This commit is contained in:
Sid Stamm 2012-08-30 10:58:24 -07:00
parent 9caf073269
commit cc629bf54e
24 changed files with 615 additions and 27 deletions

View File

@ -13,7 +13,7 @@ interface nsIDocShell;
* Describes an XPCOM component used to model an enforce CSPs.
*/
[scriptable, uuid(d1680bb4-1ac0-4772-9437-1188375e44f2)]
[scriptable, uuid(230b126d-afc3-4588-9794-3e135594d626)]
interface nsIContentSecurityPolicy : nsISupports
{
@ -40,6 +40,13 @@ interface nsIContentSecurityPolicy : nsISupports
*/
readonly attribute boolean allowsInlineScript;
/**
* Whether this policy allows in-page styles.
* This includes <style> tags with text content and style="" attributes in
* HTML elements.
*/
readonly attribute boolean allowsInlineStyle;
/**
* whether this policy allows eval and eval-like functions
* such as setTimeout("code string", time).
@ -66,6 +73,7 @@ interface nsIContentSecurityPolicy : nsISupports
const unsigned short VIOLATION_TYPE_INLINE_SCRIPT = 1;
const unsigned short VIOLATION_TYPE_EVAL = 2;
const unsigned short VIOLATION_TYPE_INLINE_STYLE = 3;
/**
* Manually triggers violation report sending given a URI and reason.

View File

@ -181,6 +181,9 @@ this.CSPRep = function CSPRep(aSpecCompliant) {
// Is this a 1.0 spec compliant CSPRep ?
// Default to false if not specified.
this._specCompliant = (aSpecCompliant !== undefined) ? aSpecCompliant : false;
// Only CSP 1.0 spec compliant policies block inline styles.
this._allowInlineStyles = !aSpecCompliant;
}
// Source directives for our original CSP implementation.
@ -717,7 +720,8 @@ CSPRep.prototype = {
}
}
return (this.allowsInlineScripts === that.allowsInlineScripts)
&& (this.allowsEvalInScripts === that.allowsEvalInScripts);
&& (this.allowsEvalInScripts === that.allowsEvalInScripts)
&& (this.allowsInlineStyles == that.allowsInlineStyles);
},
/**
@ -812,6 +816,9 @@ CSPRep.prototype = {
newRep._allowInlineScripts = this.allowsInlineScripts
&& aCSPRep.allowsInlineScripts;
newRep._allowInlineStyles = this.allowsInlineStyles
&& aCSPRep.allowsInlineStyles;
newRep._innerWindowID = this._innerWindowID ?
this._innerWindowID : aCSPRep._innerWindowID;
@ -872,6 +879,14 @@ CSPRep.prototype = {
return this._allowInlineScripts;
},
/**
* Returns true if inline styles are enabled through the "inline-style"
* keyword.
*/
get allowsInlineStyles () {
return this._allowInlineStyles;
},
/**
* Sends a warning message to the error console and web developer console.
* @param aMsg

View File

@ -40,6 +40,7 @@ function ContentSecurityPolicy() {
// default options "wide open" since this policy will be intersected soon
this._policy._allowInlineScripts = true;
this._policy._allowInlineStyles = true;
this._policy._allowEval = true;
this._request = "";
@ -128,6 +129,10 @@ ContentSecurityPolicy.prototype = {
return this._policy.toString();
},
get allowsInlineStyle() {
return this._reportOnlyMode || this._policy.allowsInlineStyles;
},
get allowsInlineScript() {
return this._reportOnlyMode || this._policy.allowsInlineScripts;
},
@ -155,6 +160,12 @@ ContentSecurityPolicy.prototype = {
// is enabled, resulting in a call to this function. Therefore we need to
// check that the policy was in fact violated before logging any violations
switch (aViolationType) {
case Ci.nsIContentSecurityPolicy.VIOLATION_TYPE_INLINE_STYLE:
if (!this._policy.allowsInlineStyles)
this._asyncReportViolation('self',null,'inline style base restriction',
'violated base restriction: Inline Stylesheets will not apply',
aSourceFile, aScriptSample, aLineNum);
break;
case Ci.nsIContentSecurityPolicy.VIOLATION_TYPE_INLINE_SCRIPT:
if (!this._policy.allowsInlineScripts)
this._asyncReportViolation('self',null,'inline script base restriction',

View File

@ -26,6 +26,7 @@
#include "nsXPCOMCIDInternal.h"
#include "nsUnicharInputStream.h"
#include "nsContentUtils.h"
#include "nsStyleUtil.h"
using namespace mozilla;
using namespace mozilla::dom;
@ -378,6 +379,11 @@ nsStyleLinkElement::DoUpdateStyleSheet(nsIDocument *aOldDocument,
nsAutoString text;
nsContentUtils::GetNodeTextContent(thisContent, false, text);
if (!nsStyleUtil::CSPAllowsInlineStyle(thisContent->NodePrincipal(),
doc->GetDocumentURI(),
mLineNumber, text, &rv))
return rv;
// Parse the style sheet.
rv = doc->CSSLoader()->
LoadInlineStyle(thisContent, text, mLineNumber, title, media,

View File

@ -20,6 +20,7 @@
#include "nsIDOMMutationEvent.h"
#include "nsXULElement.h"
#include "nsContentUtils.h"
#include "nsStyleUtil.h"
namespace css = mozilla::css;
using namespace mozilla::dom;
@ -235,6 +236,11 @@ nsStyledElementNotElementCSSInlineStyle::ParseStyleAttribute(const nsAString& aV
{
nsIDocument* doc = OwnerDoc();
if (!nsStyleUtil::CSPAllowsInlineStyle(NodePrincipal(),
doc->GetDocumentURI(), 0, aValue,
nullptr))
return;
if (aForceInDataDoc ||
!doc->IsLoadedAsData() ||
doc->IsStaticDocument()) {

View File

@ -362,6 +362,7 @@ MOCHITEST_FILES_B = \
test_viewport_scroll.html \
test_CSP.html \
file_CSP.sjs \
file_CSP.css \
file_CSP_main.html \
file_CSP_main.html^headers^ \
file_CSP_main_spec_compliant.html \
@ -391,6 +392,13 @@ MOCHITEST_FILES_B = \
file_CSP_evalscript_main_spec_compliant.html^headers^ \
file_CSP_evalscript_main_spec_compliant_allowed.html \
file_CSP_evalscript_main_spec_compliant_allowed.html^headers^ \
test_CSP_inlinestyle.html \
file_CSP_inlinestyle_main.html \
file_CSP_inlinestyle_main.html^headers^ \
file_CSP_inlinestyle_main_spec_compliant.html \
file_CSP_inlinestyle_main_spec_compliant.html^headers^ \
file_CSP_inlinestyle_main_spec_compliant_allowed.html \
file_CSP_inlinestyle_main_spec_compliant_allowed.html^headers^ \
file_csp_bug768029.html \
file_csp_bug768029.sjs \
file_csp_bug773891.html \

View File

@ -0,0 +1,20 @@
/*
* Moved this CSS from an inline stylesheet to an external file when we added
* inline-style blocking in bug 763879.
* This test may hang if the load for this .css file is blocked due to a
* malfunction of CSP, but should pass if the style_good test passes.
*/
/* CSS font embedding tests */
@font-face {
font-family: "arbitrary_good";
src: url('file_CSP.sjs?testid=font_good&type=application/octet-stream');
}
@font-face {
font-family: "arbitrary_bad";
src: url('http://example.org/tests/content/base/test/file_CSP.sjs?testid=font_bad&type=application/octet-stream');
}
.div_arbitrary_good { font-family: "arbitrary_good"; }
.div_arbitrary_bad { font-family: "arbitrary_bad"; }

View File

@ -21,7 +21,6 @@ function handleRequest(request, response)
}
if ("content" in query) {
response.setHeader("Content-Type", "text/html", false);
response.write(unescape(query['content']));
}
}

View File

@ -1,3 +1,8 @@
<!--
-- The original CSP implementation predates the CSP 1.0 spec and didn't
-- block inline styles, so when the prefixed X-Content-Security-Policy header is used,
-- as it is for this file, inline styles should be allowed.
-->
<html>
<head>
<title>CSP inline script tests</title>

View File

@ -0,0 +1,55 @@
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
<html>
<head>
<title>CSP inline script tests</title>
<!-- content= "div#linkstylediv { color: #0f0; }" -->
<link rel="stylesheet" type="text/css"
href='file_CSP.sjs?type=text/css&content=div%23linkstylediv%20%7B%20color%3A%20%230f0%3B%20%7D' />
</head>
<body>
<style type="text/css">
div#inlinestylediv {
color: #00ff00;
}
</style>
<div id='linkstylediv'>Link tag (external) stylesheet test (should be green)</div>
<div id='attrstylediv' style="color: #00ff00;">Attribute stylesheet test (should be green)</div>
<div id='inlinestylediv'>Inline stylesheet test (should be green)</div>
<!-- tests for SMIL stuff - animations -->
<svg xmlns="http://www.w3.org/2000/svg"
xmlns:xlink="http://www.w3.org/1999/xlink"
width="100%"
height="100px">
<!-- Animates XML attribute, which is mapped into style. -->
<text id="xmlTest" x="0" y="15">
This should be green since the animation should be allowed by CSP.
<animate attributeName="fill" attributeType="XML"
values="lime;green;lime" dur="2s"
repeatCount="indefinite" />
</text>
<!-- Animates override value for CSS property. -->
<text id="cssOverrideTest" x="0" y="35">
This should be green since the animation should be allowed by CSP.
<animate attributeName="fill" attributeType="CSS"
values="lime;green;lime" dur="2s"
repeatCount="indefinite" />
</text>
<!-- Animates override value for CSS property targeted via ID. -->
<text id="cssOverrideTestById" x="0" y="55">
This should be green since the animation should be allowed by CSP.
</text>
<animate xlink:href="#cssOverrideTestById"
attributeName="fill"
values="lime;green;lime"
dur="2s" repeatCount="indefinite" />
</svg>
</body>
</html>

View File

@ -0,0 +1,2 @@
X-Content-Security-Policy: default-src 'self'
Cache-Control: no-cache

View File

@ -0,0 +1,92 @@
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
<html>
<head>
<title>CSP inline script tests</title>
<!-- content= "div#linkstylediv { color: #0f0; }" -->
<link rel="stylesheet" type="text/css"
href='file_CSP.sjs?type=text/css&content=div%23linkstylediv%20%7B%20color%3A%20%230f0%3B%20%7D' />
<!-- content= "div#removepropertydiv { color: #0f0; }" -->
<link rel="stylesheet" type="text/css"
href='file_CSP.sjs?type=text/css&content=div%23removepropertydiv%20%7B%20color%3A%20%230f0%3B%20%7D' />
<!-- content= "div#modifydiv { color: #0f0; }" -->
<link rel="stylesheet" type="text/css"
href='file_CSP.sjs?type=text/css&content=div%23modifydiv%20%7B%20color%3A%20%230f0%3B%20%7D' />
<!-- content= "div#modifycsstextdiv { color: #0f0; }" -->
<link rel="stylesheet" type="text/css"
href='file_CSP.sjs?type=text/css&content=div%23modifycsstextdiv%20%7B%20color%3A%20%230f0%3B%20%7D' />
<script>
function cssTest() {
var elem = document.getElementById('csstextstylediv');
elem.style.cssText = "color: #FF0000;";
elem = document.getElementById('setpropertydiv');
elem.style.setProperty("color", "#FF0000");
var styleObj = document.styleSheets[1].cssRules[0].style;
styleObj.removeProperty("color");
elem = document.getElementById('removepropertydiv');
getComputedStyle(elem, null).color;
elem = document.getElementById('colorattrdiv');
elem.style.color = "#00FF00";
var styleObj = document.styleSheets[2].cssRules[0].style;
styleObj.setProperty("color", "#FF0000");
document.styleSheets[3].cssRules[0].style.cssText = "color: #FF0000;";
}
</script>
</head>
<body onload='cssTest()'>
<style type="text/css">
div#inlinestylediv {
color: #FF0000;
}
</style>
<div id='linkstylediv'>Link tag (external) stylesheet test (should be green)</div>
<div id='inlinestylediv'>Inline stylesheet test (should be black)</div>
<div id='attrstylediv' style="color: #FF0000;">Attribute stylesheet test (should be black)</div>
<div id='csstextstylediv'>cssText test (should be black)</div>
<div id='setpropertydiv'> setProperty test (should be black) </div>
<div id='removepropertydiv'> removeProperty test (should be green) </div>
<div id='colorattrdiv'> color attribute (should be black) </div>
<div id='modifydiv'> modify rule from style sheet (should be green) </div>
<div id='modifycsstextdiv'> modify rule from style sheet via cssText(should be green) </div>
<!-- tests for SMIL stuff - animations -->
<svg xmlns="http://www.w3.org/2000/svg"
xmlns:xlink="http://www.w3.org/1999/xlink"
width="100%"
height="100px">
<!-- Animates XML attribute, which is mapped into style. -->
<text id="xmlTest" x="0" y="15">
This shouldn't be red since the animation should be blocked by CSP.
<animate attributeName="fill" attributeType="XML"
values="red;orange;red" dur="2s"
repeatCount="indefinite" />
</text>
<!-- Animates override value for CSS property. -->
<text id="cssOverrideTest" x="0" y="35">
This shouldn't be red since the animation should be blocked by CSP.
<animate attributeName="fill" attributeType="CSS"
values="red;orange;red" dur="2s"
repeatCount="indefinite" />
</text>
<!-- Animates override value for CSS property targeted via ID. -->
<text id="cssOverrideTestById" x="0" y="55">
This shouldn't be red since the animation should be blocked by CSP.
</text>
<animate xlink:href="#cssOverrideTestById"
attributeName="fill"
values="red;orange;red"
dur="2s" repeatCount="indefinite" />
</svg>
</body>
</html>

View File

@ -0,0 +1,2 @@
Content-Security-Policy: default-src 'self' ; script-src 'self' 'unsafe-inline'
Cache-Control: no-cache

View File

@ -0,0 +1,104 @@
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
<html>
<head>
<title>CSP inline script tests</title>
<!-- content= "div#linkstylediv { color: #0f0; }" -->
<link rel="stylesheet" type="text/css"
href='file_CSP.sjs?type=text/css&content=div%23linkstylediv%20%7B%20color%3A%20%230f0%3B%20%7D' />
<!-- content= "div#removepropertydiv { color: #0f0; }" -->
<link rel="stylesheet" type="text/css"
href='file_CSP.sjs?type=text/css&content=div%23removepropertydiv%20%7B%20color%3A%20%230f0%3B%20%7D' />
<!-- content= "div#modifydiv { color: #f00; }" -->
<link rel="stylesheet" type="text/css"
href='file_CSP.sjs?type=text/css&content=div%23modifydiv%20%7B%20color%3A%20%23f00%3B%20%7D' />
<!-- content= "div#modifycsstextdiv { color: #f00; }" -->
<link rel="stylesheet" type="text/css"
href='file_CSP.sjs?type=text/css&content=div%23modifycsstextdiv%20%7B%20color%3A%20%23f00%3B%20%7D' />
<script>
function cssTest() {
// CSSStyleDeclaration.cssText
var elem = document.getElementById('csstextstylediv');
elem.style.cssText = "color: #00FF00;";
// If I call getComputedStyle as below, this test passes as the parent page
// correctly detects that the text is colored green - if I remove this, getComputedStyle
// thinks the text is black when called by the parent page.
getComputedStyle(elem, null).color;
elem = document.getElementById('setpropertydiv');
elem.style.setProperty("color", "#00FF00");
getComputedStyle(elem, null).color;
var styleObj = document.styleSheets[1].cssRules[0].style;
styleObj.removeProperty("color");
elem = document.getElementById('removepropertydiv');
getComputedStyle(elem, null).color;
elem = document.getElementById('colorattrdiv');
elem.style.color = "#00FF00";
getComputedStyle(elem, null).color;
var styleObj = document.styleSheets[2].cssRules[0].style;
styleObj.setProperty("color", "#00FF00");
elem = document.getElementById('modifydiv');
getComputedStyle(elem, null).color;
document.styleSheets[3].cssRules[0].style.cssText = "color: #00FF00;";
elem = document.getElementById('modifycsstextdiv');
getComputedStyle(elem, null).color;
}
</script>
</head>
<body onload='cssTest()'>
<style type="text/css">
div#inlinestylediv {
color: #00FF00;
}
</style>
<div id='linkstylediv'>Link tag (external) stylesheet test (should be green)</div>
<div id='inlinestylediv'>Inline stylesheet test (should be green)</div>
<div id='attrstylediv' style="color: #00FF00;">Attribute stylesheet test (should be green)</div>
<div id='csstextstylediv'>style.cssText test (should be green)</div>
<div id='setpropertydiv'> setProperty test (should be green) </div>
<div id='removepropertydiv'> removeProperty test (should be black) </div>
<div id='colorattrdiv'> color attribute (should be green) </div>
<div id='modifydiv'> modify rule from style sheet (should be green) </div>
<div id='modifycsstextdiv'> modify rule from style sheet via cssText(should be green) </div>
<!-- tests for SMIL stuff - animations -->
<svg xmlns="http://www.w3.org/2000/svg"
xmlns:xlink="http://www.w3.org/1999/xlink"
width="100%"
height="100px">
<!-- Animates XML attribute, which is mapped into style. -->
<text id="xmlTest" x="0" y="15">
This should be green since the animation should be allowed by CSP.
<animate attributeName="fill" attributeType="XML"
values="lime;green;lime" dur="2s"
repeatCount="indefinite" />
</text>
<!-- Animates override value for CSS property. -->
<text id="cssOverrideTest" x="0" y="35">
This should be green since the animation should be allowed by CSP.
<animate attributeName="fill" attributeType="CSS"
values="lime;green;lime" dur="2s"
repeatCount="indefinite" />
</text>
<!-- Animates override value for CSS property targeted via ID. -->
<text id="cssOverrideTestById" x="0" y="55">
This should be green since the animation should be allowed by CSP.
</text>
<animate xlink:href="#cssOverrideTestById"
attributeName="fill"
values="lime;green;lime"
dur="2s" repeatCount="indefinite" />
</svg>
</body>
</html>

View File

@ -0,0 +1,2 @@
Content-Security-Policy: default-src 'self' ; script-src 'self' 'unsafe-inline' ; style-src 'self' 'unsafe-inline'
Cache-Control: no-cache

View File

@ -5,21 +5,11 @@
<link rel='stylesheet' type='text/css'
href='file_CSP.sjs?testid=style_good&type=text/css' />
<!-- Used to embed inline styles here for testing fonts, but can't do that -->
<!-- due to bug 763879 (block inline styles). Moved these to an external, CSS -->
<!-- file (file_CSP.css). -->
<link rel='stylesheet' type='text/css' href='file_CSP.css' />
<style>
/* CSS font embedding tests */
@font-face {
font-family: "arbitrary_good";
src: url('file_CSP.sjs?testid=font_good&type=application/octet-stream');
}
@font-face {
font-family: "arbitrary_bad";
src: url('http://example.org/tests/content/base/test/file_CSP.sjs?testid=font_bad&type=application/octet-stream');
}
.div_arbitrary_good { font-family: "arbitrary_good"; }
.div_arbitrary_bad { font-family: "arbitrary_bad"; }
</style>
</head>
<body>
<!-- these should be stopped by CSP. :) -->

View File

@ -0,0 +1,155 @@
<!DOCTYPE HTML>
<html>
<head>
<title>Test for Content Security Policy inline stylesheets stuff</title>
<script type="text/javascript" src="/tests/SimpleTest/SimpleTest.js"></script>
<link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css" />
</head>
<body>
<p id="display"></p>
<div id="content" style="display: none">
</div>
<iframe style="width:100%;height:300px;" id='cspframe'></iframe>
<iframe style="width:100%;height:300px;" id='cspframe2'></iframe>
<iframe style="width:100%;height:300px;" id='cspframe3'></iframe>
<script class="testbody" type="text/javascript">
var path = "/tests/content/base/test/";
//////////////////////////////////////////////////////////////////////
// set up and go
SimpleTest.waitForExplicitFinish();
var done = 0;
// Our original CSP implementation does not block inline styles.
function checkStyles(evt) {
var cspframe = document.getElementById('cspframe');
var color;
// black means the style wasn't applied. green colors are used for styles
//expected to be applied. A color is red if a style is erroneously applied
color = window.getComputedStyle(cspframe.contentDocument.getElementById('linkstylediv'),null)['color'];
ok('rgb(0, 255, 0)' === color, 'External Stylesheet (original CSP implementation) (' + color + ')');
color = window.getComputedStyle(cspframe.contentDocument.getElementById('inlinestylediv'),null)['color'];
ok('rgb(0, 255, 0)' === color, 'Inline Style TAG (original CSP implementation) (' + color + ')');
color = window.getComputedStyle(cspframe.contentDocument.getElementById('attrstylediv'),null)['color'];
ok('rgb(0, 255, 0)' === color, 'Style Attribute (original CSP implementation) (' + color + ')');
// SMIL tests
color = window.getComputedStyle(cspframe.contentDocument.getElementById('xmlTest',null))['fill'];
ok('rgb(0, 255, 0)' === color, 'XML Attribute styling (SMIL) (' + color + ')');
color = window.getComputedStyle(cspframe.contentDocument.getElementById('cssOverrideTest',null))['fill'];
ok('rgb(0, 255, 0)' === color, 'CSS Override styling (SMIL) (' + color + ')');
color = window.getComputedStyle(cspframe.contentDocument.getElementById('cssOverrideTestById',null))['fill'];
ok('rgb(0, 255, 0)' === color, 'CSS Override styling via ID lookup (SMIL) (' + color + ')');
checkIfDone();
}
// When a CSP 1.0 compliant policy is specified we should block inline
// styles when it's not explicitly allowed.
function checkStylesSpecCompliant(evt) {
var cspframe = document.getElementById('cspframe2');
var color;
// black means the style wasn't applied. green colors are used for styles
//expected to be applied. A color is red if a style is erroneously applied
color = window.getComputedStyle(cspframe2.contentDocument.getElementById('linkstylediv'),null)['color'];
ok('rgb(0, 255, 0)' === color, 'External Stylesheet (CSP 1.0 spec compliant) (' + color + ')');
color = window.getComputedStyle(cspframe2.contentDocument.getElementById('inlinestylediv'),null)['color'];
ok('rgb(0, 0, 0)' === color, 'Inline Style TAG (CSP 1.0 spec compliant) (' + color + ')');
color = window.getComputedStyle(cspframe2.contentDocument.getElementById('attrstylediv'),null)['color'];
ok('rgb(0, 0, 0)' === color, 'Style Attribute (CSP 1.0 spec compliant) (' + color + ')');
color = window.getComputedStyle(cspframe2.contentDocument.getElementById('csstextstylediv'),null)['color'];
ok('rgb(0, 0, 0)' === color, 'cssText (CSP 1.0 spec compliant) (' + color + ')');
// make sure that not only was the style not applied, but that cssText wasn't set
ok(cspframe2.contentDocument.getElementById('csstextstylediv').style.cssText !== "color: rgb(0, 255, 0);", "block the set of style.cssText");
// SMIL tests
color = window.getComputedStyle(cspframe2.contentDocument.getElementById('xmlTest',null))['fill'];
ok('rgb(0, 0, 0)' === color, 'XML Attribute styling (SMIL) (' + color + ')');
color = window.getComputedStyle(cspframe2.contentDocument.getElementById('cssOverrideTest',null))['fill'];
ok('rgb(0, 0, 0)' === color, 'CSS Override styling (SMIL) (' + color + ')');
color = window.getComputedStyle(cspframe2.contentDocument.getElementById('cssOverrideTestById',null))['fill'];
ok('rgb(0, 0, 0)' === color, 'CSS Override styling via ID lookup (SMIL) (' + color + ')');
color = window.getComputedStyle(cspframe2.contentDocument.getElementById('setpropertydiv'),null)['color'];
ok('rgb(0, 0, 0)' === color, 'SetProperty(' + color + ')');
color = window.getComputedStyle(cspframe2.contentDocument.getElementById('removepropertydiv'),null)['color'];
ok('rgb(0, 255, 0)' === color, 'RemoveProperty(' + color + ')');
color = window.getComputedStyle(cspframe2.contentDocument.getElementById('modifydiv'),null)['color'];
ok('rgb(0, 255, 0)' === color, 'Modify loaded style sheet via setProperty (' + color + ')');
color = window.getComputedStyle(cspframe2.contentDocument.getElementById('modifycsstextdiv'),null)['color'];
ok('rgb(0, 255, 0)' === color, 'Modify loaded style sheet via cssText (' + color + ')');
color = window.getComputedStyle(cspframe2.contentDocument.getElementById('colorattrdiv'),null)['color'];
ok('rgb(0, 0, 0)' === color, 'Color Attribute (' + color + ')');
checkIfDone();
}
// When a CSP 1.0 compliant policy is specified we should allow inline
// styles when it is explicitly allowed.
function checkStylesSpecCompliantAllowed(evt) {
var cspframe = document.getElementById('cspframe3');
var color;
// black means the style wasn't applied. green colors are used for styles
// expected to be applied. A color is red if a style is erroneously applied
color = window.getComputedStyle(cspframe3.contentDocument.getElementById('linkstylediv'),null)['color'];
ok('rgb(0, 255, 0)' === color, 'External Stylesheet (CSP 1.0 spec compliant, allowed) (' + color + ')');
color = window.getComputedStyle(cspframe3.contentDocument.getElementById('inlinestylediv'),null)['color'];
ok('rgb(0, 255, 0)' === color, 'Inline Style TAG (CSP 1.0 spec compliant, allowed) (' + color + ')');
color = window.getComputedStyle(cspframe3.contentDocument.getElementById('attrstylediv'),null)['color'];
ok('rgb(0, 255, 0)' === color, 'Style Attribute (CSP 1.0 spec compliant, allowed) (' + color + ')');
// Note that the below test will fail if "script-src: 'unsafe-inline'" breaks,
// since it relies on executing script to set .cssText
color = window.getComputedStyle(cspframe3.contentDocument.getElementById('csstextstylediv'),null)['color'];
ok('rgb(0, 255, 0)' === color, 'style.cssText (CSP 1.0 spec compliant, allowed) (' + color + ')');
// SMIL tests
color = window.getComputedStyle(cspframe3.contentDocument.getElementById('xmlTest',null))['fill'];
ok('rgb(0, 255, 0)' === color, 'XML Attribute styling (SMIL) (' + color + ')');
color = window.getComputedStyle(cspframe3.contentDocument.getElementById('cssOverrideTest',null))['fill'];
ok('rgb(0, 255, 0)' === color, 'CSS Override styling (SMIL) (' + color + ')');
color = window.getComputedStyle(cspframe3.contentDocument.getElementById('cssOverrideTestById',null))['fill'];
ok('rgb(0, 255, 0)' === color, 'CSS Override styling via ID lookup (SMIL) (' + color + ')');
color = window.getComputedStyle(cspframe3.contentDocument.getElementById('setpropertydiv'),null)['color'];
ok('rgb(0, 255, 0)' === color, 'SetProperty (' + color + ')');
color = window.getComputedStyle(cspframe3.contentDocument.getElementById('removepropertydiv'),null)['color'];
ok('rgb(0, 0, 0)' === color, 'RemoveProperty(' + color + ')');
color = window.getComputedStyle(cspframe3.contentDocument.getElementById('modifydiv'),null)['color'];
ok('rgb(0, 255, 0)' === color, 'Modify loaded style sheet via setProperty (' + color + ')');
color = window.getComputedStyle(cspframe3.contentDocument.getElementById('modifycsstextdiv'),null)['color'];
ok('rgb(0, 255, 0)' === color, 'Modify loaded style sheet via cssText (' + color + ')');
color = window.getComputedStyle(cspframe3.contentDocument.getElementById('colorattrdiv'),null)['color'];
ok('rgb(0, 255, 0)' === color, 'Color Attribute (' + color + ')');
checkIfDone();
}
function checkIfDone() {
done++;
if (done == 3)
SimpleTest.finish();
}
SpecialPowers.pushPrefEnv(
{'set':[["security.csp.speccompliant", true]]},
function() {
// save this for last so that our listeners are registered.
// ... this loads the testbed of good and bad requests.
document.getElementById('cspframe').src = 'file_CSP_inlinestyle_main.html';
document.getElementById('cspframe').addEventListener('load', checkStyles, false);
document.getElementById('cspframe2').src = 'file_CSP_inlinestyle_main_spec_compliant.html';
document.getElementById('cspframe2').addEventListener('load', checkStylesSpecCompliant, false);
document.getElementById('cspframe3').src = 'file_CSP_inlinestyle_main_spec_compliant_allowed.html';
document.getElementById('cspframe3').addEventListener('load', checkStylesSpecCompliantAllowed, false);
}
);
</script>
</pre>
</body>
</html>

View File

@ -2314,6 +2314,9 @@ nsXULPrototypeElement::SetAttrAt(uint32_t aPos, const nsAString& aValue,
nsCSSParser parser;
// XXX Get correct Base URI (need GetBaseURI on *prototype* element)
// TODO: If we implement Content Security Policy for chrome documents
// as has been discussed, the CSP should be checked here to see if
// inline styles are allowed to be applied.
parser.ParseStyleAttribute(aValue, aDocumentURI, aDocumentURI,
// This is basically duplicating what
// nsINode::NodePrincipal() does

View File

@ -154,11 +154,11 @@ nsDOMCSSAttributeDeclaration::GetCSSParsingEnvironment(CSSParsingEnvironment& aC
{
NS_ASSERTION(mElement, "Something is severely broken -- there should be an Element here!");
nsIDocument* doc = mElement->OwnerDoc();
aCSSParseEnv.mSheetURI = doc->GetDocumentURI();
aCSSParseEnv.mDocument = mElement->OwnerDoc();
aCSSParseEnv.mSheetURI = aCSSParseEnv.mDocument->GetDocumentURI();
aCSSParseEnv.mBaseURI = mElement->GetBaseURI();
aCSSParseEnv.mPrincipal = mElement->NodePrincipal();
aCSSParseEnv.mCSSLoader = doc->CSSLoader();
aCSSParseEnv.mCSSLoader = aCSSParseEnv.mDocument->CSSLoader();
}
NS_IMETHODIMP

View File

@ -19,6 +19,7 @@
#include "nsReadableUtils.h"
#include "nsIPrincipal.h"
#include "mozAutoDocUpdate.h"
#include "nsStyleUtil.h"
using namespace mozilla;
@ -92,6 +93,13 @@ nsDOMCSSDeclaration::SetCssText(const nsAString& aCssText)
return NS_ERROR_NOT_AVAILABLE;
}
nsresult result;
if (env.mDocument && !nsStyleUtil::CSPAllowsInlineStyle(
env.mDocument->NodePrincipal(),
env.mDocument->GetDocumentURI(),
0, aCssText, &result))
return result;
// For nsDOMCSSAttributeDeclaration, SetCSSDeclaration will lead to
// Attribute setting code, which leads in turn to BeginUpdate. We
// need to start the update now so that the old rule doesn't get used
@ -103,7 +111,7 @@ nsDOMCSSDeclaration::SetCssText(const nsAString& aCssText)
decl->InitializeEmpty();
nsCSSParser cssParser(env.mCSSLoader);
bool changed;
nsresult result = cssParser.ParseDeclarations(aCssText, env.mSheetURI,
result = cssParser.ParseDeclarations(aCssText, env.mSheetURI,
env.mBaseURI,
env.mPrincipal, decl, &changed);
if (NS_FAILED(result) || !changed) {
@ -229,11 +237,12 @@ nsDOMCSSDeclaration::GetCSSParsingEnvironmentForRule(css::Rule* aRule,
return;
}
nsIDocument* document = sheet->GetOwningDocument();
aCSSParseEnv.mDocument = sheet->GetOwningDocument();
aCSSParseEnv.mSheetURI = sheet->GetSheetURI();
aCSSParseEnv.mBaseURI = sheet->GetBaseURI();
aCSSParseEnv.mPrincipal = cssSheet->Principal();
aCSSParseEnv.mCSSLoader = document ? document->CSSLoader() : nullptr;
aCSSParseEnv.mCSSLoader = aCSSParseEnv.mDocument ?
aCSSParseEnv.mDocument->CSSLoader() : nullptr;
}
nsresult
@ -252,6 +261,13 @@ nsDOMCSSDeclaration::ParsePropertyValue(const nsCSSProperty aPropID,
return NS_ERROR_NOT_AVAILABLE;
}
nsresult result;
if (env.mDocument && !nsStyleUtil::CSPAllowsInlineStyle(
env.mDocument->NodePrincipal(),
env.mDocument->GetDocumentURI(),
0, aPropValue, &result))
return result;
// For nsDOMCSSAttributeDeclaration, SetCSSDeclaration will lead to
// Attribute setting code, which leads in turn to BeginUpdate. We
// need to start the update now so that the old rule doesn't get used
@ -262,7 +278,7 @@ nsDOMCSSDeclaration::ParsePropertyValue(const nsCSSProperty aPropID,
nsCSSParser cssParser(env.mCSSLoader);
bool changed;
nsresult result = cssParser.ParseProperty(aPropID, aPropValue, env.mSheetURI,
result = cssParser.ParseProperty(aPropID, aPropValue, env.mSheetURI,
env.mBaseURI, env.mPrincipal, decl,
&changed, aIsImportant);
if (NS_FAILED(result) || !changed) {
@ -283,6 +299,17 @@ nsDOMCSSDeclaration::RemoveProperty(const nsCSSProperty aPropID)
return NS_OK; // no decl, so nothing to remove
}
CSSParsingEnvironment env;
GetCSSParsingEnvironment(env);
nsresult result;
if (env.mDocument && !nsStyleUtil::CSPAllowsInlineStyle(
env.mDocument->NodePrincipal(),
env.mDocument->GetDocumentURI(),
0, NS_ConvertUTF8toUTF16(nsCSSProps::GetStringValue(aPropID)),
&result))
return result;
// For nsDOMCSSAttributeDeclaration, SetCSSDeclaration will lead to
// Attribute setting code, which leads in turn to BeginUpdate. We
// need to start the update now so that the old rule doesn't get used

View File

@ -114,9 +114,10 @@ protected:
// Information neded to parse a declaration. We need the mSheetURI
// for error reporting, mBaseURI to resolve relative URIs,
// mPrincipal for subresource loads, and mCSSLoader for determining
// whether we're in quirks mode. mBaseURI needs to be a strong
// pointer because of xml:base possibly creating base URIs on the
// mPrincipal for subresource loads, mDocument for determining
// if there's a CSP that should block inline style, and mCSSLoader
// for determining whether we're in quirks mode. mBaseURI needs to be a
// strong pointer because of xml:base possibly creating base URIs on the
// fly. This is why we don't use CSSParsingEnvironment as a return
// value, to avoid multiple-refcounting of mBaseURI.
struct CSSParsingEnvironment {
@ -124,6 +125,7 @@ protected:
nsCOMPtr<nsIURI> mBaseURI;
nsIPrincipal* mPrincipal;
mozilla::css::Loader* mCSSLoader;
nsIDocument* mDocument;
};
// On failure, mPrincipal should be set to null in aCSSParseEnv.

View File

@ -27,6 +27,7 @@
#include "nsPrintfCString.h"
#include <cstdlib> // for std::abs(int/long)
#include <cmath> // for std::abs(float/double)
#include "nsStyleUtil.h"
using namespace mozilla;
@ -2234,6 +2235,11 @@ BuildStyleRule(nsCSSProperty aProperty,
nsCOMPtr<nsIURI> baseURI = aTargetElement->GetBaseURI();
nsCSSParser parser(doc->CSSLoader());
if (!nsStyleUtil::CSPAllowsInlineStyle(doc->NodePrincipal(),
doc->GetDocumentURI(),
0, aSpecifiedValue, nullptr))
return nullptr;
nsCSSProperty propertyToCheck = nsCSSProps::IsShorthand(aProperty) ?
nsCSSProps::SubpropertyEntryFor(aProperty)[0] : aProperty;

View File

@ -20,6 +20,7 @@
#include "nsTextFormatter.h"
#include "nsCSSProps.h"
#include "nsRuleNode.h"
#include "nsIContentSecurityPolicy.h"
using namespace mozilla;
@ -313,3 +314,56 @@ nsStyleUtil::IsSignificantChild(nsIContent* aChild, bool aTextIsSignificant,
!aChild->TextIsOnlyWhitespace());
}
/* static */ bool
nsStyleUtil::CSPAllowsInlineStyle(nsIPrincipal* aPrincipal,
nsIURI* aSourceURI,
uint32_t aLineNumber,
const nsSubstring& aStyleText,
nsresult* aRv)
{
nsresult rv;
if (aRv) {
*aRv = NS_OK;
}
nsCOMPtr<nsIContentSecurityPolicy> csp;
rv = aPrincipal->GetCsp(getter_AddRefs(csp));
if (NS_FAILED(rv) && aRv) {
*aRv = rv;
return false;
}
if (csp) {
bool inlineOK;
rv = csp->GetAllowsInlineStyle(&inlineOK);
if (NS_FAILED(rv) && aRv) {
*aRv = rv;
return false;
}
if (!inlineOK) {
// Inline styles are not allowed by CSP, so report the violation and
// punt the style load.
nsAutoCString asciiSpec;
aSourceURI->GetAsciiSpec(asciiSpec);
nsAutoString styleText(aStyleText);
// cap the length of the style sample at 40 chars.
if (styleText.Length() > 40) {
styleText.Truncate(40);
styleText.Append(NS_LITERAL_STRING("..."));
}
csp->LogViolationDetails(nsIContentSecurityPolicy::VIOLATION_TYPE_INLINE_STYLE,
NS_ConvertUTF8toUTF16(asciiSpec),
aStyleText,
aLineNumber);
// The inline style should be blocked.
return false;
}
}
// No CSP or a CSP that allows inline styles.
return true;
}

View File

@ -78,6 +78,22 @@ public:
static bool IsSignificantChild(nsIContent* aChild,
bool aTextIsSignificant,
bool aWhitespaceIsSignificant);
/*
* Does this principal have a CSP that blocks the application of
* inline styles ? Returns false if application of the style should
* be blocked.
*
* Note that the principal passed in here needs to be the principal
* of the document, not of the style sheet. The document's principal
* is where any Content Security Policy that should be used to
* block or allow inline styles will be located.
*/
static bool CSPAllowsInlineStyle(nsIPrincipal* aPrincipal,
nsIURI* aSourceURI,
uint32_t aLineNumber,
const nsSubstring& aStyleText,
nsresult* aRv);
};