Low watch CVSS 5.3 chrome Policy Bypass 📄 Reporter bug report 🔧 Commit mapped

Overview

Low
Severity
5.3
CVSS
No
Exploited ITW
Fixed
Fix Status
ImpactInsufficient policy enforcement in DevTools
DescriptionInsufficient policy enforcement in DevTools
ComponentDevTools
Bug ClassPolicy Bypass
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.
Tracker470574526
Fix commita6d7acc60f1c (chromium/src) +2/-24
CWECWE-284
CVSS vectorCVSS:3.1/AV:N/AC:L/PR:N/UI:N/S:U/C:N/I:L/A:N
CISA KEVNot listed
CreditedJorian Woltjer, Mian, bug_blitzer
Reported2025-12-21 · bounty $1000 (per vendor advisory)
Fix landed2026-01-13 (public git)
Disclosed2026-03-10
Patch gap56 days — fix public in source before the advisory

Background

`InspectorResourceContentLoader`
the Blink-side DevTools helper (inspector_resource_content_loader.cc) that gathers a page’s document and stylesheet contents for the inspector by issuing its own internal resource requests through the document’s ResourceFetcher.
`mojom::FetchCacheMode::kOnlyIfCached`
a fetch cache mode that only satisfies a request from cache and never goes to the network; per the commit message it is only honoured together with network::mojom::RequestMode::kSameOrigin.
`network::mojom::RequestMode`
the CORS mode of a request; the default is kNoCors, and the patch now sets kSameOrigin on the DevTools reload request.
Service worker interception
when a document is controlled by a service worker, subresource and re-fetch requests from that document can be routed to the worker’s fetch handler, which may answer from its cache or re-issue the request to the network.

Root Cause Analysis

The vulnerable code is in the request-building path of InspectorResourceContentLoader (the hunk does not show the enclosing function’s name; it is the code that builds a ResourceRequest for document->Url() with kOnlyIfCached and hands it to document->Fetcher()). DevTools intends this re-fetch of the document to be cache-only, so that inspecting a page does not trigger new network traffic. Before the patch, the anonymous-namespace helper IsServiceWorkerPresent() checked whether the document was fetched via, or is controlled by, a service worker, and if so the code explicitly downgraded the request to mojom::FetchCacheMode::kDefault. The removed comment says this was a workaround: only-if-cached combined with the default kNoCors mode made the service worker throw when it repeated the request (crbug.com/823392, crbug.com/1098389). The result was that on any service-worker-controlled page, the DevTools-internal request lost its cache-only restriction and could reach the service worker and the network with ordinary cache semantics.

The fix deletes IsServiceWorkerPresent() and the kDefault downgrade, and always sets network::mojom::RequestMode::kSameOrigin so that kOnlyIfCached is valid and also works through the service worker. DevTools re-fetches therefore stay cache-only in every case.

Key insight
To get around a service worker incompatibility, the code dropped the cache-only policy on DevTools’ internal document re-fetch (kOnlyIfCached → kDefault) whenever a service worker was present. The fix keeps kOnlyIfCached unconditionally and pairs it with kSameOrigin, the mode it requires, so the workaround is no longer needed.

Attack Path

  1. Service-worker-controlled page An attacker-controlled site registers a service worker that controls its document, so IsServiceWorkerPresent() would return true.
  2. User opens DevTools The victim opens DevTools on that page, and InspectorResourceContentLoader builds an internal ResourceRequest for the document URL to collect its content.
  3. Cache-only policy dropped Before the patch, the service worker check reset the request’s cache mode to kDefault, so the request was no longer limited to the cache.
  4. Request leaves the cache The DevTools-initiated request could then be handled by the page’s service worker and/or go to the network, which lets the site observe or respond to inspector activity it should never see (inferred; the diff shows only the cache-mode change, not a specific observable effect).

Impact Assessment

The vendor rates this low (“Insufficient policy enforcement in DevTools”), and the advisory data provided does not report in-the-wild exploitation. The flaw is in the renderer-side inspector code, and it requires the user to open DevTools on a page controlled by a service worker. Ordinary web content can register the service worker, but nothing happens without the user’s DevTools interaction. The diff shows a DevTools-internal fetch escaping its intended cache-only policy. Any downstream effect, such as letting the page detect DevTools use or re-issuing a request with side effects, is (inferred).

Files Changed

  • third_party/blink/renderer/core/inspector/inspector_resource_content_loader.cc

Audit Directions

  • Conditional relaxation of internal-request policy
    Look for DevTools and other internal (RequestContextType::INTERNAL) loaders that change cache mode, credentials mode or request mode based on page-controlled state such as service worker presence, because the page can then choose which policy applies.
  • Incompatible fetch-option combinations
    Look for code that pairs kOnlyIfCached (or similar restrictive modes) with request modes the fetch spec says they need (kSameOrigin), and for workarounds that “fix” the incompatibility by loosening the restriction instead of correcting the mode.
  • Inspector-triggered traffic visible to the page
    Check other inspector agents that re-fetch page resources (stylesheets, scripts, source maps) for paths where a service worker or the network can see DevTools-originated requests.
From a6d7acc60f1c45f3fd24671a5283a1b4cb585948 Mon Sep 17 00:00:00 2001
From: Alex Rudenko <alexrudenko@chromium.org>
Date: Tue, 13 Jan 2026 00:39:51 -0800
Subject: [PATCH] Set resource request mode to kSameOrigin and only use the kOnlyIfCached mode

kOnlyIfCached only works if kSameOrigin is set [1]. The default request
mode is kNoCors[2]. While kNoCors + kOnlyIfCached seems to work for
getting cached resources, this combination does not work when getting
cached requests from the service worker. This CL changes the mode to
kSameOrigin which should allow the request to hit the service worker
cache.

