Medium chrome Logic Error 📄 Reporter bug report 🔧 Commit mapped

Overview

Medium
Severity
CVSS
No
Exploited ITW
Fixed
Fix Status
ImpactInappropriate implementation in Extensions
DescriptionInappropriate implementation in Extensions
ComponentExtensions
Bug ClassLogic Error
Tracker500494349
Fix commit08c00921178f (chromium/src) +285/-0
CISA KEVNot listed
CreditedGoogle
Disclosed2026-07-29

Changed Functions

FunctionChangeNotes
TestRendererStartupHelper
extensions/browser/user_script_loader_unittest.cc
modified

Files Changed

  • extensions/browser/BUILD.gn
  • extensions/browser/guest_view/web_view/web_view_renderer_state.h
  • extensions/browser/user_script_loader.cc
  • extensions/browser/user_script_loader_unittest.cc
From 08c00921178fa2a3109579bf0ed2795cad5884c9 Mon Sep 17 00:00:00 2001
From: Vlad Krot <vkrot@google.com>
Date: Thu, 25 Jun 2026 01:54:04 -0700
Subject: [PATCH] Restrict embedder content scripts to guest renderers

UserScriptLoader::SendUpdate ships its serialized content-script
shared-memory region to every same-profile renderer that has a
mojom::Renderer connection. The owner-host check that limits webview
content scripts to their owning embedder is gated on
process->IsForGuestsOnly(), so an ordinary web renderer in the same
profile falls through directly to UpdateUserScripts and receives the
region even when the loader's host is a WebUI or Controlled Frame
embedder.

Embedder content scripts (kWebUi / kControlledFrameEmbedder) are only
ever injected into the embedder's own guest views; renderer-side
CanExecuteOnFrame already prevents them from running in non-guest
frames. Shipping them to non-guest renderers therefore has no effect on
injection but unnecessarily maps the embedder's script bytes into
unrelated processes.

Return early from SendUpdate for non-guest renderer processes when the
host is an embedder, and add unit tests covering both embedder host
types. The kExtensions path and the existing guest-process owner check
are unchanged.

Fixed: 500494349
Change-Id: I20ccfba95a65b7b3e90f1922bea9a3b3dc1ba0e7
Reviewed-on: https://chromium-review.googlesource.com/c/chromium/src/+/7979439
Reviewed-by: Solomon Kinard <solomonkinard@chromium.org>
Commit-Queue: Vlad Krot <vkrot@google.com>
Cr-Commit-Position: refs/heads/main@{#1652242}
---

diff --git a/extensions/browser/BUILD.gn b/extensions/browser/BUILD.gn
index b04b5768..0e84ffa 100644
--- a/extensions/browser/BUILD.gn
+++ b/extensions/browser/BUILD.gn
@@ -1141,6 +1141,7 @@
     "updater/safe_manifest_parser_unittest.cc",
     "updater/update_data_provider_unittest.cc",
     "updater/update_service_unittest.cc",
+    "user_script_loader_unittest.cc",
     "verified_contents_unittest.cc",
     "warning_service_unittest.cc",
   ]
diff --git a/extensions/browser/guest_view/web_view/web_view_renderer_state.h b/extensions/browser/guest_view/web_view/web_view_renderer_state.h
index 4f91d0d..f1fc86a 100644
--- a/extensions/browser/guest_view/web_view/web_view_renderer_state.h
+++ b/extensions/browser/guest_view/web_view/web_view_renderer_state.h
@@ -80,6 +80,15 @@
   // Returns the number of WebView guest instances tracked by this object.
   size_t guest_count_for_testing() { return web_view_info_map_.size(); }
 
+  void AddGuestForTesting(int guest_process_id,
+                          int guest_routing_id,
+                          const WebViewInfo& web_view_info) {
+    AddGuest(guest_process_id, guest_routing_id, web_view_info);
+  }
+  void RemoveGuestForTesting(int guest_process_id, int guest_routing_id) {
+    RemoveGuest(guest_process_id, guest_routing_id);
+  }
+
  private:
   friend class WebViewGuest;
   friend struct base::DefaultSingletonTraits<WebViewRendererState>;
