Skip to content

fix(genui): only open http, https, mailto and tel URLs in openUrl - #1055

Open
zachary-satterly wants to merge 1 commit into
flutter:mainfrom
zachary-satterly:fix/openurl-scheme-allowlist
Open

zachary-satterly wants to merge 1 commit into
flutter:mainfrom
zachary-satterly:fix/openurl-scheme-allowlist

Conversation

@zachary-satterly

@zachary-satterly zachary-satterly commented Oct 5, 2026 •

Copy link
Copy Markdown

Description

OpenUrlFunction, the basic catalog's openUrl, passes whatever URL the agent sends to url_launcher (basic_functions.dart). The only gate is canLaunchUrl, which asks whether the platform has a handler for the URL. How much that filters depends on the platform:

  • Web: url_launcher_web only reports http, https, mailto, tel and sms as launchable, so this change only adds a refusal of sms:.
  • Android 11+ and iOS: canLaunchUrl is limited to the schemes the app declares in <queries> / LSApplicationQueriesSchemes, so the exposure is whatever the app has declared.
  • macOS, Windows and Linux: canLaunchUrl is true for any scheme with a registered handler, including file:. So on desktop, an agent-built button can open local files or any custom-scheme handler installed on the machine.

The A2UI basic catalog implementation guide lists a scheme allowlist for openUrl under "Security Constraints & Implementation Requirements (Mandatory)". It was added in a2ui-project/a2ui#1707, the fix for GHSA-72qq-p3r5-f7wq. a2ui-project/a2ui#2992 (merged 2026-10-06) adds a check for http, https, mailto and tel to the Dart a2ui_core basic catalog. This PR applies the same set to genui, since apps on 0.10.x run this code until they move to the new packages.

Changes:

  • openUrl opens only absolute http, https, mailto and tel URLs. Anything else logs a warning through genUiLogger and returns false without calling the launcher. Apps that need other schemes can register their own openUrl client function.
  • Tests in basic_functions_test.dart swap in a fake UrlLauncherPlatform and check which URLs reach it. This adds url_launcher_platform_interface as a dev dependency.
  • CHANGELOG entry under 0.10.5.

Where this differs from the guide, and why:

  • The guide says to throw. genui's other basic functions report bad input by returning false, so this does the same. #2992 throws, which fits a2ui_core's error model.
  • The guide says to resolve relative URLs against the current location first. Native platforms have no location to resolve against, and on the web canLaunchUrl already rejected relative URLs, so they are refused.
  • The guide allows http and https only. I used the set from #2992, which also allows mailto and tel, so the two Dart implementations agree. If you'd rather follow the guide exactly, I'll drop mailto and tel.

No issue was filed first because the change is small. The e2e workflow needs a repository secret, so it can't run from a fork.

I wrote this with help from an AI assistant (Claude) and reviewed the change myself. I couldn't run the Flutter test suite locally, so CI here is the first full run of the new tests.

Pre-launch Checklist

  • I read the Flutter Style Guide recently, and have followed its advice.
  • I signed the CLA.
  • I read the Contributors Guide.
  • I have added sample code updates to the changelog.
  • I updated/added relevant documentation (doc comments with ///).
  • If my PR is a fork PR, I've checked that e2e tests passed.

@gemini-code-assist gemini-code-assist Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Code Review

This pull request restricts the 'openUrl' function to only open absolute 'http', 'https', 'mailto', and 'tel' URLs, logging a warning and returning 'false' for any other schemes or relative URLs. It also adds corresponding unit tests. A review comment suggests adding error handling to the asynchronous 'canLaunchUrl' and 'launchUrl' calls to prevent unhandled exceptions from crashing the application.

Comment on lines +359 to +361
canLaunchUrl(uri).then((can) {
if (can) launchUrl(uri);
});

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

medium

The asynchronous calls to canLaunchUrl and launchUrl are executed without any error handling. If either of these calls throws an exception (e.g., due to platform limitations or configuration issues), it will result in an unhandled asynchronous error, which can crash the application or trigger global error handlers.

By returning the Future from launchUrl inside the then callback, we can chain the futures and catch any errors from both canLaunchUrl and launchUrl in a single .catchError block.

    canLaunchUrl(uri).then((can) {
      if (can) return launchUrl(uri);
      return false;
    }).catchError((Object e, StackTrace s) {
      genUiLogger.warning('Failed to launch URL: $uri', e, s);
      return false;
    });

OpenUrlFunction passed any URL the agent supplied to url_launcher. The
only check was canLaunchUrl, which asks whether the platform has a
handler for the URL. On desktop that is any registered scheme, including
file: URLs.

The A2UI basic catalog implementation guide has required a scheme
allowlist for openUrl since a2ui-project/a2ui#1707, and
a2ui-project/a2ui#2992 adds a check for http, https, mailto and tel to
the Dart a2ui_core basic catalog. Apply the same set here. Other URLs,
including relative ones, are refused with a logged warning and the
function returns false without calling the launcher, the way genui's
other basic functions report bad input.

Tests use a fake UrlLauncherPlatform to check which URLs reach the
launcher.
@zachary-satterly
zachary-satterly force-pushed the fix/openurl-scheme-allowlist branch from 8708e2c to acfd5fb Compare October 13, 2026 15:38

This branch has not been deployed

No deployments
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant