Low chrome Logic Error 🔧 Commit mapped

Overview

Low
Severity
CVSS
No
Exploited ITW
Fixed
Fix Status
ImpactInappropriate implementation in Omnibox
DescriptionInappropriate implementation in Omnibox
ComponentOmnibox
Bug ClassLogic Error
Tracker504210171
Fix commit930491ddf198 (chromium/src) +100/-9
CISA KEVNot listed
CreditedGoogle
Disclosed2026-06-30

Changed Functions

FunctionChangeNotes
TEST_F
ios/chrome/browser/omnibox/model/omnibox_text_controller_unittest.mm
modified
TEST_F
ios/chrome/browser/omnibox/ui/omnibox_text_field_ios_unittest.mm
modified

Files Changed

  • ios/chrome/browser/omnibox/model/BUILD.gn
  • ios/chrome/browser/omnibox/model/omnibox_text_controller.mm
  • ios/chrome/browser/omnibox/model/omnibox_text_controller_unittest.mm
  • ios/chrome/browser/omnibox/ui/omnibox_text_field_ios_unittest.mm
  • ios/chrome/browser/omnibox/ui/omnibox_text_field_paste_delegate.mm
From 930491ddf198e351de441a69f5555b9db5141863 Mon Sep 17 00:00:00 2001
From: Justin Cohen <[email protected]>
Date: Mon, 18 May 2026 09:42:14 -0700
Subject: [PATCH] ios: Sanitize drag-and-drop and refinement inputs in the Omnibox.

Update the UITextPasteDelegate and OmniboxTextController refinement
implementations in the omnibox to run incoming text and URL inputs
through the standard SanitizeTextForPaste utility.

This aligns behavior between manual clipboard pastes, drag-and-drop
delegates, and custom drop/refinement flows (such as Composebox).

Fixed: 504210171
Change-Id: I04e84f74c0be6880e8bbe2edff51726c18afa38a
Reviewed-on: https://chromium-review.googlesource.com/c/chromium/src/+/7854723
Auto-Submit: Justin Cohen <[email protected]>
Reviewed-by: Radu Nitescu <[email protected]>
Commit-Queue: Justin Cohen <[email protected]>
Cr-Commit-Position: refs/heads/main@{#1632222}
---

diff --git a/ios/chrome/browser/omnibox/model/BUILD.gn b/ios/chrome/browser/omnibox/model/BUILD.gn
index df3803d..39846d22 100644
--- a/ios/chrome/browser/omnibox/model/BUILD.gn
+++ b/ios/chrome/browser/omnibox/model/BUILD.gn
@@ -162,6 +162,7 @@
     "//extensions/buildflags",
     "//ios/chrome/browser/autocomplete/model",
     "//ios/chrome/browser/location_bar/model:test_support",
+    "//ios/chrome/browser/omnibox/ui:model_interface",
     "//ios/chrome/browser/search_engines/model:template_url_service_factory",
     "//ios/chrome/browser/shared/model/browser/test:test_support",
     "//ios/chrome/browser/shared/model/prefs:browser_prefs",
diff --git a/ios/chrome/browser/omnibox/model/omnibox_text_controller.mm b/ios/chrome/browser/omnibox/model/omnibox_text_controller.mm
index cb3d02e6..4480b5a5 100644
--- a/ios/chrome/browser/omnibox/model/omnibox_text_controller.mm
+++ b/ios/chrome/browser/omnibox/model/omnibox_text_controller.mm
@@ -837,13 +837,14 @@
 }
 
 - (void)refineWithText:(const std::u16string&)text {
+  std::u16string sanitizedText = omnibox::SanitizeTextForPaste(text);
   id<OmniboxTextInput> textInput = self.textInput;
   // Exit preedit state and append the match. Refocus if necessary.
   [textInput exitPreEditState];
-  [self setUserText:text];
+  [self setUserText:sanitizedText];
 
-  [self setWindowText:text
-               caretPos:text.length()
+  [self setWindowText:sanitizedText
+               caretPos:sanitizedText.length()
       startAutocomplete:true
       notifyTextChanged:true];
 
@@ -853,7 +854,7 @@
   [textInput.omniboxTextInputDelegate textInputDidChange:textInput];
   [textInput.view becomeFirstResponder];
   // Set the caret pos to the end of the text (crbug.com/331622199).
-  [self setCaretPos:text.length()];
+  [self setCaretPos:sanitizedText.length()];
 }
 
 #pragma mark - Private
diff --git a/ios/chrome/browser/omnibox/model/omnibox_text_controller_unittest.mm b/ios/chrome/browser/omnibox/model/omnibox_text_controller_unittest.mm
index 6be39cf..f8e97ed 100644
--- a/ios/chrome/browser/omnibox/model/omnibox_text_controller_unittest.mm
+++ b/ios/chrome/browser/omnibox/model/omnibox_text_controller_unittest.mm
@@ -16,6 +16,7 @@
 #import "ios/chrome/browser/omnibox/model/fake_omnibox_client.h"
 #import "ios/chrome/browser/omnibox/model/omnibox_autocomplete_controller.h"
 #import "ios/chrome/browser/omnibox/model/omnibox_text_model.h"
+#import "ios/chrome/browser/omnibox/ui/omnibox_text_input.h"
 #import "ios/chrome/browser/search_engines/model/template_url_service_factory.h"
 #import "ios/chrome/browser/shared/model/prefs/browser_prefs.h"
 #import "ios/chrome/browser/shared/model/prefs/pref_names.h"
@@ -26,6 +27,8 @@
 #import "testing/gmock/include/gmock/gmock.h"
 #import "testing/gtest/include/gtest/gtest.h"
 #import "testing/platform_test.h"
+#import "third_party/ocmock/OCMock/OCMock.h"
+#import "third_party/ocmock/gtest_support.h"
 
 // Mocking the text controller to not rely on the textfield view.
 @interface TestOmniboxTextController : OmniboxTextController
@@ -123,9 +126,9 @@
     omnibox_text_controller_ = nil;
     [omnibox_autocomplete_controller_ disconnect];
     omnibox_autocomplete_controller_ = nil;
+    omnibox_text_model_.reset();
     autocomplete_classifier_override_->Shutdown();
     autocomplete_controller_.reset();
-    omnibox_text_model_.reset();
     omnibox_client_.reset();
     profile_.reset();
     TestingApplicationContext::GetGlobal()->SetLocalState(nullptr);
@@ -296,3 +299,32 @@
   [omnibox_text_controller_ onCopy];
   // The test passes if it doesn't crash.
 }
+
+TEST_F(OmniboxTextControllerTest, RefineWithTextSanitizesJavaScript) {
+  id textInputMock = OCMProtocolMock(@protocol(OmniboxTextInput));
+  OCMStub([textInputMock exitPreEditState]);
+  OCMStub([textInputMock view]).andReturn([[UIView alloc] init]);
+  OCMStub([textInputMock omniboxTextInputDelegate]).andReturn(nil);
+
+  __block NSString* textValue = @"";
+  OCMStub([textInputMock setText:[OCMArg any]])
+      .andDo(^(NSInvocation* invocation) {
+        void* arg;
+        [invocation getArgument:&arg atIndex:2];
+        textValue = (__bridge NSString*)arg;
+      });
+  OCMStub([textInputMock text]).andDo(^(NSInvocation* invocation) {
+    [invocation setReturnValue:&textValue];
+  });
+
+  omnibox_text_controller_.textInput = textInputMock;
+
+  [omnibox_text_controller_ refineWithText:u"https://example.com"];
+  EXPECT_EQ(u"https://example.com", [omnibox_text_controller_ displayedText]);
+
+  [omnibox_text_controller_ refineWithText:u"javascript:alert(1)"];
+  EXPECT_EQ(u"alert(1)", [omnibox_text_controller_ displayedText]);
+
+  [omnibox_text_controller_ refineWithText:u"java\x0d\x0ascript:alert(2)"];
+  EXPECT_EQ(u"alert(2)", [omnibox_text_controller_ displayedText]);
+}
diff --git a/ios/chrome/browser/omnibox/ui/omnibox_text_field_ios_unittest.mm b/ios/chrome/browser/omnibox/ui/omnibox_text_field_ios_unittest.mm
index 971c21a3..7fd7ec8 100644
--- a/ios/chrome/browser/omnibox/ui/omnibox_text_field_ios_unittest.mm
+++ b/ios/chrome/browser/omnibox/ui/omnibox_text_field_ios_unittest.mm
@@ -12,6 +12,7 @@
 #import "base/test/allow_check_is_test_for_testing.h"
 #import "base/test/task_environment.h"
 #import "ios/chrome/browser/omnibox/public/omnibox_presentation_context.h"
+#import "ios/chrome/browser/omnibox/ui/omnibox_text_field_paste_delegate.h"
 #import "ios/chrome/browser/omnibox/ui/omnibox_text_input_delegate.h"
 #import "ios/chrome/browser/shared/model/paths/paths.h"
 #import "ios/chrome/browser/shared/ui/util/uikit_ui_util.h"
@@ -239,3 +240,51 @@
 }
 
 }  // namespace
+
+@interface OmniboxTextFieldPasteDelegate (Testing)
+@property(nonatomic, strong) NSURL* URL;
+@end
+
+TEST_F(OmniboxTextFieldIOSTest, PasteDelegateSanitizesDragAndDrop) {
+  OmniboxTextFieldPasteDelegate* delegate =
+      [[OmniboxTextFieldPasteDelegate alloc] init];
+  delegate.textInput = textfield_;
+
+  UITextRange* range = OCMClassMock([UITextRange class]);
+
+  // 1. Test standard string drop (without javascript scheme)
+  NSAttributedString* item1 =
+      [[NSAttributedString alloc] initWithString:@"https://example.com"];
+  NSAttributedString* result1 =
+      [delegate textPasteConfigurationSupporting:textfield_
+                    combineItemAttributedStrings:@[ item1 ]
+                                        forRange:range];
+  EXPECT_NSEQ(@"https://example.com", result1.string);
+
+  // 2. Test malicious javascript scheme drop
+  NSAttributedString* item2 =
+      [[NSAttributedString alloc] initWithString:@"javascript:alert(1)"];
+  NSAttributedString* result2 =
+      [delegate textPasteConfigurationSupporting:textfield_
+                    combineItemAttributedStrings:@[ item2 ]
+                                        forRange:range];
+  EXPECT_NSEQ(@"alert(1)", result2.string);
+
+  // 3. Test nested/broken javascript scheme drops
+  NSAttributedString* item3 = [[NSAttributedString alloc]
+      initWithString:@"java\x0d\x0ascript:alert(0)"];
+  NSAttributedString* result3 =
+      [delegate textPasteConfigurationSupporting:textfield_
+                    combineItemAttributedStrings:@[ item3 ]
+                                        forRange:range];
+  EXPECT_NSEQ(@"alert(0)", result3.string);
+
+  // 4. Test cached URL sanitization
+  delegate.URL = [NSURL URLWithString:@"javascript:alert(2)"];
+  NSAttributedString* result4 =
+      [delegate textPasteConfigurationSupporting:textfield_
+                    combineItemAttributedStrings:@[]
+                                        forRange:range];
+  EXPECT_NSEQ(@"alert(2)", result4.string);
+  EXPECT_EQ(nil, delegate.URL);
+}
diff --git a/ios/chrome/browser/omnibox/ui/omnibox_text_field_paste_delegate.mm b/ios/chrome/browser/omnibox/ui/omnibox_text_field_paste_delegate.mm
index c784942..ea55b2d 100644
--- a/ios/chrome/browser/omnibox/ui/omnibox_text_field_paste_delegate.mm
+++ b/ios/chrome/browser/omnibox/ui/omnibox_text_field_paste_delegate.mm
@@ -5,6 +5,8 @@
 #import "ios/chrome/browser/omnibox/ui/omnibox_text_field_paste_delegate.h"
 
Loading diff…

Regression Test / PoC

shipped with the fix
diff --git a/ios/chrome/browser/omnibox/model/omnibox_text_controller_unittest.mm b/ios/chrome/browser/omnibox/model/omnibox_text_controller_unittest.mm
index 6be39cf..f8e97ed 100644
--- a/ios/chrome/browser/omnibox/model/omnibox_text_controller_unittest.mm
+++ b/ios/chrome/browser/omnibox/model/omnibox_text_controller_unittest.mm
@@ -16,6 +16,7 @@
 #import "ios/chrome/browser/omnibox/model/fake_omnibox_client.h"
 #import "ios/chrome/browser/omnibox/model/omnibox_autocomplete_controller.h"
 #import "ios/chrome/browser/omnibox/model/omnibox_text_model.h"
+#import "ios/chrome/browser/omnibox/ui/omnibox_text_input.h"
 #import "ios/chrome/browser/search_engines/model/template_url_service_factory.h"
 #import "ios/chrome/browser/shared/model/prefs/browser_prefs.h"
 #import "ios/chrome/browser/shared/model/prefs/pref_names.h"
@@ -26,6 +27,8 @@
 #import "testing/gmock/include/gmock/gmock.h"
 #import "testing/gtest/include/gtest/gtest.h"
 #import "testing/platform_test.h"
+#import "third_party/ocmock/OCMock/OCMock.h"
+#import "third_party/ocmock/gtest_support.h"
 
 // Mocking the text controller to not rely on the textfield view.
 @interface TestOmniboxTextController : OmniboxTextController
@@ -123,9 +126,9 @@
     omnibox_text_controller_ = nil;
     [omnibox_autocomplete_controller_ disconnect];
     omnibox_autocomplete_controller_ = nil;
+    omnibox_text_model_.reset();
     autocomplete_classifier_override_->Shutdown();
     autocomplete_controller_.reset();
-    omnibox_text_model_.reset();
     omnibox_client_.reset();
     profile_.reset();
     TestingApplicationContext::GetGlobal()->SetLocalState(nullptr);
@@ -296,3 +299,32 @@
   [omnibox_text_controller_ onCopy];
   // The test passes if it doesn't crash.
 }
+
+TEST_F(OmniboxTextControllerTest, RefineWithTextSanitizesJavaScript) {
+  id textInputMock = OCMProtocolMock(@protocol(OmniboxTextInput));
+  OCMStub([textInputMock exitPreEditState]);
+  OCMStub([textInputMock view]).andReturn([[UIView alloc] init]);
+  OCMStub([textInputMock omniboxTextInputDelegate]).andReturn(nil);
+
+  __block NSString* textValue = @"";
+  OCMStub([textInputMock setText:[OCMArg any]])
+      .andDo(^(NSInvocation* invocation) {
+        void* arg;
+        [invocation getArgument:&arg atIndex:2];
+        textValue = (__bridge NSString*)arg;
+      });
+  OCMStub([textInputMock text]).andDo(^(NSInvocation* invocation) {
+    [invocation setReturnValue:&textValue];
+  });
+
+  omnibox_text_controller_.textInput = textInputMock;
+
+  [omnibox_text_controller_ refineWithText:u"https://example.com"];
+  EXPECT_EQ(u"https://example.com", [omnibox_text_controller_ displayedText]);
+
+  [omnibox_text_controller_ refineWithText:u"javascript:alert(1)"];
+  EXPECT_EQ(u"alert(1)", [omnibox_text_controller_ displayedText]);
+
+  [omnibox_text_controller_ refineWithText:u"java\x0d\x0ascript:alert(2)"];
+  EXPECT_EQ(u"alert(2)", [omnibox_text_controller_ displayedText]);
+}
diff --git a/ios/chrome/browser/omnibox/ui/omnibox_text_field_ios_unittest.mm b/ios/chrome/browser/omnibox/ui/omnibox_text_field_ios_unittest.mm
index 971c21a3..7fd7ec8 100644
--- a/ios/chrome/browser/omnibox/ui/omnibox_text_field_ios_unittest.mm
+++ b/ios/chrome/browser/omnibox/ui/omnibox_text_field_ios_unittest.mm
@@ -12,6 +12,7 @@
 #import "base/test/allow_check_is_test_for_testing.h"
 #import "base/test/task_environment.h"
 #import "ios/chrome/browser/omnibox/public/omnibox_presentation_context.h"
+#import "ios/chrome/browser/omnibox/ui/omnibox_text_field_paste_delegate.h"
 #import "ios/chrome/browser/omnibox/ui/omnibox_text_input_delegate.h"
 #import "ios/chrome/browser/shared/model/paths/paths.h"
 #import "ios/chrome/browser/shared/ui/util/uikit_ui_util.h"
@@ -239,3 +240,51 @@
 }
 
 }  // namespace
+
+@interface OmniboxTextFieldPasteDelegate (Testing)
+@property(nonatomic, strong) NSURL* URL;
+@end
+
+TEST_F(OmniboxTextFieldIOSTest, PasteDelegateSanitizesDragAndDrop) {
+  OmniboxTextFieldPasteDelegate* delegate =
+      [[OmniboxTextFieldPasteDelegate alloc] init];
+  delegate.textInput = textfield_;
+
+  UITextRange* range = OCMClassMock([UITextRange class]);
+
+  // 1. Test standard string drop (without javascript scheme)
+  NSAttributedString* item1 =
+      [[NSAttributedString alloc] initWithString:@"https://example.com"];
+  NSAttributedString* result1 =
+      [delegate textPasteConfigurationSupporting:textfield_
+                    combineItemAttributedStrings:@[ item1 ]
+                                        forRange:range];
+  EXPECT_NSEQ(@"https://example.com", result1.string);
+
+  // 2. Test malicious javascript scheme drop
+  NSAttributedString* item2 =
+      [[NSAttributedString alloc] initWithString:@"javascript:alert(1)"];
+  NSAttributedString* result2 =
+      [delegate textPasteConfigurationSupporting:textfield_
+                    combineItemAttributedStrings:@[ item2 ]
+                                        forRange:range];
+  EXPECT_NSEQ(@"alert(1)", result2.string);
+
+  // 3. Test nested/broken javascript scheme drops
+  NSAttributedString* item3 = [[NSAttributedString alloc]
+      initWithString:@"java\x0d\x0ascript:alert(0)"];
+  NSAttributedString* result3 =
+      [delegate textPasteConfigurationSupporting:textfield_
+                    combineItemAttributedStrings:@[ item3 ]
+                                        forRange:range];
+  EXPECT_NSEQ(@"alert(0)", result3.string);
+
+  // 4. Test cached URL sanitization
+  delegate.URL = [NSURL URLWithString:@"javascript:alert(2)"];
+  NSAttributedString* result4 =
+      [delegate textPasteConfigurationSupporting:textfield_
+                    combineItemAttributedStrings:@[]
+                                        forRange:range];
+  EXPECT_NSEQ(@"alert(2)", result4.string);
+  EXPECT_EQ(nil, delegate.URL);
+}
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.