Skip to content

Use -- to separate chrome URLs - #2475

Merged
natebosch merged 5 commits into
mainfrom
browser-launcher-url-arg-separator
Jul 29, 2026
Merged

Use -- to separate chrome URLs#2475
natebosch merged 5 commits into
mainfrom
browser-launcher-url-arg-separator

Conversation

@natebosch

@natebosch natebosch commented Jul 22, 2026

Copy link
Copy Markdown
Member

If any URLs are passed with leading -- characters they may be
interpreted as arguments instead. Any arguments which follow a bare --
are explicitly treated as URLs not arguments. Avoid the need for callers
to sanitize arguments with this explicit divider.

Drop an unnecessary private implementation since it's small enough for a
direct public implementation that's as small as the forwarder anyway.

BUG=b/536185918

If any URLs are passed with leading `--` characters they may be
interpreted as arguments instead. Any arguments which follow a bare `--`
are explicitly treated as URLs not arguments. Avoid the need for callers
to sanitize arguments with this explicit divider.

Drop an unnecessary private implementation since it's small enough for a
direct public implementation that's as small as the forwarder anyway.
@natebosch
natebosch requested a review from a team as a code owner July 22, 2026 23:00

@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 refactors the Chrome process launching logic by removing the private _startProcess helper and simplifying the start method to directly call Process.start using spread operators. Feedback suggests conditionally adding the -- argument separator only when urls is not empty to avoid potential issues with Chrome when no URLs are provided.

Comment thread pkgs/browser_launcher/lib/src/chrome.dart Outdated
@github-actions

github-actions Bot commented Jul 22, 2026

Copy link
Copy Markdown

Package publishing

If you have publishing permissions, you can use the links below to publish the changes after merging this PR.

Package Version Status Publish tag (post-merge)
package:api_summary 0.1.0-wip WIP (no publish necessary)
package:bazel_worker 1.1.5 already published at pub.dev
package:benchmark_harness 2.4.0 already published at pub.dev
package:boolean_selector 2.1.2 already published at pub.dev
package:browser_launcher 1.2.0-wip WIP (no publish necessary)
package:cli_config 0.2.1-wip WIP (no publish necessary)
package:cli_util 0.5.2 already published at pub.dev
package:clock 1.1.3-wip WIP (no publish necessary)
package:code_builder 4.12.0-wip WIP (no publish necessary)
package:coverage 1.15.1 already published at pub.dev
package:csslib 1.0.2 already published at pub.dev
package:extension_discovery 2.1.0 already published at pub.dev
package:file 7.0.2-wip WIP (no publish necessary)
package:file_testing 3.1.0-wip WIP (no publish necessary)
package:glob 2.1.4-wip WIP (no publish necessary)
package:graphs 2.4.0-wip WIP (no publish necessary)
package:html 0.15.7-wip WIP (no publish necessary)
package:io 1.1.0-wip WIP (no publish necessary)
package:json_rpc_2 4.1.0 already published at pub.dev
package:markdown 7.4.0 ready to publish markdown-v7.4.0
package:mime 2.1.0-wip WIP (no publish necessary)
package:oauth2 2.0.5 already published at pub.dev
package:package_config 3.0.0 already published at pub.dev
package:pool 1.5.3-wip WIP (no publish necessary)
package:process 5.0.5 (error) pubspec version (5.0.5) and changelog (5.0.6-wip) don't agree
package:pub_semver 2.2.0 already published at pub.dev
package:pubspec_parse 1.6.0-wip WIP (no publish necessary)
package:source_map_stack_trace 2.1.3-wip WIP (no publish necessary)
package:source_maps 0.10.14-wip WIP (no publish necessary)
package:source_span 1.10.2 already published at pub.dev
package:sse 4.2.1-wip WIP (no publish necessary)
package:stack_trace 1.12.2-wip (error) pubspec version (1.12.2-wip) and changelog (1.12.2-dev) don't agree
package:stream_channel 2.1.4 already published at pub.dev
package:stream_transform 2.1.2-wip WIP (no publish necessary)
package:string_scanner 1.4.2-wip WIP (no publish necessary)
package:term_glyph 1.2.3-wip WIP (no publish necessary)
package:test_reflective_loader 0.6.0 ready to publish test_reflective_loader-v0.6.0
package:timing 1.0.2 already published at pub.dev
package:unified_analytics 8.0.16 already published at pub.dev
package:watcher 1.2.2-wip WIP (no publish necessary)
package:yaml 3.1.4-wip WIP (no publish necessary)
package:yaml_edit 2.2.4 already published at pub.dev

Documentation at https://github.com/dart-lang/ecosystem/wiki/Publishing-automation.

I think in our use there is, but no reason not to handle the other case
@github-actions

github-actions Bot commented Jul 22, 2026

Copy link
Copy Markdown

PR Health

License Headers ✔️
// Copyright (c) 2026, the Dart project authors. Please see the AUTHORS file
// for details. All rights reserved. Use of this source code is governed by a
// BSD-style license that can be found in the LICENSE file.

Files
no missing headers

All source files should start with a license header.

This check can be disabled by tagging the PR with skip-license-check.

Unused Dependencies ✔️
Package Status
browser_launcher ✔️ All dependencies utilized correctly.

For details on how to fix these, see dependency_validator.

This check can be disabled by tagging the PR with skip-unused-dependencies-check.

Coverage ⚠️
File Coverage
pkgs/browser_launcher/lib/src/chrome.dart 💔 69 % ⬇️ 1 %

This check for test coverage is informational (issues shown here will not fail the PR).

This check can be disabled by tagging the PR with skip-coverage-check.

Changelog Entry ✔️
Package Changed Files

Changes to files need to be accounted for in their respective changelogs.

This check can be disabled by tagging the PR with skip-changelog-check.

API leaks ✔️

The following packages contain symbols visible in the public API, but not exported by the library. Export these symbols or remove them from your publicly visible API.

Package Leaked API symbol Leaking sources

This check can be disabled by tagging the PR with skip-leaking-check.

Breaking changes ✔️
Package Change Current Version New Version Needed Version Looking good?
browser_launcher Non-Breaking 1.1.3 1.2.0-wip 1.2.0-wip ✔️

This check can be disabled by tagging the PR with skip-breaking-check.

@@ -134,7 +134,7 @@

@lrhn lrhn Jul 23, 2026

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

(Can be moved into literal, even if it needs to be last.)

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

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

Done.

