Bug 690168. Implement the 'Allow-From' directive for X-Frame-Options. r=bzbarsky

This commit is contained in:
Phil Ames 2012-08-27 15:46:24 -04:00
parent 8ec001feea
commit cd2459e50f
10 changed files with 188 additions and 6 deletions

View File

@ -19,6 +19,8 @@ window.addEventListener('load', parent.testFramesLoaded, false);
<iframe id="sameorigin7" src="http://mochi.test:8888/tests/content/base/test/file_x-frame-options_page.sjs?testid=sameorigin7&xfo=sameorigin3"></iframe><br>
<iframe id="sameorigin8" src="http://example.com/tests/content/base/test/file_x-frame-options_page.sjs?testid=sameorigin8&xfo=sameorigin3"></iframe><br>
<iframe id="mixedpolicy" src="http://mochi.test:8888/tests/content/base/test/file_x-frame-options_page.sjs?testid=mixedpolicy&xfo=mixedpolicy"></iframe><br>
<iframe id="allow-from-allow" src="http://example.com/tests/content/base/test/file_x-frame-options_page.sjs?testid=allow-from-allow&xfo=afa"></iframe><br>
<iframe id="allow-from-deny" src="http://example.com/tests/content/base/test/file_x-frame-options_page.sjs?testid=allow-from-deny&xfo=afd"></iframe><br>
</body>
</html>

View File

@ -26,6 +26,12 @@ function handleRequest(request, response)
else if (query['xfo'] == "mixedpolicy") {
response.setHeader("X-Frame-Options", "DENY,SAMEORIGIN", false);
}
else if (query['xfo'] == "afa") {
response.setHeader("X-Frame-Options", "ALLOW-FROM http://mochi.test:8888/", false);
}
else if (query['xfo'] == "afd") {
response.setHeader("X-Frame-Options", "ALLOW-FROM http://example.com/", false);
}
// from the test harness we'll be checking for the presence of this element
// to test if the page loaded

View File

