Skip to content

Don't apply archive permissions to the wheel unpack directory - #701

Merged
agronholm merged 2 commits into
pypa:mainfrom
varun3257:unpack-root-chmod
Oct 3, 2026
Merged

agronholm merged 2 commits into
pypa:mainfrom
varun3257:unpack-root-chmod

Conversation

@varun3257

Copy link
Copy Markdown
Contributor

wheel unpack restores each member's mode from external_attr, applying it to the path that ZipFile.extract hands back. That path is not always a member of the archive: extract drops empty, "." and ".." components from the member name, so a member called ../ or / collapses to an empty name and the call returns the destination directory itself. The mode of the unpack directory then comes straight from the archive, so a wheel carrying one such member leaves the unpacked tree at whatever mode it asks for, world-writable included, which on a shared machine or CI runner lets another user rewrite the files before they are inspected, repacked or installed. I came across it reading the fix for CVE-2026-24049, which moved the chmod onto extract's return value but did not cover the case where that value is the destination itself. Skipping the chmod when extract returns the unpack root keeps the behaviour for real members unchanged and leaves the directory wheel unpack created at the mode the umask gave it. The regression test alongside fails on main with mode 0o777 and passes with this change.

Comment thread src/wheel/_commands/unpack.py Outdated
Comment thread src/wheel/_commands/unpack.py Outdated
@codecov

codecov Bot commented Oct 1, 2026 •

Copy link
Copy Markdown

Codecov Report

✅ All modified and coverable lines are covered by tests.
✅ Project coverage is 63.54%. Comparing base (b25c3c2) to head (39a4ded).

Additional details and impacted files
@@            Coverage Diff             @@
##             main     #701      +/-   ##
==========================================
+ Coverage   63.45%   63.54%   +0.08%     
==========================================
  Files          15       15              
  Lines        1267     1270       +3     
==========================================
+ Hits          804      807       +3     
  Misses        463      463              

☔ View full report in Codecov by Harness.
📢 Have feedback on the report? Share it here.

🚀 New features to boost your workflow:
  • ❄️ Test Analytics: Detect flaky tests, report on failures, and find test suite problems.

Drops the os.path.normpath() call in favour of Path.resolve(), and
shortens the comment. resolve() is called on both sides: ZipFile.extract()
returns normpath(join(dest, arcname)), so with a relative --dest (the
default is ".") an already-resolved root never compares equal and the
chmod would go ahead. strict=True can't be used, as the unpack directory
is created by extract() and does not exist yet at that point.

@agronholm agronholm left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Looks good, thanks!

@agronholm
agronholm merged commit de8efc8 into pypa:main Oct 3, 2026
16 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants