High watch CVSS 8.3 chrome Input Validation 📄 Reporter bug report 🔧 Commit mapped

Overview

High
Severity
8.3
CVSS
No
Exploited ITW
Fixed
Fix Status
ImpactInsufficient data validation in InterestGroups
DescriptionInsufficient data validation in InterestGroups
ComponentInterestGroups
Sub-componentProtected Audiences
Bug ClassInput Validation
Action tierwatch — logic/policy bug — security-relevant, no memory-unsafety
Computed by PatchGap's deterministic exploitability ruler from the fixing commit. patch-now = RCE-class & attacker-reachable in a default config, or exploited in the wild; watch = real security signal below that bar; noise = no security relevance.
Tracker498720754
Fix commit063cf5bbbc1c (chromium/src) +96/-2
CWECWE-20 (Improper input validation)
CVSS vectorCVSS:3.1/AV:N/AC:H/PR:N/UI:R/S:C/C:H/I:H/A:H
CISA KEVNot listed
CreditedGoogle
Reported2026-04-01 (per vendor advisory)
Fix landed2026-04-02 (public git)
Disclosed2026-05-05
Patch gap33 days — fix public in source before the advisory

Background

Protected Audiences / FLEDGE
Chrome’s on-device ad-auction API in which untrusted “worklet” JavaScript runs in a sandboxed process and issues a constrained set of network fetches (bidding/scoring scripts, WASM, and trusted-signals).
`AuctionURLLoaderFactoryProxy`
the browser-process proxy that intercepts worklet-originated network requests and enforces which URLs, methods, and bodies the worklet is permitted to send.
`network::ResourceRequestBody` / `DataElement`
the representation of a request upload body as a list of elements, each tagged (network::DataElement::Tag) as kBytes, a file, a data pipe, etc.
Trusted-signals KVv2 POST
the one legitimate worklet code path that issues a POST with a body (a single in-memory byte blob), as opposed to the normal GET fetches.

Root Cause Analysis

The request-vetting code in auction_url_loader_factory_proxy.cc previously validated the URL/Accept header but did not constrain the HTTP method or the shape of the upload body for worklet-originated requests.

The patch adds a block that, for any method other than kGetMethod, sets is_request_allowed = false unless the method is exactly kPostMethod and is_trusted_signals_request, and even then only permits a request_body consisting of a single element whose type is network::DataElement::Tag::kBytes. The invariant being restored is that a sandboxed worklet may only emit the request shapes its own code can legitimately produce — GETs, or a trusted-signals POST carrying one in-memory byte blob — and must never drive arbitrary methods (PUT, HEAD, etc.) or bodies containing files or multiple/zero elements. As the inline comment states, “the most important thing here is to disallow attempts to upload files,” i.e., preventing a compromised or malicious worklet from exfiltrating local files or crashing on unexpected body shapes. The second hunk’s added comment (“Note that GET request bodies are ignored”) clarifies that GET bodies are dropped when the request is rebuilt. The change strictly tightens validation; the regression tests assert that PUT/HEAD, multi-element bodies, empty bodies, and file-range bodies are all rejected while a single-byte POST is allowed.

Key insight
The core mistake was that the auction proxy failed to validate the request method and upload-body composition, letting a worklet submit disallowed methods or file/multi-element bodies; the fix adds an explicit allowlist that permits only GET and single-kBytes-element trusted-signals POSTs.

Attack Path

  1. Run worklet code A site enters an ad auction so attacker-controlled worklet JavaScript executes in the Protected Audiences sandbox.
  2. Forge a request shape The worklet (or a compromised worklet process) issues a network request with a non-GET method or a POST whose request_body contains a file element, multiple elements, or is empty — shapes the legitimate auction code never produces.
  3. Reach the proxy unvalidated (pre-patch) AuctionURLLoaderFactoryProxy forwarded the request because it did not check the method or body composition, so the body (e.g., a file upload) was passed through to the network service.
  4. Abuse the upload This could allow exfiltration of local files via AppendFileRange-style bodies or trigger mishandling of unexpected body shapes; the diff demonstrates the rejection logic and notes crash-avoidance for empty/multi-element bodies, so any concrete file-read or memory-safety outcome beyond that is (inferred).

Impact Assessment

A malicious or compromised Protected Audiences worklet could cause the browser-process auction proxy to forward disallowed HTTP methods or upload bodies — including file uploads — that the auction code should never emit, matching the advisory’s “Insufficient data validation in InterestGroups,” severity high. The practical gain is sending attacker-chosen request shapes (notably file-backed upload bodies) through a privileged, same-origin-trusted network path; escalation to concrete local-file exfiltration or a crash primitive is (inferred) from the comments and tests rather than demonstrated end-to-end. The advisory does not report in-the-wild exploitation.

Changed Functions

FunctionChangeNotes
if
content/browser/interest_group/auction_url_loader_factory_proxy.cc
modified
if
content/browser/interest_group/auction_url_loader_factory_proxy_unittest.cc
modified

Files Changed

  • content/browser/interest_group/auction_url_loader_factory_proxy.cc
  • content/browser/interest_group/auction_url_loader_factory_proxy_unittest.cc

Audit Directions

  • Request shape beyond URL
    In every per-feature URLLoaderFactory proxy, verify that method and request_body element type/count are validated, not just the URL and Accept header, especially for sandboxed/untrusted request sources.
  • File-element uploads
    Grep for request bodies passed through without checking DataElement::Tag, and ensure kFile/data-pipe elements are rejected on paths that should only carry in-memory bytes.
  • Allowlist completeness for methods
    Where only GET is expected, confirm non-GET methods (PUT, HEAD, DELETE) are explicitly rejected rather than implicitly forwarded, and that empty/multi-element bodies cannot crash the handler.
From 063cf5bbbc1c238c6189cb89ef3809beae810e62 Mon Sep 17 00:00:00 2001
From: Matt Menke <mmenke@chromium.org>
Date: Thu, 02 Apr 2026 13:03:05 -0700
Subject: [PATCH] [Protected Audiences] Add some checks around request upload bodies.

And methods as well.

Fixed: 498720754
Change-Id: I3339444bdc1da51f7b308e3e5e059106c2fb751f
Reviewed-on: https://chromium-review.googlesource.com/c/chromium/src/+/7726850
Reviewed-by: Maks Orlovich <morlovich@chromium.org>
Commit-Queue: mmenke <mmenke@chromium.org>
Cr-Commit-Position: refs/heads/main@{#1609453}
---

diff --git a/content/browser/interest_group/auction_url_loader_factory_proxy.cc b/content/browser/interest_group/auction_url_loader_factory_proxy.cc
index 8c6aa171..01e6f94 100644
--- a/content/browser/interest_group/auction_url_loader_factory_proxy.cc
+++ b/content/browser/interest_group/auction_url_loader_factory_proxy.cc
@@ -38,7 +38,9 @@
 #include "net/cookies/site_for_cookies.h"
 #include "net/http/http_request_headers.h"
 #include "net/traffic_annotation/network_traffic_annotation.h"
+#include "services/network/public/cpp/data_element.h"
 #include "services/network/public/cpp/resource_request.h"
+#include "services/network/public/cpp/resource_request_body.h"
 #include "services/network/public/mojom/cookie_manager.mojom-shared.h"
 #include "services/network/public/mojom/url_loader_factory.mojom.h"
 #include "third_party/blink/public/common/features.h"
@@ -179,6 +181,26 @@
     }
   }
 
