Medium watch CVSS 6.6 chrome Policy Bypass 📄 Reporter bug report 🔧 Commit mapped

Overview

Medium
Severity
6.6
CVSS
No
Exploited ITW
Fixed
Fix Status
ImpactInsufficient policy enforcement in PWAs
DescriptionInsufficient policy enforcement in PWAs
ComponentPWAs
Sub-componentWebInstall
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.
Tracker487568011
Fix commit35d688b7c598 (chromium/src) +105/-16
CWECWE-1268
CVSS vectorCVSS:3.1/AV:L/AC:L/PR:L/UI:R/S:U/C:N/I:H/A:H
CISA KEVNot listed
CreditedTianyi Hu
Reported2026-02-25 (per vendor advisory)
Fix landed2026-03-03 (public git)
Disclosed2026-04-07
Patch gap35 days — fix public in source before the advisory

Background

Web Install API
The navigator.install() JavaScript API and the declarative <install> element let a page request installation of a web app, handled browser-side by WebInstallServiceImpl.
`WebInstallServiceImpl`
A per-document browser-side Mojo service; a single instance persists for the lifetime of the document and handles both Install() (JS API) and InstallFromElement() (<install> element).
`triggered_from_element_`
A member flag indicating the current install came from the trusted <install> element, which legitimately bypasses the permissions-policy gate and the WEB_APP_INSTALLATION permission prompt (the element handles its own confirmation).
Permissions-policy / `WEB_APP_INSTALLATION` prompt
The gates that must be satisfied for a JS-initiated navigator.install() call to proceed.

Root Cause Analysis

InstallFromElement() set the member triggered_from_element_ = true and then delegated to Install(), but nothing ever reset the flag back to false. Because the service instance is scoped to the whole document and reused across calls, once an <install>-element install occurred the flag stayed sticky; a later navigator.install() call on the same document still saw triggered_from_element_ == true and took the branch if (triggered_from_element_ || install_target == last_committed_url_), which skips the permissions-policy check and the WEB_APP_INSTALLATION permission prompt and proceeds as if permission were granted.

The fix removes the mutable member from the control flow: it introduces a private InstallInternal(options, callback, triggered_from_element) that takes the value as a parameter, has Install() forward false and InstallFromElement() forward true, and rewrites the gate to test the local triggered_from_element. This scopes the “trusted element” state to a single call so it can no longer leak across Mojo invocations on the same document.

Key insight
A per-document service stored the “install came from the trusted <install> element” decision in a never-reset member, so one element install made every later navigator.install() on the page skip its permission checks; the fix replaces the sticky member with a per-call parameter threaded through InstallInternal.

Attack Path

  1. Host both install surfaces An attacker page includes an <install> element and also uses the navigator.install() JS API, sharing one WebInstallServiceImpl instance for the document.
  2. Prime the sticky flag The page performs an <install>-element install, which runs InstallFromElement() and sets triggered_from_element_ = true with no reset.
  3. Call the JS API The page then calls navigator.install(url, manifest_id); Install() runs with the stale triggered_from_element_ still true.
  4. Bypass the gate The gate if (triggered_from_element_ || ...) takes the element branch, skipping the permissions-policy check and the WEB_APP_INSTALLATION prompt, so the JS-initiated install proceeds without the user permission it should require.

Impact Assessment

The vendor advisory rates this medium, “Insufficient policy enforcement in PWAs,” with no exploited-in-the-wild claim. Reachable by ordinary web content on a page that uses both the <install> element and the navigator.install() API (feature-gated by kWebAppInstallation); the gain is bypassing the permissions-policy gate and the WEB_APP_INSTALLATION permission prompt so a JS-driven web-app install proceeds without the required user permission. It is a policy-enforcement/logic flaw with no memory-safety component; what the attacker could further do with the unauthorized install is not shown by the patch (inferred).

Changed Functions

FunctionChangeNotes
InstallElementAndApiInteractionBrowserTest
chrome/browser/web_applications/install_element_browsertest.cc
modified
InstallElementAndApiInteractionBrowserTest
chrome/browser/web_applications/install_element_browsertest.cc
modified
if
chrome/browser/web_applications/web_install_service_impl.cc
modified

Files Changed

  • chrome/browser/web_applications/install_element_browsertest.cc
  • chrome/browser/web_applications/web_install_service_impl.cc
  • chrome/browser/web_applications/web_install_service_impl.h

Audit Directions

  • Mutable member state on long-lived per-document services
    Audit Mojo service members that encode per-call trust/permission decisions but outlive the call; prefer passing such state as parameters.
  • Set-but-never-reset flags
    Grep for flags assigned true on one entrypoint with no corresponding reset, especially where they gate permission or policy checks.
  • Shared code paths with differing trust
    Where a “trusted” and an “untrusted” caller funnel into one internal routine, verify the trust level is derived from the immediate caller each time, not from stored state.
From 35d688b7c5984f28b1cfe4816a93b586fabf6cd6 Mon Sep 17 00:00:00 2001
From: Lia Hiscock <liahiscock@microsoft.com>
Date: Tue, 03 Mar 2026 14:39:46 -0800
Subject: [PATCH] [WebInstall] Fix triggered_from_element_ sticky state

WebInstallServiceImpl::InstallFromElement() set the member
triggered_from_element_ to true but never reset it. Because the service
instance is per-document, a subsequent navigator.install() call on the
same page reused the stale flag, skipping the permissions-policy gate
and the WEB_APP_INSTALLATION permission prompt.

Replace the mutable member with a local parameter by introducing a
private InstallInternal(options, callback, triggered_from_element)
helper. Install() forwards with false; InstallFromElement() forwards
with true. This ensures the flag is scoped to a single call and cannot
leak across Mojo method invocations.

Add a regression test that performs an <install>-element install
followed by a navigator.install() call on the same document with the
WEB_APP_INSTALLATION permission blocked, and verifies that the JS API
call is correctly denied.

Bug: 487938764, 487568011, 333795265
Change-Id: Ibe56b00bcb1018a25bfadce92dcdc98e4f9c4360
Reviewed-on: https://chromium-review.googlesource.com/c/chromium/src/+/7615447
Reviewed-by: Lu Huang <luhua@microsoft.com>
Reviewed-by: Marijn Kruisselbrink <mek@chromium.org>
Commit-Queue: Lia Hiscock <liahiscock@microsoft.com>
Cr-Commit-Position: refs/heads/main@{#1593535}
---

diff --git a/chrome/browser/web_applications/install_element_browsertest.cc b/chrome/browser/web_applications/install_element_browsertest.cc
index 46a85c1..23435c21f 100644
--- a/chrome/browser/web_applications/install_element_browsertest.cc
+++ b/chrome/browser/web_applications/install_element_browsertest.cc
@@ -665,4 +665,80 @@
   WaitForDismissEvent(kInstallElementId);
 }
 
