Overview

Low
Severity
CVSS
No
Exploited ITW
Fixed
Fix Status
ImpactInsufficient validation of untrusted input in Omnibox
DescriptionInsufficient validation of untrusted input in Omnibox
ComponentOmnibox
Bug ClassLogic Error
Tracker501757688
Fix commitf1feef305e2e (chromium/src) +32/-1
CISA KEVNot listed
CreditedGoogle
Disclosed2026-06-02

Changed Functions

FunctionChangeNotes
TEST
components/omnibox/browser/search_suggestion_parser_unittest.cc
modified

Files Changed

  • components/omnibox/browser/search_suggestion_parser.cc
  • components/omnibox/browser/search_suggestion_parser_unittest.cc
From f1feef305e2e8e9c382c9104bae90098437ebb73 Mon Sep 17 00:00:00 2001
From: Andrew Paseltiner <apaseltiner@chromium.org>
Date: Wed, 29 Apr 2026 08:21:01 -0700
Subject: [PATCH] Omnibox: Enforce safe schemes for navigation suggestions

SearchSuggestionParser now validates that navigation suggestions from
the suggest endpoint use HTTP or HTTPS schemes. This prevents
potentially malicious suggestions (e.g., javascript: URLs) from reaching
the navigation results.

The sibling EnterpriseSearchAggregatorProvider already enforces
SchemeIsHTTPOrHTTPS() on server-supplied destination URLs and has a
regression test for javascript:alert(1); the general search suggestion
parsing logic used by keyword engines and the default search provider
lacked this validation.

Fixed: 501757688
Change-Id: Iae2ac864e3c6cdddd619275a415294311a02c9f6
Reviewed-on: https://chromium-review.googlesource.com/c/chromium/src/+/7801543
Reviewed-by: Nihar Majmudar <niharm@google.com>
Commit-Queue: Andrew Paseltiner <apaseltiner@chromium.org>
Cr-Commit-Position: refs/heads/main@{#1622469}
---

diff --git a/components/omnibox/browser/search_suggestion_parser.cc b/components/omnibox/browser/search_suggestion_parser.cc
index e436e6e..ecb6c41 100644
--- a/components/omnibox/browser/search_suggestion_parser.cc
+++ b/components/omnibox/browser/search_suggestion_parser.cc
@@ -897,7 +897,7 @@
         (match_type == AutocompleteMatchType::NAVSUGGEST_PERSONALIZED)) {
       // Do not blindly trust the URL coming from the server to be valid.
       GURL url(url_formatter::FixupURL(base::UTF16ToUTF8(suggestion)));
-      if (url.is_valid()) {
+      if (url.is_valid() && url.SchemeIsHTTPOrHTTPS()) {
         std::u16string title;
         // 3rd element: optional descriptions list
         if (root_list.size() > 2u && root_list[2].is_list()) {
diff --git a/components/omnibox/browser/search_suggestion_parser_unittest.cc b/components/omnibox/browser/search_suggestion_parser_unittest.cc
index a007793f..140cc85 100644
--- a/components/omnibox/browser/search_suggestion_parser_unittest.cc
+++ b/components/omnibox/browser/search_suggestion_parser_unittest.cc
@@ -277,6 +277,37 @@
   ASSERT_EQ(expected, results.gws_event_id_hashes[0]);
 }
 
+TEST(SearchSuggestionParserTest, ParseSuggestResultsRejectsInvalidSchemes) {
+  std::string json_data =
+      R"json([
+      "query",
+      ["javascript:alert(1)", "https://example.com"],
+      ["Sign in to continue", "Valid description"],
+      [],
+      {
+        "google:suggesttype": ["NAVIGATION", "NAVIGATION"]
+      }])json";
+  std::optional<base::ListValue> root_val = base::JSONReader::ReadList(
+      json_data, base::JSON_PARSE_CHROMIUM_EXTENSIONS);
+  ASSERT_TRUE(root_val);
+
+  TestSchemeClassifier scheme_classifier;
+  AutocompleteInput input(u"query", metrics::OmniboxEventProto::NTP,
+                          scheme_classifier);
+  SearchSuggestionParser::Results results;
+  ASSERT_TRUE(SearchSuggestionParser::ParseSuggestResults(
+      *root_val, input, scheme_classifier,
+      /*default_result_relevance=*/400,
+      /*is_keyword_result=*/false, &results));
+
+  // The javascript URL should be rejected, and only the valid HTTPS URL
+  // accepted.
+  EXPECT_THAT(results.navigation_results,
+              testing::ElementsAre(testing::Property(
+                  &SearchSuggestionParser::NavigationResult::url,
+                  testing::Eq(GURL("https://example.com/")))));
+}
+
 // Tests that prerender hints can be parsed correctly.
 TEST(SearchSuggestionParserTest, ParsePrerenderSuggestion) {
   std::string json_data = R"([
Loading diff…

Regression Test / PoC

shipped with the fix
diff --git a/components/omnibox/browser/search_suggestion_parser_unittest.cc b/components/omnibox/browser/search_suggestion_parser_unittest.cc
index a007793f..140cc85 100644
--- a/components/omnibox/browser/search_suggestion_parser_unittest.cc
+++ b/components/omnibox/browser/search_suggestion_parser_unittest.cc
@@ -277,6 +277,37 @@
   ASSERT_EQ(expected, results.gws_event_id_hashes[0]);
 }
 
+TEST(SearchSuggestionParserTest, ParseSuggestResultsRejectsInvalidSchemes) {
+  std::string json_data =
+      R"json([
+      "query",
+      ["javascript:alert(1)", "https://example.com"],
+      ["Sign in to continue", "Valid description"],
+      [],
+      {
+        "google:suggesttype": ["NAVIGATION", "NAVIGATION"]
+      }])json";
+  std::optional<base::ListValue> root_val = base::JSONReader::ReadList(
+      json_data, base::JSON_PARSE_CHROMIUM_EXTENSIONS);
+  ASSERT_TRUE(root_val);
+
+  TestSchemeClassifier scheme_classifier;
+  AutocompleteInput input(u"query", metrics::OmniboxEventProto::NTP,
+                          scheme_classifier);
+  SearchSuggestionParser::Results results;
+  ASSERT_TRUE(SearchSuggestionParser::ParseSuggestResults(
+      *root_val, input, scheme_classifier,
+      /*default_result_relevance=*/400,
+      /*is_keyword_result=*/false, &results));
+
+  // The javascript URL should be rejected, and only the valid HTTPS URL
+  // accepted.
+  EXPECT_THAT(results.navigation_results,
+              testing::ElementsAre(testing::Property(
+                  &SearchSuggestionParser::NavigationResult::url,
+                  testing::Eq(GURL("https://example.com/")))));
+}
+
 // Tests that prerender hints can be parsed correctly.
 TEST(SearchSuggestionParserTest, ParsePrerenderSuggestion) {
   std::string json_data = R"([
Loading diff…

Original Bug Report

reported by vm...@google.com

UXSS via unvalidated javascript: schemes in Omnibox NAVSUGGEST

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 Chrome Security team.

Overview: The Omnibox SearchSuggestionParser fails to validate URL schemes for navigation suggestions (NAVSUGGEST) from third-party search engines. This potentially allows a malicious search provider to supply javascript: URLs. If a user manually selects this suggestion in keyword search mode, the script executes in the context of the active tab, leading to Universal Cross-Site Scripting (UXSS).

Affected files:

  • components/omnibox/browser/search_suggestion_parser.cc
  • components/omnibox/browser/search_provider.cc

Estimated timestamp from git blame: 2026-01-07

Summary

A potential Universal Cross-Site Scripting (UXSS) vulnerability exists in the Omnibox SearchSuggestionParser. When processing third-party search engine suggestions, the parser accepts navigation results (NAVSUGGEST) with javascript: schemes. If a user is tricked into selecting such a suggestion while using keyword search mode on a target site, the script payload executes in the context of the currently active tab.

Technical Details

When parsing JSON responses from a search engine’s suggest endpoint, SearchSuggestionParser::ParseSuggestResults handles NAVSUGGEST entries by passing the provided URL string to url_formatter::FixupURL (around line 899 in components/omnibox/browser/search_suggestion_parser.cc).

Because FixupURL successfully parses javascript: URLs, and ParseSuggestResults lacks an explicit scheme allowlist, the URL is accepted and pushed to the navigation results. (For comparison, EnterpriseSearchAggregatorProvider correctly enforces a SchemeIsHTTPOrHTTPS() check on its destination URLs, maintaining this security boundary).

The suggestion is converted into an AutocompleteMatch with the javascript: URL assigned to its destination_url. While the browser mitigates immediate execution by preventing navigational suggestions from being the default match during keyword mode (via providers_.GetKeywordProviderURL() != nullptr), the payload still appears as a selectable row in the Omnibox dropdown.

If the user interacts with the dropdown and selects the malicious suggestion, OmniboxEditModel::OpenMatch initiates a navigation to the javascript: URL in the CURRENT_TAB. The content layer recognizes this as a renderer debug URL, passing it via IPC to the renderer, where LocalFrame::LoadJavaScriptURL executes the payload in the context of the active origin.

Potential Reproduction Steps

Note: These are suggested steps based on code analysis; our tooling agent does not have the ability to run code to verify them.

  1. An attacker hosts an OpenSearch descriptor pointing to a malicious suggest endpoint (e.g., https://attacker.example/suggest?q={searchTerms}).
  2. A user visits the attacker’s site, causing Chrome to discover and register the search engine.
  3. The user navigates to a sensitive target site (e.g., https://example.com).
  4. The user focuses the Omnibox, enters keyword search mode for the attacker’s engine, and types a query.
  5. The attacker’s suggest endpoint returns a JSON payload containing a NAVSUGGEST entry with a javascript: payload: ["query", ["javascript:alert(document.domain)"], ["Sign in to continue"], [], {"google:suggesttype":["NAVIGATION"]}]
  6. The Omnibox displays “Sign in to continue” as a selectable suggestion in the dropdown.
  7. The user arrows down to select the suggestion and presses Enter.
  8. The JavaScript payload executes within the security context of example.com.

Suggested Fix

In components/omnibox/browser/search_suggestion_parser.cc, within the block handling NAVSUGGEST and NAVSUGGEST_PERSONALIZED, validate that the GURL has a safe scheme before accepting it.

// components/omnibox/browser/search_suggestion_parser.cc
GURL url(url_formatter::FixupURL(base::UTF16ToUTF8(suggestion)));
// Add SchemeIsHTTPOrHTTPS() check (or a similar explicit allowlist) 
if (url.is_valid() && url.SchemeIsHTTPOrHTTPS()) {
    // ... processes and adds to results->navigation_results
}

Evaluated with Chrome root at commit: 096fc8fdbfacf2546485756d03f160a3d04fcc9b


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