The intention of this part of the resource content loader seems to be to
avoid hitting the network so the combination of kOnlyIfCached +
kSameOrigin would achieve that.

[1]:
[2]:

https: //source.chromium.org/chromium/chromium/src/+/main:third_party/blink/renderer/core/fetch/request.cc;l=649;drc=f3534bf8a057a7dcb2f239e855fc71b2faf6dddf
https: //source.chromium.org/chromium/chromium/src/+/main:third_party/blink/renderer/core/fetch/fetch_request_data.h;l=240;drc=4641a043d7914ce31515751c357158d46f4c937a
Fixed: 470574526
Change-Id: Ib92a9df516ee94a8bfbea57f5da17b5e21f9261a
Reviewed-on: https://chromium-review.googlesource.com/c/chromium/src/+/7409996
Commit-Queue: Danil Somsikov <dsv@chromium.org>
Auto-Submit: Alex Rudenko <alexrudenko@chromium.org>
Reviewed-by: Danil Somsikov <dsv@chromium.org>
Cr-Commit-Position: refs/heads/main@{#1568281}
---

diff --git a/third_party/blink/renderer/core/inspector/inspector_resource_content_loader.cc b/third_party/blink/renderer/core/inspector/inspector_resource_content_loader.cc
index 53911c8..7100f9e 100644
--- a/third_party/blink/renderer/core/inspector/inspector_resource_content_loader.cc
+++ b/third_party/blink/renderer/core/inspector/inspector_resource_content_loader.cc
@@ -32,22 +32,6 @@
   return !url.IsValid() || url.IsAboutBlankURL() || url.IsAboutSrcdocURL();
 }
 
-bool IsServiceWorkerPresent(Document* document) {
-  DocumentLoader* loader = document->Loader();
-  if (!loader)
-    return false;
-
-  if (loader->GetResponse().WasFetchedViaServiceWorker())
-    return true;
-
-  WebServiceWorkerNetworkProvider* provider =
-      loader->GetServiceWorkerNetworkProvider();
-  if (!provider)
-    return false;
-
-  return provider->ControllerServiceWorkerID() >= 0;
-}
-
 }  // namespace
 
 // NOTE: While this is a RawResourceClient, it loads both raw and css stylesheet
@@ -111,17 +95,11 @@
       resource_request = ResourceRequest(document->Url());
       resource_request.SetCacheMode(mojom::FetchCacheMode::kOnlyIfCached);
     }
+    // kOnlyIfCached requires kSameOrigin mode.
+    resource_request.SetMode(network::mojom::RequestMode::kSameOrigin);
     resource_request.SetRequestContext(
         mojom::blink::RequestContextType::INTERNAL);
 
-    if (IsServiceWorkerPresent(document)) {
-      // If the request is going to be intercepted by a service worker, then
-      // don't use only-if-cached. only-if-cached will cause the service worker
-      // to throw an exception if it repeats the request, which is a problem:
-      // crbug.com/823392 crbug.com/1098389
-      resource_request.SetCacheMode(mojom::FetchCacheMode::kDefault);
-    }
-
     ResourceFetcher* fetcher = document->Fetcher();
 
     const DOMWrapperWorld* world =
Loading diff…

Original Bug Report

reported by j....@gmail.com

DevTools Sources panel with Service Worker causes POST to be resent bypassing SameSite=Strict

Steps to reproduce the problem

  1. Visit https:// jtw.sh/ samesite-post-bypass-ce7139c3/target-sw.php so that cookies are set and a service worker is registered on the target website (jtw.sh)
  2. Visit the attacker’s site: https:// vps.jorianwoltjer.com/ samesite-post-bypass-ce7139c3/csrf-sw.html
  3. Right click and choose Inspect to open the DevTools, or press Ctrl+Shift+I or F12 on the keyboard
  4. In the background, a malicious POST request with all cookies has now been sent to the target. See it in the Sources tab viewing the target-sw.php file (see recording samesite-post-bypass-sw.mp4)

Source code for reproducing this locally is also provided in the attached samesite-post-bypass-sw.zip. Run with php -S 0.0.0.0:8000

Problem Description

In DevTools, the Sources panel shows source code of the current page and its associated files. It gets these resources from the cache, or re-fetches them if needed. This new request is initiated from the current site, which is a contradiction if the original navigation came from cross-site. It means that the request for the Sources tab will contain SameSite=Strict cookies even for POST requests.

Weirdly, this re-fetching process seems to only happen if there is a Service Worker registered for the target. Otherwise the error “Content unavailable. Resource was not cached” is shown in place of the content. The service worker doesn’t have to do anything, it just needs to be registered on the target. So any site that uses a service worker and relies on SameSite for Cross-Site Request Forgery (CSRF) protection is vulnerable.

From the attacker a simple top-level form submission is enough. While it shouldn’t work initially, after the DevTools are opened on the resulting page, the request is resent and SameSite=Strict cookies are now sent with the body payload from the attacker.

<form action="..." method="post">
  <input type="text" name="name" value="value">
</form>
<script>
  document.forms[0].submit();
</script>

This requires some unusual user interaction (F12), but to me still felt unexpected. It should not be possible for an attacker to prepare a request so that it contains their malicious POST body with the user’s SameSite=Strict cookies without it being clearly visible.
The request that the DevTools sources tab sends should inherit the SameSite-ness (initiator?) of the original request so that it is as identical as possible, and doesn’t increase the risk of CSRF.

Summary

DevTools Sources panel with Service Worker causes POST to be resent bypassing SameSite=Strict

Custom Questions

Reporter credit:

Jorian Woltjer, Mian, bug_blitzer

Additional Data

Category: Security
Chrome Channel: Not sure
Regression: No \

View on issue tracker