Repository navigation
[flutter_tools] Enforce that package-declared asset paths stay within the package - #187661
Conversation
… the package `_ensureAssetPathIsValid` rejected absolute asset paths but allowed a relative path containing `..` to escape the base directory, and `_Asset.lookupAssetFile` joins the base dir with the (uncontained) relative path. For a dependency, the asset base is that package's own directory, so a package could declare an asset such as `../secret` and cause a consumer's `flutter build` to read a file from outside the package on the build machine and bundle it into the app — which the consuming app never declared. This adds a containment check for dependency-declared assets: the resolved path must stay within the package directory (mirroring the existing absolute-path rejection). App-level asset paths are unaffected (the check is scoped to `packageName != null`), so existing first-party `..` asset usage keeps working. Verified with a real `flutter build bundle` (escaping dependency asset now rejected; a normal in-package dependency asset still bundles) and a new unit test; all existing asset_bundle_package_test cases pass.
There was a problem hiding this comment.
Code Review
This pull request introduces validation to ensure that asset paths declared by package dependencies do not escape their package directory, throwing a tool exit if they resolve outside of it. A corresponding test case has been added to verify this behavior. Feedback suggests extracting the repeated evaluation of the asset file path into a local variable to improve readability and avoid redundant operations.
| if (packageName != null) { | ||
| final String base = _fileSystem.path.canonicalize(assetsBaseDir); | ||
| final String resolved = _fileSystem.path.canonicalize( | ||
| _fileSystem.path.join(assetsBaseDir, assetUri.toFilePath(windows: _platform.isWindows)), | ||
| ); | ||
| if (base != resolved && !_fileSystem.path.isWithin(base, resolved)) { | ||
| throwToolExit( | ||
| 'Asset path "${assetUri.toFilePath(windows: _platform.isWindows)}" of package ' | ||
| '"$packageName" resolves to a location outside the package directory. Package asset ' | ||
| 'paths must stay within the package.', | ||
| ); | ||
| } | ||
| } |
There was a problem hiding this comment.
The expression assetUri.toFilePath(windows: _platform.isWindows) is evaluated twice within this block. Extracting it into a local variable improves readability and avoids redundant operations.
if (packageName != null) {
final String assetPath = assetUri.toFilePath(windows: _platform.isWindows);
final String base = _fileSystem.path.canonicalize(assetsBaseDir);
final String resolved = _fileSystem.path.canonicalize(
_fileSystem.path.join(assetsBaseDir, assetPath),
);
if (base != resolved && !_fileSystem.path.isWithin(base, resolved)) {
throwToolExit(
'Asset path "$assetPath" of package '
'"$packageName" resolves to a location outside the package directory. Package asset '
'paths must stay within the package.',
);
}
}There was a problem hiding this comment.
Done — extracted assetPath, thanks.
bkonyi
left a comment
There was a problem hiding this comment.
This looks good to me overall! Just one minor comment.
|
autosubmit label was removed for flutter/flutter/187661, because The base commit of the PR is older than 7 days and can not be merged. Please merge the latest changes from the main into this branch and resubmit the PR. |
|
autosubmit label was removed for flutter/flutter/187661, because The base commit of the PR is older than 7 days and can not be merged. Please merge the latest changes from the main into this branch and resubmit the PR. |
flutter/flutter@b10d0f1...15963bc 2026-06-18 [email protected] Roll Skia from 6e84902d56c3 to 1ae2466c9ea5 (4 revisions) (flutter/flutter#188172) 2026-06-18 [email protected] Roll Packages from 6ce00a8 to 4fd05e6 (3 revisions) (flutter/flutter#188171) 2026-06-18 [email protected] [Linux] Fix vertical offset in composite_layer (flutter/flutter#188145) 2026-06-18 [email protected] [Linux] Fix incorrect GL datatypes for uniform locations (flutter/flutter#188143) 2026-06-18 [email protected] Roll Dart SDK from e05c69222ea4 to 5883736e7670 (2 revisions) (flutter/flutter#188168) 2026-06-18 [email protected] Roll Skia from 046277850e8d to 6e84902d56c3 (5 revisions) (flutter/flutter#188165) 2026-06-18 [email protected] Roll Fuchsia Linux SDK from or21OEdGtairm6nl9... to 1E2qOlNnC2Ucn-1oV... (flutter/flutter#188162) 2026-06-18 [email protected] Roll Skia from 8dd207d443d3 to 046277850e8d (1 revision) (flutter/flutter#188153) 2026-06-18 [email protected] Add entitlements.txt entries for new dart sdk binaries (flutter/flutter#188133) 2026-06-18 [email protected] Roll Dart SDK from b670723c5f07 to e05c69222ea4 (1 revision) (flutter/flutter#188146) 2026-06-18 [email protected] Fix bounds checking in FlAccessibleTextField (flutter/flutter#188137) 2026-06-18 [email protected] Roll Skia from f5a2921fe23e to 8dd207d443d3 (2 revisions) (flutter/flutter#188141) 2026-06-18 [email protected] Adds tests for disabling macos impeller (flutter/flutter#188132) 2026-06-17 [email protected] Improve Flutter Web accessibility: update flt meta viewport tag to align with WCAG 2 guidelines (flutter/flutter#182047) 2026-06-17 [email protected] Roll Dart SDK from e39bde5b1bfc to b670723c5f07 (2 revisions) (flutter/flutter#188130) 2026-06-17 [email protected] Roll Skia from 066bfbac7282 to f5a2921fe23e (1 revision) (flutter/flutter#188128) 2026-06-17 [email protected] Support --trace-systrace in release builds on Android (flutter/flutter#186359) 2026-06-17 [email protected] Isolate compiled dill caches by TargetModel (flutter/flutter#187253) 2026-06-17 98614782+auto-submit[bot]@users.noreply.github.com Reverts "refactor(web): Unify Image on Skwasm and CanvasKit (#187873)" (flutter/flutter#188124) 2026-06-17 [email protected] Use a mock EGL manager in windows unittests to avoid flaky rendering calls (flutter/flutter#188078) 2026-06-17 [email protected] [Android] Remove support for unused manifest flags (flutter/flutter#186021) 2026-06-17 [email protected] Adds windows project switch for enabling impeller (flutter/flutter#188044) 2026-06-17 [email protected] Skip prefetch SwiftPM dependencies if the project hasn't been migrated to SwiftPM yet (flutter/flutter#187206) 2026-06-17 [email protected] [flutter_tools] Bump dwds to 27.1.2 (flutter/flutter#187951) 2026-06-17 [email protected] Adds external texture devicelab test for windows impeller (flutter/flutter#187886) 2026-06-17 [email protected] Roll Skia from 5d19002eb73e to 066bfbac7282 (2 revisions) (flutter/flutter#188118) 2026-06-17 [email protected] Add note about magnifier issue when using transparent HCPP pv (flutter/flutter#187753) 2026-06-17 [email protected] [linux]: fixes crash when resizing windows (flutter/flutter#187626) 2026-06-17 [email protected] [flutter_tools] Enforce that package-declared asset paths stay within the package (flutter/flutter#187661) 2026-06-17 [email protected] Remove canvaskit_cipd_instance from DEPS (flutter/flutter#188073) If this roll has caused a breakage, revert this CL and stop the roller using the controls here: https://autoroll.skia.org/r/flutter-packages Please CC [email protected],[email protected] on the revert to ensure that a human is aware of the problem. To file a bug in Packages: https://github.com/flutter/flutter/issues/new/choose To report a problem with the AutoRoller itself, please file a bug: https://issues.skia.org/issues/new?component=1389291&template=1850622 Documentation for the AutoRoller is here: https://skia.googlesource.com/buildbot/+doc/main/autoroll/README.md
… the package (flutter#187661) Fixes a containment gap in asset resolution. ## Problem `_ensureAssetPathIsValid` (`packages/flutter_tools/lib/src/asset.dart`) rejects absolute asset paths but allows a relative path containing `..` to escape the base directory, and `_Asset.lookupAssetFile` joins the base dir with the (uncontained) relative path. For a **dependency**, the asset base is that package’s own directory, so a package can declare an asset such as `../something` and cause a consumer’s `flutter build` to read a file from **outside the package** on the build machine and bundle it into the app — even though the consuming app never declared that path. ## Fix Add a containment check for dependency-declared assets: the resolved path must stay within the package directory, mirroring the existing absolute-path rejection. The check is scoped to `packageName != null`, so **app-level asset paths are unaffected** and existing first-party `..` asset usage keeps working. ## Tests - New unit test in `asset_bundle_package_test.dart`: a dependency asset that escapes its package directory is rejected. - Verified with a real `flutter build bundle`: an escaping dependency asset is now rejected, while a normal in-package dependency asset still bundles correctly. - All existing `asset_bundle_package_test` cases pass. ## Pre-launch Checklist - [x] I read the [Contributor Guide] and followed the process outlined there for submitting PRs. - [x] I listed at least one issue that this PR fixes in the description above. - [x] I added new tests to check the change I am making. - [x] I followed the [breaking change policy] and added [Data Driven Fixes] where supported. - [x] All existing and new tests are passing. [Contributor Guide]: https://github.com/flutter/flutter/blob/master/docs/contributing/Tree-hygiene.md#overview [breaking change policy]: https://github.com/flutter/flutter/blob/master/docs/contributing/Tree-hygiene.md#handling-breaking-changes [Data Driven Fixes]: https://github.com/flutter/flutter/blob/master/docs/contributing/Data-driven-Fixes.md --------- Co-authored-by: Ben Konyi <[email protected]>
… the package (flutter#187661) Fixes a containment gap in asset resolution. ## Problem `_ensureAssetPathIsValid` (`packages/flutter_tools/lib/src/asset.dart`) rejects absolute asset paths but allows a relative path containing `..` to escape the base directory, and `_Asset.lookupAssetFile` joins the base dir with the (uncontained) relative path. For a **dependency**, the asset base is that package’s own directory, so a package can declare an asset such as `../something` and cause a consumer’s `flutter build` to read a file from **outside the package** on the build machine and bundle it into the app — even though the consuming app never declared that path. ## Fix Add a containment check for dependency-declared assets: the resolved path must stay within the package directory, mirroring the existing absolute-path rejection. The check is scoped to `packageName != null`, so **app-level asset paths are unaffected** and existing first-party `..` asset usage keeps working. ## Tests - New unit test in `asset_bundle_package_test.dart`: a dependency asset that escapes its package directory is rejected. - Verified with a real `flutter build bundle`: an escaping dependency asset is now rejected, while a normal in-package dependency asset still bundles correctly. - All existing `asset_bundle_package_test` cases pass. ## Pre-launch Checklist - [x] I read the [Contributor Guide] and followed the process outlined there for submitting PRs. - [x] I listed at least one issue that this PR fixes in the description above. - [x] I added new tests to check the change I am making. - [x] I followed the [breaking change policy] and added [Data Driven Fixes] where supported. - [x] All existing and new tests are passing. [Contributor Guide]: https://github.com/flutter/flutter/blob/master/docs/contributing/Tree-hygiene.md#overview [breaking change policy]: https://github.com/flutter/flutter/blob/master/docs/contributing/Tree-hygiene.md#handling-breaking-changes [Data Driven Fixes]: https://github.com/flutter/flutter/blob/master/docs/contributing/Data-driven-Fixes.md --------- Co-authored-by: Ben Konyi <[email protected]>
…package (flutter#189159) A package's `flutter: licenses:` entries are resolved against the package root and their contents appended to the app's generated NOTICES. Unlike dependency asset paths, license paths were not containment-checked, so a (transitive) dependency could declare `licenses: ["../secret"]` and have an arbitrary host file read during a normal `flutter build` and bundled into the shipped app. This mirrors the boundary enforced for asset paths in flutter#187661. Fix: reject dependency license paths that escape the package directory (canonicalize + `isWithin`); the app's own licenses are unaffected. Verified with a real `flutter build bundle` — a dependency declaring `licenses: [../host_secret.txt]` previously bundled the host file into `flutter_assets/NOTICES.Z`; with this change the build is rejected. --------- Co-authored-by: Ben Konyi <[email protected]>
…GeneratedPluginRegistrant injection (flutter#189156) ## Description Plugin `pluginClass`/`dartPluginClass` and the Android `package` are interpolated **verbatim** into the generated `GeneratedPluginRegistrant` source files. `flutter_plugins.dart` renders these templates via `_renderTemplateToFile` → `templateRenderer.renderString(template, context)`, and the renderer's `htmlEscapeValues` defaults to `false`, so the values are emitted with no escaping into Java/Kotlin, Swift, Objective-C and C++ source (e.g. `new {{package}}.{{class}}()`, `{{prefix}}{{class}}.register(...)`, `#import <{{name}}/{{class}}.h>`). The per-platform `validate()` methods in `platform_plugins.dart` only checked that these fields were *strings*, not that they were valid identifiers. As a result a plugin declaration whose `pluginClass`/`package` contains arbitrary source (spaces, `;`, `{}`, `()`, newlines, …) passes validation and that source lands in the consuming app's `GeneratedPluginRegistrant` and is compiled into the app. Because plugins are collected over `computeTransitiveDependencies(...)` with no opt-in from the consuming app, a **transitive** dependency can use this to have arbitrary native code compiled into an app that merely depends on it (via a plain `flutter pub get` / `build` / `run`). This is the same "a package must not escape its declared boundary at build time" boundary enforced for asset paths in flutter#187661 and for pub-cache extraction in CVE-2026-27704. Reproduced end-to-end: a dependency declaring ```yaml flutter: plugin: platforms: macos: pluginClass: "SomePlugin.register(...); <injected statements>; if false { SomePlugin" ``` resulted, after `flutter pub get`, in the injected statements appearing verbatim in `macos/Flutter/GeneratedPluginRegistrant.swift` and `ios/Runner/GeneratedPluginRegistrant.m`. ## Fix Restrict `pluginClass`, `dartPluginClass` and the Android `package` to identifier characters (dot-separated identifiers) in each platform's `validate()`, rejecting the plugin specification otherwise. Legitimate class/package names are unaffected; a value that is not a plain identifier now fails with `Invalid plugin specification <name>`. ## Tests - Added a regression test asserting a `pluginClass` containing injection characters is rejected. - Full `test/general.shard/plugins_test.dart` passes (77/77) — no legitimate plugin specification regresses. ## Pre-launch Checklist - [x] I added new tests to check the change I am making. - [x] All existing and new tests are passing. --------- Co-authored-by: Ben Konyi <[email protected]>
Fixes a containment gap in asset resolution.
Problem
_ensureAssetPathIsValid(packages/flutter_tools/lib/src/asset.dart) rejects absolute asset paths but allows a relative path containing..to escape the base directory, and_Asset.lookupAssetFilejoins the base dir with the (uncontained) relative path. For a dependency, the asset base is that package’s own directory, so a package can declare an asset such as../somethingand cause a consumer’sflutter buildto read a file from outside the package on the build machine and bundle it into the app — even though the consuming app never declared that path.Fix
Add a containment check for dependency-declared assets: the resolved path must stay within the package directory, mirroring the existing absolute-path rejection. The check is scoped to
packageName != null, so app-level asset paths are unaffected and existing first-party..asset usage keeps working.Tests
asset_bundle_package_test.dart: a dependency asset that escapes its package directory is rejected.flutter build bundle: an escaping dependency asset is now rejected, while a normal in-package dependency asset still bundles correctly.asset_bundle_package_testcases pass.Pre-launch Checklist