Low chrome Logic Error 🔧 Commit mapped

Overview

Low
Severity
CVSS
No
Exploited ITW
Fixed
Fix Status
ImpactInappropriate implementation in Chrome for iOS
DescriptionInappropriate implementation in Chrome for iOS
ComponentChrome for iOS
Bug ClassLogic Error
Tracker504209246
Fix commit48a2dca4b709 (chromium/src) +103/-8
CISA KEVNot listed
CreditedGoogle
Disclosed2026-07-29

Changed Functions

FunctionChangeNotes
AnnotationTextManagerTest
ios/web/annotations/annotations_java_script_feature.h
modified
AnnotationsJavaScriptFeature
ios/web/annotations/annotations_java_script_feature.h
modified
GetAnnotationsReplacements
ios/web/annotations/annotations_java_script_feature.mm
modified
trusted_event_check_enabled_
ios/web/annotations/annotations_java_script_feature.mm
modified
if
ios/web/annotations/annotations_java_script_feature.mm
modified
for
ios/web/annotations/annotations_java_script_feature.mm
modified
switch
ios/web/annotations/resources/text_click.ts
modified
MutationsTracker
ios/web/annotations/resources/text_click.ts
modified

Files Changed

  • ios/web/annotations/annotations_inttest.mm
  • ios/web/annotations/annotations_java_script_feature.h
  • ios/web/annotations/annotations_java_script_feature.mm
  • ios/web/annotations/resources/text_click.ts
From 48a2dca4b7094b466a37d18dc09113485466b99c Mon Sep 17 00:00:00 2001
From: Olivier ROBIN <olivierrobin@google.com>
Date: Tue, 09 Jun 2026 07:09:35 -0700
Subject: [PATCH] Check isTrusted for annotations events

Only allow trusted event to trigger native UI on annotations.

Fixed: 504209246
Change-Id: I9c2dd404cdbe4236d64f34a8a0992e6cb1daf5e7
Reviewed-on: https://chromium-review.googlesource.com/c/chromium/src/+/7885878
Commit-Queue: Olivier Robin <olivierrobin@chromium.org>
Auto-Submit: Olivier Robin <olivierrobin@chromium.org>
Reviewed-by: Mike Dougherty <michaeldo@chromium.org>
Cr-Commit-Position: refs/heads/main@{#1643932}
---

diff --git a/ios/web/annotations/annotations_inttest.mm b/ios/web/annotations/annotations_inttest.mm
index 6fabc5c..fb5b310 100644
--- a/ios/web/annotations/annotations_inttest.mm
+++ b/ios/web/annotations/annotations_inttest.mm
@@ -149,6 +149,11 @@
 
  protected:
   void SetUp() override {
+    override_feature_ = base::WrapUnique(new AnnotationsJavaScriptFeature(
+        /*trusted_event_check_enabled=*/false));
+    AnnotationsJavaScriptFeature::SetInstanceForTesting(
+        override_feature_.get());
+
     WebTestWithWebState::SetUp();
 
     AnnotationsTextManager::CreateForWebState(web_state());
@@ -175,6 +180,8 @@
     auto* manager = AnnotationsTextManager::FromWebState(web_state());
     manager->RemoveObserver(&observer_);
     WebTestWithWebState::TearDown();
+
+    AnnotationsJavaScriptFeature::SetInstanceForTesting(nullptr);
   }
 
   virtual std::string GetScriptName() { return ""; }
@@ -320,6 +327,7 @@
 
   TestAnnotationTextObserver* observer() { return &observer_; }
 
+  std::unique_ptr<AnnotationsJavaScriptFeature> override_feature_;
   base::test::ScopedFeatureList feature_;
   raw_ptr<JavaScriptContentWorld> content_world_;
   TestAnnotationTextObserver observer_;
diff --git a/ios/web/annotations/annotations_java_script_feature.h b/ios/web/annotations/annotations_java_script_feature.h
index 74464d9..b9c02773 100644
--- a/ios/web/annotations/annotations_java_script_feature.h
+++ b/ios/web/annotations/annotations_java_script_feature.h
@@ -14,6 +14,8 @@
 
 namespace web {
 
+class AnnotationTextManagerTest;
+
 extern const int kMaxAnnotationsTextLength;
 extern const int kMaxAnnotationsMetadataLength;
 
@@ -23,6 +25,9 @@
 class AnnotationsJavaScriptFeature : public JavaScriptFeature {
  public:
   static AnnotationsJavaScriptFeature* GetInstance();
+  static void SetInstanceForTesting(AnnotationsJavaScriptFeature* instance);
+
+  ~AnnotationsJavaScriptFeature() override;
 
   // Triggers the JS text extraction code. Async calls `OnTextExtracted` on
   // `AnnotationsTextManager` when done using provided `seq_id`.
@@ -49,14 +54,19 @@
                              const ScriptMessage& script_message) override;
   std::optional<std::string> GetScriptMessageHandlerName() const override;
   AnnotationsJavaScriptFeature();
-  ~AnnotationsJavaScriptFeature() override;
 
  private:
   friend class base::NoDestructor<AnnotationsJavaScriptFeature>;
+  friend class AnnotationTextManagerTest;
+
+  // Constructor that allows disabling trusted event checks, e.g. for testing.
+  explicit AnnotationsJavaScriptFeature(bool trusted_event_check_enabled);
 
   AnnotationsJavaScriptFeature(const AnnotationsJavaScriptFeature&) = delete;
   AnnotationsJavaScriptFeature& operator=(const AnnotationsJavaScriptFeature&) =
       delete;
+
+  bool trusted_event_check_enabled_ = true;
 };
 
 }  // namespace web
diff --git a/ios/web/annotations/annotations_java_script_feature.mm b/ios/web/annotations/annotations_java_script_feature.mm
index e4f8c729..100e8578 100644
--- a/ios/web/annotations/annotations_java_script_feature.mm
+++ b/ios/web/annotations/annotations_java_script_feature.mm
@@ -20,6 +20,16 @@
 namespace {
 const char kScriptName[] = "text_main";
 const char kScriptHandlerName[] = "annotations";
+
+web::AnnotationsJavaScriptFeature* g_instance_for_testing = nullptr;
+
+web::JavaScriptFeature::FeatureScript::PlaceholderReplacements
+GetAnnotationsReplacements(bool trusted_event_check_enabled) {
+  return @{
+    @"{{SkipTrustedCheckForTesting}}" : trusted_event_check_enabled ? @"false"
+                                                                    : @"true"
+  };
+}
 }  // namespace
 
 namespace web {
@@ -28,22 +38,38 @@
 const int kMaxAnnotationsMetadataLength = 256;
 
 AnnotationsJavaScriptFeature::AnnotationsJavaScriptFeature()
+    : AnnotationsJavaScriptFeature(true) {}
+
+AnnotationsJavaScriptFeature::AnnotationsJavaScriptFeature(
+    bool trusted_event_check_enabled)
     : JavaScriptFeature(
           ContentWorld::kIsolatedWorld,
           {FeatureScript::CreateWithFilename(
               kScriptName,
               FeatureScript::InjectionTime::kDocumentStart,
               FeatureScript::TargetFrames::kMainFrame,
-              FeatureScript::ReinjectionBehavior::kInjectOncePerWindow)}) {}
+              FeatureScript::ReinjectionBehavior::kInjectOncePerWindow,
+              base::BindRepeating(&GetAnnotationsReplacements,
+                                  trusted_event_check_enabled))}),
+      trusted_event_check_enabled_(trusted_event_check_enabled) {}
 
 AnnotationsJavaScriptFeature::~AnnotationsJavaScriptFeature() = default;
 
 // static
 AnnotationsJavaScriptFeature* AnnotationsJavaScriptFeature::GetInstance() {
+  if (g_instance_for_testing) {
+    return g_instance_for_testing;
+  }
   static base::NoDestructor<AnnotationsJavaScriptFeature> instance;
   return instance.get();
 }
 
+// static
+void AnnotationsJavaScriptFeature::SetInstanceForTesting(
+    AnnotationsJavaScriptFeature* instance) {
+  g_instance_for_testing = instance;
+}
+
 void AnnotationsJavaScriptFeature::ExtractText(WebState* web_state,
                                                int maximum_text_length,
                                                int seq_id) {
@@ -191,6 +217,9 @@
     manager->OnDecorated(web_state, annotations, successes, failures,
                          *cancelled);
   } else if (*command == "annotations.onClick") {
+    if (trusted_event_check_enabled_ && !script_message.is_user_interacting()) {
+      return;
+    }
     for (const auto pair : dict) {
       const std::string& key = pair.first;
       if (key != "command" && key != "data" && key != "rect" && key != "text" &&
diff --git a/ios/web/annotations/resources/text_click.ts b/ios/web/annotations/resources/text_click.ts
index 738889f..1dbf985 100644
--- a/ios/web/annotations/resources/text_click.ts
+++ b/ios/web/annotations/resources/text_click.ts
@@ -18,6 +18,20 @@
 // Delay while checking for DOM mutations.
 export const DOM_MUTATION_DELAY_MS = 300;
 
+/**
+ * Parses a string to a boolean.
+ * @param boolStr The string to parse as a boolean.
+ * @returns The boolean value.
+ */
+function stringAsBool(boolStr: string): boolean {
+  switch (boolStr) {
+    case 'true':
+      return true;
+    default:
+      return false;
+  }
+}
+
 // Monitors DOM mutations between instance construction until a call to
 // `stopObserving`.
 class MutationsTracker {
@@ -97,7 +111,9 @@
       private decorationsProvider: () => Map<number, TextDecoration>| undefined,
       private taskTimer: TaskTimer = new LiveTaskTimer(),
       private mutationCheckDelay = DOM_MUTATION_DELAY_MS,
-      private annotationForTest: Element|null = null) {}
+      private annotationForTest: Element|null = null,
+      private skipTrustedCheckForTesting =
+          stringAsBool('{{SkipTrustedCheckForTesting}}')) {}
 
   // Starts event listeners.
   start(): void {
Loading diff…

Original Bug Report

The reporter's bug is still restricted on the tracker. Chrome de-restricts security bugs ~30–90 days after the fix ships; a later run will backfill it here.