+  if (url_request.method != net::HttpRequestHeaders::kGetMethod) {
+    // If the request is not a GET and not a trusted signals POST, disallow the
+    // request.
+    if (url_request.method != net::HttpRequestHeaders::kPostMethod ||
+        !is_trusted_signals_request) {
+      is_request_allowed = false;
+    } else {
+      // For trusted signals POSTs only allow request bodies that contain a
+      // single byte element, since that's all the auction worklet code should
+      // produce. The most important thing here is to disallow attempts to
+      // upload files.
+      if (url_request.request_body &&
+          (url_request.request_body->elements()->size() != 1u ||
+           url_request.request_body->elements()->front().type() !=
+               network::DataElement::Tag::kBytes)) {
+        is_request_allowed = false;
+      }
+    }
+  }
+
   if (!is_request_allowed) {
     // Debugging for https://crbug.com/1448458
     SCOPED_CRASH_KEY_STRING32("fledge", "req-accept", accept_header);
@@ -218,6 +240,7 @@
 
   if (url_request.method == net::HttpRequestHeaders::kPostMethod) {
     new_request.method = std::move(url_request.method);
+    // Note that GET request bodies are ignored.
     new_request.request_body = std::move(url_request.request_body);
     std::optional<std::string> content_type =
         url_request.headers.GetHeader(net::HttpRequestHeaders::kContentType);
diff --git a/content/browser/interest_group/auction_url_loader_factory_proxy_unittest.cc b/content/browser/interest_group/auction_url_loader_factory_proxy_unittest.cc
index f401ce8b..fffaa6c 100644
--- a/content/browser/interest_group/auction_url_loader_factory_proxy_unittest.cc
+++ b/content/browser/interest_group/auction_url_loader_factory_proxy_unittest.cc
@@ -8,6 +8,7 @@
 
 #include <vector>
 
+#include "base/containers/span.h"
 #include "base/functional/bind.h"
 #include "base/functional/callback_helpers.h"
 #include "base/memory/ref_counted.h"
@@ -31,7 +32,9 @@
 #include "net/http/http_request_headers.h"
 #include "net/traffic_annotation/network_traffic_annotation.h"
 #include "net/traffic_annotation/network_traffic_annotation_test_helper.h"
+#include "services/network/public/cpp/data_element.h"
 #include "services/network/public/cpp/resource_request.h"
+#include "services/network/public/cpp/resource_request_body.h"
 #include "services/network/public/mojom/client_security_state.mojom.h"
 #include "services/network/public/mojom/ip_address_space.mojom.h"
 #include "services/network/public/mojom/url_loader_factory.mojom.h"
@@ -351,7 +354,26 @@
     // Check method, body and content-type for POST requests.
     if (request.method == net::HttpRequestHeaders::kPostMethod) {
       EXPECT_EQ(observed_request.method, net::HttpRequestHeaders::kPostMethod);
-      EXPECT_EQ(request.request_body, observed_request.request_body);
+      ASSERT_EQ(!!request.request_body, !!observed_request.request_body);
+      // If there's a request body, both bodies should both have a single
+      // matching element of type kBytes.
+      if (request.request_body) {
+        const auto& request_elements = *request.request_body->elements();
+        const auto& observed_elements =
+            *observed_request.request_body->elements();
+        ASSERT_EQ(request_elements.size(), 1u);
+        ASSERT_EQ(observed_elements.size(), 1u);
+        ASSERT_EQ(request_elements.front().type(),
+                  network::DataElement::Tag::kBytes);
+        ASSERT_EQ(observed_elements.front().type(),
+                  network::DataElement::Tag::kBytes);
+        EXPECT_EQ(request_elements.front()
+                      .As<network::DataElementBytes>()
+                      .AsStringPiece(),
+                  observed_elements.front()
+                      .As<network::DataElementBytes>()
+                      .AsStringPiece());
+      }
       if (request.headers.GetHeader(net::HttpRequestHeaders::kContentType)
               .has_value()) {
         EXPECT_EQ(
@@ -574,6 +596,24 @@
                    ExpectedResponse::kReject);
     TryMakeRequest("https://host.test/", std::nullopt,
                    ExpectedResponse::kReject);
+
+    // Methods other than GET should be rejected for non-signals URLs. Method
+    // logic for signals URLs is checked further down.
+    network::ResourceRequest request;
+    request.url = GURL(kScriptUrl);
+    request.headers.SetHeader(net::HttpRequestHeaders::kAccept,
+                              kAcceptJavascript);
+    request.method = net::HttpRequestHeaders::kPostMethod;
+    TryMakeRequest(request, ExpectedResponse::kReject);
+    request.method = net::HttpRequestHeaders::kHeadMethod;
+    TryMakeRequest(request, ExpectedResponse::kReject);
+
+    request.url = GURL(kWasmUrl);
+    request.headers.SetHeader(net::HttpRequestHeaders::kAccept, kAcceptWasm);
+    request.method = net::HttpRequestHeaders::kPostMethod;
+    TryMakeRequest(request, ExpectedResponse::kReject);
+    request.method = net::HttpRequestHeaders::kHeadMethod;
+    TryMakeRequest(request, ExpectedResponse::kReject);
   }
 }
 
@@ -748,9 +788,11 @@
         "https://host.test/trusted_signals?hostname=top.test&keys=%23%26%3D",
         kAcceptJson, ExpectedResponse::kAllow);
 
-    // Valid Trusted Signals KVv2 POST request
+    // Valid Trusted Signals KVv2 POST request.
     network::ResourceRequest request;
     request.method = net::HttpRequestHeaders::kPostMethod;
+    request.request_body = network::ResourceRequestBody::CreateFromCopyOfBytes(
+        base::as_byte_span("1234"));
     request.url = GURL(kTrustedSignalsBaseUrl);
     request.headers.SetHeader(net::HttpRequestHeaders::kAccept,
                               kAcceptAdAuctionTrustedSignals);
@@ -758,6 +800,35 @@
                               kAdAuctionTrustedSignalsContentType);
     TryMakeRequest(request, ExpectedResponse::kAllow);
 
