unpack_strategy/zip: move volume labels by basename - #23765
Merged
Conversation
Contributor
There was a problem hiding this comment.
馃煛 Changes recommended
Special basenames such as .. can still escape the temporary extraction directory.
Once you've addressed the issues Copilot identified, you can request another Copilot review.
Pull request overview
Hardens macOS ZIP extraction against symlink-based destination redirection.
Changes:
- Moves volume labels using their basename and destination directory.
- Adds ZIP fixtures covering symlink collisions and normal extraction.
File summaries
| File | Description |
|---|---|
Library/Homebrew/extend/os/mac/unpack_strategy/zip.rb |
Secures volume-label moves. |
Library/Homebrew/test/unpack_strategy/zip_spec.rb |
Adds volume-label extraction tests. |
Review details
- Files reviewed: 2/2 changed files
- Comments generated: 1
- Review effort level: Balanced
馃挕 Add a code-review agent skill for context-aware, tailored reviews. Learn more in the docs.
dduugg
force-pushed
the
zip-volume-label-destination
branch
from
September 3, 2026 23:26
6831bd0 to
2966819
Compare
Contributor
There was a problem hiding this comment.
馃煛 Changes recommended
The move remains vulnerable when FileUtils.mv falls back to copying across filesystems.
Once you've addressed the issues Copilot identified, you can request another Copilot review.
Review details
- Files reviewed: 2/2 changed files
- Comments generated: 1
- Review effort level: Balanced
dduugg
force-pushed
the
zip-volume-label-destination
branch
from
September 3, 2026 23:52
2966819 to
e30b53a
Compare
MikeMcQuaid
enabled auto-merge
September 4, 2026 07:52
github-merge-queue
Bot
removed this pull request from the merge queue due to failed status checks
Sep 4, 2026
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
The macOS ZIP strategy re-extracts volume label members with
ditto, becauseunzipskips them, and it learns their names by scrapingunzip's stderr. That scraped name was then used on both sides ofFileUtils.mv. Passing a full destination path makesFileUtilscallFile.directory?on it, which follows a symlink, so a memberunziphad already extracted under the same name could redirect the move. An archive carrying a volume label and a symlink with identical names therefore moves the label's content to wherever the symlink points, outside the unpack directory.Handing
FileUtils.mvthe directory instead lets it derive the leaf fromFile.basename, so the only path followed isunpack_diritself, and the final component is created byFile.rename, which replaces a symlink rather than writing through it. Thebasenamealso confines a label name containing../, whichunzipdoes report verbatim, and the leaf check drops the namesbasenamealone cannot confine, sincePathname#/resolves.,..and a bare/straight back out of the staging directory. Across filesystemsFileUtils.mvcopies rather than renames, and that copy opens the destination path, so a symlinkunzipleft on the same name is cleared before the move. For an ordinary archive the resulting destination is unchanged.brew benchmarkresults.brewcommands to reproduce the bug? Reproducing needs a hand-built archive rather thanbrewcommands, so the added spec builds one instead.brew lgtm(style, typechecking and tests) locally?Claude Code with Opus 5, with local review and testing.