Medium watch CVSS 6.5 chrome Authorization Error 📄 Reporter bug report 🔧 Commit mapped

Overview

Medium
Severity
6.5
CVSS
No
Exploited ITW
Fixed
Fix Status
ImpactIncorrect authorization in SiteSettings
DescriptionIncorrect authorization in SiteSettings
ComponentSiteSettings
Bug ClassAuthorization Error
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.
Tracker514489238
Fix commitffb88da0146b (chromium/src) +42/-2
CWECWE-863
CVSS vectorCVSS:3.1/AV:N/AC:L/PR:N/UI:R/S:U/C:N/I:H/A:N
CISA KEVNot listed
CreditedGoogle
Reported2026-05-19 (per vendor advisory)
Fix landed2026-07-27 (public git)
Disclosed2026-09-01
Patch gap36 days — fix public in source before the advisory

Background

`ChromeContentBrowserClient::IsJitDisabledForSite()`
the embedder hook that decides whether a renderer hosting a given site URL should launch with V8 JIT disabled, based on the JAVASCRIPT_JIT content setting.
`JAVASCRIPT_JIT` content setting
a per-site/default policy that lets users (or enterprise policy) turn off V8’s just-in-time compiler to reduce attack surface for a site.
Site URL / `SiteInfo::Create()`
the URL identifying a renderer’s agent cluster/site, which is not always http(s): it can be blob:null/<guid>, filesystem:, or data: (e.g. for a sandboxed iframe that navigates to a Blob).
`ChildProcessSecurityPolicy::IsWebSafeScheme()`
the canonical check for schemes that can host web-controllable content (http, https, blob, filesystem, data, …), as opposed to privileged schemes like chrome://.

Root Cause Analysis

IsJitDisabledForSite() consulted the JAVASCRIPT_JIT content setting but then gated the disable decision on if (!site_url.SchemeIsHTTPOrHTTPS()) return false;, meaning any non-http(s) site URL caused the function to report JIT as enabled regardless of the configured setting. However, SiteInfo::Create() can supply web-controllable but non-http(s) agent-cluster URLs — notably blob:null/<guid> for a sandboxed iframe that navigates to a Blob, plus filesystem: and data: — so a frame running attacker script could keep V8 JIT enabled even though the user or policy had blocked JIT, violating the invariant that the JIT policy applies to all web-controlled content.

The fix replaces the scheme test with if (!policy->IsWebSafeScheme(site_url.GetScheme())) return false;, using ChildProcessSecurityPolicy::IsWebSafeScheme(), so that blob:, filesystem:, and data: site URLs now honor the content setting while genuinely non-web schemes (chrome://, chrome-untrusted://, file://) still keep JIT enabled. This aligns the JIT gating with the existing AreV8OptimizationsEnabledForSite() logic. The shipped tests confirm that with the default set to CONTENT_SETTING_BLOCK, blob:, filesystem:, and data: URLs return true (JIT disabled) while WebUI/file: schemes return false.

Key insight
The single mistake was gating the JIT-disable decision on SchemeIsHTTPOrHTTPS() instead of the broader IsWebSafeScheme(), which silently exempted web-controllable non-http(s) site URLs (blob:, filesystem:, data:) from a JIT-off policy; the fix widens the gate to all web-safe schemes so the content setting is enforced wherever web content can run.

Attack Path

  1. User/policy disables JIT The JAVASCRIPT_JIT content setting is set to block (per-site or default), intending V8 JIT to be off for web content.
  2. Load a non-http(s) web origin Attacker content causes a renderer whose site URL is a web-safe non-http(s) scheme, e.g. a sandboxed iframe that navigates to a Blob so SiteInfo::Create() yields blob:null/<guid> (or filesystem:/data:).
  3. Bypass the JIT policy Pre-patch, IsJitDisabledForSite() hits !SchemeIsHTTPOrHTTPS() and returns false, so the renderer hosting that frame launches with V8 JIT still enabled despite the block setting.
  4. Regain JIT attack surface The attacker’s script runs with the JIT compiler available, restoring the broader V8 exploitation surface the setting was meant to remove, which can be combined with a separate V8/JIT bug (inferred).

Impact Assessment

An attacker gains execution of web-controlled JavaScript with V8 JIT enabled in a renderer that the user’s or enterprise policy’s JAVASCRIPT_JIT block setting should have covered, defeating the JIT-hardening mitigation for blob:, filesystem:, and data: origins. The advisory classifies this as incorrect authorization in SiteSettings; it does not report exploitation in the wild, and leveraging the restored JIT surface into actual memory corruption or code execution requires a separate vulnerability and is (inferred). The precondition is that JIT is configured off and the attacker can host script under a web-safe non-http(s) site URL.

Changed Functions

FunctionChangeNotes
IsJitDisabledForSiteTest
chrome/browser/chrome_content_browser_client_unittest.cc
modified
TEST_F
chrome/browser/chrome_content_browser_client_unittest.cc
modified
ChromeContentBrowserClientPreferredColorSchemeAndroidTest
chrome/browser/chrome_content_browser_client_unittest.cc
modified

Files Changed

  • chrome/browser/chrome_content_browser_client.cc
  • chrome/browser/chrome_content_browser_client_unittest.cc

Audit Directions

  • Scheme checks that under-approximate "web content"
    Search for SchemeIsHTTPOrHTTPS() used as a proxy for “can host web-controlled script” and evaluate whether ChildProcessSecurityPolicy::IsWebSafeScheme() is the correct, broader gate (blob/filesystem/data origins are easy to miss).
  • Security/mitigation policies keyed on site URL
    Audit other content-setting and per-site hardening decisions (isolation, sandbox flags, feature gating) that branch on the site URL scheme, since SiteInfo::Create() can produce non-http(s) agent-cluster URLs for sandboxed/Blob frames.
  • Consistency between paired policy checks
    Compare related predicates such as AreV8OptimizationsEnabledForSite() and IsJitDisabledForSite() for divergent scheme handling, which is a common source of one-sided mitigation gaps.
From ffb88da0146b5ba583e5285b1bd1fad24c7d2e34 Mon Sep 17 00:00:00 2001
From: Javier Castro <jacastro@chromium.org>
Date: Mon, 27 Jul 2026 14:37:23 -0700
Subject: [PATCH] Apply JAVASCRIPT_JIT setting to all web-safe site URLs

ChromeContentBrowserClient::IsJitDisabledForSite() only consulted the
JAVASCRIPT_JIT content setting for http/https site URLs.
SiteInfo::Create() can however supply non-http(s) agent-cluster URLs
(e.g. blob:null/<guid> for a sandboxed iframe that navigates to a Blob),
so the early return left JIT enabled for the renderer hosting that frame
regardless of the configured content setting.

Match the existing AreV8OptimizationsEnabledForSite() and
gate the early return on
ChildProcessSecurityPolicy::IsWebSafeScheme() so that blob:,
filesystem: and data: site URLs also honour the content
setting, while WebUI and other non-web schemes still keep
JIT enabled.

Bug: 514489238
Internal CL: https://chrome-internal-review.googlesource.com/c/chrome/experimental/chromium/src/+/9599485
TAG=agy
CONV=f98b2970-8cc8-47b6-8a92-3821abb33f5d

Change-Id: I02f6fe0c57d442219026cb4e8ea58e34b5ce0be9
Reviewed-on: https://chromium-review.googlesource.com/c/chromium/src/+/8155983
Reviewed-by: Alex Moshchuk <alexmos@chromium.org>
Commit-Queue: Javier Castro <jacastro@chromium.org>
Cr-Commit-Position: refs/heads/main@{#1668994}
---
diff --git a/chrome/browser/chrome_content_browser_client.cc b/chrome/browser/chrome_content_browser_client.cc
index 398e316a..a5579e31 100644
--- a/chrome/browser/chrome_content_browser_client.cc
+++ b/chrome/browser/chrome_content_browser_client.cc
@@ -8134,8 +8134,10 @@
                                          nullptr) == CONTENT_SETTING_BLOCK;
   }
 
-  // Only disable JIT for web schemes.
-  if (!site_url.SchemeIsHTTPOrHTTPS()) {
+  // Only disable JIT for schemes that might actually load web content. This
+  // enables JIT for schemes such as chrome:// and chrome-untrusted://.
+  auto* policy = ChildProcessSecurityPolicy::GetInstance();
+  if (!policy->IsWebSafeScheme(site_url.GetScheme())) {
     return false;
   }
 
diff --git a/chrome/browser/chrome_content_browser_client_unittest.cc b/chrome/browser/chrome_content_browser_client_unittest.cc
index 727a0dd..80ff6f1e8 100644
--- a/chrome/browser/chrome_content_browser_client_unittest.cc
+++ b/chrome/browser/chrome_content_browser_client_unittest.cc
@@ -2154,6 +2154,44 @@
   EXPECT_FALSE(IsOriginIsolatedByUser(url));
 }
 
