`UnapprovedPathMode` has loopholes
- Dominant language
- Rust
- Stars
- 48.2k
- Forks
- 4.8k
- Avg merge
- 3d 16h
- Merged PRs (30d)
- 171
Description
## Bevy version and features
53ddd5e615ae15ad796b36724ddd67516bec0c84 (2026/04/03)
## Problem
The asset system can deny access to absolute paths, and relative paths containing `..` and `.`. See [UnapprovedPathMode](https://docs.rs/bevy/latest/bevy/asset/enum.UnapprovedPathMode.html)
This works for `AssetServer::load` and similar - they end up calling `AssetServer::load_with_meta_transform` which does a `AssetPath::is_unapproved` check ([source](https://github.com/bevyengine/bevy/blob/a66cedd6c3ca729530a8a0e4128b41deada99f58/crates/bevy_asset/src/server/mod.rs#L512))
But there's a bunch of load functions that don't do any checks:
- `AssetServer::load_erased`
- `AssetServer::load_untyped`
- `AssetServer::load_untyped_async`
- `AssetServer::load_unknown_type_with_meta_transform`
- `LoadContext::read_asset_bytes`
There's also problems with `NestedLoader`, which is used to load assets within loaders. First, it doesn't inherit the override flag from `load_override` and similar, so `UnapprovedPathMode::Deny` is broken - loads will be falsely rejected. Second, various paths either have no checks or incorrect checks:
- Deferred:
- `should_load_dependencies = true`,
- Static typed.
- Forbid works, but Deny is broken as `NestedLoader<'_, '_, StaticTyped, Deferred>::load` calls `load_with_meta_transform` with `override_unapproved` hard coded to `true`.
- Dynamic and unknown typed.
- Calls `AssetServer::load_erased_with_meta_transform/load_unknown_type_with_meta_transform`, which should do the check (but currently doesn't).
- `should_load_dependencies = false`.
- Calls `get_or_create_path_handle` which doesn't do any checks.
- Immediate:
- No checks.
## Solution?
I started working on some unit tests and fixes - branch [here](https://github.com/bevyengine/bevy/compare/main...greeble-dev:bevy:nested-loader-unapproved-paths). But I've hit pause due to some issues.
First, while most of the problems can be solved with more plumbing and checks, I'm not so sure about `load_override` semantics and `NestedLoader`. Is it actually correct to inherit the parent override? Is this transitive to all dependencies and immediate loads? How does this get routed through the system?
Second, maybe the checks are in the wrong place? Currently they're in `AssetServer`. But what if the checks were in `AssetSource` instead? So `AssetPlugin::unapproved_path_mode` would go away - instead individual asset sources would have their own parameter.
This has some upsides:
- Somewhat centralizes the checks.
- They do get duplicated across sources, but it's harder to miss a check elsewhere since almost everything goes through the source eventually.
- One flaw is `get_or_create_path_handle`, which doesn't go through the source.
- Makes the `AssetServer` less dependent on `Path` and OS specific quirks.
- `AssetSource` becomes the authority for what paths actually mean (#19079).
- Would allow more complex handling, like disallowing problematic unicode in URLs.
- Different sources can have different settings.
- Not sure if this is actually a good thing, but worth exploring?
- Might also remove the need for `UnapprovedPathMode::Deny` and the `NestedLoader` override inheritance.
- The user can map the same path as two different sources with different approvals.
- So instead of calling `load_override("path")` they call `load("my_unapproved_source://path")`.
The big downside is ergonomics. Instead of setting a single `AssetPlugin::unapproved_path_mode`, the user has to set it on each source.
So I'm not sure about solutions, but I can make a PR with failing unit tests if desired.
Contributor guide
Research direction
Start with the AssetServer load methods and crates/bevy_asset/src/server/mod.rs around the is_unapproved check, then trace NestedLoader and the listed dependency-loading paths. Review the unit tests and fixes on the linked nested-loader branch. Done requires agreed load_override and AssetSource semantics plus coverage showing that every listed path handles unapproved paths consistently.
Written by the indexing model from the issue text.
Assessment
- Tech stack
- rust
- Domain
- game-dev, security
- Issue type
- Bug
- Difficulty
- 5/5
- Estimated time
- Over a week
- Activity status
- Quiet
- Clarity
- Needs clarification
- Newbie friendliness
- 35/100