Chrome · Chrome for iOS
CVE-2026-17913
Logic Error in Chrome for iOS
Overview
Low
Severity
—
CVSS
No
Exploited ITW
Fixed
Fix Status
Changed Functions
| Function | Change | Notes |
|---|---|---|
AnnotationTextManagerTestios/web/annotations/annotations_java_script_feature.h |
modified | |
AnnotationsJavaScriptFeatureios/web/annotations/annotations_java_script_feature.h |
modified | |
GetAnnotationsReplacementsios/web/annotations/annotations_java_script_feature.mm |
modified | |
trusted_event_check_enabled_ios/web/annotations/annotations_java_script_feature.mm |
modified | |
ifios/web/annotations/annotations_java_script_feature.mm |
modified | |
forios/web/annotations/annotations_java_script_feature.mm |
modified | |
switchios/web/annotations/resources/text_click.ts |
modified | |
MutationsTrackerios/web/annotations/resources/text_click.ts |
modified |
Files Changed
ios/web/annotations/annotations_inttest.mmios/web/annotations/annotations_java_script_feature.hios/web/annotations/annotations_java_script_feature.mmios/web/annotations/resources/text_click.ts
Patch
From 48a2dca4b7094b466a37d18dc09113485466b99c Mon Sep 17 00:00:00 2001 From: Olivier ROBIN <[email protected]> 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 <[email protected]> Auto-Submit: Olivier Robin <[email protected]> Reviewed-by: Mike Dougherty <[email protected]> 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.
References
On This Page