+class IsJitDisabledForSiteTest : public ChromeContentBrowserClientTest {
+ protected:
+  bool IsJitDisabledForSite(const GURL& site_url) {
+    return browser_client_.IsJitDisabledForSite(&profile_, site_url);
+  }
+
+  ChromeContentBrowserClient browser_client_;
+};
+
+TEST_F(IsJitDisabledForSiteTest, DefaultContentSettingAppliesToWebSafeSchemes) {
+  auto* map = HostContentSettingsMapFactory::GetForProfile(&profile_);
+  map->SetDefaultContentSetting(ContentSettingsType::JAVASCRIPT_JIT,
+                                ContentSetting::CONTENT_SETTING_BLOCK);
+
+  EXPECT_TRUE(IsJitDisabledForSite(GURL()));
+  EXPECT_TRUE(IsJitDisabledForSite(GURL("http://example.test")));
+  EXPECT_TRUE(IsJitDisabledForSite(GURL("https://example.test")));
+
+  // The default content setting also covers web-safe schemes other than
+  // http(s), since those can host web-controlled script as well.
+  EXPECT_TRUE(IsJitDisabledForSite(GURL("blob:https://example.test/guid")));
+  EXPECT_TRUE(IsJitDisabledForSite(GURL("blob:null/guid")));
+  EXPECT_TRUE(IsJitDisabledForSite(GURL("filesystem:http://example.test/f")));
+  EXPECT_TRUE(IsJitDisabledForSite(GURL("data:text/html,hello")));
+
+  // Schemes that are not web safe, such as WebUI schemes, are unaffected.
+  EXPECT_FALSE(IsJitDisabledForSite(GURL("chrome://settings")));
+  EXPECT_FALSE(IsJitDisabledForSite(GURL("chrome-untrusted://foo")));
+  EXPECT_FALSE(IsJitDisabledForSite(GURL("file:///tmp/foo.html")));
+}
+
+TEST_F(IsJitDisabledForSiteTest, AllowedByDefault) {
+  EXPECT_FALSE(IsJitDisabledForSite(GURL()));
+  EXPECT_FALSE(IsJitDisabledForSite(GURL("https://example.test")));
+  EXPECT_FALSE(IsJitDisabledForSite(GURL("blob:null/guid")));
+  EXPECT_FALSE(IsJitDisabledForSite(GURL("chrome://settings")));
+}
+
 #if BUILDFLAG(IS_ANDROID)
 
 class ChromeContentBrowserClientPreferredColorSchemeAndroidTest
Loading diff…

Regression Test / PoC

shipped with the fix
diff --git a/chrome/browser/chrome_content_browser_client_unittest.cc b/chrome/browser/chrome_content_browser_client_unittest.cc
index 727a0dd..80ff6f1e8 100644
--- a/chrome/browser/chrome_content_browser_client_unittest.cc
+++ b/chrome/browser/chrome_content_browser_client_unittest.cc
@@ -2154,6 +2154,44 @@
   EXPECT_FALSE(IsOriginIsolatedByUser(url));
 }
 
+class IsJitDisabledForSiteTest : public ChromeContentBrowserClientTest {
+ protected:
+  bool IsJitDisabledForSite(const GURL& site_url) {
+    return browser_client_.IsJitDisabledForSite(&profile_, site_url);
+  }
+
+  ChromeContentBrowserClient browser_client_;
+};
+
+TEST_F(IsJitDisabledForSiteTest, DefaultContentSettingAppliesToWebSafeSchemes) {
+  auto* map = HostContentSettingsMapFactory::GetForProfile(&profile_);
+  map->SetDefaultContentSetting(ContentSettingsType::JAVASCRIPT_JIT,
+                                ContentSetting::CONTENT_SETTING_BLOCK);
+
+  EXPECT_TRUE(IsJitDisabledForSite(GURL()));
+  EXPECT_TRUE(IsJitDisabledForSite(GURL("http://example.test")));
+  EXPECT_TRUE(IsJitDisabledForSite(GURL("https://example.test")));
+
+  // The default content setting also covers web-safe schemes other than
+  // http(s), since those can host web-controlled script as well.
+  EXPECT_TRUE(IsJitDisabledForSite(GURL("blob:https://example.test/guid")));
+  EXPECT_TRUE(IsJitDisabledForSite(GURL("blob:null/guid")));
+  EXPECT_TRUE(IsJitDisabledForSite(GURL("filesystem:http://example.test/f")));
+  EXPECT_TRUE(IsJitDisabledForSite(GURL("data:text/html,hello")));
+
+  // Schemes that are not web safe, such as WebUI schemes, are unaffected.
+  EXPECT_FALSE(IsJitDisabledForSite(GURL("chrome://settings")));
+  EXPECT_FALSE(IsJitDisabledForSite(GURL("chrome-untrusted://foo")));
+  EXPECT_FALSE(IsJitDisabledForSite(GURL("file:///tmp/foo.html")));
+}
+
+TEST_F(IsJitDisabledForSiteTest, AllowedByDefault) {
+  EXPECT_FALSE(IsJitDisabledForSite(GURL()));
+  EXPECT_FALSE(IsJitDisabledForSite(GURL("https://example.test")));
+  EXPECT_FALSE(IsJitDisabledForSite(GURL("blob:null/guid")));
+  EXPECT_FALSE(IsJitDisabledForSite(GURL("chrome://settings")));
+}
+
 #if BUILDFLAG(IS_ANDROID)
 
 class ChromeContentBrowserClientPreferredColorSchemeAndroidTest