@ -108,6 +108,16 @@ var testFramesLoaded = function() {
var test10 = frame.contentDocument.getElementById("test");
is(test10, null, "test mixedpolicy");
// iframe from different origin, allow-from: this origin - should load
frame = harness.contentDocument.getElementById("allow-from-allow");
var test11 = frame.contentDocument.getElementById("test").textContent;
is(test11, "allow-from-allow", "test allow-from-allow");
// iframe from different origin, with allow-from: other - should not load
frame = harness.contentDocument.getElementById("allow-from-deny");
var test12 = frame.contentDocument.getElementById("test");
is(test12, null, "test allow-from-deny");
// call tests to check principal comparison, e.g. a document can open a window
// to a data: or javascript: document which frames an
// X-Frame-Options: SAMEORIGIN document and the frame should load

View File

@ -11,6 +11,7 @@
#include "nsDocShellCID.h"
#include "nsIWebNavigationInfo.h"
#include "nsIDOMWindow.h"
#include "nsNetUtil.h"
#include "nsAutoPtr.h"
#include "nsIHttpChannel.h"
#include "nsIScriptSecurityManager.h"
@ -258,9 +259,15 @@ nsDSURIContentListener::SetParentContentListener(nsIURIContentListener*
bool nsDSURIContentListener::CheckOneFrameOptionsPolicy(nsIRequest *request,
const nsAString& policy) {
// return early if header does not have one of the two values with meaning
static const char allowFrom[] = "allow-from ";
const PRUint32 allowFromLen = ArrayLength(allowFrom) - 1;
bool isAllowFrom =
StringHead(policy, allowFromLen).LowerCaseEqualsLiteral(allowFrom);
// return early if header does not have one of the values with meaning
if (!policy.LowerCaseEqualsLiteral("deny") &&
!policy.LowerCaseEqualsLiteral("sameorigin"))
!policy.LowerCaseEqualsLiteral("sameorigin") &&
!isAllowFrom)
return true;
nsCOMPtr<nsIHttpChannel> httpChannel = do_QueryInterface(request);
@ -342,18 +349,32 @@ bool nsDSURIContentListener::CheckOneFrameOptionsPolicy(nsIRequest *request,
return false;
}
topDoc = do_GetInterface(curDocShellItem);
nsCOMPtr<nsIURI> topUri;
topDoc->NodePrincipal()->GetURI(getter_AddRefs(topUri));
nsCOMPtr<nsIURI> uri;
// If the X-Frame-Options value is SAMEORIGIN, then the top frame in the
// parent chain must be from the same origin as this document.
if (policy.LowerCaseEqualsLiteral("sameorigin")) {
nsCOMPtr<nsIURI> uri;
httpChannel->GetURI(getter_AddRefs(uri));
topDoc = do_GetInterface(curDocShellItem);
nsCOMPtr<nsIURI> topUri;
topDoc->NodePrincipal()->GetURI(getter_AddRefs(topUri));
rv = ssm->CheckSameOriginURI(uri, topUri, true);
if (NS_FAILED(rv))
return false; /* wasn't same-origin */
}
// If the X-Frame-Options value is "allow-from [uri]", then the top
// frame in the parent chain must be from that origin
if (isAllowFrom) {
rv = NS_NewURI(getter_AddRefs(uri),
Substring(policy, allowFromLen));
if (NS_FAILED(rv))
return false;
rv = ssm->CheckSameOriginURI(uri, topUri, true);
if (NS_FAILED(rv))
return false;
}
}
return true;

View File

@ -63,6 +63,10 @@ MOCHITEST_FILES = \
browserElement_XFrameOptionsSameOrigin.js \
test_browserElement_inproc_XFrameOptionsSameOrigin.html \
file_browserElement_XFrameOptionsSameOrigin.html \
browserElement_XFrameOptionsAllowFrom.js \
test_browserElement_inproc_XFrameOptionsAllowFrom.html \
file_browserElement_XFrameOptionsAllowFrom.html \
file_browserElement_XFrameOptionsAllowFrom.sjs \
browserElement_Alert.js \
test_browserElement_inproc_Alert.html \
browserElement_AlertInFrame.js \
@ -154,6 +158,7 @@ MOCHITEST_FILES += \
test_browserElement_oop_XFrameOptions.html \
test_browserElement_oop_XFrameOptionsDeny.html \
test_browserElement_oop_XFrameOptionsSameOrigin.html \
test_browserElement_oop_XFrameOptionsAllowFrom.html \
test_browserElement_oop_Alert.html \
test_browserElement_oop_AlertInFrame.html \
test_browserElement_oop_TargetTop.html \

View File

@ -0,0 +1,53 @@
/* Any copyright is dedicated to the public domain.
http://creativecommons.org/publicdomain/zero/1.0/ */
// Bug 690168 - Support Allow-From notation for X-Frame-Options header.
"use strict";
SimpleTest.waitForExplicitFinish();
var initialScreenshot = null;
function runTest() {
browserElementTestHelpers.setEnabledPref(true);
browserElementTestHelpers.addPermission();
var count = 0;
var iframe = document.createElement('iframe');
iframe.mozbrowser = true;
iframe.height = '1000px';
// The innermost page we load will fire an alert when it successfully loads.
iframe.addEventListener('mozbrowsershowmodalprompt', function(e) {
switch (e.detail.message) {
case 'step 1':
// Make the page wait for us to unblock it (which we do after we finish
// taking the screenshot).
e.preventDefault();
iframe.getScreenshot().onsuccess = function(sshot) {
if (initialScreenshot == null)
initialScreenshot = sshot.target.result;
e.detail.unblock();
};
break;
case 'step 2':
ok(false, 'cross origin page loaded');
break;
case 'finish':
// The page has now attempted to load the X-Frame-Options page; take
// another screenshot.
iframe.getScreenshot().onsuccess = function(sshot) {
is(sshot.target.result, initialScreenshot, "Screenshots should be identical");
SimpleTest.finish();
};
break;
}
});
document.body.appendChild(iframe);
iframe.src = 'http://example.com/tests/dom/browser-element/mochitest/file_browserElement_XFrameOptionsAllowFrom.html';
}
runTest();

View File

@ -0,0 +1,43 @@
<html>
<body>
<!-- Try to load in a frame a cross-origin page which sends:
"X-Frame-Options: Allow-From http://mochi.test:8888/",
and a cross-origin page which sends
"X-Frame-Options: Allow-From http://example.com/". -->
<script>
// Make sure these iframes aren't too tall; they both need to fit inside the
// iframe this page is contained in, without scrolling, in order for the test's
// screenshots to work properly.
var frame_src = 'http://example.com/tests/dom/browser-element/mochitest/file_browserElement_XFrameOptionsAllowFrom.sjs';
var iframe1 = document.createElement('iframe');
iframe1.height = '300px';
var iframe2 = document.createElement('iframe');
iframe2.height = '300px';
document.body.appendChild(iframe1);
document.body.appendChild(iframe2);
iframe1.addEventListener('load', function iframe1Load() {
iframe1.removeEventListener('load', iframe1Load);
// This causes our embedder to take a screenshot (and blocks until the
// screenshot is completed).
var iframe2Loaded = false;
iframe2.addEventListener('load', function iframe2Load() {
iframe2.removeEventListener('load', iframe2Load);
iframe2Loaded = true;
alert('finish');
});
setTimeout(function() { iframe2.src = frame_src; }, 1000);
});
iframe1.src = frame_src + '?iframe1';
</script>
</body>
</html>

View File

@ -0,0 +1,16 @@
function handleRequest(request, response)
{
var content = 'step 1';
if (request.queryString == "iframe1") {
response.setHeader("X-Frame-Options", "Allow-From http://mochi.test:8888/")
content = 'finish';
} else {
response.setHeader("X-Frame-Options", "Allow-From http://example.com")
}
response.setHeader("Content-Type", "text/html", false);
// Tests rely on this page not being entirely blank, because they take
// screenshots to determine whether this page was loaded.
response.write("<html><body>XFrameOptions test<script>alert('" + content + "')</script></body></html>");
}

View File

@ -0,0 +1,13 @@
<!DOCTYPE HTML>
<html>
<head>
<title>Test for Bug 690168</title>
<script type="application/javascript" src="/tests/SimpleTest/SimpleTest.js"></script>
<script type="application/javascript" src="browserElementTestHelpers.js"></script>
<link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css"/>
</head>
<body>
<script type="application/javascript;version=1.7" src="browserElement_XFrameOptionsAllowFrom.js">
</script>
</body>
</html>

View File

@ -0,0 +1,13 @@
<!DOCTYPE HTML>
<html>
<head>
<title>Test for Bug 690168</title>
<script type="application/javascript" src="/tests/SimpleTest/SimpleTest.js"></script>
<script type="application/javascript" src="browserElementTestHelpers.js"></script>
<link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css"/>
</head>
<body>
<script type="application/javascript;version=1.7" src="browserElement_XFrameOptionsAllowFrom.js">
</script>
</body>
</html>