+///////////////////////////////////////////////////////////////////////////////
+// Regression tests for interactions between <install> element and JS API.
+///////////////////////////////////////////////////////////////////////////////
+
+// Test fixture that enables both the <install> element and the
+// navigator.install() JS API, so we can test interactions between them on the
+// same document (same WebInstallServiceImpl instance).
+class InstallElementAndApiInteractionBrowserTest
+    : public InstallElementBrowserTest {
+ public:
+  InstallElementAndApiInteractionBrowserTest() {
+    additional_features_.InitWithFeatures(
+        {blink::features::kWebAppInstallation}, {});
+  }
+
+ private:
+  base::test::ScopedFeatureList additional_features_;
+};
+
+// Regression test for crbug.com/487568011: triggered_from_element was set by
+// InstallFromElement() but never reset, causing subsequent Install() calls via
+// navigator.install() on the same document to bypass the permissions-policy and
+// permission prompt checks.
+IN_PROC_BROWSER_TEST_F(InstallElementAndApiInteractionBrowserTest,
+                       InstallApiRespectsPermissionsAfterElementInstall) {
+  // Navigate to a page with <install> elements.
+  ASSERT_TRUE(ui_test_utils::NavigateToURL(
+      browser(), https_server()->GetURL(kInstallElementPageStartUrl)));
+
+  auto auto_accept = SetAutoAcceptPWAInstallConfirmationForTesting();
+
+  // Set the <install> element's installurl to a another test page.
+  const GURL element_install_url =
+      https_server()->GetURL(kCustomIdPageInstallUrl);
+  ASSERT_TRUE(SetButtonInstallUrl(element_install_url));
+
+  // Step 1: Click the <install> element. This calls InstallFromElement() on the
+  // browser-side WebInstallServiceImpl instance for this document, which passes
+  // triggered_from_element = true.
+  {
+    ui_test_utils::BrowserCreatedObserver browser_created_observer;
+    ASSERT_TRUE(ClickElementWithId(kInstallElementId));
+    Browser* web_app_browser = browser_created_observer.Wait();
+    WaitForPromptActionEvent(kInstallElementId);
+    ASSERT_TRUE(AppBrowserController::IsWebApp(web_app_browser));
+  }
+
+  // Step 2: From the SAME page (same WebInstallServiceImpl instance), call
+  // navigator.install(url, manifest_id) via JS. This uses the Install() Mojo
+  // method, NOT InstallFromElement(). The permission check should NOT be
+  // bypassed by the sticky triggered_from_element_ flag from step 1.
+  const GURL api_install_url =
+      https_server()->GetURL(kNoCustomIdPageInstallUrl);
+  const GURL api_manifest_id = https_server()->GetURL(kNoCustomIdPageId);
+
+  // Block the WEB_APP_INSTALLATION permission so that if the logic to prompt
+  // for permission is correctly reached, it will be denied. With the sticky
+  // state bug, the permission check would've been skipped entirely and the
+  // install would've succeeded.
+  BlockWebInstallPermission(api_install_url);
+
+  auto result = content::EvalJs(
+      web_contents(), "navigator.install('" + api_install_url.spec() + "', '" +
+                          api_manifest_id.spec() +
+                          "')"
+                          ".then(result => 'success')"
+                          ".catch(error => error.name)");
+
+  // The Install API call should fail because the WEB_APP_INSTALLATION
+  // permission was blocked. With the sticky state bug,
+  // triggered_from_element_ would still be true from step 1, causing
+  // Install() to skip all permission checks and auto-grant, resulting in
+  // 'success' instead.
+  EXPECT_EQ("AbortError", result.ExtractString());
+}
+
 }  // namespace web_app
diff --git a/chrome/browser/web_applications/web_install_service_impl.cc b/chrome/browser/web_applications/web_install_service_impl.cc
index 3ef74d6c..faadccb 100644
--- a/chrome/browser/web_applications/web_install_service_impl.cc
+++ b/chrome/browser/web_applications/web_install_service_impl.cc
@@ -190,6 +190,21 @@
 
 void WebInstallServiceImpl::Install(blink::mojom::InstallOptionsPtr options,
                                     InstallCallback callback) {
+  InstallInternal(std::move(options), std::move(callback),
+                  /*triggered_from_element=*/false);
+}
+
+void WebInstallServiceImpl::InstallFromElement(
+    blink::mojom::InstallOptionsPtr options,
+    InstallCallback callback) {
+  InstallInternal(std::move(options), std::move(callback),
+                  /*triggered_from_element=*/true);
+}
+
+void WebInstallServiceImpl::InstallInternal(
+    blink::mojom::InstallOptionsPtr options,
+    InstallCallback callback,
+    bool triggered_from_element) {
   // Create source ids for UKM logging.
   ukm::SourceId requesting_page_source_id =
       options ? render_frame_host().GetPageUkmSourceId()
@@ -251,18 +266,18 @@
         }
         std::move(callback).Run(install_result, manifest_id_result);
       },
-      std::move(callback), triggered_from_element_, requesting_page_source_id,
+      std::move(callback), triggered_from_element, requesting_page_source_id,
       installed_app_source_id);
 
   web_app::WebInstallServiceType install_type =
       options ? web_app::WebInstallServiceType::kBackgroundDocument
               : web_app::WebInstallServiceType::kCurrentDocument;
   base::UmaHistogramEnumeration(
-      triggered_from_element_ ? kInstallElementTypeUma : kInstallApiTypeUma,
+      triggered_from_element ? kInstallElementTypeUma : kInstallApiTypeUma,
       install_type);
   base::UmaHistogramEnumeration(
       base::StrCat({"WebApp.WebInstallService.",
-                    triggered_from_element_ ? "Element" : "Api",
+                    triggered_from_element ? "Element" : "Api",
                     ".InstallType"}),
       install_type);
 
