docs: capture todo - uploads keep the client extension (stored XSS, g15office bemfi2zs)
This commit is contained in:
@@ -0,0 +1,32 @@
|
||||
---
|
||||
created: 2026-10-11T00:00:00Z
|
||||
title: Uploads keep the client file extension, so a valid image can be served as HTML (stored XSS)
|
||||
priority: high
|
||||
area: lagoon/attach, fonoteka.go uploads
|
||||
severity: major
|
||||
g15office: bemfi2zs
|
||||
files:
|
||||
- ../fonoteka.go/plugins/golem15/fonoteka/controllers/api/collection_media_controller.go (storeUpload)
|
||||
- ../fonoteka.go/plugins/golem15/fonoteka/classes/album_files.go (StorePublicFile)
|
||||
- modules/lagoon/attach
|
||||
---
|
||||
|
||||
## Problem
|
||||
|
||||
Found in the Phase 12 code review (WR-01 in `.planning/phases/12-p-ytarium-api-collections-and-albums/12-REVIEW.md`).
|
||||
|
||||
Public uploads keep the extension the client sent. The web server serves `storage/app/uploads/public` and picks the Content-Type from the extension; the blob's `ContentType` metadata does not reach it. A PNG with `<script>` in a `tEXt` chunk passes both upload checks (`image|mimes:...` sniffs the content, `IsAllowedImage` decodes it). Uploaded as `x.html`, it is stored as `<hex>.html`, its URL comes back in the 201 response and every listing, and a browser renders it as HTML.
|
||||
|
||||
WinterCMS's `File::getDiskName` behaves the same way (inherited, not introduced), but any household member or write-scoped token can exploit it. Still present as of 2026-10-11: `storeUpload` appends `filepath.Ext(upload.Filename)` to the disk name.
|
||||
|
||||
## Solution
|
||||
|
||||
- Derive the stored extension from the sniffed MIME type (`image/png` → `png`, and so on) in one shared helper, preferably in `lagoon/attach` so every plugin gets it. Keep the client name only in `file_name`.
|
||||
- The URL changes only for uploads whose client extension disagrees with their content; the parity corpus does not record that case.
|
||||
- Defence in depth: `X-Content-Type-Options: nosniff` and a file-type allow-list on the web server's uploads location, documented in the deploy docs.
|
||||
- Shares lines with the CR-01 fix in the application project (odd extensions breaking every listing).
|
||||
|
||||
## Acceptance
|
||||
|
||||
- A PNG uploaded as `x.html` is stored with a `.png` disk name and its URL ends in `.png`.
|
||||
- A regression test covers that upload.
|
||||
Reference in New Issue
Block a user