///
/// When launching, all [additionalArguments] will be passed directly to
/// Chrome as command line arguments.
static Future<Chrome> startWithDebugPort(

@lrhn lrhn Jul 23, 2026

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

Who calls this function?
Can we trust additionalArguments to not already contain -- and/or a non-argument?

If it's called as startWithDebugPort([url2Text], additionalArguments: ['--', url1Text]),
it looks like it would currently work , so if it's a public function, someone might be doing that.
(A -- would break if headless is true, since it'd add --headless after the --, and then fail to treat it as a URL, but if headless is false, it could work.)

Adding an extra -- will likely give an error after trying to use the second -- as a URL, making this a breaking change, even if for a very questionable use.

Should we validate that additionalArguments are additional flags? Maybe that's non-trivial if you can have --flag value, so we'd have to understand the flag syntax to know if value belongs to a flag or will be read as a URL. (Can chrome switches be in two arguments like that? It seems much safer if they can't.)

Maybe just check that additionalArguments does not contain --.
I'd make it an error if it does, and then unconditionally add the -- before the URIs.

We also don't check if the URIs contain flags?
Which would also currently work.
So if someone passes urls as [--headless', myUrl], that would also currently work.
Definitely not intended use, but how confident are we about breaking it?

That is, we're preventing unvalidate URIs starting with -- from being treated as flags unintendedly. What if it was intended?

(I say "break the .....s, they had it coming", but that may just be me.)

So, my recommendation: Check if additionalArguments contain --, and throw an ArgumentError if it does. Then continue doing what this PR does.

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

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

Who calls this function?
Can we trust additionalArguments to not already contain -- and/or a non-argument?

Some of our dev tooling (I think to localhost URLs we control) and potentially any third party use of browser launching.

For the use cases we support, I suspect we can trust it to not already contain -- or a non-argument, and it would be valid for us to just have that expectation.

Maybe just check that additionalArguments does not contain --.
I'd make it an error if it does, and then unconditionally add the -- before the URI

Yeah I'm leaning towards using an assert that args does not contain -- and unconditionally adding it.
It's even safe to include it when the URLs is an empty list so I think I'll do that.

So if someone passes urls as [--headless', myUrl], that would also currently work.
Definitely not intended use, but how confident are we about breaking it?

I think it is OK to break this (mis)use of the API. We are very unlikely to have used it this way in any of our supported use cases.

So, my recommendation: Check if additionalArguments contain --, and throw an ArgumentError if it does. Then continue doing what this PR does.

WDYT about using an assert?

return await Process.start(_executable, processArgs);
}
}) =>
Process.start(_executable, [...args, if (urls.isNotEmpty) '--', ...urls]);

@lrhn lrhn Jul 23, 2026

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

(See above. If not rejecting earlier if args already contains --, check here and only add -- if not.)

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

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

Added assert

@lrhn

lrhn commented Jul 23, 2026

Copy link
Copy Markdown
Member

What is the threat/misuse model here?

Is someone calling start or startWithDebugPort with user supplied URL strings that are not validated,
but the extra arguments are trusted?
Can the URL strings starting with -- ever be a valid and useful URL? If not, should we just reject early, throwing an exception, instead of putting a -- in front and making it a later "Bad URL" error?
(The URL arguments to chrome will not have a base-URI to resolve against. A scheme cannot start with -. A schemeless host must start with //. That makes --... a path only URI reference with not base URI. That's never useful.)

That is: Should we validate inputs more, and reject any extra-argument that does not start with - or -- or that is just --,
and/or reject any URL that starts with -?

(Just putting -- in front of the URLs feeels like it's only protecting against a specific use-case, instead of the general problem.)

@natebosch

natebosch commented Jul 24, 2026

Copy link
Copy Markdown
Member Author

What is the threat/misuse model here?

See b/536185918

The theory is that a malicious actor is able to control some URL that will be launched by a dev tool, they use this exploit to pass a flag like --renderer-cmd-prefix and then somehow graduate that to an RCE.

Is someone calling start or startWithDebugPort with user supplied URL strings that are not validated,
but the extra arguments are trusted?

Not that I can think of, but maybe? I think our use is with localhost URLs that aren't under the control of third party packages, but I also don't want to track down every use of this API and verify if they're using it in a safe way.

We don't explicitly support usage outside our tools, but it's always possible some third party is using this in an unsafe way and would be helped by this fix. It shouldn't hurt to protect from poorly validated use even if we're using it safely today.

Can the URL strings starting with -- ever be a valid and useful URL? If not, should we just reject early, throwing an exception, instead of putting a -- in front and making it a later "Bad URL" error?

I'm not sure, probably not, but I think I'm happy for chrome to own that knowledge instead of us. Passing a "bad" URL that starts with a -- will open up an error page in chrome, which I think is a fine result for that case.

That is: Should we validate inputs more, and reject any extra-argument that does not start with - or -- or that is just --,
and/or reject any URL that starts with -?

IMO no. It's quite plausible that there is some developer friendliness in an approach like that (fail early and more clearly), but the usage is so narrow it's worth taking a shortcut and letting chrome own this stuff. I think we do need to protect against callers who passed their own -- at the end of args though.

Callers should be either controlling or validating the args they pass, if they aren't doing that nothing we can do here will help them. It's reasonable that a caller might use less caution for the urls argument if launching to third party content is intended.

(Just putting -- in front of the URLs feeels like it's only protecting against a specific use-case, instead of the general problem.)

I'd call it a solution well tailored for the use case. The API we own here is a slim wrapper on the chrome executable, and this change is IMO a more valid translation from Dart to chrome CLI. It's questionable whether we ever should have put a distinction between arguments and URLs in the Dart API, given that the caller is anyways making this call with the domain knowledge of the -- CLI flags. Given that we do have separate args and urls in Dart API, using -- between them is a better translation to the CLI API, and any deeper validation is rightly handled by chrome.

This use is unlikely, but if there is already a (hopefully trailing)
`--` in `args` or in `urls` then a second `--` will be loaded in a tab
as an error.

Add an assert that the arguments don't contain their own `--`.

Remove the conditional before the `--` element. The API is designed for
use with a non-empty list of URLs, and a trailing `--` has no effect to
the process anyway, so make the code match the expected case without a
misleading conditional.
@natebosch
natebosch merged commit b827a6e into main Jul 29, 2026
21 checks passed
@natebosch
natebosch deleted the browser-launcher-url-arg-separator branch July 29, 2026 17:18
copybara-service Bot pushed a commit to dart-lang/sdk that referenced this pull request Jul 30, 2026
Revisions updated by `dart tools/rev_sdk_deps.dart`.

Rolls a subset of the packages which were rolled and reverted in
https://dart-review.googlesource.com/c/sdk/+/529340

`test` is held back due to triggering a VM issue with some flutter
engine tests.

`dartdoc` is being rolled in
https://dart-review.googlesource.com/c/sdk/+/529460

As was the case with the reverted CL, `http` is being rolled in
https://dart-review.googlesource.com/c/sdk/+/524264

core (https://github.com/dart-lang/core/compare/25926e8..fe516ee):
  fe516ee1  Mon Jul 27 20:19:16 2026 +0200  Moritz  Consolidate no-response workflow into no_response.yml (dart-lang/core#982)
  2deeeade  Mon Jul 27 18:05:47 2026 +0530  Abhishak Kumar Malviya  fix: async cache storing exception fixed (dart-lang/core#548)
  bed8f40e  Fri Jul 24 11:29:13 2026 -0700  Nate Bosch  Add `value` getter to `Result` (dart-lang/core#969)

ecosystem (https://github.com/dart-lang/ecosystem/compare/848b3bf..edfdb3b):
  edfdb3b  Thu Jul 23 17:27:43 2026 +0200  Moritz  Add want-lgtm reusable workflow (dart-lang/ecosystem#432)
  a6c8ef0  Thu Jul 23 14:29:53 2026 +0200  Moritz  Validate target PR number and comment ownership in post_summaries workflow (dart-lang/ecosystem#434)
  80a9928  Thu Jul 23 13:49:48 2026 +0200  Moritz  Fix canary workflow (dart-lang/ecosystem#433)
  910d668  Wed Jul 15 18:04:05 2026 +0200  Moritz  Pass GITHUB_TOKEN to firehose:comment steps in workflows (dart-lang/ecosystem#431)
  980c6df  Sat Jul 11 00:20:55 2026 +0200  Moritz  Fix `-WIP` ending detection (dart-lang/ecosystem#430)
  60c0f67  Wed Jul 8 20:38:35 2026 +0200  Moritz  Run `pub get` before `dart format` in groundskeeper (dart-lang/ecosystem#429)
  bcef699  Tue Jul 7 10:17:24 2026 +0200  Moritz  Fix `groundskeeper` workflow (dart-lang/ecosystem#428)
  77def11  Fri Jul 3 11:43:12 2026 +0200  Moritz  Simplify groundskeeper (dart-lang/ecosystem#426)
  3dd606a  Wed Jul 1 18:19:25 2026 +0000  dependabot[bot]  Bump the github-actions group with 3 updates (dart-lang/ecosystem#427)
  d6ec975  Wed Jul 1 14:41:23 2026 +0200  Moritz  Add changelog updater (dart-lang/ecosystem#424)
  f8a4fbb  Wed Jul 1 10:00:59 2026 +0200  Moritz  Add `Groundskeeper` workflow (dart-lang/ecosystem#425)

i18n (https://github.com/dart-lang/i18n/compare/d0683bd..e1b5a79):
  e1b5a798  Tue Jul 14 13:10:29 2026 +0200  Moritz  Add weekday API (dart-lang/i18n#1071)
  04b78e38  Thu Jul 9 14:16:22 2026 +0200  Moritz  Add stale workflow (dart-lang/i18n#1069)
  01e20101  Thu Jul 9 13:42:08 2026 +0200  Moritz  Add stale workflow
  03efda51  Thu Jul 9 13:19:31 2026 +0200  Moritz  Update ICU4X (dart-lang/i18n#1068)
  4d8a1b39  Tue Jul 7 11:23:06 2026 +0200  Moritz  feat(intl4x): add locale.dart entrypoint exposing only Locale (dart-lang/i18n#1067)
  b2b7ce08  Mon Jul 6 03:23:28 2026 -0700  Copybara-Service  Merge pull request `#1038` from donny-dont:patch-1
  cca1dc44  Mon Jul 6 03:10:59 2026 -0700  Copybara-Service  Merge pull request `#1040` from suxoikorm:main
  28352851  Mon Jul 6 02:40:25 2026 -0700  Copybara-Service  Merge pull request `#1062` from AbdeMohlbi:issue_116
  9f8e7998  Mon Jul 6 02:16:05 2026 -0700  Copybara-Service  Merge pull request `#1050` from LaijieJi:patch-1
  be8e2c0d  Fri Jul 3 14:50:28 2026 +0200  Moritz  Merge branch 'main' into issue_116
  8b119830  Fri Jul 3 14:33:33 2026 +0200  Moritz  Merge branch 'main' into patch-1
  96aab97a  Fri Jul 3 14:30:55 2026 +0200  Moritz  Merge branch 'main' into main
  9e700b2a  Thu Jul 2 09:37:45 2026 +0200  Moritz  Merge branch 'main' into patch-1
  a6f174b8  Wed Jul 1 08:32:07 2026 -0700  Googler  No public description
  fb1feb18  Wed Jul 1 08:32:33 2026 -0700  Copybara-Service  Merge pull request `#1058` from dart-lang:upgrade_analyzer
  77926a26  Thu May 7 20:52:55 2026 +0100  abdessalem  gemini has a point
  c2f96cba  Thu May 7 20:45:40 2026 +0100  abdessalem  revert the formating changes
  ebb0a441  Thu May 7 20:41:40 2026 +0100  abdessalem  update select documentation to clarify other fallback behavior and when `ArgumentError` is thrown
  b13c2d48  Mon Mar 9 10:35:50 2026 +0100  Moritz  Merge branch 'main' into patch-1
  6581675f  Mon Feb 9 09:13:25 2026 +0100  Laijie  Fix typo in number_format.dart documentation
  69d1e6a6  Thu Jan 29 15:25:06 2026 +0400  Luka Katsadze  Merge branch 'main' into main
  55a61fa6  Fri Dec 19 15:51:58 2025 -0800  Don Olmstead  Escape text direction code points
  a140f2ef  Mon Dec 29 21:42:09 2025 +0400  Luka Katsadze  fix: correct formatting of Georgian Lari symbol entry in changelog
  76a370c5  Mon Dec 29 21:34:35 2025 +0400  Luka Katsadze  chore: update changelog
  9df0c191  Mon Dec 29 21:31:39 2025 +0400  Luka Katsadze  feat: add georgian lari symbol support

shelf (https://github.com/dart-lang/shelf/compare/71248e7..6918a76):
  6918a76  Mon Jul 20 18:45:56 2026 -0700  Kevin Moore  feat(compliance): add GitHub Actions Job Summary to test runs (dart-lang/shelf#527)
  e9c742d  Mon Jul 20 20:28:03 2026 +0300  Yusuf İhsan Görgel  fix: join multiple Cookie header values with '; ' instead of ',' (`#521`) (dart-lang/shelf#536)
  833433e  Mon Jul 6 16:59:42 2026 -0700  Kevin Moore  chore: standardize min SDK to ^3.9.0 and unify lints
  7af986e  Wed Jul 1 12:27:06 2026 +0000  dependabot[bot]  Bump the github-actions group with 2 updates (dart-lang/shelf#535)

tools (https://github.com/dart-lang/tools/compare/3f850c4..b827a6e):
  b827a6e3  Wed Jul 29 10:18:52 2026 -0700  Nate Bosch  Use -- to separate chrome URLs (dart-lang/tools#2475)
  0fc3b0e7  Wed Jul 29 10:02:32 2026 -0700  Sam Rawlins  markdown: escape alt text in images (dart-lang/tools#2478)
  01f45d85  Thu Jul 23 16:49:10 2026 -0700  Nate Bosch  [glob] Limit expansion during option flattening (dart-lang/tools#2477)
  acc91d65  Thu Jul 23 16:00:59 2026 -0700  Kevin Moore  Add tests for PoolResource and close, and fix allowRelease leak (dart-lang/tools#2368)
  0e54bd66  Thu Jul 23 15:25:22 2026 -0700  Nate Bosch  Parse hosted URLs as URLs (dart-lang/tools#2476)
  7e124963  Wed Jul 22 15:01:34 2026 -0700  Sam Rawlins  Markdown: optimize email regex (dart-lang/tools#2474)
  906d9d4e  Wed Jul 22 13:25:24 2026 -0700  Sam Rawlins  Markdown: More tag filter tests (dart-lang/tools#2473)
  588fbad4  Wed Jul 22 10:54:42 2026 -0700  Sam Rawlins  Expand HTML filter regexp to include more characters that would denote an HTML tag (dart-lang/tools#2472)
  e4f20cc7  Wed Jul 22 09:33:59 2026 -0700  Sam Rawlins  Respect enableTagfilter when inlineOnly is passed (dart-lang/tools#2471)
  c4082344  Wed Jul 22 07:41:06 2026 -0700  Jacob MacDonald  add legend to multiselect dialogs (dart-lang/tools#2470)
  a2ecf08f  Thu Jul 16 09:30:49 2026 -0700  Jacob MacDonald  add an event type for events coming from package:skills (dart-lang/tools#2468)
  6a85977a  Mon Jul 13 10:12:17 2026 -0700  Kevin Moore  [api_summary] Support latest analyzer version (dart-lang/tools#2460)
  58b0fd0a  Mon Jul 13 11:01:18 2026 +0200  Moritz  Add no-response workflow (dart-lang/tools#2459)
  dc9ef3a9  Wed Jul 8 17:03:20 2026 -0700  Kevin Moore  chore(html): use MIT license header instead of BSD (dart-lang/tools#2458)

web (https://github.com/dart-lang/web/compare/eb8c3fc..12a9ca2):
  12a9ca2  Sun Jul 19 19:27:25 2026 -0700  Kevin Moore  test(js_interop_gen): reformat integration test goldens for dart_style 3.1.12 (dart-lang/web#565)
  6b5e93a  Wed Jul 1 06:55:34 2026 +0000  dependabot[bot]  Bump actions/checkout from 6.0.2 to 7.0.0 in the github-actions group (dart-lang/web#563)
  b8f47ab  Tue Jun 30 16:50:11 2026 -0700  Kevin Moore  [chore] Improve notice about updating the js_type_supertypes.dart (dart-lang/web#562)

R=alexmarkov@google.com

Change-Id: I410cb87fd72a63e4deacaad61206fbbdf6937f5f
Reviewed-on: https://dart-review.googlesource.com/c/sdk/+/529520
Commit-Queue: Nate Bosch <nbosch@google.com>
Reviewed-by: Alexander Markov <alexmarkov@google.com>
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.

2 participants