+    // Methods other than GET and POST are rejected.
+    request.method = net::HttpRequestHeaders::kPutMethod;
+    TryMakeRequest(request, ExpectedResponse::kReject);
+    request.method = net::HttpRequestHeaders::kHeadMethod;
+    TryMakeRequest(request, ExpectedResponse::kReject);
+    // Restore method.
+    request.method = net::HttpRequestHeaders::kPostMethod;
+
+    // Uploads with multiple elements should be rejected. This isn't too
+    // important, but need to make sure they don't crash.
+    request.request_body->AppendCopyOfBytes(base::as_byte_span("5678"));
+    TryMakeRequest(request, ExpectedResponse::kReject);
+
+    // Empty uploads should be rejected. This case is mostly to make sure they
+    // don't result in crashes.
+    request.request_body = base::MakeRefCounted<network::ResourceRequestBody>();
+    TryMakeRequest(request, ExpectedResponse::kReject);
+
+    // Uploads of files should be rejected. There's no code wired up to actually
+    // read the file, so don't need a path to a real file.
+    request.request_body->AppendFileRange(
+        base::FilePath(), /*offset=*/0u, /*length=*/2u,
+        /*expected_modification_time=*/base::Time());
+    TryMakeRequest(request, ExpectedResponse::kReject);
+
+    // Restore the upload body.
+    request.request_body = network::ResourceRequestBody::CreateFromCopyOfBytes(
+        base::as_byte_span("1234"));
+
     // Invalid Trusted Signals KVv2 POST request with mismatched base url.
     request.url = GURL("https://host.test/trusted_signals?");
     TryMakeRequest(request, ExpectedResponse::kReject);
Loading diff…

Regression Test / PoC

shipped with the fix
diff --git a/content/browser/interest_group/auction_url_loader_factory_proxy_unittest.cc b/content/browser/interest_group/auction_url_loader_factory_proxy_unittest.cc
index f401ce8b..fffaa6c 100644
--- a/content/browser/interest_group/auction_url_loader_factory_proxy_unittest.cc
+++ b/content/browser/interest_group/auction_url_loader_factory_proxy_unittest.cc
@@ -8,6 +8,7 @@
 
 #include <vector>
 
+#include "base/containers/span.h"
 #include "base/functional/bind.h"
 #include "base/functional/callback_helpers.h"
 #include "base/memory/ref_counted.h"
@@ -31,7 +32,9 @@
 #include "net/http/http_request_headers.h"
 #include "net/traffic_annotation/network_traffic_annotation.h"
 #include "net/traffic_annotation/network_traffic_annotation_test_helper.h"
+#include "services/network/public/cpp/data_element.h"
 #include "services/network/public/cpp/resource_request.h"
+#include "services/network/public/cpp/resource_request_body.h"
 #include "services/network/public/mojom/client_security_state.mojom.h"
 #include "services/network/public/mojom/ip_address_space.mojom.h"
 #include "services/network/public/mojom/url_loader_factory.mojom.h"
