CVE-2026-5919
Overview
Files Changed
chrome/browser/net/websocket_browsertest.cccomponents/private_ai/websocket_client.cccontent/browser/websockets/websocket_connector_impl.cccontent/browser/websockets/websocket_connector_impl.hdevice/fido/cable/fido_tunnel_device.ccdevice/fido/cable/v2_authenticator.cc
Patch
From e30962b8baf2af0d8f4a09e1d71a4403801d9524 Mon Sep 17 00:00:00 2001
From: Chris Fredrickson <cfredric@chromium.org>
Date: Mon, 23 Feb 2026 08:12:52 -0800
Subject: [PATCH] Remove separate SiteForCookies arg from WebSocketConnector::Connect
This removes the SiteForCookies arg from WebSocketConnector::Connect,
since the impl already has an IsolationInfo which includes a
SiteForCookies that ought to be used instead. We also remove the
associated plumbing all the way to the callsites of
NetworkContext::CreateWebSocket, and update them accordingly.
Of those callsites, all but WebSocketConnectorImpl::Connect used
hardcoded SiteForCookies values that have been preserved or were
irrelevant (due to network::mojom::kWebSocketOptionBlockAllCookies).
Previously, WebSocketConnectorImpl::Connect used a value that was
dynamically computed by the renderer. Now,
WebSocketConnectorImpl::Connect uses the SiteForCookies from the
browser-supplied IsolationInfo (from RenderFrameHostImpl,
ServiceWorkerHost, DedicatedWorkerHost, or SharedWorkerHost, as
applicable).
Fixed: 483423893
Change-Id: I556f728dfd94f516b89fcabe87c2e38b66ec31ca
Reviewed-on: https://chromium-review.googlesource.com/c/chromium/src/+/7595833
Reviewed-by: Ken Buchanan <kenrb@chromium.org>
Commit-Queue: Christian Dullweber <dullweber@chromium.org>
Reviewed-by: Adam Rice <ricea@chromium.org>
Auto-Submit: Chris Fredrickson <cfredric@chromium.org>
Commit-Queue: Chris Fredrickson <cfredric@chromium.org>
Reviewed-by: Christian Dullweber <dullweber@chromium.org>
Cr-Commit-Position: refs/heads/main@{#1588717}
---
diff --git a/chrome/browser/net/websocket_browsertest.cc b/chrome/browser/net/websocket_browsertest.cc
index 3c2c81f..ed785fc 100644
--- a/chrome/browser/net/websocket_browsertest.cc
+++ b/chrome/browser/net/websocket_browsertest.cc
@@ -162,7 +162,6 @@
content::RenderProcessHost* const process = frame->GetProcess();
const std::vector<std::string> requested_protocols;
- const net::SiteForCookies site_for_cookies;
// The actual value of this doesn't actually matter, it just can't be empty,
// to avoid a DCHECK.
const net::IsolationInfo isolation_info =
@@ -171,10 +170,10 @@
const url::Origin origin;
process->GetStoragePartition()->GetNetworkContext()->CreateWebSocket(
- url, requested_protocols, site_for_cookies,
- net::StorageAccessApiStatus::kNone, isolation_info,
- std::move(additional_headers), ToOriginatingProcessId(process->GetID()),
- origin, network::mojom::ClientSecurityState::New(),
+ url, requested_protocols, net::StorageAccessApiStatus::kNone,
+ isolation_info, std::move(additional_headers),
+ ToOriginatingProcessId(process->GetID()), origin,
+ network::mojom::ClientSecurityState::New(),
network::mojom::kWebSocketOptionNone,
net::MutableNetworkTrafficAnnotationTag(TRAFFIC_ANNOTATION_FOR_TESTS),
std::move(handshake_client),
diff --git a/components/private_ai/websocket_client.cc b/components/private_ai/websocket_client.cc
index 27bbf8de0..34b7579e 100644
--- a/components/private_ai/websocket_client.cc
+++ b/components/private_ai/websocket_client.cc
@@ -159,8 +159,7 @@
"X-WebChannel-Content-Type", "application/x-protobuf"));
network_context_->CreateWebSocket(
- service_url_, requested_protocols, net::SiteForCookies(),
- net::StorageAccessApiStatus::kNone,
+ service_url_, requested_protocols, net::StorageAccessApiStatus::kNone,
net::IsolationInfo::CreateForInternalRequest(
url::Origin::Create(service_url_)),
std::move(additional_headers), network::OriginatingProcessId::browser(),
diff --git a/content/browser/websockets/websocket_connector_impl.cc b/content/browser/websockets/websocket_connector_impl.cc
index 4b2ef187..7438fed 100644
--- a/content/browser/websockets/websocket_connector_impl.cc
+++ b/content/browser/websockets/websocket_connector_impl.cc
@@ -76,7 +76,6 @@
void WebSocketConnectorImpl::Connect(
const GURL& url,
const std::vector<std::string>& requested_protocols,
- const net::SiteForCookies& site_for_cookies,
const std::optional<std::string>& user_agent,
net::StorageAccessApiStatus storage_access_api_status,
mojo::PendingRemote<network::mojom::WebSocketHandshakeClient>
@@ -96,11 +95,11 @@
GetContentClient()->browser()->CreateWebSocket(
frame,
base::BindOnce(ConnectCalledByContentBrowserClient, requested_protocols,
- site_for_cookies, storage_access_api_status,
- isolation_info_, frame_id_, origin_,
- client_security_state_->Clone(), options,
+ storage_access_api_status, isolation_info_, frame_id_,
+ origin_, client_security_state_->Clone(), options,
std::move(throttling_profile_id)),
- url, site_for_cookies, user_agent, std::move(handshake_client));
+ url, isolation_info_.site_for_cookies(), user_agent,
+ std::move(handshake_client));
return;
}
std::vector<network::mojom::HttpHeaderPtr> headers;
@@ -121,9 +120,8 @@
frame_id_);
storage_partition->GetNetworkContext()->CreateWebSocket(
- url, requested_protocols, site_for_cookies, storage_access_api_status,
- isolation_info_, std::move(headers),
- ToOriginatingProcessId(frame_id_.child_id), origin_,
+ url, requested_protocols, storage_access_api_status, isolation_info_,
+ std::move(headers), ToOriginatingProcessId(frame_id_.child_id), origin_,
client_security_state_->Clone(), options,
net::MutableNetworkTrafficAnnotationTag(kTrafficAnnotation),
std::move(handshake_client),
@@ -133,7 +131,6 @@
void WebSocketConnectorImpl::ConnectCalledByContentBrowserClient(
const std::vector<std::string>& requested_protocols,
- const net::SiteForCookies& site_for_cookies,
net::StorageAccessApiStatus storage_access_api_status,
const net::IsolationInfo& isolation_info,
const content::GlobalRenderFrameHostId& frame_id,
@@ -155,10 +152,9 @@
return;
}
process->GetStoragePartition()->GetNetworkContext()->CreateWebSocket(
- url, requested_protocols, site_for_cookies, storage_access_api_status,
- isolation_info, std::move(additional_headers),
- ToOriginatingProcessId(frame_id.child_id), origin,
- std::move(client_security_state), options,
+ url, requested_protocols, storage_access_api_status, isolation_info,
+ std::move(additional_headers), ToOriginatingProcessId(frame_id.child_id),
+ origin, std::move(client_security_state), options,
net::MutableNetworkTrafficAnnotationTag(kTrafficAnnotation),
std::move(handshake_client),
process->GetStoragePartition()->CreateURLLoaderNetworkObserverForFrame(
diff --git a/content/browser/websockets/websocket_connector_impl.h b/content/browser/websockets/websocket_connector_impl.h
index 162168e..8c13410 100644
--- a/content/browser/websockets/websocket_connector_impl.h
+++ b/content/browser/websockets/websocket_connector_impl.h
@@ -44,7 +44,6 @@
// WebSocketConnector implementation
void Connect(const GURL& url,
const std::vector<std::string>& requested_protocols,
- const net::SiteForCookies& site_for_cookies,
const std::optional<std::string>& user_agent,
net::StorageAccessApiStatus storage_access_api_status,
mojo::PendingRemote<network::mojom::WebSocketHandshakeClient>
@@ -55,7 +54,6 @@
private:
static void ConnectCalledByContentBrowserClient(
const std::vector<std::string>& requested_protocols,
- const net::SiteForCookies& site_for_cookies,
net::StorageAccessApiStatus storage_access_api_status,
const net::IsolationInfo& isolation_info,
const content::GlobalRenderFrameHostId& frame_id,
diff --git a/device/fido/cable/fido_tunnel_device.cc b/device/fido/cable/fido_tunnel_device.cc
index dac3d31..2acd6519 100644
--- a/device/fido/cable/fido_tunnel_device.cc
+++ b/device/fido/cable/fido_tunnel_device.cc
@@ -143,8 +143,8 @@
base::BindRepeating(&FidoTunnelDevice::OnTunnelData,
base::Unretained(this)));
network_context_factory.Run()->CreateWebSocket(
- url, {kCableWebSocketProtocol}, net::SiteForCookies(),
- net::StorageAccessApiStatus::kNone, net::IsolationInfo(),
+ url, {kCableWebSocketProtocol}, net::StorageAccessApiStatus::kNone,
+ net::IsolationInfo(),
/*additional_headers=*/{}, network::OriginatingProcessId::browser(),
url::Origin::Create(url), network::mojom::ClientSecurityState::New(),
network::mojom::kWebSocketOptionBlockAllCookies,
@@ -201,10 +201,10 @@
headers.emplace_back(
network::mojom::HttpHeader::New(kCableSignalConnectionHeader, "true"));
network_context_factory.Run()->CreateWebSocket(
- url, {kCableWebSocketProtocol}, net::SiteForCookies(),
- net::StorageAccessApiStatus::kNone, net::IsolationInfo(),
- std::move(headers), network::OriginatingProcessId::browser(),
- url::Origin::Create(url), network::mojom::ClientSecurityState::New(),
+ url, {kCableWebSocketProtocol}, net::StorageAccessApiStatus::kNone,
+ net::IsolationInfo(), std::move(headers),
+ network::OriginatingProcessId::browser(), url::Origin::Create(url),
+ network::mojom::ClientSecurityState::New(),
network::mojom::kWebSocketOptionBlockAllCookies,
net::MutableNetworkTrafficAnnotationTag(kTrafficAnnotation),
websocket_client_->BindNewHandshakeClientPipe(),
diff --git a/device/fido/cable/v2_authenticator.cc b/device/fido/cable/v2_authenticator.cc
index e7abce10..7dcf5af 100644
--- a/device/fido/cable/v2_authenticator.cc
+++ b/device/fido/cable/v2_authenticator.cc
@@ -379,7 +379,7 @@
DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
network_context_factory_.Run()->CreateWebSocket(
- target_, {device::kCableWebSocketProtocol}, net::SiteForCookies(),
+ target_, {device::kCableWebSocketProtocol},
net::StorageAccessApiStatus::kNone, net::IsolationInfo(),
/*additional_headers=*/{}, network::OriginatingProcessId::browser(),
url::Origin::Create(target_),
Regression Test / PoC
diff --git a/chrome/browser/net/websocket_browsertest.cc b/chrome/browser/net/websocket_browsertest.cc
index 3c2c81f..ed785fc 100644
--- a/chrome/browser/net/websocket_browsertest.cc
+++ b/chrome/browser/net/websocket_browsertest.cc
@@ -162,7 +162,6 @@
content::RenderProcessHost* const process = frame->GetProcess();
const std::vector<std::string> requested_protocols;
- const net::SiteForCookies site_for_cookies;
// The actual value of this doesn't actually matter, it just can't be empty,
// to avoid a DCHECK.
const net::IsolationInfo isolation_info =
@@ -171,10 +170,10 @@
const url::Origin origin;
process->GetStoragePartition()->GetNetworkContext()->CreateWebSocket(
- url, requested_protocols, site_for_cookies,
- net::StorageAccessApiStatus::kNone, isolation_info,
- std::move(additional_headers), ToOriginatingProcessId(process->GetID()),
- origin, network::mojom::ClientSecurityState::New(),
+ url, requested_protocols, net::StorageAccessApiStatus::kNone,
+ isolation_info, std::move(additional_headers),
+ ToOriginatingProcessId(process->GetID()), origin,
+ network::mojom::ClientSecurityState::New(),
network::mojom::kWebSocketOptionNone,
net::MutableNetworkTrafficAnnotationTag(TRAFFIC_ANNOTATION_FOR_TESTS),
std::move(handshake_client),
diff --git a/net/websockets/websocket_channel_test.cc b/net/websockets/websocket_channel_test.cc
index d339cf1..ca4f651 100644
--- a/net/websockets/websocket_channel_test.cc
+++ b/net/websockets/websocket_channel_test.cc
@@ -763,7 +763,6 @@
const GURL& new_socket_url,
const std::vector<std::string>& requested_subprotocols,
const url::Origin& new_origin,
- const SiteForCookies& new_site_for_cookies,
StorageAccessApiStatus new_storage_access_api_status,
const IsolationInfo& new_isolation_info,
const HttpRequestHeaders& additional_headers,
@@ -773,7 +772,6 @@
std::unique_ptr<WebSocketStream::ConnectDelegate> new_connect_delegate) {
socket_url = new_socket_url;
origin = new_origin;
- site_for_cookies = new_site_for_cookies;
storage_access_api_status = new_storage_access_api_status;
isolation_info = new_isolation_info;
url_request_context = new_url_request_context;
@@ -783,7 +781,6 @@
GURL socket_url;
url::Origin origin;
- SiteForCookies site_for_cookies;
StorageAccessApiStatus storage_access_api_status;
IsolationInfo isolation_info;
raw_ptr<URLRequestContext> url_request_context;
@@ -828,9 +825,9 @@
CreateEventInterface(), connect_data_.url_request_context.get());
channel_->SendAddChannelRequestForTesting(
connect_data_.socket_url, connect_data_.requested_subprotocols,
- connect_data_.origin, connect_data_.site_for_cookies,
- net::StorageAccessApiStatus::kNone, connect_data_.isolation_info,
- HttpRequestHeaders(), TRAFFIC_ANNOTATION_FOR_TESTS,
+ connect_data_.origin, net::StorageAccessApiStatus::kNone,
+ connect_data_.isolation_info, HttpRequestHeaders(),
+ TRAFFIC_ANNOTATION_FOR_TESTS,
base::BindOnce(&WebSocketStreamCreationCallbackArgumentSaver::Create,
base::Unretained(&connect_data_.argument_saver)));
}
@@ -866,8 +863,7 @@
ConnectData()
: url_request_context(CreateTestURLRequestContextBuilder()->Build()),
socket_url("ws://ws/"),
- origin(url::Origin::Create(GURL("http://ws"))),
- site_for_cookies(SiteForCookies::FromUrl(GURL("http://ws/"))) {
+ origin(url::Origin::Create(GURL("http://ws"))) {
this->isolation_info =
IsolationInfo::Create(IsolationInfo::RequestType::kOther, origin,
origin, SiteForCookies::FromOrigin(origin));
@@ -882,8 +878,6 @@
std::vector<std::string> requested_subprotocols;
// Origin of the request
url::Origin origin;
- // First party for cookies for the request.
- net::SiteForCookies site_for_cookies;
// Whether the calling context has opted into the Storage Access API.
StorageAccessApiStatus storage_access_api_status =
StorageAccessApiStatus::kNone;
@@ -1008,8 +1002,6 @@
TEST_F(WebSocketChannelTest, EverythingIsPassedToTheCreatorFunction) {
connect_data_.socket_url = GURL("ws://example.com/test");
connect_data_.origin = url::Origin::Create(GURL("http://example.com"));
- connect_data_.site_for_cookies =
- SiteForCookies::FromUrl(GURL("http://example.com/"));
connect_data_.isolation_info = net::IsolationInfo::Create(
IsolationInfo::RequestType::kOther, connect_data_.origin,
connect_data_.origin, SiteForCookies::FromOrigin(connect_data_.origin));
@@ -1025,8 +1017,6 @@
EXPECT_EQ(connect_data_.socket_url, actual.socket_url);
EXPECT_EQ(connect_data_.origin.Serialize(), actual.origin.Serialize());
- EXPECT_TRUE(
- connect_data_.site_for_cookies.IsEquivalent(actual.site_for_cookies));
EXPECT_EQ(connect_data_.storage_access_api_status,
actual.storage_access_api_status);
EXPECT_TRUE(
diff --git a/net/websockets/websocket_end_to_end_test.cc b/net/websockets/websocket_end_to_end_test.cc
index 3a56887..f95c0d7 100644
--- a/net/websockets/websocket_end_to_end_test.cc
+++ b/net/websockets/websocket_end_to_end_test.cc
@@ -419,8 +419,6 @@
InitialiseContext();
}
url::Origin origin = url::Origin::Create(GURL("http://localhost"));
- net::SiteForCookies site_for_cookies =
- net::SiteForCookies::FromOrigin(origin);
IsolationInfo isolation_info =
IsolationInfo::Create(IsolationInfo::RequestType::kOther, origin,
origin, SiteForCookies::FromOrigin(origin));
@@ -428,9 +426,8 @@
channel_ = std::make_unique<WebSocketChannel>(std::move(event_interface),
context_.get());
channel_->SendAddChannelRequest(
- GURL(socket_url), sub_protocols_, origin, site_for_cookies,
- StorageAccessApiStatus::kNone, isolation_info, HttpRequestHeaders(),
- TRAFFIC_ANNOTATION_FOR_TESTS);
+ GURL(socket_url), sub_protocols_, origin, StorageAccessApiStatus::kNone,
+ isolation_info, HttpRequestHeaders(), TRAFFIC_ANNOTATION_FOR_TESTS);
}
// Send the connect request to |socket_url| and wait for a response. Returns
diff --git a/net/websockets/websocket_stream_cookie_test.cc b/net/websockets/websocket_stream_cookie_test.cc
index 99b0eed..bcfc4d8 100644
--- a/net/websockets/websocket_stream_cookie_test.cc
+++ b/net/websockets/websocket_stream_cookie_test.cc
@@ -54,7 +54,6 @@
public:
void CreateAndConnect(const GURL& url,
const url::Origin& origin,
- const SiteForCookies& site_for_cookies,
const IsolationInfo& isolation_info,
const WebSocketExtraHeaders& cookie_header,
const std::string& response_body) {
@@ -63,7 +62,7 @@
url.GetPath(), url.GetHost(), origin, cookie_header,
/*send_additional_request_headers=*/{}, /*extra_headers=*/{}),
response_body);
- CreateAndConnectStream(url, NoSubProtocols(), origin, site_for_cookies,
+ CreateAndConnectStream(url, NoSubProtocols(), origin,
StorageAccessApiStatus::kNone, isolation_info,
HttpRequestHeaders(), nullptr);
}
@@ -148,7 +147,6 @@
const GURL url(GetParam().url);
const GURL cookie_url(GetParam().cookie_url);
const url::Origin origin = url::Origin::Create(GURL(GetParam().url));
- const SiteForCookies site_for_cookies = SiteForCookies::FromOrigin(origin);
const IsolationInfo isolation_info =
IsolationInfo::Create(IsolationInfo::RequestType::kOther, origin, origin,
SiteForCookies::FromOrigin(origin));
@@ -171,8 +169,8 @@
ASSERT_TRUE(is_called);
ASSERT_TRUE(set_cookie_result);
- CreateAndConnect(url, origin, site_for_cookies, isolation_info,
- GetParam().cookie_header, WebSocketStandardResponse(""));
+ CreateAndConnect(url, origin, isolation_info, GetParam().cookie_header,
+ WebSocketStandardResponse(""));
WaitUntilConnectDone();
EXPECT_FALSE(has_failed());
}
@@ -185,7 +183,6 @@
const GURL url(GetParam().url);
const GURL cookie_url(GetParam().cookie_url);
const url::Origin origin = url::Origin::Create(GURL(GetParam().url));
- const SiteForCookies site_for_cookies = SiteForCookies::FromOrigin(origin);
const IsolationInfo isolation_info =
IsolationInfo::Create(IsolationInfo::RequestType::kOther, origin, origin,
SiteForCookies::FromOrigin(origin));
@@ -203,8 +200,7 @@
CookieStore* store =
url_request_context_host_.GetURLRequestContext()->cookie_store();
- CreateAndConnect(url, origin, site_for_cookies, isolation_info,
- /*cookie_header=*/{}, response);
+ CreateAndConnect(url, origin, isolation_info, /*cookie_header=*/{}, response);
WaitUntilConnectDone();
EXPECT_FALSE(has_failed()) << failure_message();
diff --git a/net/websockets/websocket_stream_test.cc b/net/websockets/websocket_stream_test.cc
index 7915234..1ba83c3 100644
--- a/net/websockets/websocket_stream_test.cc
+++ b/net/websockets/websocket_stream_test.cc
@@ -112,10 +112,6 @@
return url::Origin::Create(GURL(kOrigin));
}
-static net::SiteForCookies SiteForCookies() {
- return net::SiteForCookies::FromOrigin(Origin());
-}
-
static IsolationInfo CreateIsolationInfo() {
url::Origin origin = Origin();
return IsolationInfo::Create(IsolationInfo::RequestType::kOther, origin,
@@ -196,8 +192,7 @@
WebSocketExtraHeadersToString(extra_response_headers)) +
additional_data_);
CreateAndConnectStream(socket_url, sub_protocols, Origin(),
- SiteForCookies(), storage_access_api_status,
- CreateIsolationInfo(),
+ storage_access_api_status, CreateIsolationInfo(),
WebSocketExtraHeadersToHttpRequestHeaders(
send_additional_request_headers),
std::move(timer_));
@@ -331,8 +326,7 @@
EXPECT_FALSE(request->is_pending());
CreateAndConnectStream(socket_url, sub_protocols, Origin(),
- SiteForCookies(), storage_access_api_status,
- CreateIsolationInfo(),
+ storage_access_api_status, CreateIsolationInfo(),
WebSocketExtraHeadersToHttpRequestHeaders(
send_additional_request_headers),
std::move(timer_));
@@ -360,8 +354,7 @@
extra_request_headers),
response_body);
CreateAndConnectStream(socket_url, sub_protocols, Origin(),
- SiteForCookies(), storage_access_api_status,
- CreateIsolationInfo(),
+ storage_access_api_status, CreateIsolationInfo(),
WebSocketExtraHeadersToHttpRequestHeaders(
send_additional_request_headers),
nullptr);
@@ -388,9 +381,8 @@
/*extra_headers=*/{}),
WebSocketStandardResponse(extra_response_headers));
CreateAndConnectStream(socket_url, sub_protocols, Origin(),
- SiteForCookies(), storage_access_api_status,
- CreateIsolationInfo(), HttpRequestHeaders(),
- nullptr);
+ storage_access_api_status, CreateIsolationInfo(),
+ HttpRequestHeaders(), nullptr);
}
// Like CreateAndConnectStandard(), but take raw mock data.
@@ -404,7 +396,7 @@
ASSERT_EQ(BASIC_HANDSHAKE_STREAM, stream_type_);
AddRawExpectations(std::move(socket_data));
- CreateAndConnectStream(GURL(url), sub_protocols, Origin(), SiteForCookies(),
+ CreateAndConnectStream(GURL(url), sub_protocols, Origin(),
storage_access_api_status, CreateIsolationInfo(),
additional_headers, std::move(timer_));
}
diff --git a/services/network/test/test_network_context.h b/services/network/test/test_network_context.h
index 278c242e..e561dcc 100644
--- a/services/network/test/test_network_context.h
+++ b/services/network/test/test_network_context.h
@@ -198,7 +198,6 @@
void CreateWebSocket(
const GURL& url,
const std::vector<std::string>& requested_protocols,
- const net::SiteForCookies& site_for_cookies,
net::StorageAccessApiStatus storage_access_api_status,
const net::IsolationInfo& isolation_info,
std::vector<mojom::HttpHeaderPtr> additional_headers,
diff --git a/services/network/websocket_factory_unittest.cc b/services/network/websocket_factory_unittest.cc
index 2732347..31e0099 100644
--- a/services/network/websocket_factory_unittest.cc
+++ b/services/network/websocket_factory_unittest.cc
@@ -83,9 +83,9 @@
// WebSocket objects are owned by the factory and will be deleted
// asynchronously.
factory_->CreateWebSocket(
- url, requested_protocols, net::SiteForCookies(),
- net::StorageAccessApiStatus::kNone, net::IsolationInfo(), {},
- network::OriginatingProcessId::browser(), url::Origin::Create(url),
+ url, requested_protocols, net::StorageAccessApiStatus::kNone,
+ net::IsolationInfo(), {}, network::OriginatingProcessId::browser(),
+ url::Origin::Create(url),
/*client_security_state=*/nullptr, /*options=*/0,
TRAFFIC_ANNOTATION_FOR_TESTS, std::move(handshake_client),
mojo::NullRemote(), mojo::NullRemote(), mojo::NullRemote(),
diff --git a/third_party/blink/renderer/modules/websockets/websocket_channel_impl_test.cc b/third_party/blink/renderer/modules/websockets/websocket_channel_impl_test.cc
index adb2bed7..0ff005f 100644
--- a/third_party/blink/renderer/modules/websockets/websocket_channel_impl_test.cc
+++ b/third_party/blink/renderer/modules/websockets/websocket_channel_impl_test.cc
@@ -198,19 +198,16 @@
ConnectArgs(
const KURL& url,
const Vector<String>& protocols,
- const net::SiteForCookies& site_for_cookies,
const String& user_agent,
mojo::PendingRemote<network::mojom::blink::WebSocketHandshakeClient>
handshake_client)
: url(url),
protocols(protocols),
- site_for_cookies(site_for_cookies),
user_agent(user_agent),
handshake_client(std::move(handshake_client)) {}
... (truncated)
Original Bug Report
WebSocketConnector.Connect() passes renderer-controlled site_for_cookies and storage_access_api_status unvalidated, enabling third-party cookie blocking bypass
WebSocketConnector.Connect() takes site_for_cookies and storage_access_api_status as renderer-supplied Mojo parameters. Neither is validated against browser-side state before being passed through to the network stack.
Data flow (zero validation at every hop): Renderer → WebSocketConnectorImpl::Connect() (websocket_connector_impl.cc:123) → NetworkContext::CreateWebSocket() (network_context.cc:1948) → websocket_stream.cc:143 url_request_->set_site_for_cookies(site_for_cookies) → websocket_stream.cc:148-154 sets kStorageAccessGrantEligible override
A compromised renderer spoofs site_for_cookies to the target origin and storage_access_api_status to kAccessViaAPI, then opens a cross-site WebSocket. The network stack treats the connection as first-party with a Storage Access grant, bypassing third-party cookie blocking. SameSite=None cookies from the target are included in the handshake.
SameSite=Lax/Strict cookies are NOT affected — the browser-controlled initiator_origin still reflects the true cross-site context.
The codebase already has a browser-controlled alternative ready: network_context.mojom:1409-1411: “Currently, |isolation_info|’s SiteForCookies field is ignored, but it will eventually replace the |site_for_cookies| parameter.”
Fix: use isolation_info.SiteForCookies() and derive storage_access_api_status from browser-side permission state.
Related: RestrictedCookieManager::ValidateAccessToCookiesAt() has the same trust issue — site_for_cookies mismatches are LOG(ERROR) only per crbug.com/402207912.
VERSION Chrome Version: 143.0.7499.192 stable Operating System: Linux (Kali 6.18.3) — bug is platform-independent
REPRODUCTION CASE See attached ws-cookie-bypass-poc.py.
Prerequisites:
- Chromium 143+ installed (or set CHROMIUM=/path/to/chrome)
- openssl CLI (for cert generation)
- Python 3 + websockets (pip install websockets)
- /etc/hosts: 127.0.0.1 example.test target-domain.example
- Run: python3 ws-cookie-bypass-poc.py
Output: Phase 1 (control, unmodified forward): 0 cookies — correct Phase 2 (spoofed SFC + SAA status): session_none=LEAKED_NONE — bypass
The PoC uses MojoInterfaceInterceptor to capture a real Connect() message, modifies site_for_cookies and storage_access_api_status in-place, and forwards through a new WebSocketConnector pipe. Requires –enable-blink-features=MojoJS to simulate compromised renderer Mojo access.
Reporter Credit: Richard Belisle