Loading diff…

Original Bug Report

reported by rj...@google.com

Bypass of JITless policy via non-HTTP/HTTPS schemes (e.g., blob: URLs)

Flapjack, 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. Please see https://chromium.googlesource.com/chromium/src/+/main/docs/security/ai-generated-security-bugs-faq.md for more information.

Overview: A logic flaw in ChromeContentBrowserClient::IsJitDisabledForSite potentially allows web content to bypass the JavaScript JIT content setting. By executing code within a sandboxed frame using a blob: URL, an attacker can force the browser to enable JIT optimizations in that process, circumventing defense-in-depth policies.

Affected files:

  • chrome/browser/chrome_content_browser_client.cc

Estimated timestamp from git blame: 2021-06-16

Summary

The ChromeContentBrowserClient::IsJitDisabledForSite function contains a logic flaw that potentially allows sites to bypass the “JavaScript JIT” content setting restriction. This setting is intended as a defense-in-depth mitigation to prevent JIT-related vulnerabilities from being exploited. Because the function explicitly enables JIT for any URL that does not strictly use the HTTP or HTTPS schemes, it fails to enforce the policy for other web-safe schemes like blob:, data:, and filesystem:.

Vulnerability Details

In chrome/browser/chrome_content_browser_client.cc, the IsJitDisabledForSite function evaluates whether JIT should be disabled for a given site_url. The implementation has the following check:

  // Only disable JIT for web schemes.
  if (!site_url.SchemeIsHTTPOrHTTPS()) {
    return false;
  }

The check !site_url.SchemeIsHTTPOrHTTPS() returns false (meaning JIT is enabled) for any URL that is not http: or https:.

When a site creates a sandboxed iframe and navigates it to an opaque-origin blob: URL, content::SiteInfo::Create determines that the frame requires a dedicated process (RequiresDedicatedProcessInternal returns true because the frame is sandboxed). As a result, the agent_cluster_url_or_default passed to IsJitDisabledForSite resolves to the literal blob: URL (e.g., blob:https://example.com/guid).

Since GURL::SchemeIsHTTPOrHTTPS() returns false for blob schemes, the function immediately returns false, enabling JIT for that execution context regardless of global user settings or enterprise policies blocking JIT. This behavior is inconsistent with AreV8OptimizationsEnabledForSite in the same file, which correctly uses ChildProcessSecurityPolicy::GetInstance()->IsWebSafeScheme(site_url.GetScheme()) to restrict optimizations across all relevant web schemes.

Potential Attacker Steps

Note: These are suggested/potential steps based on static analysis. Our tooling agent does not have the ability to run code to confirm a working Proof of Concept.

  1. The victim’s browser is configured to disable JavaScript JIT globally via the JAVASCRIPT_JIT content setting or enterprise policy.
  2. An attacker hosts a malicious website that creates a Blob containing a V8 JIT exploit payload and calls URL.createObjectURL(blob).
  3. The attacker dynamically creates an iframe with the attribute sandbox="allow-scripts" to force an opaque origin and dedicated process allocation, setting its src to the generated blob: URL.
  4. When the browser allocates the new renderer process for the sandboxed iframe, IsJitDisabledForSite evaluates the blob: URL, hits the early return, and enables JIT.
  5. The attacker’s payload executes with JIT optimizations enabled, bypassing the policy and allowing the underlying V8 JIT vulnerability to be exploited.

Suggested Fix

Update ChromeContentBrowserClient::IsJitDisabledForSite to use a more inclusive check for web-safe schemes. Instead of restricting the check to SchemeIsHTTPOrHTTPS(), use ChildProcessSecurityPolicy::GetInstance()->IsWebSafeScheme(site_url.GetScheme()), similar to the approach taken in AreV8OptimizationsEnabledForSite. This ensures that JIT policies are accurately enforced for all schemes that can load and execute web content.

Evaluated with Chrome root at commit: b7d0c4d810da1b31400f198c70d9720fc8f0e5a0


Results so far have been promising, but there can be wrong deductions. Feel free to adjust as follows:

  • If you are familiar with the severity guidelines, you may adjust the severity.
  • If this is a false positive, and there’s no work to be done, please close as WAI.
  • If there is work to do here but not a vulnerability, please change the issue type to Task/Bug/FR.

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