--- a/content/base/public/nsIContentSecurityPolicy.idl
+++ b/content/base/public/nsIContentSecurityPolicy.idl
@@ -8,17 +8,17 @@ interface nsIURI;
interface nsIHttpChannel;
interface nsIDocShell;
/**
* nsIContentSecurityPolicy
* 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
{
/**
* Set to true when the CSP has been read in and parsed and is ready to
* enforce. This is a barrier for the nsDocument so it doesn't load any
* sub-content until either it knows that a CSP is ready or will not be used.
*/
@@ -58,16 +58,31 @@ interface nsIContentSecurityPolicy : nsI
* well.
* @return
* Whether or not the effects of the eval call should be allowed
* (block the call if false).
*/
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
* is present in the policy
*
* @param violationType
* one of the VIOLATION_TYPE_* constants, e.g. inline-script or eval
* @param sourceFile
* name of the source file containing the violation (if available)
* @param contentSample
@@ -77,16 +92,17 @@ interface nsIContentSecurityPolicy : nsI
*/
void logViolationDetails(in unsigned short violationType,
in AString sourceFile,
in AString scriptSample,
in int32_t lineNum);
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.
* The URI may be null, in which case "self" is sent.
* @param blockedURI
* the URI that violated the policy
* @param violatedDirective
* the directive that was violated.
--- a/content/base/src/CSPUtils.jsm
+++ b/content/base/src/CSPUtils.jsm
@@ -176,16 +176,19 @@ this.CSPRep = function CSPRep(aSpecCompl
this._allowInlineScripts = false;
// don't auto-populate _directives, so it is easier to find bugs
this._directives = {};
// 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 by default.
+ this._allowInlineStyles = !aSpecCompliant;
}
// Source directives for our original CSP implementation.
// These can be removed when the original implementation is deprecated.
CSPRep.SRC_DIRECTIVES_OLD = {
DEFAULT_SRC: "default-src",
SCRIPT_SRC: "script-src",
STYLE_SRC: "style-src",
@@ -712,17 +715,18 @@ CSPRep.prototype = {
return false;
}
for (var i in this._directives) {
if (!that._directives[i] || !this._directives[i].equals(that._directives[i])) {
return false;
}
}
return (this.allowsInlineScripts === that.allowsInlineScripts)
- && (this.allowsEvalInScripts === that.allowsEvalInScripts);
+ && (this.allowsEvalInScripts === that.allowsEvalInScripts)
+ && (this.allowsInlineStyles === that.allowsInlineStyles);
},
/**
* Generates canonical string representation of the policy.
*/
toString:
function csp_toString() {
var dirs = [];
@@ -807,16 +811,19 @@ CSPRep.prototype = {
}
newRep._allowEval = this.allowsEvalInScripts
&& aCSPRep.allowsEvalInScripts;
newRep._allowInlineScripts = this.allowsInlineScripts
&& aCSPRep.allowsInlineScripts;
+ newRep._allowInlineStyles = this.allowsInlineStyles
+ && aCSPRep.allowsInlineStyles;
+
newRep._innerWindowID = this._innerWindowID ?
this._innerWindowID : aCSPRep._innerWindowID;
return newRep;
},
/**
* Copies default source list to each unspecified directive.
@@ -868,16 +875,24 @@ CSPRep.prototype = {
* Returns true if inline scripts are enabled through the "inline"
* keyword.
*/
get allowsInlineScripts () {
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
* The message to send
* @param aSource (optional)
* The URL of the file in which the error occurred
* @param aScriptLine (optional)
* The line in the source file which the error occurred
* @param aLineNum (optional)
--- a/content/base/src/contentSecurityPolicy.js
+++ b/content/base/src/contentSecurityPolicy.js
@@ -35,16 +35,17 @@ function ContentSecurityPolicy() {
CSPdebug("CSP CREATED");
this._isInitialized = false;
this._reportOnlyMode = false;
this._policy = CSPRep.fromString("default-src *");
// 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 = "";
this._requestOrigin = "";
this._requestPrincipal = "";
this._referrer = "";
this._docRequest = null;
CSPdebug("CSP POLICY INITED TO 'default-src *'");
@@ -139,16 +140,24 @@ ContentSecurityPolicy.prototype = {
getAllowsEval: function(shouldReportViolation) {
// report it?
shouldReportViolation.value = !this._policy.allowsEvalInScripts;
// allow it to execute?
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
* is present in the policy
*
* @param aViolationType
* one of the VIOLATION_TYPE_* constants, e.g. inline-script or eval
* @param aSourceFile
* name of the source file containing the violation (if available)
@@ -158,16 +167,22 @@ ContentSecurityPolicy.prototype = {
* source line number of the violation (if available)
*/
logViolationDetails:
function(aViolationType, aSourceFile, aScriptSample, aLineNum) {
// allowsInlineScript and allowsEval both return true when report-only mode
// 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',
'violated base restriction: Inline Scripts will not execute',
aSourceFile, aScriptSample, aLineNum);
break;
case Ci.nsIContentSecurityPolicy.VIOLATION_TYPE_EVAL:
if (!this._policy.allowsEvalInScripts)
--- a/content/base/src/nsStyleLinkElement.cpp
+++ b/content/base/src/nsStyleLinkElement.cpp
@@ -21,16 +21,17 @@
#include "nsIDOMNode.h"
#include "nsIDOMStyleSheet.h"
#include "nsNetUtil.h"
#include "nsUnicharUtils.h"
#include "nsCRT.h"
#include "nsXPCOMCIDInternal.h"
#include "nsUnicharInputStream.h"
#include "nsContentUtils.h"
+#include "nsStyleUtil.h"
using namespace mozilla;
using namespace mozilla::dom;
nsStyleLinkElement::nsStyleLinkElement()
: mDontLoadStyle(false)
, mUpdatesEnabled(true)
, mLineNumber(1)
@@ -361,16 +362,21 @@ nsStyleLinkElement::DoUpdateStyleSheet(n
}
bool doneLoading = false;
nsresult rv = NS_OK;
if (isInline) {
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,
scopeElement, aObserver, &doneLoading, &isAlternate);
}
else {
// XXXbz clone the URI here to work around content policies modifying URIs.
nsCOMPtr<nsIURI> clonedURI;
--- a/content/base/src/nsStyledElement.cpp
+++ b/content/base/src/nsStyledElement.cpp
@@ -15,16 +15,17 @@
#include "nsServiceManagerUtils.h"
#include "nsIDocument.h"
#include "mozilla/css/StyleRule.h"
#include "nsCSSParser.h"
#include "mozilla/css/Loader.h"
#include "nsIDOMMutationEvent.h"
#include "nsXULElement.h"
#include "nsContentUtils.h"
+#include "nsStyleUtil.h"
namespace css = mozilla::css;
using namespace mozilla::dom;
//----------------------------------------------------------------------
// nsIContent methods
nsIAtom*
@@ -230,16 +231,21 @@ nsStyledElementNotElementCSSInlineStyle:
void
nsStyledElementNotElementCSSInlineStyle::ParseStyleAttribute(const nsAString& aValue,
nsAttrValue& aResult,
bool aForceInDataDoc)
{
nsIDocument* doc = OwnerDoc();
+ if (!nsStyleUtil::CSPAllowsInlineStyle(NodePrincipal(),
+ doc->GetDocumentURI(), 0, aValue,
+ nullptr))
+ return;
+
if (aForceInDataDoc ||
!doc->IsLoadedAsData() ||
doc->IsStaticDocument()) {
bool isCSS = true; // assume CSS until proven otherwise
if (!IsInNativeAnonymousSubtree()) { // native anonymous content
// always assumes CSS
nsAutoString styleType;
--- a/content/base/test/Makefile.in
+++ b/content/base/test/Makefile.in
@@ -334,16 +334,17 @@ MOCHITEST_FILES_B = \
test_copypaste.html \
test_bug503481.html \
file_bug503481.sjs \
test_bug503481b.html \
file_bug503481b_inner.html \
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 \
file_CSP_main_spec_compliant.html^headers^ \
file_CSP_main.js \
file_CSP_main_spec_compliant.js \
test_CSP_frameancestors.html \
file_CSP_frameancestors.sjs \
@@ -363,16 +364,23 @@ MOCHITEST_FILES_B = \
file_CSP_evalscript_main.html \
file_CSP_evalscript_main.html^headers^ \
file_CSP_evalscript_main.js \
file_CSP_evalscript_main_allowed.js \
file_CSP_evalscript_main_spec_compliant.html \
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 \
file_csp_bug773891.sjs \
test_bug540854.html \
bug540854.sjs \
test_bug548463.html \
test_bug545644.html \
new file mode 100644
--- /dev/null
+++ b/content/base/test/file_CSP.css
@@ -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"; }
+
--- a/content/base/test/file_CSP.sjs
+++ b/content/base/test/file_CSP.sjs
@@ -16,12 +16,11 @@ function handleRequest(request, response
if ("type" in query) {
response.setHeader("Content-Type", unescape(query['type']), false);
} else {
response.setHeader("Content-Type", "text/html", false);
}
if ("content" in query) {
- response.setHeader("Content-Type", "text/html", false);
response.write(unescape(query['content']));
}
}
--- a/content/base/test/file_CSP_inlinescript_main.html
+++ b/content/base/test/file_CSP_inlinescript_main.html
@@ -1,8 +1,13 @@
+<!--
+-- 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>
</head>
<body onload="window.parent.scriptRan(false, 'eventattr', 'event attribute in body tag fired')">
<script type="text/javascript">
window.parent.scriptRan(false, "textnode", "text node in a script tag executed.");
new file mode 100644
--- /dev/null
+++ b/content/base/test/file_CSP_inlinestyle_main.html
@@ -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 <set> should be allowed by CSP.
+ </text>
+ <set xlink:href="#cssSetTestById"
+ attributeName="fill"
+ to="lime" />
+ </svg>
+ </body>
+</html>
new file mode 100644
--- /dev/null
+++ b/content/base/test/file_CSP_inlinestyle_main.html^headers^
@@ -0,0 +1,2 @@
+X-Content-Security-Policy: default-src 'self'
+Cache-Control: no-cache
new file mode 100644
--- /dev/null
+++ b/content/base/test/file_CSP_inlinestyle_main_spec_compliant.html
@@ -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 <set> should be blocked by CSP.
+ </text>
+ <set xlink:href="#cssSetTestById"
+ attributeName="fill"
+ to="red" />
+ </svg>
+ </body>
+</html>
new file mode 100644
--- /dev/null
+++ b/content/base/test/file_CSP_inlinestyle_main_spec_compliant.html^headers^
@@ -0,0 +1,2 @@
+Content-Security-Policy: default-src 'self' ; script-src 'self' 'unsafe-inline'
+Cache-Control: no-cache
new file mode 100644
--- /dev/null
+++ b/content/base/test/file_CSP_inlinestyle_main_spec_compliant_allowed.html
@@ -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 <set> should be allowed by CSP.
+ </text>
+ <set xlink:href="#cssSetTestById"
+ attributeName="fill"
+ to="lime" />
+ </svg>
+ </body>
+</html>
new file mode 100644
--- /dev/null
+++ b/content/base/test/file_CSP_inlinestyle_main_spec_compliant_allowed.html^headers^
@@ -0,0 +1,2 @@
+Content-Security-Policy: default-src 'self' ; script-src 'self' 'unsafe-inline' ; style-src 'self' 'unsafe-inline'
+Cache-Control: no-cache
--- a/content/base/test/file_CSP_main.html
+++ b/content/base/test/file_CSP_main.html
@@ -1,30 +1,20 @@
<html>
<head>
<link rel='stylesheet' type='text/css'
href='http://example.org/tests/content/base/test/file_CSP.sjs?testid=style_bad&type=text/css' />
<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. :) -->
<img src="http://example.org/tests/content/base/test/file_CSP.sjs?testid=img_bad&type=img/png"> </img>
<audio src="http://example.org/tests/content/base/test/file_CSP.sjs?testid=media_bad&type=audio/vorbis"></audio>
<script src='http://example.org/tests/content/base/test/file_CSP.sjs?testid=script_bad&type=text/javascript'></script>
<iframe src='http://example.org/tests/content/base/test/file_CSP.sjs?testid=frame_bad&content=FAIL'></iframe>
<object width="10" height="10">
new file mode 100644
--- /dev/null
+++ b/content/base/test/test_CSP_inlinestyle.html
@@ -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>
--- a/content/smil/nsSMILCSSValueType.cpp
+++ b/content/smil/nsSMILCSSValueType.cpp
@@ -10,16 +10,17 @@
#include "nsStyleAnimation.h"
#include "nsSMILParserUtils.h"
#include "nsSMILValue.h"
#include "nsCSSValue.h"
#include "nsColor.h"
#include "nsPresContext.h"
#include "mozilla/dom/Element.h"
#include "nsDebug.h"
+#include "nsStyleUtil.h"
using namespace mozilla::dom;
/*static*/ nsSMILCSSValueType nsSMILCSSValueType::sSingleton;
struct ValueWrapper {
ValueWrapper(nsCSSProperty aPropID, const nsStyleAnimation::Value& aValue) :
mPropID(aPropID), mCSSValue(aValue) {}
@@ -386,16 +387,23 @@ nsSMILCSSValueType::ValueFromString(nsCS
{
NS_ABORT_IF_FALSE(aValue.IsNull(), "Outparam should be null-typed");
nsPresContext* presContext = GetPresContextForElement(aTargetElement);
if (!presContext) {
NS_WARNING("Not parsing animation value; unable to get PresContext");
return;
}
+ nsIDocument* doc = aTargetElement->GetCurrentDoc();
+ if (doc && !nsStyleUtil::CSPAllowsInlineStyle(doc->NodePrincipal(),
+ doc->GetDocumentURI(),
+ 0, aString, nullptr)) {
+ return;
+ }
+
nsStyleAnimation::Value parsedValue;
if (ValueFromStringHelper(aPropID, aTargetElement, presContext,
aString, parsedValue, aIsContextSensitive)) {
sSingleton.Init(aValue);
aValue.mU.mPtr = new ValueWrapper(aPropID, parsedValue);
}
}
--- a/content/xul/content/src/nsXULElement.cpp
+++ b/content/xul/content/src/nsXULElement.cpp
@@ -2300,16 +2300,19 @@ nsXULPrototypeElement::SetAttrAt(uint32_
else if (mAttributes[aPos].mName.Equals(nsGkAtoms::style)) {
mHasStyleAttribute = true;
// Parse the element's 'style' attribute
nsRefPtr<css::StyleRule> rule;
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
mNodeInfo->NodeInfoManager()->
DocumentPrincipal(),
getter_AddRefs(rule));
if (rule) {
mAttributes[aPos].mValue.SetTo(rule, &aValue);
--- a/layout/style/nsStyleUtil.cpp
+++ b/layout/style/nsStyleUtil.cpp
@@ -5,16 +5,17 @@
#include "nsStyleUtil.h"
#include "nsStyleConsts.h"
#include "nsIContent.h"
#include "nsReadableUtils.h"
#include "nsCSSProps.h"
#include "nsRuleNode.h"
+#include "nsIContentSecurityPolicy.h"
using namespace mozilla;
//------------------------------------------------------------------------------
// Font Algorithm Code
//------------------------------------------------------------------------------
// Compare two language strings
@@ -410,8 +411,66 @@ nsStyleUtil::IsSignificantChild(nsIConte
return true;
}
return aTextIsSignificant && isText && aChild->TextLength() != 0 &&
(aWhitespaceIsSignificant ||
!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;
+}
--- a/layout/style/nsStyleUtil.h
+++ b/layout/style/nsStyleUtil.h
@@ -2,16 +2,18 @@
/* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
#ifndef nsStyleUtil_h___
#define nsStyleUtil_h___
#include "nsCoord.h"
#include "nsCSSProperty.h"
+#include "nsIPrincipal.h"
+#include "nsSubstring.h"
#include "gfxFontFeatures.h"
class nsCSSValue;
class nsStringComparator;
class nsIContent;
struct gfxFontFeature;
class nsCSSValueList;
template <class E> class nsTArray;
@@ -85,12 +87,28 @@ public:
static float ColorComponentToFloat(uint8_t aAlpha);
/*
* Does this child count as significant for selector matching?
*/
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);
+
};
#endif /* nsStyleUtil_h___ */
--- a/testing/mochitest/b2g.json
+++ b/testing/mochitest/b2g.json
@@ -22,16 +22,17 @@
"dom/imptests/webapps/DOMCore/tests/approved/test_Range-mutations.html":"Test timed out.",
"dom/encoding/test/test_stringencoding.html":"Test timed out on b2g board",
"content/events/test/test_bug615597.html":"",
"content/html/content/test/forms/test_input_sanitization.html":"",
"dom/tests/mochitest/ajax/jquery/test_jQuery.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_XHR_parameters.html":"",
"content/base/test/test_XHR_system.html":"",
"content/base/test/test_base.xhtml":"",
"content/base/test/test_bug338583.html":"",
"content/base/test/test_bug372086.html":"",
"content/base/test/test_bug466080.html":"",
"content/base/test/test_bug475156.html":"",