2.9 KiB
| title | status | type | priority | created_at | updated_at |
|---|---|---|---|---|---|
| Normalize '.' path segments so ./-prefixed tar entries are accepted | completed | bug | high | 2026-07-21T16:25:26Z | 2026-07-21T16:30:49Z |
Symptom
Uploading a GNU-tar-style tarball fails with archive entry has an unsafe path: './bin_bg.wasm' (a PathTraversal 400). Not wasm-specific — .wasm is fully allowed (mime.rs maps it to application/wasm; extensions are not checked on upload).
Root cause
check_entry_path (core/upload.rs ~664-670) splits the entry name on / and rejects any segment equal to . (alongside .. and empty). GNU tar czf out.tgz . prefixes every entry with ./ (e.g. ./index.html, ./bin_bg.wasm), so the leading . segment trips the traversal guard. This also breaks the root-index.html requirement for such tarballs (./index.html != index.html), making ./-prefixed archives unusable. Zip tools don't emit ./, so only the new compressed-tar path exposed this.
Fix
Treat . segments as the no-ops they are: normalize them out (strip leading ./, drop interior . segments) and use the normalized /-joined path as the stored R2 key. Keep rejecting .., absolute paths, backslashes, NUL, drive letters, over-length/over-depth, and empty-after-normalization. Apply to both zip and tar paths for consistency (share via check_entry_path). Depth is measured on the normalized path.
Tasks
- Normalize
.segments in check_entry_path; return the normalized path for use as the R2 key - Update zip + tar manifest builders to store the normalized path (so index.html detection sees the normalized name)
- Tests:
./-prefixed tar entry accepted (incl. ./index.html satisfies root requirement and ./bin_bg.wasm accepted), interior./normalized,..still rejected,.-only / empty-after-normalization rejected - All six validation commands pass (native + wasm)
- Commit, push, deploy
Summary of Changes
check_entry_path now returns the normalized path (Result<String, _>): . current-directory segments are stripped (leading ./ and interior /./) instead of rejected, while .., absolute paths, backslashes, NUL, drive letters, empty (//) segments, and names that normalize to nothing are still rejected. Depth is measured on the normalized segment count. Both build_zip_manifest and read_tar_entries store the normalized path, so a GNU-tar ./index.html satisfies the root-level requirement and ./bin_bg.wasm is accepted.
.wasm was never blocked — the upload validator does not check extensions, and mime.rs maps .wasm to application/wasm.
+10 tests: 3 tar ./-prefix wrappers (gzip/zstd/xz) plus 7 direct check_entry_path cases (leading/interior . normalization, .. still rejected, .-only rejected, empty segment rejected, depth measured post-normalization). All six validation commands pass: 196 unit + 20 doctests.