Repository navigation
[beta] [flutter_tools] Contain web asset server path resolution to its search roots - #195201
Conversation
…h roots (flutter#194064) `flutter_tools`' web asset servers resolve the incoming request path against each of their search roots with `Uri.resolve`. That treats the request path as a URI *reference* rather than as a path, and a reference carrying a scheme, an authority or an absolute path replaces the base instead of resolving below it. The result of a lookup is therefore not guaranteed to be inside the root that was being searched, and nothing downstream checks that it is. This PR makes the containment explicit instead of relying on the shape of the request path. Two helpers are added to `web_server_utilities.dart`: * `isRelativeRequestPath` rejects a request path that is not a relative reference: one with a scheme or a Windows drive letter in its first segment, an authority, an absolute path, a backslash, or a NUL. * `resolveRequestPathUnder` resolves against a given root and returns the result only when it lands inside that root, compared with `path.isWithin` so the comparison is per-component and case-insensitive on Windows. Every `resolve()` call in `ReleaseAssetServer.handle` and `WebAssetServer.handleRequest` now goes through the helper, so each search root keeps its own boundary: the canvaskit fallback, the built asset fallback, the project `web` directory, and the roots consulted by `_resolveDartFile`. `.` and `..` segments are deliberately still accepted and handled by the containment check, so a request path that normalizes back inside the root keeps working. A `/packages/<package>/<path>` request is resolved the same way below that package's `packageUriRoot`, looked up by name, instead of through a `package:` URI built from the request segments. `ReleaseAssetServer` checks a root's allowed extensions against the request path before resolving it, so regular asset requests skip resolution for the source-map-only roots. This also keeps exceptions from escaping the handler. On `master` today, `GET /..%2F..%2F..%2Ftmp/foo` throws `UnsupportedError: Illegal path character` out of `toFilePath`, because the decoded segment contains a separator and so names no file path at all. Likewise `GET /packages/foo.dart` throws an `ArgumentError` out of `PackageConfig.resolve`. Both are now answered like any other path that does not resolve inside a root. `ReleaseAssetServer does not serve files outside of the project` is extended with the reference shapes that resolve outside a search root, and `WebAssetServer does not serve files outside of the project` is added as the debug-handler equivalent, including `dartSourceContents` and `/packages/` requests with an absolute path or nothing after the package name. Both fail against unmodified `lib/` and pass with the change. Positive tests cover a package path with a dot segment that stays inside the package root, and nested build output paths, including an asset name containing a colon in a later segment, which stays valid in a relative reference and must keep working. `dart analyze` and `dart format` are clean on the five touched files. Rebased on current `master`; locally both web suites (`devfs_web_test.dart`, `web_asset_server_test.dart`) pass in full, 84 tests. The 4 failures I mentioned in an earlier version of this description came from running them without `FLUTTER_ROOT` set. There is no public issue tracking this; it came out of maintainer feedback rather than a filed report. Happy to open one if the process needs the link. - [x] I read the [Contributor Guide] and followed the process outlined there for submitting PRs. - [x] I read the [AI contribution guidelines] and understand my responsibilities, or I am not using AI tools. - [x] I read the [Tree Hygiene] wiki page, which explains my responsibilities. - [x] I read and followed the [Flutter Style Guide], including [Features we expect every widget to implement]. - [x] I signed the [CLA]. - [ ] I listed at least one issue that this PR fixes in the description above. - [x] I updated/added relevant in-code documentation (doc comments with `///`). - [x] If this PR introduces a new feature or capability, I created and linked a website documentation issue or PR in [flutter/website] (or verified none is needed). - [x] I added new tests to check the change I am making, or this PR is [test-exempt]. - [x] I followed the [breaking change policy] and added [Data Driven Fixes] where supported. - [x] All existing and new tests are passing. <!-- Links --> [Contributor Guide]: https://github.com/flutter/flutter/blob/main/docs/contributing/Tree-hygiene.md#overview [AI contribution guidelines]: https://github.com/flutter/flutter/blob/main/docs/contributing/Tree-hygiene.md#ai-contribution-guidelines [Tree Hygiene]: https://github.com/flutter/flutter/blob/main/docs/contributing/Tree-hygiene.md [test-exempt]: https://github.com/flutter/flutter/blob/main/docs/contributing/Tree-hygiene.md#tests [Flutter Style Guide]: https://github.com/flutter/flutter/blob/main/docs/contributing/Style-guide-for-Flutter-repo.md [Features we expect every widget to implement]: https://github.com/flutter/flutter/blob/main/docs/contributing/Style-guide-for-Flutter-repo.md#features-we-expect-every-widget-to-implement [CLA]: https://cla.developers.google.com/ [flutter/tests]: https://github.com/flutter/tests [flutter/website]: https://github.com/flutter/website [breaking change policy]: https://github.com/flutter/flutter/blob/main/docs/contributing/Tree-hygiene.md#handling-breaking-changes [Data Driven Fixes]: https://github.com/flutter/flutter/blob/main/docs/contributing/Data-driven-Fixes.md
|
This pull request was opened from and to a release candidate branch. This should only be done as part of the official Flutter release process. If you are attempting to make a regular contribution to the Flutter project, please close this PR and follow the instructions at Tree Hygiene for detailed instructions on contributing to Flutter. Reviewers: Use caution before merging pull requests to release branches. Ensure the proper procedure has been followed. |
There was a problem hiding this comment.
Code Review
This pull request introduces path traversal defenses in the Flutter web asset servers (ReleaseAssetServer and WebAssetServer) by adding utility functions isRelativeRequestPath and resolveRequestPathUnder to validate request paths and ensure resolved file paths reside strictly within their designated root directories. The asset servers are updated to use these utilities when resolving canvaskit, assets, packages, SDKs, and source maps, and extensive tests are added to verify that files outside the project roots are not served. I have no feedback to provide as there are no review comments to evaluate.
6630731
into
flutter:flutter-3.50-candidate.0
Cherry-pick of #194064 to beta (
flutter-3.50-candidate.0).Impact Description:
Tightens how the web development server in
flutter_toolsmaps request paths to files, so every lookup stays within the directory it is meant to serve from. It also stops some malformed request paths from throwing out of the request handler.This only affects development with
flutter runon web, not deployed apps.Changelog Description:
#194064 When running a web app with
flutter run, the development server resolves some request paths incorrectly.Workaround:
None.
Risk:
What is the risk level of this cherry-pick?
Test Coverage:
Are you confident that your fix is well-tested by automated tests?
Validation Steps:
In
packages/flutter_tools, runtest/general.shard/web/devfs_web_test.dartandtest/general.shard/web/web_asset_server_test.dart. All tests pass on this branch, and the new tests fail without the fix.Note:
web_asset_server.dartneeded a small manual merge because #194053 isn't on the 3.50 branch. The logic is the same as on master.