[GH-ISSUE #4124] [Bug] ZIPs created from the current directory by bsdtar fail inventory and extraction #1623

Closed
opened 2026-10-16 21:08:06 +00:00 by bsakaguchi · 1 comment
Owner

Originally created by @Adkid-Zephyr on GitHub (Oct 15, 2026).
Original GitHub issue: https://github.com/morluto/rea/issues/4124

Area

Artifact inventory and extraction.

Observed behavior

A ZIP created by macOS bsdtar from . contains ./, ./sub/, and ./sub/data.txt. Both inspect-artifact and extract-artifact exit 1 before producing their result:

{"code":"artifact_operation_failed","message":"Artifact path is not normalized: \"./\"","details":{"reason":"path"}}

Reproduction

Executed on source revision fe7bcc3fbeafe73fed97f1d6d75f6ee3a0821d4a, Node 24.18.0, macOS arm64, bsdtar 3.5.3 - libarchive 3.7.4, using an isolated profile and a small self-created text fixture:

mkdir -p source/sub
printf 'standard ZIP fixture\n' > source/sub/data.txt
(cd source && /usr/bin/tar --format=zip -cf ../sample.zip .)
/usr/bin/tar -tf sample.zip
node scripts/rea.mjs inspect-artifact /absolute/path/sample.zip --json
node scripts/rea.mjs extract-artifact /absolute/path/sample.zip --json

The two CLI operations failed on the same fixture. Independently, production-boundary tests passed for unprefixed ZIP members and failed for both ./ and repeated ././ prefixes on the unmodified base.

Expected behavior

Interpret the leading current-directory prefix at the ZIP adapter boundary and represent the archive root once. Inventory and extraction should then agree on sub/data.txt while preserving distinct physical ZIP member identities and the file's bytes.

Impact and scope

Proposed severity: P2. An ordinary archive producer generates inputs that REA cannot inventory or extract. Repacking with member names that omit the leading ./ is a workaround; unprefixed controls work. No data loss was observed. Other archive producers and operating systems were not tested.

Earliest failure and cause

ZipArtifactReader forwards the producer's ./ directory/member spelling to the shared logical-path validator. normalizeArtifactPath rejects the current-directory component. Extraction uses the same adapter, so correcting only inventory would leave that consumer inconsistent.

Additional context

Prepared with AI assistance. A local fix and regression tests are ready for a linked Draft PR. Checked open/closed ZIP work and the ZIP-reader history; #4006 concerns out-of-file reads, #2717 adds byte-backed Java archive access, and #4031 changes extraction selection/destination. None addresses this producer spelling. No imported code or archive target was executed.

Originally created by @Adkid-Zephyr on GitHub (Oct 15, 2026). Original GitHub issue: https://github.com/morluto/rea/issues/4124 ### Area Artifact inventory and extraction. ### Observed behavior A ZIP created by macOS `bsdtar` from `.` contains `./`, `./sub/`, and `./sub/data.txt`. Both `inspect-artifact` and `extract-artifact` exit 1 before producing their result: ```json {"code":"artifact_operation_failed","message":"Artifact path is not normalized: \"./\"","details":{"reason":"path"}} ``` ### Reproduction Executed on source revision `fe7bcc3fbeafe73fed97f1d6d75f6ee3a0821d4a`, Node 24.18.0, macOS arm64, `bsdtar 3.5.3 - libarchive 3.7.4`, using an isolated profile and a small self-created text fixture: ```sh mkdir -p source/sub printf 'standard ZIP fixture\n' > source/sub/data.txt (cd source && /usr/bin/tar --format=zip -cf ../sample.zip .) /usr/bin/tar -tf sample.zip node scripts/rea.mjs inspect-artifact /absolute/path/sample.zip --json node scripts/rea.mjs extract-artifact /absolute/path/sample.zip --json ``` The two CLI operations failed on the same fixture. Independently, production-boundary tests passed for unprefixed ZIP members and failed for both `./` and repeated `././` prefixes on the unmodified base. ### Expected behavior Interpret the leading current-directory prefix at the ZIP adapter boundary and represent the archive root once. Inventory and extraction should then agree on `sub/data.txt` while preserving distinct physical ZIP member identities and the file's bytes. ### Impact and scope Proposed severity: **P2**. An ordinary archive producer generates inputs that REA cannot inventory or extract. Repacking with member names that omit the leading `./` is a workaround; unprefixed controls work. No data loss was observed. Other archive producers and operating systems were not tested. ### Earliest failure and cause `ZipArtifactReader` forwards the producer's `./` directory/member spelling to the shared logical-path validator. `normalizeArtifactPath` rejects the current-directory component. Extraction uses the same adapter, so correcting only inventory would leave that consumer inconsistent. ### Additional context Prepared with AI assistance. A local fix and regression tests are ready for a linked Draft PR. Checked open/closed ZIP work and the ZIP-reader history; #4006 concerns out-of-file reads, #2717 adds byte-backed Java archive access, and #4031 changes extraction selection/destination. None addresses this producer spelling. No imported code or archive target was executed.
Author
Owner

@morluto commented on GitHub (Oct 15, 2026):

Fixed on main in 6b1c36e33. The ZIP reader now normalizes leading current-directory components once, retains each original decoded name and central-directory identity, and skips only actual root-directory markers. Entry kind also has one owner, so Unix symlinks with directory-looking names remain symlinks. Traversal and non-directory root names remain rejected.

Validation: the compiled CLI regressions failed on the old runtime and passed after the fix. Reader/boundary/public CLI checks passed (14 cases), including duplicate occurrence identity, root-only inventory/extraction, symlink metadata and decompression budgets. Main CI passed all 36 jobs: https://github.com/morluto/rea/actions/runs/38788727652. Closing as addressed.

<!-- gh-comment-id:6200003180 --> @morluto commented on GitHub (Oct 15, 2026): Fixed on main in 6b1c36e33. The ZIP reader now normalizes leading current-directory components once, retains each original decoded name and central-directory identity, and skips only actual root-directory markers. Entry kind also has one owner, so Unix symlinks with directory-looking names remain symlinks. Traversal and non-directory root names remain rejected. Validation: the compiled CLI regressions failed on the old runtime and passed after the fix. Reader/boundary/public CLI checks passed (14 cases), including duplicate occurrence identity, root-only inventory/extraction, symlink metadata and decompression budgets. Main CI passed all 36 jobs: https://github.com/morluto/rea/actions/runs/38788727652. Closing as addressed.
Sign in to join this conversation.
No milestone
No project
No assignees
1 participant
Notifications
Due date
The due date is invalid or out of range. Please use the format "yyyy-mm-dd".

No due date set.

Dependencies

No dependencies set.

Reference
morluto/rea#1623
No description provided.