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

This commit is contained in:
Ian Melven 2012-08-30 10:58:24 -07:00
parent cfd62fd2ec
commit 6937a45eae
22 changed files with 560 additions and 17 deletions

View File

@ -13,7 +13,7 @@ interface nsIDocShell;
* Describes an XPCOM component used to model an enforce CSPs. * Describes an XPCOM component used to model an enforce CSPs.
*/ */
[scriptable, uuid(91E1F257-914C-4D4F-902C-F67F772839AB)] [scriptable, uuid(230b126d-afc3-4588-9794-3e135594d626)]
interface nsIContentSecurityPolicy : nsISupports interface nsIContentSecurityPolicy : nsISupports
{ {
@ -62,6 +62,21 @@ interface nsIContentSecurityPolicy : nsISupports
*/ */
boolean getAllowsEval(out boolean shouldReportViolation); boolean getAllowsEval(out boolean shouldReportViolation);
/**
* Whether this policy allows in-page styles.
* This includes <style> tags with text content and style="" attributes in
* HTML elements.
* @param shouldReportViolation
* Whether or not the use of eval should be reported.
* This function always returns "true" for report-only policies, but when
* the report-only policy is violated, shouldReportViolation is true as
* well.
* @return
* Whether or not the effects of the eval call should be allowed
* (block the call if false).
*/
boolean getAllowsInlineStyle(out boolean shouldReportViolation);
/** /**
* Log policy violation on the Error Console and send a report if a report-uri * Log policy violation on the Error Console and send a report if a report-uri
* is present in the policy * is present in the policy
@ -82,6 +97,7 @@ interface nsIContentSecurityPolicy : nsISupports
const unsigned short VIOLATION_TYPE_INLINE_SCRIPT = 1; const unsigned short VIOLATION_TYPE_INLINE_SCRIPT = 1;
const unsigned short VIOLATION_TYPE_EVAL = 2; 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. * 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 ? // Is this a 1.0 spec compliant CSPRep ?
// Default to false if not specified. // Default to false if not specified.
this._specCompliant = (aSpecCompliant !== undefined) ? aSpecCompliant : false; this._specCompliant = (aSpecCompliant !== undefined) ? aSpecCompliant : false;
// Only CSP 1.0 spec compliant policies block inline styles by default.
this._allowInlineStyles = !aSpecCompliant;
} }
// Source directives for our original CSP implementation. // Source directives for our original CSP implementation.
@ -717,7 +720,8 @@ CSPRep.prototype = {
} }
} }
return (this.allowsInlineScripts === that.allowsInlineScripts) 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 newRep._allowInlineScripts = this.allowsInlineScripts
&& aCSPRep.allowsInlineScripts; && aCSPRep.allowsInlineScripts;
newRep._allowInlineStyles = this.allowsInlineStyles
&& aCSPRep.allowsInlineStyles;
newRep._innerWindowID = this._innerWindowID ? newRep._innerWindowID = this._innerWindowID ?
this._innerWindowID : aCSPRep._innerWindowID; this._innerWindowID : aCSPRep._innerWindowID;
@ -872,6 +879,14 @@ CSPRep.prototype = {
return this._allowInlineScripts; 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. * Sends a warning message to the error console and web developer console.
* @param aMsg * @param aMsg

View File

@ -40,6 +40,7 @@ function ContentSecurityPolicy() {
// default options "wide open" since this policy will be intersected soon // default options "wide open" since this policy will be intersected soon
this._policy._allowInlineScripts = true; this._policy._allowInlineScripts = true;
this._policy._allowInlineStyles = true;
this._policy._allowEval = true; this._policy._allowEval = true;
this._request = ""; this._request = "";
@ -144,6 +145,14 @@ ContentSecurityPolicy.prototype = {
return this._reportOnlyMode || this._policy.allowsEvalInScripts; return this._reportOnlyMode || this._policy.allowsEvalInScripts;
}, },
getAllowsInlineStyle: function(shouldReportViolation) {
// report it?
shouldReportViolation.value = !this._policy.allowsInlineStyles;
// allow it to execute?
return this._reportOnlyMode || this._policy.allowsInlineStyles;
},
/** /**
* Log policy violation on the Error Console and send a report if a report-uri * Log policy violation on the Error Console and send a report if a report-uri
* is present in the policy * is present in the policy
@ -163,6 +172,12 @@ ContentSecurityPolicy.prototype = {
// is enabled, resulting in a call to this function. Therefore we need to // 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 // check that the policy was in fact violated before logging any violations
switch (aViolationType) { 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: case Ci.nsIContentSecurityPolicy.VIOLATION_TYPE_INLINE_SCRIPT:
if (!this._policy.allowsInlineScripts) if (!this._policy.allowsInlineScripts)
this._asyncReportViolation('self',null,'inline script base restriction', this._asyncReportViolation('self',null,'inline script base restriction',

View File

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

View File

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

View File

@ -339,6 +339,7 @@ MOCHITEST_FILES_B = \
test_viewport_scroll.html \ test_viewport_scroll.html \
test_CSP.html \ test_CSP.html \
file_CSP.sjs \ file_CSP.sjs \
file_CSP.css \
file_CSP_main.html \ file_CSP_main.html \
file_CSP_main.html^headers^ \ file_CSP_main.html^headers^ \
file_CSP_main_spec_compliant.html \ file_CSP_main_spec_compliant.html \
@ -368,6 +369,13 @@ MOCHITEST_FILES_B = \
file_CSP_evalscript_main_spec_compliant.html^headers^ \ file_CSP_evalscript_main_spec_compliant.html^headers^ \
file_CSP_evalscript_main_spec_compliant_allowed.html \ file_CSP_evalscript_main_spec_compliant_allowed.html \
file_CSP_evalscript_main_spec_compliant_allowed.html^headers^ \ 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.html \
file_csp_bug768029.sjs \ file_csp_bug768029.sjs \
file_csp_bug773891.html \ 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) { if ("content" in query) {
response.setHeader("Content-Type", "text/html", false);
response.write(unescape(query['content'])); 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> <html>
<head> <head>
<title>CSP inline script tests</title> <title>CSP inline script tests</title>

View File

@ -0,0 +1,63 @@
<?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" />
<!-- Sets value for CSS property targeted via ID. -->
<text id="cssSetTestById" x="0" y="75">
This should be green since the &lt;set&gt; should be allowed by CSP.
</text>
<set xlink:href="#cssSetTestById"
attributeName="fill"
to="lime" />
</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,79 @@
<?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#modifycsstextdiv { color: #0f0; }" -->
<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() {
var elem = document.getElementById('csstextstylediv');
elem.style.cssText = "color: #00FF00;";
getComputedStyle(elem, null).color;
document.styleSheets[1].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: #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='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" />
<!-- Sets value for CSS property targeted via ID. -->
<text id="cssSetTestById" x="0" y="75">
This shouldn't be red since the &lt;set&gt; should be blocked by CSP.
</text>
<set xlink:href="#cssSetTestById"
attributeName="fill"
to="red" />
</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,84 @@
<?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#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;
document.styleSheets[1].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='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" />
<!-- Sets value for CSS property targeted via ID. -->
<text id="cssSetTestById" x="0" y="75">
This should be green since the &lt;set&gt; should be allowed by CSP.
</text>
<set xlink:href="#cssSetTestById"
attributeName="fill"
to="lime" />
</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' <link rel='stylesheet' type='text/css'
href='file_CSP.sjs?testid=style_good&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> </head>
<body> <body>
<!-- these should be stopped by CSP. :) --> <!-- these should be stopped by CSP. :) -->

View File

@ -0,0 +1,142 @@
<!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 + ')');
color = window.getComputedStyle(cspframe.contentDocument.getElementById('cssSetTestById',null))['fill'];
ok('rgb(0, 255, 0)' === color, 'CSS Set Element styling via ID lookup (SMIL) (' + color + ')');
checkIfDone();
}
// When a CSP 1.0 compliant policy is specified we should block inline
// styles applied by <style> element, style attribute, and SMIL <animate> and <set> tags
// (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, 255, 0)' === color, 'cssText (CSP 1.0 spec compliant) (' + color + ')');
// 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('cssSetTestById',null))['fill'];
ok('rgb(0, 0, 0)' === color, 'CSS Set Element styling via ID lookup (SMIL) (' + color + ')');
color = window.getComputedStyle(cspframe2.contentDocument.getElementById('modifycsstextdiv'),null)['color'];
ok('rgb(0, 255, 0)' === color, 'Modify loaded style sheet via cssText (' + 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('cssSetTestById',null))['fill'];
ok('rgb(0, 255, 0)' === color, 'CSS Set Element styling via ID lookup (SMIL) (' + color + ')');
color = window.getComputedStyle(cspframe3.contentDocument.getElementById('modifycsstextdiv'),null)['color'];
ok('rgb(0, 255, 0)' === color, 'Modify loaded style sheet via cssText (' + 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

@ -15,6 +15,7 @@
#include "nsPresContext.h" #include "nsPresContext.h"
#include "mozilla/dom/Element.h" #include "mozilla/dom/Element.h"
#include "nsDebug.h" #include "nsDebug.h"
#include "nsStyleUtil.h"
using namespace mozilla::dom; using namespace mozilla::dom;
@ -391,6 +392,13 @@ nsSMILCSSValueType::ValueFromString(nsCSSProperty aPropID,
return; return;
} }
nsIDocument* doc = aTargetElement->GetCurrentDoc();
if (doc && !nsStyleUtil::CSPAllowsInlineStyle(doc->NodePrincipal(),
doc->GetDocumentURI(),
0, aString, nullptr)) {
return;
}
nsStyleAnimation::Value parsedValue; nsStyleAnimation::Value parsedValue;
if (ValueFromStringHelper(aPropID, aTargetElement, presContext, if (ValueFromStringHelper(aPropID, aTargetElement, presContext,
aString, parsedValue, aIsContextSensitive)) { aString, parsedValue, aIsContextSensitive)) {

View File

@ -2305,6 +2305,9 @@ nsXULPrototypeElement::SetAttrAt(uint32_t aPos, const nsAString& aValue,
nsCSSParser parser; nsCSSParser parser;
// XXX Get correct Base URI (need GetBaseURI on *prototype* element) // 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, parser.ParseStyleAttribute(aValue, aDocumentURI, aDocumentURI,
// This is basically duplicating what // This is basically duplicating what
// nsINode::NodePrincipal() does // nsINode::NodePrincipal() does

View File

@ -10,6 +10,7 @@
#include "nsReadableUtils.h" #include "nsReadableUtils.h"
#include "nsCSSProps.h" #include "nsCSSProps.h"
#include "nsRuleNode.h" #include "nsRuleNode.h"
#include "nsIContentSecurityPolicy.h"
using namespace mozilla; using namespace mozilla;
@ -415,3 +416,61 @@ nsStyleUtil::IsSignificantChild(nsIContent* aChild, bool aTextIsSignificant,
!aChild->TextIsOnlyWhitespace()); !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)) {
if (aRv)
*aRv = rv;
return false;
}
if (csp) {
bool inlineOK = true;
bool reportViolation = false;
rv = csp->GetAllowsInlineStyle(&reportViolation, &inlineOK);
if (NS_FAILED(rv)) {
if (aRv)
*aRv = rv;
return false;
}
if (reportViolation) {
// Inline styles are not allowed by CSP, so report the violation
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);
}
if (!inlineOK) {
// The inline style should be blocked.
return false;
}
}
// No CSP or a CSP that allows inline styles.
return true;
}

View File

@ -7,6 +7,8 @@
#include "nsCoord.h" #include "nsCoord.h"
#include "nsCSSProperty.h" #include "nsCSSProperty.h"
#include "nsIPrincipal.h"
#include "nsSubstring.h"
#include "gfxFontFeatures.h" #include "gfxFontFeatures.h"
class nsCSSValue; class nsCSSValue;
@ -90,6 +92,22 @@ public:
static bool IsSignificantChild(nsIContent* aChild, static bool IsSignificantChild(nsIContent* aChild,
bool aTextIsSignificant, bool aTextIsSignificant,
bool aWhitespaceIsSignificant); 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);
}; };

View File

@ -27,6 +27,7 @@
"dom/tests/mochitest/ajax/jquery/test_jQuery.html":"", "dom/tests/mochitest/ajax/jquery/test_jQuery.html":"",
"content/base/test/test_CSP_inlinescript.html":"", "content/base/test/test_CSP_inlinescript.html":"",
"content/base/test/test_CSP_inlinestyle.html":"",
"content/base/test/test_XHRSendData.html":"", "content/base/test/test_XHRSendData.html":"",
"content/base/test/test_XHR_parameters.html":"", "content/base/test/test_XHR_parameters.html":"",
"content/base/test/test_XHR_system.html":"", "content/base/test/test_XHR_system.html":"",