@@ -351,7 +354,26 @@
     // Check method, body and content-type for POST requests.
     if (request.method == net::HttpRequestHeaders::kPostMethod) {
       EXPECT_EQ(observed_request.method, net::HttpRequestHeaders::kPostMethod);
-      EXPECT_EQ(request.request_body, observed_request.request_body);
+      ASSERT_EQ(!!request.request_body, !!observed_request.request_body);
+      // If there's a request body, both bodies should both have a single
+      // matching element of type kBytes.
+      if (request.request_body) {
+        const auto& request_elements = *request.request_body->elements();
+        const auto& observed_elements =
+            *observed_request.request_body->elements();
+        ASSERT_EQ(request_elements.size(), 1u);
+        ASSERT_EQ(observed_elements.size(), 1u);
+        ASSERT_EQ(request_elements.front().type(),
+                  network::DataElement::Tag::kBytes);
+        ASSERT_EQ(observed_elements.front().type(),
+                  network::DataElement::Tag::kBytes);
+        EXPECT_EQ(request_elements.front()
+                      .As<network::DataElementBytes>()
+                      .AsStringPiece(),
+                  observed_elements.front()
+                      .As<network::DataElementBytes>()
+                      .AsStringPiece());
+      }
       if (request.headers.GetHeader(net::HttpRequestHeaders::kContentType)
               .has_value()) {
         EXPECT_EQ(
@@ -574,6 +596,24 @@
                    ExpectedResponse::kReject);
     TryMakeRequest("https://host.test/", std::nullopt,
                    ExpectedResponse::kReject);
+
+    // Methods other than GET should be rejected for non-signals URLs. Method
+    // logic for signals URLs is checked further down.
+    network::ResourceRequest request;
+    request.url = GURL(kScriptUrl);
+    request.headers.SetHeader(net::HttpRequestHeaders::kAccept,
+                              kAcceptJavascript);
+    request.method = net::HttpRequestHeaders::kPostMethod;
+    TryMakeRequest(request, ExpectedResponse::kReject);
+    request.method = net::HttpRequestHeaders::kHeadMethod;
+    TryMakeRequest(request, ExpectedResponse::kReject);
+
+    request.url = GURL(kWasmUrl);
+    request.headers.SetHeader(net::HttpRequestHeaders::kAccept, kAcceptWasm);
+    request.method = net::HttpRequestHeaders::kPostMethod;
+    TryMakeRequest(request, ExpectedResponse::kReject);
+    request.method = net::HttpRequestHeaders::kHeadMethod;
+    TryMakeRequest(request, ExpectedResponse::kReject);
   }
 }
 
@@ -748,9 +788,11 @@
         "https://host.test/trusted_signals?hostname=top.test&keys=%23%26%3D",
         kAcceptJson, ExpectedResponse::kAllow);
 
-    // Valid Trusted Signals KVv2 POST request
+    // Valid Trusted Signals KVv2 POST request.
     network::ResourceRequest request;
     request.method = net::HttpRequestHeaders::kPostMethod;
+    request.request_body = network::ResourceRequestBody::CreateFromCopyOfBytes(
+        base::as_byte_span("1234"));
     request.url = GURL(kTrustedSignalsBaseUrl);
     request.headers.SetHeader(net::HttpRequestHeaders::kAccept,
                               kAcceptAdAuctionTrustedSignals);
@@ -758,6 +800,35 @@
                               kAdAuctionTrustedSignalsContentType);
     TryMakeRequest(request, ExpectedResponse::kAllow);
 
+    // Methods other than GET and POST are rejected.
+    request.method = net::HttpRequestHeaders::kPutMethod;
+    TryMakeRequest(request, ExpectedResponse::kReject);
+    request.method = net::HttpRequestHeaders::kHeadMethod;
+    TryMakeRequest(request, ExpectedResponse::kReject);
+    // Restore method.
+    request.method = net::HttpRequestHeaders::kPostMethod;
+
+    // Uploads with multiple elements should be rejected. This isn't too
+    // important, but need to make sure they don't crash.
+    request.request_body->AppendCopyOfBytes(base::as_byte_span("5678"));
+    TryMakeRequest(request, ExpectedResponse::kReject);
+
+    // Empty uploads should be rejected. This case is mostly to make sure they
+    // don't result in crashes.
+    request.request_body = base::MakeRefCounted<network::ResourceRequestBody>();
+    TryMakeRequest(request, ExpectedResponse::kReject);
+
+    // Uploads of files should be rejected. There's no code wired up to actually
+    // read the file, so don't need a path to a real file.
+    request.request_body->AppendFileRange(
+        base::FilePath(), /*offset=*/0u, /*length=*/2u,
+        /*expected_modification_time=*/base::Time());
+    TryMakeRequest(request, ExpectedResponse::kReject);
+
+    // Restore the upload body.
+    request.request_body = network::ResourceRequestBody::CreateFromCopyOfBytes(
+        base::as_byte_span("1234"));
+
     // Invalid Trusted Signals KVv2 POST request with mismatched base url.
     request.url = GURL("https://host.test/trusted_signals?");
     TryMakeRequest(request, ExpectedResponse::kReject);
Loading diff…

Original Bug Report

reported by vm...@google.com

Arbitrary File Read Sandbox Escape in AuctionURLLoaderFactoryProxy

