Skip to content

Commit

Permalink
Add WPT tests for basic authentication with SR based prefetch
Browse files Browse the repository at this point in the history
- speculation-rules base prefetch logic should not send the
  username/passwords with prefetch request to cross origin urls.

Bug: 1302365
Change-Id: I59dd147ecd590ce2e80a1652fbb12f78c16d0859
Reviewed-on: https://chromium-review.googlesource.com/c/chromium/src/+/3635863
Reviewed-by: Jeremy Roman <jbroman@chromium.org>
Commit-Queue: Iman Saboori <isaboori@google.com>
Cr-Commit-Position: refs/heads/main@{#1002029}
  • Loading branch information
isaboori authored and Chromium LUCI CQ committed May 11, 2022
1 parent 56156ab commit 84cc571
Show file tree
Hide file tree
Showing 3 changed files with 90 additions and 3 deletions.
Original file line number Diff line number Diff line change
@@ -0,0 +1,32 @@

def main(request, response):
def fmt(x):
return f'"{x.decode("utf-8")}"' if x is not None else "undefined"

purpose = request.headers.get("Purpose", b"").decode("utf-8")
sec_purpose = request.headers.get("Sec-Purpose", b"").decode("utf-8")

headers = [(b"Content-Type", b"text/html"), (b'WWW-Authenticate', 'Basic')]
status = 200 if request.auth.username is not None or sec_purpose.startswith(
"prefetch") else 401

content = f'''
<!DOCTYPE html>
<script src="/common/dispatcher/dispatcher.js"></script>
<script src="utils.sub.js"></script>
<script>
window.requestHeaders = {{
purpose: "{purpose}",
sec_purpose: "{sec_purpose}"
}};
window.requestCredentials = {{
username: {fmt(request.auth.username)},
password: {fmt(request.auth.password)}
}};
const uuid = new URLSearchParams(location.search).get('uuid');
window.executor = new Executor(uuid);
</script>
'''
return status, headers, content
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,7 @@ class PrefetchAgent extends RemoteContext {
}

getExecutorURL(options = {}) {
let {hostname, protocol, executor, ...extra} = options;
let {hostname, username, password, protocol, executor, ...extra} = options;
let params = new URLSearchParams({uuid: this.context_id, ...extra});
if(executor === undefined) {
executor = "executor.sub.html";
Expand All @@ -23,6 +23,12 @@ class PrefetchAgent extends RemoteContext {
if(hostname !== undefined) {
url.hostname = hostname;
}
if(username !== undefined) {
url.username = username;
}
if(password !== undefined) {
url.password = password;
}
if(protocol !== undefined) {
url.protocol = protocol;
url.port = protocol === "https" ? "{{ports[https][0]}}" : "{{ports[http][0]}}";
Expand All @@ -48,6 +54,8 @@ class PrefetchAgent extends RemoteContext {
location.href = url;
});
}, [url]);
url.username = '';
url.password = '';
assert_equals(
await this.execute_script(() => location.href),
url.toString(),
Expand All @@ -73,6 +81,10 @@ class PrefetchAgent extends RemoteContext {
async getRequestCookies() {
return this.execute_script(() => window.requestCookies);
}

async getRequestCredentials() {
return this.execute_script(() => window.requestCredentials);
}
}

// Produces n URLs with unique UUIDs which will record when they are prefetched.
Expand All @@ -98,9 +110,8 @@ async function isUrlPrefetched(url) {

// Must also include /common/utils.js and /common/dispatcher/dispatcher.js to use this.
async function spawnWindow(t, options = {}) {
let {executor, ...extra} = options;
let agent = new PrefetchAgent(token(), t);
let w = window.open(agent.getExecutorURL({executor}), extra);
let w = window.open(agent.getExecutorURL(options), options);
t.add_cleanup(() => w.close());
return agent;
}
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,44 @@
<!DOCTYPE html>
<meta name="timeout" content="long">
<script src="/resources/testharness.js"></script>
<script src="/resources/testharnessreport.js"></script>
<script src="/common/dispatcher/dispatcher.js"></script>
<script src="/common/utils.js"></script>
<script src="resources/utils.sub.js"></script>
<meta name="variant" content="?cross-origin=true">
<meta name="variant" content="?cross-origin=false">
<script>
let cross_origin = Object.fromEntries(new URLSearchParams(location.search))["cross-origin"] === "true";
promise_test(async t => {
assert_implements(HTMLScriptElement.supports('speculationrules'), "Speculation Rules not supported");

let executor = "authenticate.py";
let credentials = { username: "user", password: "pass" };
let agent = await spawnWindow(t, { executor, ...credentials });
let request_credentials = await agent.getRequestCredentials();
assert_equals(request_credentials.username, credentials.username);
assert_equals(request_credentials.password, credentials.password);

let host = cross_origin ? { hostname: PREFETCH_PROXY_BYPASS_HOST } : {};
let nextUrl = agent.getExecutorURL({ page: 2, executor, ...host });
await agent.forceSinglePrefetch(nextUrl, { requires: ["anonymous-client-ip-when-cross-origin"] });
await agent.navigate(nextUrl);

let requestHeaders = await agent.getRequestHeaders();
request_credentials = await agent.getRequestCredentials();
if (cross_origin) {
assert_equals(request_credentials.username, undefined);
assert_equals(request_credentials.password, undefined);

assert_in_array(requestHeaders.purpose, ["", "prefetch"]);
assert_equals(requestHeaders.sec_purpose, "prefetch;anonymous-client-ip");
}
else {
assert_equals(request_credentials.username, credentials.username);
assert_equals(request_credentials.password, credentials.password);

assert_prefetched(await agent.getRequestHeaders());
}

}, "test www-authenticate basic does not forward credentials to cross-origin pages.");
</script>

0 comments on commit 84cc571

Please sign in to comment.