Commit e423572a authored by Ulan Degenbaev's avatar Ulan Degenbaev Committed by Commit Bot

Reveal only the initial frame URL in performance.measureMemory

This changes the reporting in performance.measureMemory to reveal only
the URL at the frame creation time for cross-origin iframes and
thus prevents URL information leak if the iframe navigates after loading.

This is done by saving the first cross-origin URL loaded in LocalFrame
in LocalFrame::DidAttachDocument and using that URL for reporting.

The patch also adds a new test for redirecting iframe and fixes
the existing tests to wait until child iframes are loaded.

Bug: 1049093
Change-Id: I0162efb82061f036450586dea0b191cacdce23f2
Reviewed-on: https://chromium-review.googlesource.com/c/chromium/src/+/2098722
Commit-Queue: Ulan Degenbaev <ulan@chromium.org>
Reviewed-by: default avatarKentaro Hara <haraken@chromium.org>
Cr-Commit-Position: refs/heads/master@{#749663}
parent 4ebe8f60
......@@ -577,6 +577,13 @@ void LocalFrame::DidAttachDocument() {
GetInputMethodController().DidAttachDocument(document);
GetSpellChecker().DidAttachDocument(document);
GetTextSuggestionController().DidAttachDocument(document);
if (IsCrossOriginToParentFrame() && !first_url_cross_origin_to_parent_) {
first_url_cross_origin_to_parent_ = GetDocument()->Url().GetString();
}
}
base::Optional<String> LocalFrame::FirstUrlCrossOriginToParent() const {
return first_url_cross_origin_to_parent_;
}
void LocalFrame::Reload(WebFrameLoadType load_type) {
......
......@@ -546,6 +546,10 @@ class CORE_EXPORT LocalFrame final : public Frame,
// Indicate that this frame was attached as a MainFrame.
void WasAttachedAsLocalMainFrame();
// Returns the first URL loaded in this frame that is cross-origin to the
// parent frame.
base::Optional<String> FirstUrlCrossOriginToParent() const;
private:
friend class FrameNavigationDisabler;
......@@ -701,6 +705,14 @@ class CORE_EXPORT LocalFrame final : public Frame,
Member<SystemClipboard> system_clipboard_;
// Access to the global raw/unsanitized system clipboard
Member<RawSystemClipboard> raw_system_clipboard_;
// Stores the first URL that was loaded in this frame that is cross-origin
// to the parent frame. For this URL we know that the parent origin provided
// it by either setting the |src| attribute or by navigating the iframe.
// Thus we can safely reveal it to the parent origin in Web APIs.
// TODO(ulan): Move this to the browser process once performance.measureMemory
// starts using Performance Manager to support cross-site iframes.
base::Optional<String> first_url_cross_origin_to_parent_;
};
inline FrameLoader& LocalFrame::Loader() const {
......
......@@ -87,11 +87,17 @@ const Frame* GetFrame(v8::Local<v8::Context> context) {
}
String GetUrl(const Frame* frame) {
// TODO(ulan): Find a way to return the URL at frames open time.
// TODO(ulan): Refactor the rest of the code to make the parameter LocalFrame.
const LocalFrame* local_frame = To<LocalFrame>(frame);
ExecutionContext* execution_context =
local_frame->GetDocument()->ToExecutionContext();
return execution_context->Url().GetString();
if (local_frame->IsCrossOriginToParentFrame()) {
// The function must be called only for the first cross-origin iframe on
// the path down from the main frame. Thus the parent frame is guaranteed
// to be the same origin as the main frame.
DCHECK(!local_frame->Tree().Parent()->IsCrossOriginToMainFrame());
base::Optional<String> url = local_frame->FirstUrlCrossOriginToParent();
return url ? url.value() : "";
}
return local_frame->GetDocument()->Url().GetString();
}
// To avoid information leaks cross-origin iframes are considered opaque for
......
......@@ -6270,8 +6270,8 @@ http/tests/devtools/wasm-isolated-code-cache/wasm-cache-test.js [ Skip ]
# Memory measurement tests are run as virtual tests.
external/wpt/measure-memory/measure-memory.tentative.any.html [ Skip ]
external/wpt/measure-memory/measure-memory.tentative.any.worker.html [ Skip ]
external/wpt/measure-memory/measure-memory-cross-origin-iframe.tentative.window.html [ Skip ]
external/wpt/measure-memory/measure-memory-cross-origin-redirecting-iframe.tentative.window.html [ Skip ]
external/wpt/measure-memory/measure-memory-same-origin-iframe.tentative.window.html [ Skip ]
# Assertion errors need to be fixed
......
// META: script=/common/get-host-info.sub.js
// META: script=./resources/common.js
// META: timeout=long
'use strict';
promise_test(async testCase => {
const frame = document.createElement("iframe");
const child = getUrl(CROSS_ORIGIN, "resources/child.sub.html");
const grandchild = getUrl(CROSS_ORIGIN, "resources/grandchild.sub.html");
const grandchildLoaded = new Promise(resolve => {
window.onmessage = function(message) {
if (message.data === 'grandchild-loaded') {
resolve(message);
}
}
});
const frame = document.createElement('iframe');
const child = getUrl(CROSS_ORIGIN, 'resources/child.sub.html');
frame.src = child;
document.body.append(frame);
await grandchildLoaded;
try {
let result = await performance.measureMemory();
checkMeasureMemory(result, {
......
// META: script=/common/get-host-info.sub.js
// META: script=./resources/common.js
'use strict';
promise_test(async testCase => {
const grandchildLoaded = new Promise(resolve => {
window.onmessage = function(message) {
if (message.data === 'grandchild-loaded') {
resolve(message);
}
}
});
const frame = document.createElement('iframe');
const redirecting_child = getUrl(CROSS_ORIGIN, 'resources/redirecting-child.sub.html');
frame.src = redirecting_child;
document.body.append(frame);
await grandchildLoaded;
try {
let result = await performance.measureMemory();
checkMeasureMemory(result, {
allowed: [window.location.href, redirecting_child]
});
} catch (error) {
if (!(error instanceof DOMException)) {
throw error;
}
assert_equals(error.name, 'SecurityError');
}
}, 'Well-formed result of performance.measureMemory with cross-origin iframe.');
// META: script=/common/get-host-info.sub.js
// META: script=./resources/common.js
// META: timeout=long
'use strict';
promise_test(async testCase => {
const frame = document.createElement("iframe");
const child = getUrl(SAME_ORIGIN, "resources/child.sub.html");
const grandchild = getUrl(SAME_ORIGIN, "resources/grandchild.sub.html");
const grandchildLoaded = new Promise(resolve => {
window.onmessage = function(message) {
if (message.data === 'grandchild-loaded') {
resolve(message);
}
}
});
const frame = document.createElement('iframe');
const child = getUrl(SAME_ORIGIN, 'resources/child.sub.html');
const grandchild = getUrl(SAME_ORIGIN, 'resources/grandchild.sub.html');
frame.src = child;
document.body.append(frame);
await grandchildLoaded;
try {
let result = await performance.measureMemory();
checkMeasureMemory(result, {
......
<!doctype html>
<meta charset=utf-8>
<html>
<script>
window.onmessage = function (message) {
// Forward the message to the parent.
window.parent.postMessage(message.data, '*');
}
window.onload = function () {
window.parent.postMessage('grandchild-loaded', '*');
}
</script>
<body>
Hello from child iframe.
<iframe src="grandchild.sub.html"></iframe>
......
const SAME_ORIGIN = {origin: get_host_info().HTTPS_ORIGIN, name: "SAME_ORIGIN"};
const CROSS_ORIGIN = {origin: get_host_info().HTTPS_NOTSAMESITE_ORIGIN, name: "CROSS_ORIGIN"}
const SAME_ORIGIN = {origin: get_host_info().HTTP_ORIGIN, name: "SAME_ORIGIN"};
const CROSS_ORIGIN = {origin: get_host_info().HTTP_REMOTE_ORIGIN, name: "CROSS_ORIGIN"}
function checkMeasureMemoryBreakdown(breakdown, options) {
let allowed = new Set(options.allowed);
......@@ -31,5 +31,5 @@ function checkMeasureMemory(result, options) {
function getUrl(host, relativePath) {
const path = new URL(relativePath, window.location).pathname;
return `${host.origin}/${path}`;
return `${host.origin}${path}`;
}
\ No newline at end of file
<!doctype html>
<meta charset=utf-8>
<html>
<script>
window.onload = function () {
window.parent.postMessage('grandchild-loaded', '*');
}
</script>
<body>
Hello from grandchild iframe.
</body>
......
<!doctype html>
<meta charset=utf-8>
<html>
<script>
window.onload = function () {
document.location.href = document.location.href.replace('redirecting-child', 'child');
}
</script>
<body>
Hello from child iframe.
</body>
</html>
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment