Chrome · Desktop
CVE-2026-110916
Input Validation in Desktop
Overview
Medium
Severity
—
CVSS
No
Exploited ITW
Fixed
Fix Status
Changed Functions
| Function | Change | Notes |
|---|---|---|
TEST_Fchrome/browser/new_tab_page/modules/v2/calendar/google_calendar_page_handler_unittest.cc |
modified |
Files Changed
chrome/browser/new_tab_page/modules/v2/calendar/google_calendar_page_handler_unittest.ccchrome/browser/new_tab_page/modules/v2/calendar/google_calendar_page_handler.cc
Patch
From 90430e752b9669b5054a31d2d4f539cd50ff75e4 Mon Sep 17 00:00:00 2001 From: Dhruv Kathpalia <[email protected]> Date: Thu, 17 Sep 2026 16:44:55 -0700 Subject: [PATCH] Fail request if Google Calendar events contain invalid URLs Validates that all URLs in Google Calendar events and attachments (event link, conference URI, attachment file URL, and attachment icon link) are valid and use HTTP or HTTPS schemes. If any event or attachment contains an invalid or non-compliant URL, the request fails early and returns an empty list of events. This prevents the Google Calendar module from being displayed on the New Tab Page in broken, incomplete, or inconsistent UI states. Bug: b:501886301 Change-Id: I9c8d6665f2d127a3e0838a361ef54b5f212b599f Reviewed-on: https://chromium-review.googlesource.com/c/chromium/src/+/8408565 Commit-Queue: Dhruv Kathpalia <[email protected]> Reviewed-by: Tibor Goldschwendt <[email protected]> Cr-Commit-Position: refs/heads/main@{#1700272} --- diff --git a/chrome/browser/new_tab_page/modules/v2/calendar/google_calendar_page_handler.cc b/chrome/browser/new_tab_page/modules/v2/calendar/google_calendar_page_handler.cc index 14ec608f6..f1b2d544 100644 --- a/chrome/browser/new_tab_page/modules/v2/calendar/google_calendar_page_handler.cc +++ b/chrome/browser/new_tab_page/modules/v2/calendar/google_calendar_page_handler.cc @@ -208,14 +208,35 @@ google_apis::calendar::CalendarEvent::ResponseStatus::kDeclined) { continue; } + GURL event_url = GURL(event->html_link()); + if (!event_url.is_valid() || !event_url.SchemeIsHTTPOrHTTPS()) { + return; + } + + if (!event->conference_data_uri().is_empty() && + (!event->conference_data_uri().is_valid() || + !event->conference_data_uri().SchemeIsHTTPOrHTTPS())) { + return; + } + ntp::calendar::mojom::CalendarEventPtr formatted_event = ntp::calendar::mojom::CalendarEvent::New(); formatted_event->title = event->summary(); formatted_event->start_time = event->start_time().date_time(); formatted_event->end_time = event->end_time().date_time(); - formatted_event->url = GURL(event->html_link()); + formatted_event->url = event_url; formatted_event->location = event->location(); for (const auto& attachment : event->attachments()) { + if (!attachment.icon_link().is_valid() || + !attachment.icon_link().SchemeIsHTTPOrHTTPS()) { + return; + } + if (!attachment.file_url().is_empty() && + (!attachment.file_url().is_valid() || + !attachment.file_url().SchemeIsHTTPOrHTTPS())) { + return; + } + ntp::calendar::mojom::AttachmentPtr formatted_attachment = ntp::calendar::mojom::Attachment::New(); formatted_attachment->title = attachment.title(); diff --git a/chrome/browser/new_tab_page/modules/v2/calendar/google_calendar_page_handler_unittest.cc b/chrome/browser/new_tab_page/modules/v2/calendar/google_calendar_page_handler_unittest.cc index f86ce21..de647fd 100644 --- a/chrome/browser/new_tab_page/modules/v2/calendar/google_calendar_page_handler_unittest.cc +++ b/chrome/browser/new_tab_page/modules/v2/calendar/google_calendar_page_handler_unittest.cc @@ -432,3 +432,99 @@ EXPECT_EQ(response.size(), 3u); } + +TEST_F(GoogleCalendarPageHandlerTest, GetEventsFiltersPrivilegedSchemes) { + auto create_event = []() { + return base::DictValue() + .Set("kind", "calendar#event") + .Set("status", "confirmed") + .Set("htmlLink", "https://foo.com/valid") + .Set("created", "2018-05-14T18:55:59.000Z") + .Set("updated", "2021-03-17T10:42:53.637Z") + .Set("summary", "Test Event") + .Set("start", CreateEventTime(/*is_all_day_event=*/false, + /*is_end_time=*/false)) + .Set("end", CreateEventTime(/*is_all_day_event=*/false, + /*is_end_time=*/true)) + .Set("conferenceData", CreateConferenceData()) + .Set("attendees", CreateAttendees(0)); + }; + + auto test_invalid_event = [this](base::DictValue event) { + base::ListValue events; + events.Append(std::move(event)); + + base::DictValue result_dict = + base::DictValue() + .Set("kind", "calendar#events") + .Set("etag", "\"p32ofplf5q6gf20g\"") + .Set("summary", "[email protected]") + .Set("updated", "2021-06-18T07:17:10.718Z") + .Set("timeZone", "America/Los_Angeles") + .Set("accessRole", "owner") + .Set("items", std::move(events)); + std::string json; + JSONStringValueSerializer serializer(&json); + ASSERT_TRUE(serializer.Serialize(result_dict)); + + std::unique_ptr<GoogleCalendarPageHandler> handler = + CreateHandlerWithTestData(std::move(json)); + + std::vector<ntp::calendar::mojom::CalendarEventPtr> response; + base::MockCallback<GoogleCalendarPageHandler::GetEventsCallback> callback; + EXPECT_CALL(callback, Run(testing::_)) + .Times(1) + .WillOnce( + [&](std::vector<ntp::calendar::mojom::CalendarEventPtr> events) { + response = std::move(events); + }); + + base::RunLoop run_loop; + handler->GetEvents( + google_apis::test_util::CreateQuitCallback(&run_loop, callback.Get())); + run_loop.Run(); + + // If any event or attachment has an invalid URL, the entire request fails + // and returns an empty list so the module is not displayed. + EXPECT_TRUE(response.empty()); + }; + + // 1. Test invalid htmlLink. + { + base::DictValue event = create_event(); + event.Set("htmlLink", "chrome://settings"); + event.Set("attachments", CreateAttachments()); + test_invalid_event(std::move(event)); + } + + // 2. Test invalid attachment fileUrl (with a valid iconLink). + { + base::DictValue invalid_attachment = + base::DictValue() + .Set("fileUrl", "chrome://settings") + .Set("title", "Attachment With Invalid File URL") + .Set("iconLink", "https://foo-icon.com/valid"); + base::ListValue attachments; + attachments.Append(std::move(invalid_attachment)); + + base::DictValue event = create_event(); + event.Set("attachments", std::move(attachments)); + test_invalid_event(std::move(event)); + } + + // 3. Test invalid attachment iconLink (with a valid fileUrl). + { + base::DictValue invalid_attachment = + base::DictValue() + .Set("fileUrl", "https://foo-file.com/valid") + .Set("title", "Attachment With Invalid Icon Link") + .Set("iconLink", "chrome://image?url=https://attacker.example"); + base::ListValue attachments; + attachments.Append(std::move(invalid_attachment)); + + base::DictValue event = create_event(); + event.Set("attachments", std::move(attachments)); + test_invalid_event(std::move(event)); + } +} +
Loading diff…
Regression Test / PoC
shipped with the fix
diff --git a/chrome/browser/new_tab_page/modules/v2/calendar/google_calendar_page_handler_unittest.cc b/chrome/browser/new_tab_page/modules/v2/calendar/google_calendar_page_handler_unittest.cc
index f86ce21..de647fd 100644
--- a/chrome/browser/new_tab_page/modules/v2/calendar/google_calendar_page_handler_unittest.cc
+++ b/chrome/browser/new_tab_page/modules/v2/calendar/google_calendar_page_handler_unittest.cc
@@ -432,3 +432,99 @@
EXPECT_EQ(response.size(), 3u);
}
+
+TEST_F(GoogleCalendarPageHandlerTest, GetEventsFiltersPrivilegedSchemes) {
+ auto create_event = []() {
+ return base::DictValue()
+ .Set("kind", "calendar#event")
+ .Set("status", "confirmed")
+ .Set("htmlLink", "https://foo.com/valid")
+ .Set("created", "2018-05-14T18:55:59.000Z")
+ .Set("updated", "2021-03-17T10:42:53.637Z")
+ .Set("summary", "Test Event")
+ .Set("start", CreateEventTime(/*is_all_day_event=*/false,
+ /*is_end_time=*/false))
+ .Set("end", CreateEventTime(/*is_all_day_event=*/false,
+ /*is_end_time=*/true))
+ .Set("conferenceData", CreateConferenceData())
+ .Set("attendees", CreateAttendees(0));
+ };
+
+ auto test_invalid_event = [this](base::DictValue event) {
+ base::ListValue events;
+ events.Append(std::move(event));
+
+ base::DictValue result_dict =
+ base::DictValue()
+ .Set("kind", "calendar#events")
+ .Set("etag", "\"p32ofplf5q6gf20g\"")
+ .Set("summary", "[email protected]")
+ .Set("updated", "2021-06-18T07:17:10.718Z")
+ .Set("timeZone", "America/Los_Angeles")
+ .Set("accessRole", "owner")
+ .Set("items", std::move(events));
+ std::string json;
+ JSONStringValueSerializer serializer(&json);
+ ASSERT_TRUE(serializer.Serialize(result_dict));
+
+ std::unique_ptr<GoogleCalendarPageHandler> handler =
+ CreateHandlerWithTestData(std::move(json));
+
+ std::vector<ntp::calendar::mojom::CalendarEventPtr> response;
+ base::MockCallback<GoogleCalendarPageHandler::GetEventsCallback> callback;
+ EXPECT_CALL(callback, Run(testing::_))
+ .Times(1)
+ .WillOnce(
+ [&](std::vector<ntp::calendar::mojom::CalendarEventPtr> events) {
+ response = std::move(events);
+ });
+
+ base::RunLoop run_loop;
+ handler->GetEvents(
+ google_apis::test_util::CreateQuitCallback(&run_loop, callback.Get()));
+ run_loop.Run();
+
+ // If any event or attachment has an invalid URL, the entire request fails
+ // and returns an empty list so the module is not displayed.
+ EXPECT_TRUE(response.empty());
+ };
+
+ // 1. Test invalid htmlLink.
+ {
+ base::DictValue event = create_event();
+ event.Set("htmlLink", "chrome://settings");
+ event.Set("attachments", CreateAttachments());
+ test_invalid_event(std::move(event));
+ }
+
+ // 2. Test invalid attachment fileUrl (with a valid iconLink).
+ {
+ base::DictValue invalid_attachment =
+ base::DictValue()
+ .Set("fileUrl", "chrome://settings")
+ .Set("title", "Attachment With Invalid File URL")
+ .Set("iconLink", "https://foo-icon.com/valid");
+ base::ListValue attachments;
+ attachments.Append(std::move(invalid_attachment));
+
+ base::DictValue event = create_event();
+ event.Set("attachments", std::move(attachments));
+ test_invalid_event(std::move(event));
+ }
+
+ // 3. Test invalid attachment iconLink (with a valid fileUrl).
+ {
+ base::DictValue invalid_attachment =
+ base::DictValue()
+ .Set("fileUrl", "https://foo-file.com/valid")
+ .Set("title", "Attachment With Invalid Icon Link")
+ .Set("iconLink", "chrome://image?url=https://attacker.example");
+ base::ListValue attachments;
+ attachments.Append(std::move(invalid_attachment));
+
+ base::DictValue event = create_event();
+ event.Set("attachments", std::move(attachments));
+ test_invalid_event(std::move(event));
+ }
+}
+
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.
References
On This Page