Repository navigation
flutter_tools: validate plugin identifiers before generating registrant code - #190462
auto-submit[bot] merged 3 commits into
Conversation
There was a problem hiding this comment.
Code Review
This pull request introduces validation for plugin identifiers and Dart file names across Android, iOS, macOS, Windows, and Linux platforms to prevent code injection vulnerabilities during registrant source code generation, and adds corresponding unit tests. The review feedback suggests refactoring validateDartFileName to accept a fieldName parameter to make it reusable for other plugin types, along with updating all corresponding call sites.
|
Hi @herdiyana256! Thanks for the contribution. Is there an issue associated with the problem this PR is solving? |
|
Hi bkonyi, No public issue for this one on purpose -- it's a supply-chain code-injection bug (a malicious pub.dev package's pluginClass/dartFileName gets compiled and executed inside every app that depends on it), so I didn't want to file a public issue spelling out the exploit before there's a fix in place. Happy to route it through Flutter's security reporting channel if you'd prefer that over discussing further in the open PR -- let me know. |
|
Update: filed the formal report through Google's official vulnerability disclosure channel. Google IssueTracker: https://issuetracker.google.com/issues/542989102 |
bkonyi
left a comment
There was a problem hiding this comment.
Thanks for filing the issue! Overall, this LGTM, but I think there's another case we need to handle.
We do still support a "legacy" plugin format in the pubspec that looks like this:
flutter:
plugin:
androidPackage: "com.evil.Payload.run(); //"
pluginClass: "EvilPlugin"
I don't think this case is covered by this PR. Either we'll need to update Plugin._fromLegacyYaml to add validation steps, or update the various Plugin constructors to perform validation there.
|
Good catch, thanks. You're right, Fixed in d98ace8 by calling |
chingjun
left a comment
There was a problem hiding this comment.
Looks like we are validating every field we read from yaml. Would it be better if we wrap that in a new function to establish a new pattern to read fields from yaml? That makes the intention clearer, and makes it less likely to forget performing the check when new fields are added.
Something like:
final package = readPluginIdentifier(from: yaml, key: 'package');
final dartFileName = readDartFileName(from: yaml, key: kDartFileName);
...This makes it more obvious that this is a pattern, than having the validatePluginIdentifier() call several lines below.
7b35b07 to
bdefea7
Compare
|
Heads up: I've rebased and significantly narrowed this PR. The core fix here (restricting What's left is the one vector that fix doesn't reach: the legacy plugin format @bkonyi flagged. @chingjun the read-fields-via-helper suggestion targets the |
|
Thanks for pointing out #189156, I thought this looks familiar :) In that case, can you follow the pattern established in #189156? i.e. check that in the The existing mechanism with |
flutter#189156 restricts pluginClass/dartPluginClass/package to identifiers in each platform's validate(), but the legacy plugin format is parsed by Plugin._fromLegacyYaml, which builds AndroidPlugin/IOSPlugin through their plain constructors and never reaches that validation. A legacy-format pubspec whose androidPackage, pluginClass or iosPrefix contains injection characters therefore still lands verbatim in the generated GeneratedPluginRegistrant and is compiled into any app that (transitively) depends on the plugin. Follow the pattern flutter#189156 established: check the identifiers in _validateLegacyYaml alongside the existing type checks, so every invalid field is reported at once through Plugin.fromYaml's error list rather than exiting on the first one. This only requires making the existing identifier predicate visible outside platform_plugins.dart, matching isValidPluginDartFileName. Adds five tests covering each field's injection payload, the combined multiple-error output and an unchanged valid legacy declaration.
bdefea7 to
419defa
Compare
|
Done, moved the check into The only other production change is making the existing predicate non-private ( Tests moved next to the existing injection tests in the plugin-parsing group and now go through @bkonyi sorry for dismissing your approval again with the force-push, PTAL when you get a chance. |
bkonyi
left a comment
There was a problem hiding this comment.
One small comment, then this LGTM!
| @@ -215,6 +215,7 @@ class Plugin { | |||
| bool isDirectDependency, { | |||
| required bool isDevDependency, | |||
| }) { | |||
| // SAFETY: This constructor is only invoked from .fromYaml, which validates. | |||
There was a problem hiding this comment.
Nit: I don't think we need this comment here.
There was a problem hiding this comment.
Agreed, it is a leftover from the earlier revision where the validation lived in the constructor path, so it no longer describes anything real.
I have left it in for the moment only because a one line commit would dismiss both approvals again. If the branch needs another master sync before it lands I will drop it in the same push, and if you would rather have it gone now just say so and I will push it and re-request review.
|
autosubmit label was removed for flutter/flutter/190462, because This PR has not met approval requirements for merging. The PR author is not a member of flutter-hackers and needs 1 more review(s) in order to merge this PR.
|
|
autosubmit label was removed for flutter/flutter/190462, because - The status or check suite ci.yaml validation has failed. Please fix the issues identified (or deflake) before re-applying this label. |
|
autosubmit label was removed for flutter/flutter/190462, because - The status or check suite Google testing has failed. Please fix the issues identified (or deflake) before re-applying this label. |
|
Both remaining red checks look unrelated to this change, so I don't think there is anything to fix here. Dashboard Checks is red on two Mac
Across the last 60 try builds of each, those two shards are at 8/60 and 10/60 failures on a wide spread of commits. Google testing ("Internal smoke tests failed") went red on at least six other PRs in the same window: #190935, #190667, #190929, #190014, #189500 and #185430, all of which have since merged. For what it's worth, the diff only adds string checks inside @bkonyi could you re-apply |
|
Correction to my previous comment, and an update. The argument I made about The actual evidence is better than what I offered, and it points at the master sync rather than at this PR:
a57d2e0 is purely the merge commit, so the diff under test is byte for byte identical in both runs. The same content passed at 19:39 and failed at 23:05. The only variable is the 87 commits that came in with the sync, which include four Dart SDK rolls and #190480's move of I have synced master again, so this is now on 9c298d7 and everything is re-running. Both approvals carried over, since a merge from base does not dismiss them the way my earlier force-pushes did. The @bkonyi the On your nit: I deliberately kept this push a pure merge from base so the approvals would survive, which meant not touching the comment in the same commit. Happy to drop it as a follow-up, or now if you would rather have it gone before this lands and do not mind re-approving. |
|
@bkonyi three things need a maintainer click before this can land, none of which I can do:
No rush on my end, whenever you get a moment. |
…#12453) Manual roll Flutter from 27b098811f3b to c2437523d308 (179 revisions) Manual roll requested by [email protected] flutter/flutter@27b0988...c243752 2026-08-12 [email protected] Enable Gradle CI cache on all test targets that require android_sdk (flutter/flutter#190723) 2026-08-12 [email protected] [analysis] Reland "Added initial implementation of the flutter_analyzer_plugin (#175679)" (flutter/flutter#191022) 2026-08-12 [email protected] Switch testing to gradle bin distribution type instead of all (flutter/flutter#190738) 2026-08-12 [email protected] Convert Mockito instances in Kotlin to Mockk (flutter/flutter#189884) 2026-08-12 [email protected] Report individual test results to LUCI ResultDB (flutter/flutter#190254) 2026-08-12 [email protected] Toggleable reaction duration respects overrides (flutter/flutter#190857) 2026-08-12 [email protected] Roll Skia from e00dbd7448c4 to fee7272f5bc2 (1 revision) (flutter/flutter#191007) 2026-08-12 [email protected] Started caching text shadows by content. (flutter/flutter#190681) 2026-08-12 [email protected] Adds agent skill for spawning led tasks. (flutter/flutter#190937) 2026-08-12 [email protected] Roll Packages from aaaf246 to 94485f1 (8 revisions) (flutter/flutter#191008) 2026-08-12 [email protected] flutter_tools: validate plugin identifiers before generating registrant code (flutter/flutter#190462) 2026-08-12 [email protected] Roll Skia from 112f36148949 to e00dbd7448c4 (3 revisions) (flutter/flutter#190993) 2026-08-12 [email protected] Roll Skia from 7d366c802307 to 112f36148949 (3 revisions) (flutter/flutter#190983) 2026-08-12 [email protected] remove bringup for flavors test (flutter/flutter#190940) 2026-08-12 [email protected] Roll Skia from 1f10a20bdd61 to 7d366c802307 (2 revisions) (flutter/flutter#190980) 2026-08-12 [email protected] Remove `--no-sim-use-hardfp` flag (flutter/flutter#190790) 2026-08-12 [email protected] Roll pub packages (flutter/flutter#190977) 2026-08-12 [email protected] Roll Skia from 339bedab6766 to 1f10a20bdd61 (1 revision) (flutter/flutter#190975) 2026-08-12 [email protected] RawTooltip respects AnimationStyle updates and reverseCurve (flutter/flutter#190889) 2026-08-12 [email protected] ci: Support --target_arch option in prepare_package.dart (flutter/flutter#190960) 2026-08-12 [email protected] Roll Fuchsia Linux SDK from SFq4FVodIOQAS26Lr... to -uHuSGv3wt7QAlDwa... (flutter/flutter#190973) 2026-08-12 [email protected] Removes building of ci/android_debug_x86 as nobody should be consuming it. (flutter/flutter#190951) 2026-08-12 [email protected] Adds error about wimp_heavy not being implemented. (flutter/flutter#189945) 2026-08-11 [email protected] Add clang, cmake, and ninja deps to Linux windowing_test (flutter/flutter#190119) 2026-08-11 [email protected] [AGP 9.1.0 Migration #1] Add Android Gradle Plugin Public API migration documentation (flutter/flutter#190842) 2026-08-11 [email protected] [flutter_tools] Fix deadlock in debug adapters when process exits early (flutter/flutter#190931) 2026-08-11 [email protected] [tool] Define modular dependency injection containers and bootstrapper (flutter/flutter#190724) 2026-08-11 [email protected] [web] Unify MaskFilter and ColorFilter primitives across CanvasKit and Skwasm (flutter/flutter#190314) 2026-08-11 [email protected] Roll pub packages (flutter/flutter#190958) 2026-08-11 [email protected] [Impeller] Move image upload scheduling waits to GPU disable (flutter/flutter#190445) 2026-08-11 [email protected] Started generating the windows platform for macrobenchmarks (flutter/flutter#190932) 2026-08-11 [email protected] [web] Unify ui.Vertices (flutter/flutter#190563) 2026-08-11 [email protected] Fix accessibility_inspector service extensions map mutability (flutter/flutter#190888) 2026-08-11 [email protected] Add batch3 a11y_assessment for vpat (flutter/flutter#189042) 2026-08-11 [email protected] Remove Xcode environment when building swift tools in Xcode pre-action (flutter/flutter#190848) 2026-08-11 [email protected] [tool] Add missing play element in web test index.html to fix warning (flutter/flutter#190675) 2026-08-11 [email protected] Offload blocking work in ProcessTextPlugin to the background (flutter/flutter#189823) 2026-08-11 [email protected] [flutter_tools] Replace usages of package:dds/dap.dart with package:dap_adapters/dap_adapters.dart (flutter/flutter#190667) 2026-08-11 [email protected] Always update swift package dependencies (flutter/flutter#190886) 2026-08-11 [email protected] [flutter_tools] Add --preset option to flutter test (flutter/flutter#190878) 2026-08-11 [email protected] Include the examples cross imports checker in the analzyer. (flutter/flutter#190674) 2026-08-11 [email protected] [devicelab] Remove orphaned screenshot test files (flutter/flutter#190879) 2026-08-11 [email protected] Remove the bringup flag from the linux_arm_host_desktop_engine builder (flutter/flutter#190935) 2026-08-11 [email protected] Reduce web_skwasm_tests subshards from 8 to 2 (flutter/flutter#190728) ...
AndroidPlugin/IOSPlugin/MacOSPlugin/WindowsPlugin/LinuxPlugin.fromYaml(packages/flutter_tools/lib/src/platform_plugins.dart) readpackage,pluginClass, anddartPluginClassstraight out of a plugin's ownpubspec.yaml, validated only as "must be a string" (seeplugins.dart'svalidate()methods).flutter_plugins.dartthen mustache-renders theGeneratedPluginRegistrantsource for each platform with these values substituted directly, e.g.flutterEngine.getPlugins().add(new {{package}}.{{class}}());, with no escaping at all (MustacheTemplateRendererdefaults tohtmlEscapeValues: false).A
pluginClasssuch asEvil(); some.malicious.Payload.run(flutterEngine); //therefore produces a raw, independently-executing statement in the generated file, which gets compiled into every app that depends on the plugin the next time it runsflutter build/flutter run. Confirmed by rendering the actual_androidPluginRegistryTemplateNewEmbeddingtemplate with the realmustache_templatepackage:The injected
Payload.run(flutterEngine);lands as its own statement, not as part of the class name. Same shape applies to iOS/macOS Swift, Windows/Linux C++, and the Dart-only plugin registrant (dartPluginClass/dartFileName, the latter interpolated raw into a Dartimport '...'string, so a value containing'breaks out of that too).Adds
validatePluginIdentifier(rejects anything that isn't a valid, optionally dot-separated identifier) andvalidateDartFileName(rejects anything that isn't a safe relative*.dartpath), called from every platform'sfromYaml.Existing plugin fixtures across
android_plugin_test.dart,plugins_test.dart,dart_plugin_test.dart,plugin_parsing_test.dart, and the macOS/Swift-package-manager suites (315 tests total) all continue to pass unchanged. Addsplatform_plugins_test.dartcovering the injection payload for all five platforms plus thedartFileNameimport-string-breakout case (10 new tests).