diff --git a/extensions/browser/user_script_loader.cc b/extensions/browser/user_script_loader.cc
index 4e2b46b..f5d8358 100644
--- a/extensions/browser/user_script_loader.cc
+++ b/extensions/browser/user_script_loader.cc
@@ -534,6 +534,22 @@
     return SendUpdateResult::kNoActionTaken;
   }
 
+  switch (host_id().type) {
+    case mojom::HostID::HostType::kExtensions:
+      break;
+    case mojom::HostID::HostType::kWebUi:
+    case mojom::HostID::HostType::kControlledFrameEmbedder:
+      // Embedder content scripts are only ever injected into the embedder's
+      // own guest views and never into ordinary web frames, so they only need
+      // to be sent to guest renderers.
+      if (!process->IsForGuestsOnly()) {
+        return SendUpdateResult::kNoActionTaken;
+      }
+      break;
+    default:
+      NOTREACHED();
+  }
+
   base::ReadOnlySharedMemoryRegion region_for_process =
       shared_memory.Duplicate();
   if (!region_for_process.IsValid()) {
diff --git a/extensions/browser/user_script_loader_unittest.cc b/extensions/browser/user_script_loader_unittest.cc
new file mode 100644
index 0000000..3cc7818
--- /dev/null
+++ b/extensions/browser/user_script_loader_unittest.cc
@@ -0,0 +1,259 @@
+// Copyright 2026 The Chromium Authors
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "extensions/browser/user_script_loader.h"
+
+#include <memory>
+#include <set>
+#include <string>
+#include <utility>
+
+#include "base/functional/bind.h"
+#include "base/functional/callback_helpers.h"
+#include "base/memory/raw_ptr.h"
+#include "base/memory/read_only_shared_memory_region.h"
+#include "base/run_loop.h"
+#include "base/test/bind.h"
+#include "content/public/browser/render_process_host.h"
+#include "content/public/test/mock_render_process_host.h"
+#include "extensions/browser/embedder_user_script_loader.h"
+#include "extensions/browser/extensions_test.h"
+#include "extensions/browser/guest_view/web_view/web_view_renderer_state.h"
+#include "extensions/browser/renderer_startup_helper.h"
+#include "extensions/common/mojom/host_id.mojom.h"
+#include "extensions/common/mojom/renderer.mojom.h"
+#include "extensions/common/user_script.h"
+#include "mojo/public/cpp/bindings/associated_receiver_set.h"
+#include "testing/gtest/include/gtest/gtest.h"
+#include "url/gurl.h"
+
+namespace extensions {
+
+namespace {
+
+// A RendererStartupHelper that intercepts mojom::Renderer messages and records
+// which processes receive UpdateUserScripts. This lets the tests below verify
+// that user scripts are only shipped to the intended set of renderers.
+class TestRendererStartupHelper : public RendererStartupHelper,
+                                  public mojom::Renderer {
+ public:
+  explicit TestRendererStartupHelper(content::BrowserContext* browser_context)
+      : RendererStartupHelper(browser_context) {}
+
+  static std::unique_ptr<KeyedService> Build(
+      content::BrowserContext* browser_context) {
+    return std::make_unique<TestRendererStartupHelper>(browser_context);
+  }
+
+  bool ProcessReceivedUpdateUserScripts(
+      content::RenderProcessHost* process) const {
+    return updated_processes_.contains(process);
+  }
+
+ protected:
+  // RendererStartupHelper:
+  mojo::PendingAssociatedRemote<mojom::Renderer> BindNewRendererRemote(
+      content::RenderProcessHost* process) override {
+    mojo::AssociatedRemote<mojom::Renderer> remote;
+    renderer_receivers_.Add(
+        this, remote.BindNewEndpointAndPassDedicatedReceiver(), process);
+    return remote.Unbind();
+  }
+
+ private:
+  // mojom::Renderer:
+  void ActivateExtension(const ExtensionId& extension_id) override {}
+  void SetActivityLoggingEnabled(bool enabled) override {}
+  void SetPolicyActivityLoggingEnabled(bool enabled) override {}
+  void LoadExtensions(
+      std::vector<mojom::ExtensionLoadedParamsPtr> loaded_extensions) override {
+  }
+  void UnloadExtension(const ExtensionId& extension_id) override {}
+  void SuspendExtension(
+      const ExtensionId& extension_id,
+      mojom::Renderer::SuspendExtensionCallback callback) override {
+    std::move(callback).Run();
+  }
+  void CancelSuspendExtension(const ExtensionId& extension_id) override {}
+  void SetDeveloperMode(bool current_developer_mode) override {}
+  void SetUserScriptsAllowed(const ExtensionId& extension_id,
+                             bool allowed) override {}
+  void SetSessionInfo(version_info::Channel channel,
+                      mojom::FeatureSessionType session) override {}
+  void SetSystemFont(const std::string& font_family,
+                     const std::string& font_size) override {}
+  void SetWebViewPartitionID(const std::string& partition_id) override {}
+  void SetScriptingAllowlist(
+      const std::vector<ExtensionId>& extension_ids) override {}
+  void UpdateUserScriptWorlds(
+      std::vector<mojom::UserScriptWorldInfoPtr> info) override {}
+  void ClearUserScriptWorldConfig(
+      const ExtensionId& extension_id,
+      const std::optional<std::string>& world_id) override {}
+  void ShouldSuspend(ShouldSuspendCallback callback) override {
+    std::move(callback).Run();
+  }
+  void TransferBlobs(TransferBlobsCallback callback) override {
+    std::move(callback).Run();
+  }
+  void UpdatePermissions(const ExtensionId& extension_id,
+                         PermissionSet active_permissions,
Loading diff…

Regression Test / PoC

shipped with the fix
diff --git a/extensions/browser/user_script_loader_unittest.cc b/extensions/browser/user_script_loader_unittest.cc
new file mode 100644
index 0000000..3cc7818
--- /dev/null
+++ b/extensions/browser/user_script_loader_unittest.cc
@@ -0,0 +1,259 @@
+// Copyright 2026 The Chromium Authors
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "extensions/browser/user_script_loader.h"
+
+#include <memory>
+#include <set>
+#include <string>
+#include <utility>
+
+#include "base/functional/bind.h"
+#include "base/functional/callback_helpers.h"
+#include "base/memory/raw_ptr.h"
+#include "base/memory/read_only_shared_memory_region.h"
+#include "base/run_loop.h"
+#include "base/test/bind.h"
+#include "content/public/browser/render_process_host.h"
+#include "content/public/test/mock_render_process_host.h"
+#include "extensions/browser/embedder_user_script_loader.h"
+#include "extensions/browser/extensions_test.h"
+#include "extensions/browser/guest_view/web_view/web_view_renderer_state.h"
+#include "extensions/browser/renderer_startup_helper.h"
+#include "extensions/common/mojom/host_id.mojom.h"
+#include "extensions/common/mojom/renderer.mojom.h"
+#include "extensions/common/user_script.h"
+#include "mojo/public/cpp/bindings/associated_receiver_set.h"
+#include "testing/gtest/include/gtest/gtest.h"
+#include "url/gurl.h"
+
+namespace extensions {
+
+namespace {
+
+// A RendererStartupHelper that intercepts mojom::Renderer messages and records
+// which processes receive UpdateUserScripts. This lets the tests below verify
+// that user scripts are only shipped to the intended set of renderers.
+class TestRendererStartupHelper : public RendererStartupHelper,
+                                  public mojom::Renderer {
+ public:
+  explicit TestRendererStartupHelper(content::BrowserContext* browser_context)
+      : RendererStartupHelper(browser_context) {}
+
+  static std::unique_ptr<KeyedService> Build(
+      content::BrowserContext* browser_context) {
+    return std::make_unique<TestRendererStartupHelper>(browser_context);
+  }
+
+  bool ProcessReceivedUpdateUserScripts(
+      content::RenderProcessHost* process) const {
+    return updated_processes_.contains(process);
+  }
+
+ protected:
+  // RendererStartupHelper:
+  mojo::PendingAssociatedRemote<mojom::Renderer> BindNewRendererRemote(
+      content::RenderProcessHost* process) override {
+    mojo::AssociatedRemote<mojom::Renderer> remote;
+    renderer_receivers_.Add(
+        this, remote.BindNewEndpointAndPassDedicatedReceiver(), process);
+    return remote.Unbind();
+  }
+
+ private:
+  // mojom::Renderer:
+  void ActivateExtension(const ExtensionId& extension_id) override {}
+  void SetActivityLoggingEnabled(bool enabled) override {}
+  void SetPolicyActivityLoggingEnabled(bool enabled) override {}
+  void LoadExtensions(
+      std::vector<mojom::ExtensionLoadedParamsPtr> loaded_extensions) override {
+  }
+  void UnloadExtension(const ExtensionId& extension_id) override {}
+  void SuspendExtension(
+      const ExtensionId& extension_id,
+      mojom::Renderer::SuspendExtensionCallback callback) override {
+    std::move(callback).Run();
+  }
+  void CancelSuspendExtension(const ExtensionId& extension_id) override {}
+  void SetDeveloperMode(bool current_developer_mode) override {}
+  void SetUserScriptsAllowed(const ExtensionId& extension_id,
+                             bool allowed) override {}
+  void SetSessionInfo(version_info::Channel channel,
+                      mojom::FeatureSessionType session) override {}
+  void SetSystemFont(const std::string& font_family,
+                     const std::string& font_size) override {}
+  void SetWebViewPartitionID(const std::string& partition_id) override {}
+  void SetScriptingAllowlist(
+      const std::vector<ExtensionId>& extension_ids) override {}
+  void UpdateUserScriptWorlds(
+      std::vector<mojom::UserScriptWorldInfoPtr> info) override {}
+  void ClearUserScriptWorldConfig(
+      const ExtensionId& extension_id,
+      const std::optional<std::string>& world_id) override {}
+  void ShouldSuspend(ShouldSuspendCallback callback) override {
+    std::move(callback).Run();
+  }
+  void TransferBlobs(TransferBlobsCallback callback) override {
+    std::move(callback).Run();
+  }
+  void UpdatePermissions(const ExtensionId& extension_id,
+                         PermissionSet active_permissions,
+                         PermissionSet withheld_permissions,
+                         URLPatternSet policy_blocked_hosts,
+                         URLPatternSet policy_allowed_hosts,
+                         bool uses_default_policy_host_restrictions) override {}
+  void UpdateDefaultPolicyHostRestrictions(
+      URLPatternSet default_policy_blocked_hosts,
+      URLPatternSet default_policy_allowed_hosts) override {}
+  void UpdateUserHostRestrictions(URLPatternSet user_blocked_hosts,
+                                  URLPatternSet user_allowed_hosts) override {}
+  void UpdateTabSpecificPermissions(const ExtensionId& extension_id,
+                                    URLPatternSet new_hosts,
+                                    int tab_id,
+                                    bool update_origin_allowlist) override {}
+  void UpdateUserScripts(base::ReadOnlySharedMemoryRegion shared_memory,
+                         mojom::HostIDPtr host_id) override {
+    updated_processes_.insert(renderer_receivers_.current_context());
+  }
+  void ClearTabSpecificPermissions(
+      const std::vector<ExtensionId>& extension_ids,
+      int tab_id,
+      bool update_origin_allowlist) override {}
+  void WatchPages(const std::vector<std::string>& css_selectors) override {}
+
+  std::set<content::RenderProcessHost*> updated_processes_;
+  mojo::AssociatedReceiverSet<mojom::Renderer, content::RenderProcessHost*>
+      renderer_receivers_;
+};
+
+class UserScriptLoaderUnitTest : public ExtensionsTest {
+ public:
+  UserScriptLoaderUnitTest() = default;
+  UserScriptLoaderUnitTest(const UserScriptLoaderUnitTest&) = delete;
+  UserScriptLoaderUnitTest& operator=(const UserScriptLoaderUnitTest&) = delete;
+  ~UserScriptLoaderUnitTest() override = default;
+
+  void SetUp() override {
+    ExtensionsTest::SetUp();
+    helper_ = static_cast<TestRendererStartupHelper*>(
+        RendererStartupHelperFactory::GetInstance()->SetTestingFactoryAndUse(
+            browser_context(),
+            base::BindRepeating(&TestRendererStartupHelper::Build)));
+  }
+
+  void TearDown() override {
+    helper_ = nullptr;
+    ExtensionsTest::TearDown();
+  }
+
+ protected:
+  TestRendererStartupHelper* helper() { return helper_; }
+
+  std::unique_ptr<content::MockRenderProcessHost> CreateAndInitializeProcess(
+      bool is_for_guests_only) {
+    auto process = std::make_unique<content::MockRenderProcessHost>(
+        browser_context(), is_for_guests_only);
+    helper_->OnRenderProcessHostCreated(process.get());
+    if (is_for_guests_only) {
+      helper_->OnRenderProcessLaunched(process.get());
+    }
+    return process;
+  }
+
+  void LoadScriptsAndWait(UserScriptLoader* loader,
+                          content::RenderProcessHost* embedder_process) {
+    auto script = std::make_unique<UserScript>();
+    script->set_id(UserScript::GenerateUserScriptID());
+    script->set_host_id(loader->host_id());
+    auto content = UserScript::Content::CreateInlineCode(
+        GURL("https://embedder.example/inline.js"));
+    content->set_content("/* content script body */");
+    script->js_scripts().push_back(std::move(content));
+
+    UserScriptList scripts;
+    scripts.push_back(std::move(script));
+
+    base::RunLoop run_loop;
+    loader->AddScripts(
+        std::move(scripts), embedder_process->GetDeprecatedID(),
+        /*render_frame_id=*/0,
+        base::BindLambdaForTesting(
+            [&run_loop](UserScriptLoader*,
+                        const std::optional<std::string>& error) {
+              EXPECT_FALSE(error.has_value()) << *error;
+              run_loop.Quit();
+            }));
+    run_loop.Run();
+    // Flush any pending mojo messages so that UpdateUserScripts is delivered.
+    helper_->FlushAllForTesting();
+    base::RunLoop().RunUntilIdle();
+  }
+
+ private:
+  raw_ptr<TestRendererStartupHelper> helper_ = nullptr;
+};
+
+// Test success case that embedder content script is sent to
+// guest renderer.
+TEST_F(UserScriptLoaderUnitTest, EmbedderScriptsSentToGuestRenderer) {
+  std::unique_ptr<content::MockRenderProcessHost> guest_process =
+      CreateAndInitializeProcess(/*is_for_guests_only=*/true);
+  ASSERT_TRUE(helper()->IsProcessInitializedForTesting(guest_process.get()));
+
+  const std::string owner_host = "isolated-app://embedder.example";
+  WebViewRendererState::WebViewInfo web_view_info;
+  web_view_info.owner_host = owner_host;
+  const int dummy_routing_id = 1;
+  WebViewRendererState::GetInstance()->AddGuestForTesting(
+      guest_process->GetDeprecatedID(), dummy_routing_id, web_view_info);
+  base::ScopedClosureRunner cleanup_guest(base::BindOnce(
+      [](int process_id, int routing_id) {
+        WebViewRendererState::GetInstance()->RemoveGuestForTesting(process_id,
+                                                                   routing_id);
+      },
+      guest_process->GetDeprecatedID(), dummy_routing_id));
+
+  EmbedderUserScriptLoader loader(
+      browser_context(),
+      mojom::HostID(mojom::HostID::HostType::kControlledFrameEmbedder,
+                    owner_host));
+  LoadScriptsAndWait(&loader, guest_process.get());
+
+  EXPECT_TRUE(loader.initial_load_complete());
+  EXPECT_TRUE(helper()->ProcessReceivedUpdateUserScripts(guest_process.get()));
+}
+
+// Content scripts that an embedder adds to its own guests
+// only ever inject into those guests, so they should not be shipped to
+// unrelated renderer processes hosting ordinary web content in the same
+// profile.
+TEST_F(UserScriptLoaderUnitTest, EmbedderScriptsNotSentToNonGuestRenderer) {
+  const struct {
+    mojom::HostID::HostType host_type;
+    const char* host;
+  } test_cases[] = {
+      {mojom::HostID::HostType::kControlledFrameEmbedder,
+       "isolated-app://embedder.example"},
+      {mojom::HostID::HostType::kWebUi, "chrome://embedder.example/"},
+  };
+
+  for (const auto& test_case : test_cases) {
+    SCOPED_TRACE(testing::Message() << "host=" << test_case.host);
+
+    std::unique_ptr<content::MockRenderProcessHost> web_process =
+        CreateAndInitializeProcess(/*is_for_guests_only=*/false);
+    ASSERT_TRUE(helper()->IsProcessInitializedForTesting(web_process.get()));
+
+    EmbedderUserScriptLoader loader(
+        browser_context(), mojom::HostID(test_case.host_type, test_case.host));
+    LoadScriptsAndWait(&loader, web_process.get());
+
+    EXPECT_TRUE(loader.initial_load_complete());
+    EXPECT_FALSE(helper()->ProcessReceivedUpdateUserScripts(web_process.get()));
+  }
+}
+
+}  // namespace
+
+}  // namespace extensions
Loading diff…

Original Bug Report

reported by vm...@google.com

IWA content script source leak to same-profile renderers

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 logic flaw in UserScriptLoader::SendUpdate potentially allows regular web renderers to receive the private content script source code of Isolated Web Apps (IWAs). The security check meant to restrict IWA scripts to their specific origin is completely bypassed for non-guest renderers. A compromised renderer can map the broadcasted shared memory region and read the cross-origin IWA scripts, bypassing Site Isolation.

Affected files:

  • extensions/browser/user_script_loader.cc

Estimated timestamp from git blame: 2025-02-10

Summary

There is a potential cross-origin information disclosure vulnerability in extensions/browser/user_script_loader.cc. The function UserScriptLoader::SendUpdate is responsible for distributing serialized user scripts (including those from Isolated Web Apps’ <controlledframe> elements) to renderer processes via shared memory. However, a logic error causes the script-to-origin authorization check to be skipped for all standard (non-guest) web renderers, indiscriminately broadcasting sensitive IWA scripts to them.

Vulnerability Details

  1. When an Isolated Web App (IWA) registers a content script (e.g., via controlledframe.addContentScripts), a mojom::HostID of type kControlledFrameEmbedder is generated for it.
  2. The script source code is serialized into a base::ReadOnlySharedMemoryRegion.
  3. The browser process iterates over all active RenderProcessHost instances to distribute the updated scripts, calling UserScriptLoader::SendUpdate().
  4. Normal web tabs and IWAs share the same BrowserContext (Profile). Thus, the initial IsSameContext check in SendUpdate passes for normal web renderers.
  5. SendUpdate contains a validation block designed to prevent scripts from being sent to the wrong origin:
#if BUILDFLAG(ENABLE_GUEST_VIEW)
  if (process->IsForGuestsOnly() &&
      !CanExecuteScriptEverywhere(browser_context_, host_id())) {
    // ...
    std::string owner_host;
    // ... 
    switch (host_id().type) {
      case mojom::HostID::HostType::kControlledFrameEmbedder:
        if (owner_host != host_id().id) {
          return SendUpdateResult::kNoActionTaken;
        }
    }
  }
#endif
  1. For a standard web renderer (e.g., an attacker’s website), process->IsForGuestsOnly() evaluates to false.
  2. As a result, the entire validation block is skipped. The strict owner_host != host_id().id comparison is never executed.
  3. Execution falls through to renderer->UpdateUserScripts(...), dispatching an IPC with the duplicated shared memory handle containing the IWA’s scripts to the regular web renderer.

Potential Attack Steps

Note: Our tooling agent does not have the ability to run code, so these are suggested/potential steps based on static analysis.

  1. The victim installs and opens an IWA that registers private content scripts.
  2. The victim navigates to an attacker-controlled website (e.g., https://evil.com) in a normal browser tab within the same Profile.
  3. The attacker leverages a separate vulnerability (such as a V8 memory corruption bug) to gain code execution within the sandboxed renderer process for evil.com.
  4. The compromised renderer intercepts the mojom::Renderer::UpdateUserScripts IPC broadcasted by the browser process.
  5. The attacker maps the ReadOnlySharedMemoryRegion and unpickles the data to read the plaintext source code of the IWA’s content scripts, bypassing IWA and Site Isolation boundaries.

Suggested Fix

The authorization checks for specific host types (like kControlledFrameEmbedder) must not be gated exclusively behind process->IsForGuestsOnly(). The logic should be refactored so that if a HostID is explicitly tied to a specific embedder or origin, it is only sent to renderers matching that origin, regardless of whether the target process is a guest view or a normal tab. For example, explicitly block kControlledFrameEmbedder scripts from being sent to any renderer where the origin does not match the host_id().id.

Evaluated with Chrome root at commit: 137d451a126685dd5010e6609db9f6d4a78d8234


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
Links in the report