зеркало из https://github.com/mozilla/gecko-dev.git
Bug 1529068 - Implementation of the navigate-to CSP directive as defined in CSP Level 3. r=ckerschb,mccr8
https://www.w3.org/TR/CSP3/#directive-navigate-to Differential Revision: https://phabricator.services.mozilla.com/D37139 --HG-- extra : moz-landing-system : lando
This commit is contained in:
Родитель
a777ff11b6
Коммит
934da4e096
|
@ -4025,7 +4025,8 @@ nsDocShell::DisplayLoadError(nsresult aError, nsIURI* aURI,
|
|||
CopyUTF8toUTF16(host, *formatStrs.AppendElement());
|
||||
error = "netTimeout";
|
||||
} else if (NS_ERROR_CSP_FRAME_ANCESTOR_VIOLATION == aError ||
|
||||
NS_ERROR_CSP_FORM_ACTION_VIOLATION == aError) {
|
||||
NS_ERROR_CSP_FORM_ACTION_VIOLATION == aError ||
|
||||
NS_ERROR_CSP_NAVIGATE_TO_VIOLATION == aError) {
|
||||
// CSP error
|
||||
cssClass.AssignLiteral("neterror");
|
||||
error = "cspBlocked";
|
||||
|
@ -9867,6 +9868,21 @@ static bool HasHttpScheme(nsIURI* aURI) {
|
|||
|
||||
nsCOMPtr<nsIContentSecurityPolicy> csp = aLoadState->Csp();
|
||||
if (csp) {
|
||||
// Check CSP navigate-to
|
||||
bool allowsNavigateTo = false;
|
||||
aRv = csp->GetAllowsNavigateTo(aLoadState->URI(), aLoadInfo,
|
||||
false, /* aWasRedirected */
|
||||
false, /* aEnforceWhitelist */
|
||||
&allowsNavigateTo);
|
||||
if (NS_FAILED(aRv)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
if (!allowsNavigateTo) {
|
||||
aRv = NS_ERROR_CSP_NAVIGATE_TO_VIOLATION;
|
||||
return false;
|
||||
}
|
||||
|
||||
// Navigational requests that are same origin need to be upgraded in case
|
||||
// upgrade-insecure-requests is present.
|
||||
bool upgradeInsecureRequests = false;
|
||||
|
|
|
@ -3004,6 +3004,25 @@ nsresult Document::StartDocumentLoad(const char* aCommand, nsIChannel* aChannel,
|
|||
nsresult rv = InitReferrerInfo(aChannel);
|
||||
NS_ENSURE_SUCCESS(rv, rv);
|
||||
|
||||
// Check CSP navigate-to
|
||||
// We need to enforce the CSP of the document that initiated the load,
|
||||
// which is the CSP to inherit.
|
||||
nsCOMPtr<nsIContentSecurityPolicy> cspToInherit = loadInfo->GetCspToInherit();
|
||||
if (cspToInherit) {
|
||||
bool allowsNavigateTo = false;
|
||||
rv = cspToInherit->GetAllowsNavigateTo(
|
||||
mDocumentURI, loadInfo,
|
||||
!loadInfo->RedirectChain().IsEmpty(), /* aWasRedirected */
|
||||
true, /* aEnforceWhitelist */
|
||||
&allowsNavigateTo);
|
||||
NS_ENSURE_SUCCESS(rv, rv);
|
||||
|
||||
if (!allowsNavigateTo) {
|
||||
aChannel->Cancel(NS_ERROR_CSP_NAVIGATE_TO_VIOLATION);
|
||||
return NS_OK;
|
||||
}
|
||||
}
|
||||
|
||||
rv = InitCSP(aChannel);
|
||||
NS_ENSURE_SUCCESS(rv, rv);
|
||||
|
||||
|
|
|
@ -62,6 +62,7 @@ interface nsIContentSecurityPolicy : nsISerializable
|
|||
const unsigned short BLOCK_ALL_MIXED_CONTENT = 18;
|
||||
const unsigned short SANDBOX_DIRECTIVE = 19;
|
||||
const unsigned short WORKER_SRC_DIRECTIVE = 20;
|
||||
const unsigned short NAVIGATE_TO_DIRECTIVE = 21;
|
||||
|
||||
/**
|
||||
* Accessor method for a read-only string version of the policy at a given
|
||||
|
@ -145,6 +146,24 @@ interface nsIContentSecurityPolicy : nsISerializable
|
|||
in unsigned long aLineNumber,
|
||||
in unsigned long aColumnNumber);
|
||||
|
||||
/*
|
||||
* Whether this policy allows a navigation subject to the navigate-to
|
||||
* policy.
|
||||
* @param aURI The target URI
|
||||
* @param aLoadInfo used to check if the navigation was initiated by a form submission. This
|
||||
* is important since the form-action directive overrides navigate-to in that case.
|
||||
* @param aWasRedirect True if a redirect has happened. Important for path-sensitivity.
|
||||
* @param aEnforceWhitelist True if the whitelist of allowed targets must be enforced. If
|
||||
* this is true, the whitelist must be enforced even if 'unsafe-allow-redirects' is
|
||||
* used. If 'unsafe-allow-redirects' is not used then the whitelist is always enforced
|
||||
* @return
|
||||
* Whether or not the effects of the navigation is allowed
|
||||
*/
|
||||
boolean getAllowsNavigateTo(in nsIURI aURI,
|
||||
in nsILoadInfo aLoadInfo,
|
||||
in boolean aWasRedirected,
|
||||
in boolean aEnforceWhitelist);
|
||||
|
||||
/**
|
||||
* whether this policy allows eval and eval-like functions
|
||||
* such as setTimeout("code string", time).
|
||||
|
|
|
@ -95,6 +95,10 @@ deprecatedReferrerDirective = Referrer Directive ‘%1$S’ has been deprecated.
|
|||
# %1$S is the name of the src that is ignored.
|
||||
# %2$S is the name of the directive that causes the src to be ignored.
|
||||
IgnoringSrcBecauseOfDirective=Ignoring ‘%1$S’ because of ‘%2$S’ directive.
|
||||
# LOCALIZATION NOTE (IgnoringSourceWithinDirective):
|
||||
# %1$S is the ignored src
|
||||
# %2$S is the directive which supports src
|
||||
IgnoringSourceWithinDirective = Ignoring source “%1$S” (Not supported within ‘%2$S’).
|
||||
|
||||
# CSP Errors:
|
||||
# LOCALIZATION NOTE (couldntParseInvalidSource):
|
||||
|
|
|
@ -597,6 +597,91 @@ nsCSPContext::GetAllowsInline(nsContentPolicyType aContentType,
|
|||
return NS_OK;
|
||||
}
|
||||
|
||||
NS_IMETHODIMP
|
||||
nsCSPContext::GetAllowsNavigateTo(nsIURI* aURI, nsILoadInfo* aLoadInfo,
|
||||
bool aWasRedirected, bool aEnforceWhitelist,
|
||||
bool* outAllowsNavigateTo) {
|
||||
/*
|
||||
* The matrix below shows the different values of (aWasRedirect,
|
||||
* aEnforceWhitelist) for the three different checks we do.
|
||||
*
|
||||
* Navigation | Start Loading | Initiate Redirect | Document
|
||||
* | (nsDocShell) | (nsCSPService) |
|
||||
* -----------------------------------------------------------------
|
||||
* A -> B (false,false) - (false,true)
|
||||
* A -> ... -> B (false,false) (true,false) (true,true)
|
||||
*/
|
||||
*outAllowsNavigateTo = false;
|
||||
|
||||
EnsureIPCPoliciesRead();
|
||||
// The 'form-action' directive overrules 'navigate-to' for form submissions.
|
||||
// So in case this is a form submission and the directive 'form-action' is
|
||||
// present then there is nothing for us to do here, see: 6.3.3.1.2
|
||||
// https://www.w3.org/TR/CSP3/#navigate-to-pre-navigate
|
||||
if (aLoadInfo->GetIsFormSubmission()) {
|
||||
for (unsigned long i = 0; i < mPolicies.Length(); i++) {
|
||||
if (mPolicies[i]->hasDirective(
|
||||
nsIContentSecurityPolicy::FORM_ACTION_DIRECTIVE)) {
|
||||
*outAllowsNavigateTo = true;
|
||||
return NS_OK;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
bool atLeastOneBlock = false;
|
||||
for (unsigned long i = 0; i < mPolicies.Length(); i++) {
|
||||
if (!mPolicies[i]->allowsNavigateTo(aURI, aWasRedirected,
|
||||
aEnforceWhitelist)) {
|
||||
if (!mPolicies[i]->getReportOnlyFlag()) {
|
||||
atLeastOneBlock = true;
|
||||
}
|
||||
|
||||
// If the load encountered a server side redirect, the spec suggests to
|
||||
// remove the path component from the URI, see:
|
||||
// https://www.w3.org/TR/CSP3/#source-list-paths-and-redirects
|
||||
nsCOMPtr<nsIURI> blockedURIForReporting = aURI;
|
||||
if (aWasRedirected) {
|
||||
nsAutoCString prePathStr;
|
||||
nsCOMPtr<nsIURI> prePathURI;
|
||||
nsresult rv = aURI->GetPrePath(prePathStr);
|
||||
NS_ENSURE_SUCCESS(rv, rv);
|
||||
rv = NS_NewURI(getter_AddRefs(blockedURIForReporting), prePathStr);
|
||||
NS_ENSURE_SUCCESS(rv, rv);
|
||||
}
|
||||
|
||||
// Lines numbers and source file for the violation report
|
||||
uint32_t lineNumber = 0;
|
||||
uint32_t columnNumber = 0;
|
||||
nsAutoCString spec;
|
||||
JSContext* cx = nsContentUtils::GetCurrentJSContext();
|
||||
if (cx) {
|
||||
nsJSUtils::GetCallingLocation(cx, spec, &lineNumber, &columnNumber);
|
||||
// If GetCallingLocation fails linenumber & columnNumber are set to 0
|
||||
// anyway so we can skip checking if that is the case.
|
||||
}
|
||||
|
||||
// Report the violation
|
||||
nsresult rv = AsyncReportViolation(
|
||||
nullptr, // aTriggeringElement
|
||||
nullptr, // aCSPEventListener
|
||||
blockedURIForReporting, // aBlockedURI
|
||||
nsCSPContext::BlockedContentSource::eSelf, // aBlockedSource
|
||||
nullptr, // aOriginalURI
|
||||
NS_LITERAL_STRING("navigate-to"), // aViolatedDirective
|
||||
i, // aViolatedPolicyIndex
|
||||
EmptyString(), // aObserverSubject
|
||||
NS_ConvertUTF8toUTF16(spec), // aSourceFile
|
||||
EmptyString(), // aScriptSample
|
||||
lineNumber, // aLineNum
|
||||
columnNumber); // aColumnNum
|
||||
NS_ENSURE_SUCCESS(rv, rv);
|
||||
}
|
||||
}
|
||||
|
||||
*outAllowsNavigateTo = !atLeastOneBlock;
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
/**
|
||||
* Reduces some code repetition for the various logging situations in
|
||||
* LogViolationDetails.
|
||||
|
|
|
@ -467,6 +467,22 @@ nsCSPBaseSrc* nsCSPParser::keywordSource() {
|
|||
}
|
||||
return new nsCSPKeywordSrc(CSP_UTF16KeywordToEnum(mCurToken));
|
||||
}
|
||||
|
||||
if (CSP_IsKeyword(mCurToken, CSP_UNSAFE_ALLOW_REDIRECTS)) {
|
||||
if (!CSP_IsDirective(mCurDir[0],
|
||||
nsIContentSecurityPolicy::NAVIGATE_TO_DIRECTIVE)) {
|
||||
// Only allow 'unsafe-allow-redirects' within navigate-to.
|
||||
AutoTArray<nsString, 2> params = {
|
||||
NS_LITERAL_STRING("unsafe-allow-redirects"),
|
||||
NS_LITERAL_STRING("navigate-to")};
|
||||
logWarningErrorToConsole(nsIScriptError::warningFlag,
|
||||
"IgnoringSourceWithinDirective", params);
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
return new nsCSPKeywordSrc(CSP_UTF16KeywordToEnum(mCurToken));
|
||||
}
|
||||
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
|
@ -858,6 +874,19 @@ nsCSPDirective* nsCSPParser::directiveName() {
|
|||
return nullptr;
|
||||
}
|
||||
|
||||
// Bug 1529068: Implement navigate-to directive.
|
||||
// Once all corner cases are resolved we can remove that special
|
||||
// if-handling here and let the parser just fall through to
|
||||
// return new nsCSPDirective.
|
||||
if (CSP_IsDirective(mCurToken,
|
||||
nsIContentSecurityPolicy::NAVIGATE_TO_DIRECTIVE) &&
|
||||
!StaticPrefs::security_csp_enableNavigateTo()) {
|
||||
AutoTArray<nsString, 1> params = {mCurToken};
|
||||
logWarningErrorToConsole(nsIScriptError::warningFlag,
|
||||
"couldNotProcessUnknownDirective", params);
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
// Make sure the directive does not already exist
|
||||
// (see http://www.w3.org/TR/CSP11/#parsing)
|
||||
if (mPolicy->hasDirective(CSP_StringToCSPDirective(mCurToken))) {
|
||||
|
|
|
@ -252,6 +252,24 @@ CSPService::AsyncOnChannelRedirect(nsIChannel* oldChannel,
|
|||
|
||||
nsCOMPtr<nsILoadInfo> loadInfo = oldChannel->LoadInfo();
|
||||
|
||||
// Check CSP navigate-to
|
||||
// We need to enforce the CSP of the document that initiated the load,
|
||||
// which is the CSP to inherit.
|
||||
nsCOMPtr<nsIContentSecurityPolicy> cspToInherit = loadInfo->GetCspToInherit();
|
||||
if (cspToInherit) {
|
||||
bool allowsNavigateTo = false;
|
||||
rv = cspToInherit->GetAllowsNavigateTo(newUri, loadInfo,
|
||||
true, /* aWasRedirected */
|
||||
false, /* aEnforceWhitelist */
|
||||
&allowsNavigateTo);
|
||||
NS_ENSURE_SUCCESS(rv, rv);
|
||||
|
||||
if (!allowsNavigateTo) {
|
||||
oldChannel->Cancel(NS_ERROR_CSP_NAVIGATE_TO_VIOLATION);
|
||||
return NS_OK;
|
||||
}
|
||||
}
|
||||
|
||||
// No need to continue processing if CSP is disabled or if the protocol
|
||||
// is *not* subject to CSP.
|
||||
// Please note, the correct way to opt-out of CSP using a custom
|
||||
|
|
|
@ -316,6 +316,7 @@ CSPDirective CSP_ContentTypeToDirective(nsContentPolicyType aType) {
|
|||
return nsIContentSecurityPolicy::NO_DIRECTIVE;
|
||||
|
||||
// Fall through to error for all other directives
|
||||
// Note that we should never end up here for navigate-to
|
||||
default:
|
||||
MOZ_ASSERT(false, "Can not map nsContentPolicyType to CSPDirective");
|
||||
}
|
||||
|
@ -1473,6 +1474,31 @@ bool nsCSPPolicy::hasDirective(CSPDirective aDir) const {
|
|||
return false;
|
||||
}
|
||||
|
||||
bool nsCSPPolicy::allowsNavigateTo(nsIURI* aURI, bool aWasRedirected,
|
||||
bool aEnforceWhitelist) const {
|
||||
bool allowsNavigateTo = true;
|
||||
|
||||
for (unsigned long i = 0; i < mDirectives.Length(); i++) {
|
||||
if (mDirectives[i]->equals(
|
||||
nsIContentSecurityPolicy::NAVIGATE_TO_DIRECTIVE)) {
|
||||
// Early return if we can skip the whitelist AND 'unsafe-allow-redirects'
|
||||
// is present.
|
||||
if (!aEnforceWhitelist &&
|
||||
mDirectives[i]->allows(CSP_UNSAFE_ALLOW_REDIRECTS, EmptyString(),
|
||||
false)) {
|
||||
return true;
|
||||
}
|
||||
// Otherwise, check against the whitelist.
|
||||
if (!mDirectives[i]->permits(aURI, EmptyString(), aWasRedirected, false,
|
||||
false, false)) {
|
||||
allowsNavigateTo = false;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return allowsNavigateTo;
|
||||
}
|
||||
|
||||
/*
|
||||
* Use this function only after ::allows() returned 'false'. Most and
|
||||
* foremost it's used to get the violated directive before sending reports.
|
||||
|
|
|
@ -87,7 +87,8 @@ static const char* CSPStrDirectives[] = {
|
|||
"child-src", // CHILD_SRC_DIRECTIVE
|
||||
"block-all-mixed-content", // BLOCK_ALL_MIXED_CONTENT
|
||||
"sandbox", // SANDBOX_DIRECTIVE
|
||||
"worker-src" // WORKER_SRC_DIRECTIVE
|
||||
"worker-src", // WORKER_SRC_DIRECTIVE
|
||||
"navigate-to" // NAVIGATE_TO_DIRECTIVE
|
||||
};
|
||||
|
||||
inline const char* CSP_CSPDirectiveToString(CSPDirective aDir) {
|
||||
|
@ -108,14 +109,15 @@ inline CSPDirective CSP_StringToCSPDirective(const nsAString& aDir) {
|
|||
return nsIContentSecurityPolicy::NO_DIRECTIVE;
|
||||
}
|
||||
|
||||
#define FOR_EACH_CSP_KEYWORD(MACRO) \
|
||||
MACRO(CSP_SELF, "'self'") \
|
||||
MACRO(CSP_UNSAFE_INLINE, "'unsafe-inline'") \
|
||||
MACRO(CSP_UNSAFE_EVAL, "'unsafe-eval'") \
|
||||
MACRO(CSP_NONE, "'none'") \
|
||||
MACRO(CSP_NONCE, "'nonce-") \
|
||||
MACRO(CSP_REPORT_SAMPLE, "'report-sample'") \
|
||||
MACRO(CSP_STRICT_DYNAMIC, "'strict-dynamic'")
|
||||
#define FOR_EACH_CSP_KEYWORD(MACRO) \
|
||||
MACRO(CSP_SELF, "'self'") \
|
||||
MACRO(CSP_UNSAFE_INLINE, "'unsafe-inline'") \
|
||||
MACRO(CSP_UNSAFE_EVAL, "'unsafe-eval'") \
|
||||
MACRO(CSP_NONE, "'none'") \
|
||||
MACRO(CSP_NONCE, "'nonce-") \
|
||||
MACRO(CSP_REPORT_SAMPLE, "'report-sample'") \
|
||||
MACRO(CSP_STRICT_DYNAMIC, "'strict-dynamic'") \
|
||||
MACRO(CSP_UNSAFE_ALLOW_REDIRECTS, "'unsafe-allow-redirects'")
|
||||
|
||||
enum CSPKeyword {
|
||||
#define KEYWORD_ENUM(id_, string_) id_,
|
||||
|
@ -662,6 +664,9 @@ class nsCSPPolicy {
|
|||
|
||||
bool visitDirectiveSrcs(CSPDirective aDir, nsCSPSrcVisitor* aVisitor) const;
|
||||
|
||||
bool allowsNavigateTo(nsIURI* aURI, bool aWasRedirected,
|
||||
bool aEnforceWhitelist) const;
|
||||
|
||||
private:
|
||||
nsUpgradeInsecureDirective* mUpgradeInsecDir;
|
||||
nsTArray<nsCSPDirective*> mDirectives;
|
||||
|
|
|
@ -0,0 +1,11 @@
|
|||
<!DOCTYPE HTML>
|
||||
<html>
|
||||
<head>
|
||||
<title>Bug 1529068 Implement CSP 'navigate-to' directive</title>
|
||||
</head>
|
||||
<body>
|
||||
<script type="text/javascript">
|
||||
window.location = "http://www.example.com/";
|
||||
</script>
|
||||
</body>
|
||||
</html>
|
|
@ -0,0 +1,49 @@
|
|||
// Custom *.sjs file specifically for the needs of
|
||||
// https://bugzilla.mozilla.org/show_bug.cgi?id=1529068
|
||||
|
||||
"use strict";
|
||||
Components.utils.importGlobalProperties(["URLSearchParams"]);
|
||||
|
||||
const TEST_NAVIGATION_HEAD = `
|
||||
<!DOCTYPE HTML>
|
||||
<html>
|
||||
<head>
|
||||
<title>Bug 1529068 Implement CSP 'navigate-to' directive</title>`;
|
||||
|
||||
const TEST_NAVIGATION_AFTER_META = `
|
||||
</head>
|
||||
<body>
|
||||
<script type="text/javascript">
|
||||
window.location = "`;
|
||||
|
||||
const TEST_NAVIGATION_FOOT = `";
|
||||
</script>
|
||||
</body>
|
||||
</html>
|
||||
`;
|
||||
|
||||
function handleRequest(request, response) {
|
||||
const query = new URLSearchParams(request.queryString);
|
||||
|
||||
response.setHeader("Cache-Control", "no-cache", false);
|
||||
response.setHeader("Content-Type", "text/html", false);
|
||||
|
||||
if (query.get("redir")) {
|
||||
response.setStatusLine(request.httpVersion, "302", "Found");
|
||||
response.setHeader("Location", query.get("redir"), false);
|
||||
return;
|
||||
}
|
||||
|
||||
response.write(TEST_NAVIGATION_HEAD);
|
||||
|
||||
// We need meta to set multiple CSP headers.
|
||||
if (query.get("csp")) {
|
||||
response.write("<meta http-equiv=\"Content-Security-Policy\" content=\""+query.get("csp")+"\">");
|
||||
}
|
||||
if (query.get("csp2")) {
|
||||
response.write("<meta http-equiv=\"Content-Security-Policy\" content=\""+query.get("csp2")+"\">");
|
||||
}
|
||||
|
||||
response.write(TEST_NAVIGATION_AFTER_META + query.get("target") + TEST_NAVIGATION_FOOT);
|
||||
|
||||
}
|
|
@ -0,0 +1,17 @@
|
|||
<!DOCTYPE HTML>
|
||||
<html>
|
||||
<head>
|
||||
<meta charset="utf-8">
|
||||
</head>
|
||||
<script type="text/javascript">
|
||||
// The idea with this file is to convert responses into requests.
|
||||
// This is needed because we don't have
|
||||
// specialpowers-http-notify-response
|
||||
|
||||
// Response from this file => request to www.example.com => Allowed
|
||||
// CSP error => Blocked
|
||||
fetch('http://www.example.com/');
|
||||
</script>
|
||||
<body>
|
||||
</body>
|
||||
</html>
|
|
@ -399,3 +399,7 @@ support-files =
|
|||
support-files =
|
||||
file_parent_location_js.html
|
||||
file_iframe_parent_location_js.html
|
||||
[test_navigate_to.html]
|
||||
support-files =
|
||||
file_navigate_to.sjs
|
||||
file_navigate_to_request.html
|
||||
|
|
|
@ -0,0 +1,158 @@
|
|||
<!DOCTYPE HTML>
|
||||
<html>
|
||||
<head>
|
||||
<title>Bug 1529068 Implement CSP 'navigate-to' directive</title>
|
||||
<!-- Including SimpleTest.js so we can use waitForExplicitFinish !-->
|
||||
<script 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">
|
||||
<iframe style="width:100%;" id="testframe"></iframe>
|
||||
</div>
|
||||
|
||||
<script class="testbody" type="text/javascript">
|
||||
|
||||
/*
|
||||
* Description of the test:
|
||||
* We load a page with a given CSP and verify that navigations are correctly
|
||||
* evaluated through the "navigate-to" directive.
|
||||
*/
|
||||
SpecialPowers.pushPrefEnv({"set": [["security.csp.enableNavigateTo", true]]});
|
||||
SimpleTest.waitForExplicitFinish();
|
||||
|
||||
// Note: The final website for the navigation chain must always be: www.example.com
|
||||
var tests = [
|
||||
{
|
||||
result : "blocked",
|
||||
policy : "navigate-to www.mozilla.com",
|
||||
target : "http://www.example.com/"
|
||||
},
|
||||
{
|
||||
result : "allowed",
|
||||
policy : "navigate-to www.example.com",
|
||||
target : "http://www.example.com/"
|
||||
},
|
||||
{
|
||||
// Test path-sensitivity
|
||||
result : "blocked",
|
||||
policy : "navigate-to http://www.example.com/full/path/to/file",
|
||||
target : "http://www.example.com/"
|
||||
},
|
||||
{
|
||||
// Test scheme
|
||||
result : "blocked",
|
||||
policy : "navigate-to https://www.example.com/",
|
||||
target : "http://www.example.com/"
|
||||
},
|
||||
{
|
||||
// Redirect from tracking.example.com to www.example.com
|
||||
result : "blocked",
|
||||
policy : "navigate-to www.example.com",
|
||||
target : "http://tracking.example.com/tests/dom/security/test/csp/file_navigate_to.sjs?redir=http://www.example.com/"
|
||||
},
|
||||
{
|
||||
// Redirect from tracking.example.com to www.example.com (Explicitly allowed)
|
||||
result : "allowed",
|
||||
policy : "navigate-to tracking.example.com www.example.com",
|
||||
target : "http://tracking.example.com/tests/dom/security/test/csp/file_navigate_to.sjs?redir=http://www.example.com/"
|
||||
},
|
||||
{
|
||||
// Redirect from tracking.example.com to www.example.com ('unsafe-allow-redirects')
|
||||
result : "allowed",
|
||||
policy : "navigate-to 'unsafe-allow-redirects' www.example.com",
|
||||
target : "http://tracking.example.com/tests/dom/security/test/csp/file_navigate_to.sjs?redir=http://www.example.com/"
|
||||
},
|
||||
// No path-sensitivity after redirect
|
||||
{
|
||||
result : "allowed",
|
||||
policy : "navigate-to tracking.example.com http://www.example.com/full/path/to/file",
|
||||
target : "http://tracking.example.com/tests/dom/security/test/csp/file_navigate_to.sjs?redir=http://www.example.com/"
|
||||
},
|
||||
// Multiple CSP directives, first block (origin) second allow
|
||||
{
|
||||
result : "allowed",
|
||||
policy : "img-src 'none'; navigate-to www.example.com",
|
||||
target : "http://www.example.com/"
|
||||
},
|
||||
// Multiple CSP directives, first allow (origin) second block
|
||||
{
|
||||
result : "blocked",
|
||||
policy : "img-src www.example.com mochi.test:8888; navigate-to www.mozilla.com",
|
||||
target : "http://www.example.com/"
|
||||
},
|
||||
// Multiple CSPs, first allow second block
|
||||
{
|
||||
result : "blocked",
|
||||
policy : "navigate-to www.example.com",
|
||||
policy2 : "navigate-to www.mozilla.com",
|
||||
target : "http://www.example.com/"
|
||||
},
|
||||
// Multiple CSPs, first block second allow
|
||||
{
|
||||
result : "blocked",
|
||||
policy : "navigate-to www.mozilla.com",
|
||||
policy2 : "navigate-to www.example.com",
|
||||
target : "http://www.example.com/"
|
||||
},
|
||||
];
|
||||
|
||||
// initializing to -1 so we start at index 0 when we start the test
|
||||
var counter = -1;
|
||||
|
||||
function checkResult(aResult) {
|
||||
is(aResult, tests[counter].result, "should be " + tests[counter].result + " in test " + counter +
|
||||
"(" + tests[counter].policy + ", " + tests[counter].target + ")!");
|
||||
loadNextTest();
|
||||
}
|
||||
|
||||
// We use the examiner to identify requests that hit the wire and requests
|
||||
// that are blocked by CSP and bubble up the result to the including iframe
|
||||
// document (parent).
|
||||
function examiner() {
|
||||
SpecialPowers.addObserver(this, "csp-on-violate-policy");
|
||||
SpecialPowers.addObserver(this, "specialpowers-http-notify-request");
|
||||
}
|
||||
examiner.prototype = {
|
||||
observe(subject, topic, data) {
|
||||
if (topic === "specialpowers-http-notify-request" && data === "http://www.example.com/" ) {
|
||||
checkResult("allowed");
|
||||
}
|
||||
if (topic === "csp-on-violate-policy" && data === "navigate-to") {
|
||||
checkResult("blocked");
|
||||
}
|
||||
|
||||
},
|
||||
remove() {
|
||||
SpecialPowers.removeObserver(this, "csp-on-violate-policy");
|
||||
SpecialPowers.removeObserver(this, "specialpowers-http-notify-request");
|
||||
}
|
||||
}
|
||||
window.NavigationActionExaminer = new examiner();
|
||||
|
||||
function loadNextTest() {
|
||||
counter++;
|
||||
if (counter == tests.length) {
|
||||
window.NavigationActionExaminer.remove();
|
||||
SimpleTest.finish();
|
||||
return;
|
||||
}
|
||||
|
||||
var src = "file_navigate_to.sjs";
|
||||
// append the CSP that should be used to serve the file
|
||||
src += "?csp=" + escape(tests[counter].policy);
|
||||
if( tests[counter].policy2 ) {
|
||||
src += "&csp2=" + escape(tests[counter].policy2);
|
||||
}
|
||||
src += "&target=" + escape(tests[counter].target);
|
||||
|
||||
document.getElementById("testframe").src = src;
|
||||
}
|
||||
|
||||
// start running the tests
|
||||
loadNextTest();
|
||||
|
||||
</script>
|
||||
</body>
|
||||
</html>
|
|
@ -149,6 +149,7 @@ nsresult runTestSuite(const PolicyTest* aPolicies, uint32_t aPolicyCount,
|
|||
nsCOMPtr<nsIPrefBranch> prefs = do_GetService(NS_PREFSERVICE_CONTRACTID);
|
||||
bool experimentalEnabledCache = false;
|
||||
bool strictDynamicEnabledCache = false;
|
||||
bool navigateTo = false;
|
||||
if (prefs) {
|
||||
prefs->GetBoolPref("security.csp.experimentalEnabled",
|
||||
&experimentalEnabledCache);
|
||||
|
@ -157,6 +158,9 @@ nsresult runTestSuite(const PolicyTest* aPolicies, uint32_t aPolicyCount,
|
|||
prefs->GetBoolPref("security.csp.enableStrictDynamic",
|
||||
&strictDynamicEnabledCache);
|
||||
prefs->SetBoolPref("security.csp.enableStrictDynamic", true);
|
||||
|
||||
prefs->GetBoolPref("security.csp.enableNavigateTo", &navigateTo);
|
||||
prefs->SetBoolPref("security.csp.enableNavigateTo", true);
|
||||
}
|
||||
|
||||
for (uint32_t i = 0; i < aPolicyCount; i++) {
|
||||
|
@ -170,6 +174,7 @@ nsresult runTestSuite(const PolicyTest* aPolicies, uint32_t aPolicyCount,
|
|||
experimentalEnabledCache);
|
||||
prefs->SetBoolPref("security.csp.enableStrictDynamic",
|
||||
strictDynamicEnabledCache);
|
||||
prefs->SetBoolPref("security.csp.enableNavigateTo", navigateTo);
|
||||
}
|
||||
|
||||
return NS_OK;
|
||||
|
@ -221,6 +226,12 @@ TEST(CSPParser, Directives)
|
|||
"worker-src https://example.com" },
|
||||
{ "worker-src http://worker.com; frame-src http://frame.com; child-src http://child.com",
|
||||
"worker-src http://worker.com; frame-src http://frame.com; child-src http://child.com" },
|
||||
{ "navigate-to http://example.com",
|
||||
"navigate-to http://example.com"},
|
||||
{ "navigate-to 'unsafe-allow-redirects' http://example.com",
|
||||
"navigate-to 'unsafe-allow-redirects' http://example.com"},
|
||||
{ "script-src 'unsafe-allow-redirects' http://example.com",
|
||||
"script-src http://example.com"},
|
||||
// clang-format on
|
||||
};
|
||||
|
||||
|
|
|
@ -6855,6 +6855,12 @@
|
|||
value: true
|
||||
mirror: always
|
||||
|
||||
# Navigate-to CSP 3 directive
|
||||
- name: security.csp.enableNavigateTo
|
||||
type: bool
|
||||
value: false
|
||||
mirror: always
|
||||
|
||||
# No way to enable on Android, Bug 1552602
|
||||
- name: security.webauth.u2f
|
||||
type: bool
|
||||
|
|
|
@ -1,2 +1,3 @@
|
|||
prefs: [security.csp.enableNavigateTo:true]
|
||||
disabled:
|
||||
if (os == "win"): https://bugzilla.mozilla.org/show_bug.cgi?id=1450635
|
||||
|
|
|
@ -1,3 +1,8 @@
|
|||
[child-navigates-parent-allowed.html]
|
||||
disabled:
|
||||
if os == "linux": https://bugzilla.mozilla.org/show_bug.cgi?id=1450660
|
||||
|
||||
expected: TIMEOUT
|
||||
|
||||
[Test that the child can navigate the parent because the relevant policy belongs to the navigation initiator (in this case the child, which has the policy `navigate-to 'self'`)]
|
||||
expected: NOTRUN
|
||||
|
|
|
@ -1,6 +1,9 @@
|
|||
[child-navigates-parent-blocked.sub.html]
|
||||
disabled:
|
||||
if (os == "android") and not e10s: https://bugzilla.mozilla.org/show_bug.cgi?id=1511193
|
||||
|
||||
expected: TIMEOUT
|
||||
|
||||
[Violation report status OK.]
|
||||
expected: FAIL
|
||||
|
||||
|
@ -8,5 +11,5 @@
|
|||
expected: FAIL
|
||||
|
||||
[Test that the child can't navigate the parent because the relevant policy belongs to the navigation initiator (in this case the child which has the policy `navigate-to 'none'`)]
|
||||
expected: FAIL
|
||||
expected: NOTRUN
|
||||
|
||||
|
|
|
@ -1,7 +0,0 @@
|
|||
[form-blocked.sub.html]
|
||||
[Violation report status OK.]
|
||||
expected: FAIL
|
||||
|
||||
[Test that the child iframe navigation is not allowed]
|
||||
expected: FAIL
|
||||
|
|
@ -1,10 +0,0 @@
|
|||
[form-cross-origin-blocked.sub.html]
|
||||
[Test that the child iframe navigation is blocked]
|
||||
expected: FAIL
|
||||
|
||||
[Violation report status OK.]
|
||||
expected: FAIL
|
||||
|
||||
[Test that the child iframe navigation is not allowed]
|
||||
expected: FAIL
|
||||
|
|
@ -1,10 +1,10 @@
|
|||
[form-redirected-blocked.sub.html]
|
||||
expected:
|
||||
if (os == "android"): TIMEOUT
|
||||
[Test that the child iframe navigation is blocked]
|
||||
expected: FAIL
|
||||
|
||||
[Violation report status OK.]
|
||||
expected: FAIL
|
||||
expected:
|
||||
if (os == "android"): NOTRUN
|
||||
|
||||
[Test that the child iframe navigation is not allowed]
|
||||
expected: FAIL
|
||||
|
||||
expected:
|
||||
if (os == "android"): NOTRUN
|
||||
|
|
|
@ -1,7 +0,0 @@
|
|||
[href-location-blocked.sub.html]
|
||||
[Violation report status OK.]
|
||||
expected: FAIL
|
||||
|
||||
[Test that the child iframe navigation is not allowed]
|
||||
expected: FAIL
|
||||
|
|
@ -1,10 +1,3 @@
|
|||
[href-location-cross-origin-blocked.sub.html]
|
||||
[Test that the child iframe navigation is blocked]
|
||||
expected: FAIL
|
||||
|
||||
[Violation report status OK.]
|
||||
expected: FAIL
|
||||
|
||||
[Test that the child iframe navigation is not allowed]
|
||||
expected: FAIL
|
||||
|
||||
disabled:
|
||||
if (os == "android"): Passes on debug but fails on optimized
|
||||
|
|
|
@ -1,10 +1,10 @@
|
|||
[href-location-redirected-blocked.sub.html]
|
||||
expected:
|
||||
if (os == "android"): TIMEOUT
|
||||
[Test that the child iframe navigation is blocked]
|
||||
expected: FAIL
|
||||
|
||||
[Violation report status OK.]
|
||||
expected: FAIL
|
||||
expected:
|
||||
if (os == "android"): NOTRUN
|
||||
|
||||
[Test that the child iframe navigation is not allowed]
|
||||
expected: FAIL
|
||||
|
||||
expected:
|
||||
if (os == "android"): NOTRUN
|
||||
|
|
|
@ -1,7 +0,0 @@
|
|||
[link-click-blocked.sub.html]
|
||||
[Violation report status OK.]
|
||||
expected: FAIL
|
||||
|
||||
[Test that the child iframe navigation is not allowed]
|
||||
expected: FAIL
|
||||
|
|
@ -1,10 +0,0 @@
|
|||
[link-click-cross-origin-blocked.sub.html]
|
||||
[Test that the child iframe navigation is blocked]
|
||||
expected: FAIL
|
||||
|
||||
[Violation report status OK.]
|
||||
expected: FAIL
|
||||
|
||||
[Test that the child iframe navigation is not allowed]
|
||||
expected: FAIL
|
||||
|
|
@ -1,10 +1,10 @@
|
|||
[link-click-redirected-blocked.sub.html]
|
||||
expected:
|
||||
if (os == "android"): TIMEOUT
|
||||
[Test that the child iframe navigation is blocked]
|
||||
expected: FAIL
|
||||
|
||||
[Violation report status OK.]
|
||||
expected: FAIL
|
||||
expected:
|
||||
if (os == "android"): NOTRUN
|
||||
|
||||
[Test that the child iframe navigation is not allowed]
|
||||
expected: FAIL
|
||||
|
||||
expected:
|
||||
if (os == "android"): NOTRUN
|
||||
|
|
|
@ -1,7 +0,0 @@
|
|||
[meta-refresh-blocked.sub.html]
|
||||
[Violation report status OK.]
|
||||
expected: FAIL
|
||||
|
||||
[Test that the child iframe navigation is not allowed]
|
||||
expected: FAIL
|
||||
|
|
@ -1,10 +0,0 @@
|
|||
[meta-refresh-cross-origin-blocked.sub.html]
|
||||
[Test that the child iframe navigation is blocked]
|
||||
expected: FAIL
|
||||
|
||||
[Violation report status OK.]
|
||||
expected: FAIL
|
||||
|
||||
[Test that the child iframe navigation is not allowed]
|
||||
expected: FAIL
|
||||
|
|
@ -1,10 +1,11 @@
|
|||
[meta-refresh-redirected-blocked.sub.html]
|
||||
[Test that the child iframe navigation is blocked]
|
||||
expected: FAIL
|
||||
expected:
|
||||
if (os == "android"): TIMEOUT
|
||||
|
||||
[Violation report status OK.]
|
||||
expected: FAIL
|
||||
[Test that the child iframe navigation is blocked]
|
||||
expected:
|
||||
if (os == "android"): NOTRUN
|
||||
|
||||
[Test that the child iframe navigation is not allowed]
|
||||
expected: FAIL
|
||||
|
||||
expected:
|
||||
if (os == "android"): NOTRUN
|
||||
|
|
|
@ -1,10 +0,0 @@
|
|||
[parent-navigates-child-blocked.html]
|
||||
[Test that the parent can't navigate the child because the relevant policy belongs to the navigation initiator (in this case the parent)]
|
||||
expected: FAIL
|
||||
|
||||
[Violation report status OK.]
|
||||
expected: FAIL
|
||||
|
||||
[Test that the parent can't navigate the child because the relevant policy belongs to the navigation initiator (in this case the parent, which has the policy `navigate-to support/wait_for_navigation.html;`)]
|
||||
expected: FAIL
|
||||
|
|
@ -1,5 +1,7 @@
|
|||
[spv-only-sent-to-initiator.sub.html]
|
||||
expected: TIMEOUT
|
||||
[Test that no spv event is raised]
|
||||
expected: NOTRUN
|
||||
expected: FAIL
|
||||
|
||||
[Violation report status OK.]
|
||||
expected: FAIL
|
||||
|
||||
|
|
|
@ -1,6 +1,9 @@
|
|||
[blocked-end-of-chain.sub.html]
|
||||
disabled:
|
||||
if os == "android" and not e10s: https://bugzilla.mozilla.org/show_bug.cgi?id=1511193
|
||||
[Test that the child iframe navigation is blocked]
|
||||
expected: FAIL
|
||||
|
||||
expected: TIMEOUT
|
||||
|
||||
[Test that the child iframe navigation is blocked]
|
||||
expected: NOTRUN
|
||||
|
||||
|
|
|
@ -792,6 +792,7 @@ with modules["PROFILE"]:
|
|||
# =======================================================================
|
||||
with modules["SECURITY"]:
|
||||
# Error code for CSP
|
||||
errors["NS_ERROR_CSP_NAVIGATE_TO_VIOLATION"] = FAILURE(97)
|
||||
errors["NS_ERROR_CSP_FORM_ACTION_VIOLATION"] = FAILURE(98)
|
||||
errors["NS_ERROR_CSP_FRAME_ANCESTOR_VIOLATION"] = FAILURE(99)
|
||||
|
||||
|
|
Загрузка…
Ссылка в новой задаче