Project Fortify, an experimental security project, has identified the following potential security issue. If you’re a feature owner CC-ed on this bug, please do your best to review these reports without the security team.

Overview: A compromised FLEDGE auction worklet can potentially read arbitrary local files, escaping the sandbox. The AuctionURLLoaderFactoryProxy blindly forwards attacker-controlled POST request bodies to a browser-privileged URLLoaderFactory. By embedding a DataElementFile in the request body, the worklet can trick the browser into opening and uploading local files to an attacker-controlled server.

Affected files:

  • content/browser/interest_group/auction_url_loader_factory_proxy.cc
  • content/browser/interest_group/ad_auction_service_impl.cc
  • content/browser/network_context_client_base_impl.cc

Estimated timestamp from git blame: 2024-12-10

Vulnerability Description

A confused deputy vulnerability exists in the browser-side AuctionURLLoaderFactoryProxy that can potentially allow a compromised sandboxed auction worklet (e.g., compromised via a V8 bug) to achieve an arbitrary file read on the user’s system.

The AuctionURLLoaderFactoryProxy::CreateLoaderAndStart function mediates network requests from the worklet. To allow legitimate requests, it enforces a URL allowlist (e.g., script_url_) and expects certain Accept headers. However, if the worklet issues a POST request, the proxy copies the request_body directly into the outgoing new_request without any sanitization:

// content/browser/interest_group/auction_url_loader_factory_proxy.cc:219
if (url_request.method == net::HttpRequestHeaders::kPostMethod) {
  new_request.method = std::move(url_request.method);
  new_request.request_body = std::move(url_request.request_body); // VULNERABILITY: Unsanitized copy
  // ...
}

This request_body can maliciously contain a DataElementFile referencing any local file path on the disk (e.g., /home/user/.ssh/id_rsa).

The proxy forwards this new_request using its underlying trusted URLLoaderFactory. This factory is created in AdAuctionServiceImpl::CreateUnderlyingTrustedURLLoaderFactory using url_loader_factory::TerminalParams::ForBrowserProcess(...). Because it’s designated as a browser-process factory, its process_id is set to network::OriginatingProcessId::browser().

When the Network Service receives this request, it attempts to read the file specified in the DataElementFile for the POST upload. It sends an OnFileUploadRequested IPC back to the browser process. In NetworkContextClientBase::OnFileUploadRequested, the browser checks if the process has read access:

// content/browser/network_context_client_base_impl.cc:41
if (!process_id.is_browser() &&
    !cpsp->CanReadFile(ToChildProcessId(process_id.renderer_process_id()), file_path)) {
  // Access check performed here
}

Since process_id.is_browser() is true, the ChildProcessSecurityPolicy::CanReadFile check is completely bypassed. The browser opens the requested local file and returns the handle to the Network Service, which then uploads the file’s contents to the attacker’s server.

Potential Reproduction Steps

Note: These are suggested/potential steps based on code analysis; our tooling agent does not currently have the ability to run code to verify a working proof-of-concept.

  1. An attacker compromises an auction worklet process (e.g., kServiceWithJit sandbox).
  2. The attacker invokes CreateLoaderAndStart on the URLLoaderFactory remote bound to the AuctionURLLoaderFactoryProxy in the browser process.
  3. The attacker crafts a ResourceRequest where:
    • url is set to the worklet’s own allowed script_url.
    • method is POST.
    • headers includes Accept: application/javascript to pass proxy validation.
    • request_body contains a DataElementFile pointing to a target local file.
  4. The proxy forwards the request via its browser-privileged URLLoaderFactory.
  5. The Network Service asks the browser to open the file. The browser complies because the request is marked as originating from the browser process.
  6. The file is uploaded as the POST payload to the attacker’s script URL endpoint.

Suggested Fix

In AuctionURLLoaderFactoryProxy::CreateLoaderAndStart, explicitly sanitize the request_body to ensure it does not contain any DataElementFile or DataElementDataPipe elements that the worklet should not have access to. If the request_body contains a file element, the proxy should immediately reject the request by calling receiver_.ReportBadMessage("Invalid request body");.

Evaluated with Chrome root at commit: ff3d2b74fa39431785bd60e51463b08fcc71ee33


Results so far have been promising, but there can be wrong deductions. If this proves to be a false positive, please close as WAI; data from false positives will be used to improve accuracy over time. And please feel free to reach out to me directly if you have concerns or feedback on the project.

View on issue tracker