--- # pages-aalu title: Normalize '.' path segments so ./-prefixed tar entries are accepted status: completed type: bug priority: high created_at: 2026-07-21T16:25:26Z updated_at: 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 - [x] Normalize `.` segments in check_entry_path; return the normalized path for use as the R2 key - [x] Update zip + tar manifest builders to store the normalized path (so index.html detection sees the normalized name) - [x] 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 - [x] All six validation commands pass (native + wasm) - [x] Commit, push, deploy ## Summary of Changes `check_entry_path` now returns the normalized path (`Result`): `.` 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.