Summary
The current static asset middleware can intercept unrelated routes, panic on short paths, skip directory indexes, and return files without a MIME type.
Relevant code:
Reproduction
Create a dedicated fixture directory next to the reproduction package. Do not expose the repository or process working directory as the static root:
testdata/
└── public/
├── index.html
└── app.txt
For example, put index fixture in index.html and asset fixture in app.txt, then serve only that directory:
async fn main {
let app = @mocket.new()
app.static_assets("/assets", @static_file.new("testdata/public"))
app.get("/api", _ => "api ok")
app.listen("127.0.0.1:49288")
}
Observed on main@c545db2, native backend:
| Request |
Result |
GET /assets |
404 |
GET /assets/ |
404 instead of serving testdata/public/index.html |
GET /assets/app.txt |
200 with asset fixture, but no Content-Type |
GET /api |
empty reply; process exits with PanicError |
The first condition in static_assets calls next() when match_path(path, url) succeeds. For an unrelated shorter URL, execution instead reaches event.req.url[path.length():], which aborts on the out-of-range slice.
The default filesystem provider compounds this:
get_meta returns Some for every candidate without checking the filesystem, so probing stops before index/fallthrough logic can work.
get_type always returns None.
- file reads use synchronous
moonbitlang/x/fs inside an async request path;
- all I/O errors are collapsed to 404.
Expected behavior
- Match the mount as a path prefix with a segment boundary.
- Never slice until the mount relationship and bounds are validated.
- Resolve a safe relative path confined to the configured root.
- Make
get_meta return None for absent candidates and provide real size/mtime/type metadata.
- Serve configured directory indexes correctly.
- Use supported async filesystem I/O, or clearly document unavoidable blocking behavior.
- Preserve fallthrough and distinguish not-found from other I/O failures.
- Add black-box tests for mounted paths, unrelated routes, short URLs, indexes, MIME types, HEAD, and fallthrough.
Summary
The current static asset middleware can intercept unrelated routes, panic on short paths, skip directory indexes, and return files without a MIME type.
Relevant code:
Reproduction
Create a dedicated fixture directory next to the reproduction package. Do not expose the repository or process working directory as the static root:
For example, put
index fixtureinindex.htmlandasset fixtureinapp.txt, then serve only that directory:Observed on
main@c545db2, native backend:GET /assetsGET /assets/testdata/public/index.htmlGET /assets/app.txtasset fixture, but noContent-TypeGET /apiPanicErrorThe first condition in
static_assetscallsnext()whenmatch_path(path, url)succeeds. For an unrelated shorter URL, execution instead reachesevent.req.url[path.length():], which aborts on the out-of-range slice.The default filesystem provider compounds this:
get_metareturnsSomefor every candidate without checking the filesystem, so probing stops before index/fallthrough logic can work.get_typealways returnsNone.moonbitlang/x/fsinside an async request path;Expected behavior
get_metareturnNonefor absent candidates and provide real size/mtime/type metadata.