You cannot select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
vibed/pages/.beans/pages-aalu--normalize-path-...

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.