@@ -314,10 +329,10 @@
 
   // Skip requesting permission in two cases:
   // 1. The install URL matches the current document URL (user is installing
-  //    the page they're on, even if using background install syntax).
-  // 2. Install triggered from the <install> element (permission is handled
-  //    by the element itself). In both cases, the install dialog is shown.
-  if (triggered_from_element_ || install_target == last_committed_url_) {
+  //    the page they're currently on, just using background install syntax).
+  // 2. Install triggered from the <install> element.
+  // In both cases, the install dialog is always shown.
+  if (triggered_from_element || install_target == last_committed_url_) {
     OnPermissionDecided(
         std::move(callback_with_metrics),
         std::vector<content::PermissionResult>({content::PermissionResult(
@@ -341,13 +356,6 @@
       weak_ptr_factory_.GetWeakPtr(), std::move(callback_with_metrics)));
 }
 
-void WebInstallServiceImpl::InstallFromElement(
-    blink::mojom::InstallOptionsPtr options,
-    InstallCallback callback) {
-  triggered_from_element_ = true;
-  Install(std::move(options), std::move(callback));
-}
-
 void WebInstallServiceImpl::OnInstallNotSupportedDialogClosed(
     InstallCallbackWithMetrics callback_with_metrics) {
   std::move(callback_with_metrics)
diff --git a/chrome/browser/web_applications/web_install_service_impl.h b/chrome/browser/web_applications/web_install_service_impl.h
index 6164059..b8325df 100644
--- a/chrome/browser/web_applications/web_install_service_impl.h
+++ b/chrome/browser/web_applications/web_install_service_impl.h
@@ -98,6 +98,13 @@
                           InstallCallback callback) override;
 
Loading diff…

Regression Test / PoC

shipped with the fix
diff --git a/chrome/browser/web_applications/install_element_browsertest.cc b/chrome/browser/web_applications/install_element_browsertest.cc
index 46a85c1..23435c21f 100644
--- a/chrome/browser/web_applications/install_element_browsertest.cc
+++ b/chrome/browser/web_applications/install_element_browsertest.cc
@@ -665,4 +665,80 @@
   WaitForDismissEvent(kInstallElementId);
 }
 
+///////////////////////////////////////////////////////////////////////////////
+// Regression tests for interactions between <install> element and JS API.
+///////////////////////////////////////////////////////////////////////////////
+
+// Test fixture that enables both the <install> element and the
+// navigator.install() JS API, so we can test interactions between them on the
+// same document (same WebInstallServiceImpl instance).
+class InstallElementAndApiInteractionBrowserTest
+    : public InstallElementBrowserTest {
+ public:
+  InstallElementAndApiInteractionBrowserTest() {
+    additional_features_.InitWithFeatures(
+        {blink::features::kWebAppInstallation}, {});
+  }
+
+ private:
+  base::test::ScopedFeatureList additional_features_;
+};
+
+// Regression test for crbug.com/487568011: triggered_from_element was set by
+// InstallFromElement() but never reset, causing subsequent Install() calls via
+// navigator.install() on the same document to bypass the permissions-policy and
+// permission prompt checks.
+IN_PROC_BROWSER_TEST_F(InstallElementAndApiInteractionBrowserTest,
+                       InstallApiRespectsPermissionsAfterElementInstall) {
+  // Navigate to a page with <install> elements.
+  ASSERT_TRUE(ui_test_utils::NavigateToURL(
+      browser(), https_server()->GetURL(kInstallElementPageStartUrl)));
+
+  auto auto_accept = SetAutoAcceptPWAInstallConfirmationForTesting();
+
+  // Set the <install> element's installurl to a another test page.
+  const GURL element_install_url =
+      https_server()->GetURL(kCustomIdPageInstallUrl);
+  ASSERT_TRUE(SetButtonInstallUrl(element_install_url));
+
+  // Step 1: Click the <install> element. This calls InstallFromElement() on the
+  // browser-side WebInstallServiceImpl instance for this document, which passes
+  // triggered_from_element = true.
+  {
+    ui_test_utils::BrowserCreatedObserver browser_created_observer;
+    ASSERT_TRUE(ClickElementWithId(kInstallElementId));
+    Browser* web_app_browser = browser_created_observer.Wait();
+    WaitForPromptActionEvent(kInstallElementId);
+    ASSERT_TRUE(AppBrowserController::IsWebApp(web_app_browser));
+  }
+
+  // Step 2: From the SAME page (same WebInstallServiceImpl instance), call
+  // navigator.install(url, manifest_id) via JS. This uses the Install() Mojo
+  // method, NOT InstallFromElement(). The permission check should NOT be
+  // bypassed by the sticky triggered_from_element_ flag from step 1.
+  const GURL api_install_url =
+      https_server()->GetURL(kNoCustomIdPageInstallUrl);
+  const GURL api_manifest_id = https_server()->GetURL(kNoCustomIdPageId);
+
+  // Block the WEB_APP_INSTALLATION permission so that if the logic to prompt
+  // for permission is correctly reached, it will be denied. With the sticky
+  // state bug, the permission check would've been skipped entirely and the
+  // install would've succeeded.
+  BlockWebInstallPermission(api_install_url);
+
+  auto result = content::EvalJs(
+      web_contents(), "navigator.install('" + api_install_url.spec() + "', '" +
+                          api_manifest_id.spec() +
+                          "')"
+                          ".then(result => 'success')"
+                          ".catch(error => error.name)");
+
+  // The Install API call should fail because the WEB_APP_INSTALLATION
+  // permission was blocked. With the sticky state bug,
+  // triggered_from_element_ would still be true from step 1, causing
+  // Install() to skip all permission checks and auto-grant, resulting in
+  // 'success' instead.
+  EXPECT_EQ("AbortError", result.ExtractString());
+}
+
 }  // namespace web_app
Loading diff…

Original Bug Report

reported by os...@gmail.com

WebInstallService InstallFromElement() bypasses WEB_APP_INSTALLATION permission prompt via renderer-callable Mojo method


Report description

WebInstallService InstallFromElement() bypasses WEB_APP_INSTALLATION permission prompt via renderer-callable Mojo method


Bug location

Where do you want to report your vulnerability?

Chrome VRP – Report security issues affecting the Chrome browser. See program rules

Which URL (or repository) have you found the vulnerability in?

https://source.chromium.org/chromium/chromium/src/+/main:chrome/browser/web_applications/web_install_service_impl.cc


The problem

Please describe the technical details of the vulnerability

WebInstallServiceImpl::InstallFromElement() sets triggered_from_element_ = true then calls Install(). In RequestWebInstallPermission(), this flag causes the WEB_APP_INSTALLATION permission to be auto-granted without showing a permission prompt. A compromised renderer can call InstallFromElement() directly via Mojo to install cross-origin PWAs without user consent to the permission.

The permission bypass was designed intentionally for the <install> HTML element — the rationale being that a user clicking an <install> element already implies consent, so no separate permission prompt is needed. However, this design violates the Chromium security model: Mojo does not enforce which renderer code path invoked a method. The browser cannot distinguish between a call from a legitimate <install> element click handler and a call from compromised renderer code. Both arrive as the same InstallFromElement() IPC. The triggered_from_element_ flag is therefore a renderer-trusted boolean that the browser uses to make a security decision — a classic Mojo trust boundary violation. The existing test InstallWithUrl_IgnoresDeniedPermission explicitly asserts this bypass works, confirming the design intent, but the threat model was not considered.

Vulnerable files:

Transmission chain:

  1. The renderer calls WebInstallService::InstallFromElement(options, callback) via Mojo. This method is intended only for the <install> HTML element, but any renderer code can call it.
  2. In WebInstallServiceImpl::InstallFromElement(), the browser unconditionally sets triggered_from_element_ = true, then delegates to Install().
  3. Install() eventually calls RequestWebInstallPermission(). When the permission status is ASK, the method checks triggered_from_element_:
if (triggered_from_element_) {
  // Do not show permission prompt for installs from an element entry point.
  // Grant permission by default if not already granted or denied.
  std::move(callback).Run(
      std::vector<content::PermissionResult>({content::PermissionResult(
          PermissionStatus::GRANTED,
          content::PermissionStatusSource::UNSPECIFIED)}));
  return;
}
  1. The permission is auto-granted and the install proceeds without a permission prompt.
  2. Additionally, triggered_from_element_ is never reset, so all subsequent Install() calls on the same WebInstallServiceImpl instance also bypass the permission prompt (sticky state).

Steps to reproduce:

  1. Check out stable tag: git checkout -b poc 145.0.7632.117
  2. git apply poc_patch.diff
  3. autoninja -C out/Default chrome -j8
  4. Place PoC files (index.html, serve.py) in a directory and start the server:
    • python3 serve.py
  5. Launch patched Chrome:
    • out/Default/Chromium.app/Contents/MacOS/Chromium --user-data-dir=/tmp/chrome-poc-test --enable-blink-features=WebAppInstallation http://localhost:8080/index.html
  6. Click “Install cross-origin PWA”. The install dialog appears directly without a permission prompt.

The renderer patch replaces Install() with InstallFromElement() in the renderer’s navigator.install() implementation, simulating a compromised renderer. --enable-blink-features=WebAppInstallation is needed to expose the JS API, but the underlying Mojo interface is already bound by default (the kWebAppInstallation base feature is enabled by default).

Bisect: Introducing commit: 1fd86f307a82e5a58bb86dafcb0e19cb7c29016e

  • Author: Kristin Lee (kristinlee@microsoft.com), Dec 19 2025
  • Message: “[<install> Element] Do not show the permission prompt”
  • CL: https://chromium-review.googlesource.com/c/chromium/src/+/7266918
  • Evidence: parent f8e2c3ebdf4e904db590874c40168861ccf4f606 has no InstallFromElement or triggered_from_element_; this commit adds both.
  • Cr-Commit-Position: refs/heads/main@{#1560950} — after M144 branch (1552494), before M145 branch (1568190).
  • Earliest affected: M145. Latest confirmed: M145 (145.0.7632.117).

Note: a follow-up commit 8d4441fe2c2a7f58a3db483da333f9ed0b933822 (Jan 20 2026, CL https://chromium-review.googlesource.com/c/chromium/src/+/7278710) changed the bypass from “allow unless previously denied” to “always GRANTED unconditionally,” worsening the bug in M146+.

Suggested fix (attached as fix.diff):

Remove triggered_from_element_ from the permission bypass condition in Install():

-  if (triggered_from_element_ || install_target == last_committed_url_) {
+  if (install_target == last_committed_url_) {

The browser cannot verify that InstallFromElement() was called from a genuine <install> element versus a compromised renderer. The fix removes the renderer-trusted flag from the permission decision, so InstallFromElement() goes through the normal RequestWebInstallPermission() flow. The triggered_from_element_ field is retained for UMA/UKM metrics routing only. The existing InstallWithUrl_IgnoresDeniedPermission test is updated to expect permission denial instead of bypass.

Impact analysis

A compromised renderer can install arbitrary cross-origin PWAs without the WEB_APP_INSTALLATION permission prompt. This bypasses the permission model that gates the Web Install API.

User harm:

  • Phishing: Install a malicious PWA mimicking a banking/email app — PWAs appear as native apps with no browser chrome, making phishing more convincing.
  • Persistence: PWAs persist beyond browser sessions and appear in the OS app list/dock, giving the attacker a persistent foothold.
  • No user awareness: The permission prompt is the user’s only signal that a cross-origin install is happening. Bypassing it means the user sees only the install dialog (which may look legitimate) without the preceding “allow this site to install apps?” consent.

Reachability: A compromised renderer in any top-level frame on desktop Chrome. Reachable via renderer exploits (e.g., V8 bugs) or compromised ad network code.

Compounding factors:

  1. The triggered_from_element_ flag is sticky — once set, it persists for the lifetime of the WebInstallServiceImpl instance, so all subsequent Install() calls on the same document also bypass the permission prompt.
  2. On M145 (stable), the bypass is in RequestWebInstallPermission() after the GRANTED/DENIED switch — so the bypass fires when permission status is ASK (the default). A user who previously clicked “Block” is protected. However, on main (shipping in M146), the bypass was moved before RequestWebInstallPermission() entirely (line 320: if (triggered_from_element_ || ...) → OnPermissionDecided(GRANTED)), meaning the permission is auto-granted even if the user previously denied it. This makes the M146 variant strictly worse.

The cause

What version of Chrome have you found the security issue in?

145.0.7632.117 (Stable)

No, it is not related to a crash.

Choose the type of vulnerability

Permissions Bypass

How would you like to be publicly acknowledged for your report?

Tianyi Hu

View on issue tracker