mirror of
https://gitlab.winehq.org/wine/wine-gecko.git
synced 2024-09-13 09:24:08 -07:00
Bug 886164 - Enforce CSP in sandboxed iframe. r=grobinson
This commit is contained in:
parent
b1e3c18e72
commit
b8838cbd40
@ -16,6 +16,7 @@
|
||||
#include "nsJSPrincipals.h"
|
||||
#include "nsCOMPtr.h"
|
||||
#include "nsPrincipal.h"
|
||||
#include "nsIContentSecurityPolicy.h"
|
||||
|
||||
class nsIURI;
|
||||
|
||||
@ -53,6 +54,7 @@ public:
|
||||
virtual ~nsNullPrincipal();
|
||||
|
||||
nsCOMPtr<nsIURI> mURI;
|
||||
nsCOMPtr<nsIContentSecurityPolicy> mCSP;
|
||||
};
|
||||
|
||||
#endif // nsNullPrincipal_h__
|
||||
|
@ -149,8 +149,7 @@ nsNullPrincipal::GetHashValue(uint32_t *aResult)
|
||||
NS_IMETHODIMP
|
||||
nsNullPrincipal::GetSecurityPolicy(void** aSecurityPolicy)
|
||||
{
|
||||
// We don't actually do security policy caching. And it's not like anyone
|
||||
// can set a security policy for us anyway.
|
||||
// We don't actually do security policy caching.
|
||||
*aSecurityPolicy = nullptr;
|
||||
return NS_OK;
|
||||
}
|
||||
@ -158,8 +157,7 @@ nsNullPrincipal::GetSecurityPolicy(void** aSecurityPolicy)
|
||||
NS_IMETHODIMP
|
||||
nsNullPrincipal::SetSecurityPolicy(void* aSecurityPolicy)
|
||||
{
|
||||
// We don't actually do security policy caching. And it's not like anyone
|
||||
// can set a security policy for us anyway.
|
||||
// We don't actually do security policy caching.
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
@ -172,16 +170,20 @@ nsNullPrincipal::GetURI(nsIURI** aURI)
|
||||
NS_IMETHODIMP
|
||||
nsNullPrincipal::GetCsp(nsIContentSecurityPolicy** aCsp)
|
||||
{
|
||||
// CSP on a null principal makes no sense
|
||||
*aCsp = nullptr;
|
||||
NS_IF_ADDREF(*aCsp = mCSP);
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
NS_IMETHODIMP
|
||||
nsNullPrincipal::SetCsp(nsIContentSecurityPolicy* aCsp)
|
||||
{
|
||||
// CSP on a null principal makes no sense
|
||||
return NS_ERROR_NOT_AVAILABLE;
|
||||
// If CSP was already set, it should not be destroyed! Instead, it should
|
||||
// get set anew when a new principal is created.
|
||||
if (mCSP)
|
||||
return NS_ERROR_ALREADY_INITIALIZED;
|
||||
|
||||
mCSP = aCsp;
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
NS_IMETHODIMP
|
||||
|
@ -49,9 +49,9 @@ function ContentSecurityPolicy() {
|
||||
|
||||
this._request = "";
|
||||
this._requestOrigin = "";
|
||||
this._requestPrincipal = "";
|
||||
this._weakRequestPrincipal = null;
|
||||
this._referrer = "";
|
||||
this._docRequest = null;
|
||||
this._weakDocRequest = { get : function() { return null; } };
|
||||
CSPdebug("CSP object initialized, no policies to enforce yet");
|
||||
|
||||
this._cache = { };
|
||||
@ -249,7 +249,7 @@ ContentSecurityPolicy.prototype = {
|
||||
return;
|
||||
|
||||
// Save the docRequest for fetching a policy-uri
|
||||
this._docRequest = aChannel;
|
||||
this._weakDocRequest = Cu.getWeakReference(aChannel);
|
||||
|
||||
// save the document URI (minus <fragment>) and referrer for reporting
|
||||
let uri = aChannel.URI.cloneIgnoringRef();
|
||||
@ -260,8 +260,9 @@ ContentSecurityPolicy.prototype = {
|
||||
this._requestOrigin = uri;
|
||||
|
||||
//store a reference to the principal, that can later be used in shouldLoad
|
||||
this._requestPrincipal = Components.classes["@mozilla.org/scriptsecuritymanager;1"].
|
||||
getService(Components.interfaces.nsIScriptSecurityManager).getChannelPrincipal(aChannel);
|
||||
this._weakRequestPrincipal = Cu.getWeakReference(Cc["@mozilla.org/scriptsecuritymanager;1"]
|
||||
.getService(Ci.nsIScriptSecurityManager)
|
||||
.getChannelPrincipal(aChannel));
|
||||
|
||||
if (aChannel.referrer) {
|
||||
let referrer = aChannel.referrer.cloneIgnoringRef();
|
||||
@ -310,13 +311,13 @@ ContentSecurityPolicy.prototype = {
|
||||
if (aSpecCompliant) {
|
||||
newpolicy = CSPRep.fromStringSpecCompliant(aPolicy,
|
||||
selfURI,
|
||||
this._docRequest,
|
||||
this._weakDocRequest.get(),
|
||||
this,
|
||||
aReportOnly);
|
||||
} else {
|
||||
newpolicy = CSPRep.fromString(aPolicy,
|
||||
selfURI,
|
||||
this._docRequest,
|
||||
this._weakDocRequest.get(),
|
||||
this,
|
||||
aReportOnly);
|
||||
}
|
||||
@ -434,8 +435,8 @@ ContentSecurityPolicy.prototype = {
|
||||
// we need to set an nsIChannelEventSink on the channel object
|
||||
// so we can tell it to not follow redirects when posting the reports
|
||||
chan.notificationCallbacks = new CSPReportRedirectSink(policy);
|
||||
if (this._docRequest) {
|
||||
chan.loadGroup = this._docRequest.loadGroup;
|
||||
if (this._weakDocRequest.get()) {
|
||||
chan.loadGroup = this._weakDocRequest.get().loadGroup;
|
||||
}
|
||||
|
||||
chan.QueryInterface(Ci.nsIUploadChannel)
|
||||
@ -454,7 +455,7 @@ ContentSecurityPolicy.prototype = {
|
||||
.getService(Ci.nsIContentPolicy);
|
||||
if (contentPolicy.shouldLoad(Ci.nsIContentPolicy.TYPE_CSP_REPORT,
|
||||
chan.URI, this._requestOrigin,
|
||||
null, null, null, this._requestPrincipal)
|
||||
null, null, null, this._weakRequestPrincipal.get())
|
||||
!= Ci.nsIContentPolicy.ACCEPT) {
|
||||
continue; // skip unauthorized URIs
|
||||
}
|
||||
|
@ -2681,7 +2681,8 @@ nsDocument::InitCSP(nsIChannel* aChannel)
|
||||
if (csp) {
|
||||
// Copy into principal
|
||||
nsIPrincipal* principal = GetPrincipal();
|
||||
principal->SetCsp(csp);
|
||||
rv = principal->SetCsp(csp);
|
||||
NS_ENSURE_SUCCESS(rv, rv);
|
||||
#ifdef PR_LOGGING
|
||||
PR_LOG(gCspPRLog, PR_LOG_DEBUG,
|
||||
("Inserted CSP into principal %p", principal));
|
||||
|
@ -97,6 +97,19 @@ MOCHITEST_FILES := \
|
||||
file_bug836922_npolicies.html^headers^ \
|
||||
file_bug836922_npolicies_violation.sjs \
|
||||
file_bug836922_npolicies_ro_violation.sjs \
|
||||
test_bug886164.html \
|
||||
file_bug886164.html \
|
||||
file_bug886164.html^headers^ \
|
||||
file_bug886164_2.html \
|
||||
file_bug886164_2.html^headers^ \
|
||||
file_bug886164_3.html \
|
||||
file_bug886164_3.html^headers^ \
|
||||
file_bug886164_4.html \
|
||||
file_bug886164_4.html^headers^ \
|
||||
file_bug886164_5.html \
|
||||
file_bug886164_5.html^headers^ \
|
||||
file_bug886164_6.html \
|
||||
file_bug886164_6.html^headers^ \
|
||||
$(NULL)
|
||||
|
||||
MOCHITEST_CHROME_FILES := \
|
||||
|
15
content/base/test/csp/file_bug886164.html
Normal file
15
content/base/test/csp/file_bug886164.html
Normal file
@ -0,0 +1,15 @@
|
||||
<html>
|
||||
<head> <meta charset="utf-8"> </head>
|
||||
<body>
|
||||
<!-- sandbox="allow-same-origin" -->
|
||||
<!-- Content-Security-Policy: default-src 'self' -->
|
||||
|
||||
<!-- these should be stopped by CSP -->
|
||||
<img src="http://example.org/tests/content/base/test/csp/file_CSP.sjs?testid=img_bad&type=img/png"> </img>
|
||||
|
||||
<!-- these should load ok -->
|
||||
<img src="/tests/content/base/test/csp/file_CSP.sjs?testid=img_good&type=img/png" />
|
||||
<script src='/tests/content/base/test/csp/file_CSP.sjs?testid=scripta_bad&type=text/javascript'></script>
|
||||
|
||||
</body>
|
||||
</html>
|
1
content/base/test/csp/file_bug886164.html^headers^
Normal file
1
content/base/test/csp/file_bug886164.html^headers^
Normal file
@ -0,0 +1 @@
|
||||
Content-Security-Policy: default-src 'self'
|
14
content/base/test/csp/file_bug886164_2.html
Normal file
14
content/base/test/csp/file_bug886164_2.html
Normal file
@ -0,0 +1,14 @@
|
||||
<html>
|
||||
<head> <meta charset="utf-8"> </head>
|
||||
<body>
|
||||
<!-- sandbox -->
|
||||
<!-- Content-Security-Policy: default-src 'self' -->
|
||||
|
||||
<!-- these should be stopped by CSP -->
|
||||
<img src="http://example.org/tests/content/base/test/csp/file_CSP.sjs?testid=img2_bad&type=img/png"> </img>
|
||||
|
||||
<!-- these should load ok -->
|
||||
<img src="/tests/content/base/test/csp/file_CSP.sjs?testid=img2a_good&type=img/png" />
|
||||
|
||||
</body>
|
||||
</html>
|
1
content/base/test/csp/file_bug886164_2.html^headers^
Normal file
1
content/base/test/csp/file_bug886164_2.html^headers^
Normal file
@ -0,0 +1 @@
|
||||
Content-Security-Policy: default-src 'self'
|
12
content/base/test/csp/file_bug886164_3.html
Normal file
12
content/base/test/csp/file_bug886164_3.html
Normal file
@ -0,0 +1,12 @@
|
||||
<html>
|
||||
<head> <meta charset="utf-8"> </head>
|
||||
<body>
|
||||
<!-- sandbox -->
|
||||
<!-- Content-Security-Policy: default-src 'none' -->
|
||||
|
||||
<!-- these should be stopped by CSP -->
|
||||
<img src="http://example.org/tests/content/base/test/csp/file_CSP.sjs?testid=img3_bad&type=img/png"> </img>
|
||||
<img src="/tests/content/base/test/csp/file_CSP.sjs?testid=img3a_bad&type=img/png" />
|
||||
|
||||
</body>
|
||||
</html>
|
1
content/base/test/csp/file_bug886164_3.html^headers^
Normal file
1
content/base/test/csp/file_bug886164_3.html^headers^
Normal file
@ -0,0 +1 @@
|
||||
Content-Security-Policy: default-src 'none'
|
12
content/base/test/csp/file_bug886164_4.html
Normal file
12
content/base/test/csp/file_bug886164_4.html
Normal file
@ -0,0 +1,12 @@
|
||||
<html>
|
||||
<head> <meta charset="utf-8"> </head>
|
||||
<body>
|
||||
<!-- sandbox -->
|
||||
<!-- Content-Security-Policy: default-src 'none' -->
|
||||
|
||||
<!-- these should be stopped by CSP -->
|
||||
<img src="http://example.org/tests/content/base/test/csp/file_CSP.sjs?testid=img4_bad&type=img/png"> </img>
|
||||
<img src="/tests/content/base/test/csp/file_CSP.sjs?testid=img4a_bad&type=img/png" />
|
||||
|
||||
</body>
|
||||
</html>
|
1
content/base/test/csp/file_bug886164_4.html^headers^
Normal file
1
content/base/test/csp/file_bug886164_4.html^headers^
Normal file
@ -0,0 +1 @@
|
||||
Content-Security-Policy: default-src 'none'
|
26
content/base/test/csp/file_bug886164_5.html
Normal file
26
content/base/test/csp/file_bug886164_5.html
Normal file
@ -0,0 +1,26 @@
|
||||
<!DOCTYPE HTML>
|
||||
<html>
|
||||
<head> <meta charset="utf-8"> </head>
|
||||
<script type="text/javascript">
|
||||
function ok(result, desc) {
|
||||
window.parent.postMessage({ok: result, desc: desc}, "*");
|
||||
}
|
||||
|
||||
function doStuff() {
|
||||
ok(true, "documents sandboxed with allow-scripts should be able to run inline scripts");
|
||||
}
|
||||
</script>
|
||||
<script src='file_iframe_sandbox_pass.js'></script>
|
||||
<body onLoad='ok(true, "documents sandboxed with allow-scripts should be able to run script from event listeners");doStuff();'>
|
||||
I am sandboxed but with only inline "allow-scripts"
|
||||
|
||||
<!-- sandbox="allow-scripts" -->
|
||||
<!-- Content-Security-Policy: default-src 'none' 'unsafe-inline'-->
|
||||
|
||||
<!-- these should be stopped by CSP -->
|
||||
<img src="/tests/content/base/test/csp/file_CSP.sjs?testid=img5_bad&type=img/png" />
|
||||
<img src="http://example.org/tests/content/base/test/csp/file_CSP.sjs?testid=img5a_bad&type=img/png"> </img>
|
||||
<script src='/tests/content/base/test/csp/file_CSP.sjs?testid=script5_bad&type=text/javascript'></script>
|
||||
<script src='http://example.org/tests/content/base/test/csp/file_CSP.sjs?testid=script5a_bad&type=text/javascript'></script>
|
||||
</body>
|
||||
</html>
|
1
content/base/test/csp/file_bug886164_5.html^headers^
Normal file
1
content/base/test/csp/file_bug886164_5.html^headers^
Normal file
@ -0,0 +1 @@
|
||||
Content-Security-Policy: default-src 'none' 'unsafe-inline';
|
35
content/base/test/csp/file_bug886164_6.html
Normal file
35
content/base/test/csp/file_bug886164_6.html
Normal file
@ -0,0 +1,35 @@
|
||||
<!DOCTYPE HTML>
|
||||
<html>
|
||||
<head>
|
||||
<meta charset="utf-8">
|
||||
<script type="text/javascript" src="/tests/SimpleTest/EventUtils.js"></script>
|
||||
</head>
|
||||
<script type="text/javascript">
|
||||
function ok(result, desc) {
|
||||
window.parent.postMessage({ok: result, desc: desc}, "*");
|
||||
}
|
||||
|
||||
function doStuff() {
|
||||
ok(true, "documents sandboxed with allow-scripts should be able to run inline scripts");
|
||||
|
||||
document.getElementById('a_form').submit();
|
||||
|
||||
// trigger the javascript: url test
|
||||
sendMouseEvent({type:'click'}, 'a_link');
|
||||
}
|
||||
</script>
|
||||
<script src='file_iframe_sandbox_pass.js'></script>
|
||||
<body onLoad='ok(true, "documents sandboxed with allow-scripts should be able to run script from event listeners");doStuff();'>
|
||||
I am sandboxed but with "allow-scripts"
|
||||
<img src="http://example.org/tests/content/base/test/csp/file_CSP.sjs?testid=img6_bad&type=img/png"> </img>
|
||||
<script src='http://example.org/tests/content/base/test/csp/file_CSP.sjs?testid=script6_bad&type=text/javascript'></script>
|
||||
|
||||
<form method="get" action="file_iframe_sandbox_form_fail.html" id="a_form">
|
||||
First name: <input type="text" name="firstname">
|
||||
Last name: <input type="text" name="lastname">
|
||||
<input type="submit" onclick="doSubmit()" id="a_button">
|
||||
</form>
|
||||
|
||||
<a href = 'javascript:ok(true, "documents sandboxed with allow-scripts should be able to run script from javascript: URLs");' id='a_link'>click me</a>
|
||||
</body>
|
||||
</html>
|
1
content/base/test/csp/file_bug886164_6.html^headers^
Normal file
1
content/base/test/csp/file_bug886164_6.html^headers^
Normal file
@ -0,0 +1 @@
|
||||
Content-Security-Policy: default-src 'self' 'unsafe-inline';
|
185
content/base/test/csp/test_bug886164.html
Normal file
185
content/base/test/csp/test_bug886164.html
Normal file
@ -0,0 +1,185 @@
|
||||
<!DOCTYPE HTML>
|
||||
<html>
|
||||
<head>
|
||||
<meta charset="utf-8">
|
||||
<title>Bug 886164 - Enforce CSP in sandboxed iframe</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:200px;height:200px;" id='cspframe' sandbox="allow-same-origin"></iframe>
|
||||
<iframe style="width:200px;height:200px;" id='cspframe2' sandbox></iframe>
|
||||
<iframe style="width:200px;height:200px;" id='cspframe3' sandbox="allow-same-origin"></iframe>
|
||||
<iframe style="width:200px;height:200px;" id='cspframe4' sandbox></iframe>
|
||||
<iframe style="width:200px;height:200px;" id='cspframe5' sandbox="allow-scripts"></iframe>
|
||||
<iframe style="width:200px;height:200px;" id='cspframe6' sandbox="allow-same-origin allow-scripts"></iframe>
|
||||
<script class="testbody" type="text/javascript">
|
||||
|
||||
|
||||
var path = "/tests/content/base/test/csp/";
|
||||
|
||||
// These are test results: -1 means it hasn't run,
|
||||
// true/false is the pass/fail result.
|
||||
window.tests = {
|
||||
// sandbox allow-same-origin; 'self'
|
||||
img_good: -1, // same origin
|
||||
img_bad: -1, //example.com
|
||||
|
||||
// sandbox; 'self'
|
||||
img2_bad: -1, //example.com
|
||||
img2a_good: -1, // same origin & is image
|
||||
|
||||
// sandbox allow-same-origin; 'none'
|
||||
img3_bad: -1,
|
||||
img3a_bad: -1,
|
||||
|
||||
// sandbox; 'none'
|
||||
img4_bad: -1,
|
||||
img4a_bad: -1,
|
||||
|
||||
// sandbox allow-scripts; 'none' 'unsafe-inline'
|
||||
img5_bad: -1,
|
||||
img5a_bad: -1,
|
||||
script5_bad: -1,
|
||||
script5a_bad: -1,
|
||||
|
||||
// sandbox allow-same-origin allow-scripts; 'self' 'unsafe-inline'
|
||||
img6_bad: -1,
|
||||
script6_bad: -1,
|
||||
};
|
||||
|
||||
// a postMessage handler that is used by sandboxed iframes without
|
||||
// 'allow-same-origin' to communicate pass/fail back to this main page.
|
||||
// it expects to be called with an object like {ok: true/false, desc:
|
||||
// <description of the test> which it then forwards to ok()
|
||||
window.addEventListener("message", receiveMessage, false);
|
||||
|
||||
function receiveMessage(event)
|
||||
{
|
||||
ok_wrapper(event.data.ok, event.data.desc);
|
||||
}
|
||||
|
||||
var cspTestsDone = false;
|
||||
var iframeSandboxTestsDone = false;
|
||||
|
||||
// iframe related
|
||||
var completedTests = 0;
|
||||
var passedTests = 0;
|
||||
|
||||
function ok_wrapper(result, desc) {
|
||||
ok(result, desc);
|
||||
|
||||
completedTests++;
|
||||
|
||||
if (result) {
|
||||
passedTests++;
|
||||
}
|
||||
|
||||
if (completedTests === 5) {
|
||||
iframeSandboxTestsDone = true;
|
||||
if (cspTestsDone) {
|
||||
SimpleTest.finish();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
//csp related
|
||||
|
||||
// This is used to watch the blocked data bounce off CSP and allowed data
|
||||
// get sent out to the wire.
|
||||
function examiner() {
|
||||
SpecialPowers.addObserver(this, "csp-on-violate-policy", false);
|
||||
SpecialPowers.addObserver(this, "http-on-modify-request", false);
|
||||
}
|
||||
examiner.prototype = {
|
||||
observe: function(subject, topic, data) {
|
||||
// subject should be an nsURI, and should be either allowed or blocked.
|
||||
if (!SpecialPowers.can_QI(subject))
|
||||
return;
|
||||
|
||||
var testpat = new RegExp("testid=([a-z0-9_]+)");
|
||||
|
||||
//_good things better be allowed!
|
||||
//_bad things better be stopped!
|
||||
|
||||
if (topic === "http-on-modify-request") {
|
||||
//these things were allowed by CSP
|
||||
var asciiSpec = SpecialPowers.getPrivilegedProps(SpecialPowers.do_QueryInterface(subject, "nsIHttpChannel"), "URI.asciiSpec");
|
||||
if (!testpat.test(asciiSpec)) return;
|
||||
var testid = testpat.exec(asciiSpec)[1];
|
||||
|
||||
window.testResult(testid,
|
||||
/_good/.test(testid),
|
||||
asciiSpec + " allowed by csp");
|
||||
}
|
||||
|
||||
if(topic === "csp-on-violate-policy") {
|
||||
//these were blocked... record that they were blocked
|
||||
var asciiSpec = SpecialPowers.getPrivilegedProps(SpecialPowers.do_QueryInterface(subject, "nsIURI"), "asciiSpec");
|
||||
if (!testpat.test(asciiSpec)) return;
|
||||
var testid = testpat.exec(asciiSpec)[1];
|
||||
window.testResult(testid,
|
||||
/_bad/.test(testid),
|
||||
asciiSpec + " blocked by \"" + data + "\"");
|
||||
}
|
||||
},
|
||||
|
||||
// must eventually call this to remove the listener,
|
||||
// or mochitests might get borked.
|
||||
remove: function() {
|
||||
SpecialPowers.removeObserver(this, "csp-on-violate-policy");
|
||||
SpecialPowers.removeObserver(this, "http-on-modify-request");
|
||||
}
|
||||
}
|
||||
|
||||
window.examiner = new examiner();
|
||||
|
||||
window.testResult = function(testname, result, msg) {
|
||||
//test already complete.... forget it... remember the first result.
|
||||
if (window.tests[testname] != -1)
|
||||
return;
|
||||
|
||||
window.tests[testname] = result;
|
||||
is(result, true, testname + ' test: ' + msg);
|
||||
|
||||
// if any test is incomplete, keep waiting
|
||||
for (var v in window.tests)
|
||||
if(tests[v] == -1) {
|
||||
console.log(v + " is not complete");
|
||||
return;
|
||||
}
|
||||
|
||||
// ... otherwise, finish
|
||||
window.examiner.remove();
|
||||
cspTestsDone = true;
|
||||
if (iframeSandboxTestsDone) {
|
||||
SimpleTest.finish();
|
||||
}
|
||||
}
|
||||
|
||||
SimpleTest.waitForExplicitFinish();
|
||||
|
||||
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_bug886164.html';
|
||||
document.getElementById('cspframe2').src = 'file_bug886164_2.html';
|
||||
document.getElementById('cspframe3').src = 'file_bug886164_3.html';
|
||||
document.getElementById('cspframe4').src = 'file_bug886164_4.html';
|
||||
document.getElementById('cspframe5').src = 'file_bug886164_5.html';
|
||||
document.getElementById('cspframe6').src = 'file_bug886164_6.html';
|
||||
});
|
||||
|
||||
</script>
|
||||
</pre>
|
||||
</body>
|
||||
</html>
|
Loading…
Reference in New Issue
Block a user