Refuse an absolute-target symlink when unpacking an update part (#1295) #1525
No reviewers
Labels
No labels
accessibility
autorelease: pending
autorelease: tagged
bug
dependencies
documentation
duplicate
enhancement
github_actions
good first issue
help wanted
invalid
question
testing wanted
wontfix
No milestone
No assignees
1 participant
Due date
No due date set.
Dependencies
No dependencies set
Reference
MagicBeanDev/Hookshot!1525
Loading…
Reference in a new issue
No description provided.
Delete branch "contrib/symlink-link-escape"
Deleting a branch is permanent. Although the deleted branch may continue to exist for a short time before it actually gets removed, it CANNOT be undone in most cases. Continue?
What
_unpack()inbuilder/linux/linux_update.pycreates a zip-shipped symbolic link with whatever target the archive names. A relative one lands inside the folder, but an absolute one (evil -> /tmp/evil) lands wherever it says — nothing checked it. This refuses an absolute link target, mirroring how_safe_namealready refuses names that leave the folder and_check_part_directoryrefuses writing through a link. Every link real parts actually ship (the toolchain's) is relative, so the guard only rejects shapes no honest archive uses.Why
The remaining half of #1295: the earlier hardening waves (
7ac1186,b65d80e,25f5aaf) covered writing through existing links and symlinked staging folders, but a link whose destination itself escapes was still accepted. SHA-256 manifest matching helps but doesn't protect against a malicious archive supplied through a compromised release — which is exactly when an installer shouldn't write wherever the zip tells it to.Results / Testing performed
_unpack()+ one new subtest ontest_a_part_cannot_write_through_a_zip_symlink(the authoring fixtures already run cross-platform; this shape needs no symlink privileges anywhere — runs with standard-library Python on any OS). The amended test passes on Windows and Linux.tests/test_linux_update.py: same results as main here (symlink-fixture tests need Developer Mode locally; they pass in CI), no new failures from this change.🤖 Generated with Claude Code