gh-157265: Honor None result of filter for link fallbacks - #157266
Conversation
Co-authored-by: Stan Ulbrych <stan@python.org>
|
Thanks @encukou for the PR 🌮🎉.. I'm working now to backport this PR to: 3.10. |
|
Thanks @encukou for the PR 🌮🎉.. I'm working now to backport this PR to: 3.12. |
|
Thanks @encukou for the PR 🌮🎉.. I'm working now to backport this PR to: 3.11. |
|
Thanks @encukou for the PR 🌮🎉.. I'm working now to backport this PR to: 3.14. |
|
Thanks @encukou for the PR 🌮🎉.. I'm working now to backport this PR to: 3.13. |
|
Thanks @encukou for the PR 🌮🎉.. I'm working now to backport this PR to: 3.15. |
|
GH-157304 is a backport of this pull request to the 3.10 branch. |
|
GH-157305 is a backport of this pull request to the 3.12 branch. |
|
GH-157306 is a backport of this pull request to the 3.11 branch. |
|
GH-157307 is a backport of this pull request to the 3.14 branch. |
|
GH-157308 is a backport of this pull request to the 3.13 branch. |
|
GH-157309 is a backport of this pull request to the 3.15 branch. |
|
|
|
gh-157266: Adjust test for Windows On Windows (no symlinks, no hardlinks), the behaviour is the same as without the fix in GH-157266: - a/t/dummy is extracted - b/ is extracted - c/ is *not* created (the target, a/t, is not in the archive) - c/escape: c/ is created; escape is skipped (target, c/../../link_here, is not in archive) - c is not recreated as a directory - boom is not created (target is c/escape, which falls back to ..\..\link_here, which does not exist in archive)
…cks (pythonGH-157266) Co-authored-by: Stan Ulbrych <stan@python.org>
pythongh-157266: Adjust test for Windows On Windows (no symlinks, no hardlinks), the behaviour is the same as without the fix in pythonGH-157266: - a/t/dummy is extracted - b/ is extracted - c/ is *not* created (the target, a/t, is not in the archive) - c/escape: c/ is created; escape is skipped (target, c/../../link_here, is not in archive) - c is not recreated as a directory - boom is not created (target is c/escape, which falls back to ..\..\link_here, which does not exist in archive)
…cks (pythonGH-157266) Co-authored-by: Stan Ulbrych <stan@python.org>
pythongh-157266: Adjust test for Windows On Windows (no symlinks, no hardlinks), the behaviour is the same as without the fix in pythonGH-157266: - a/t/dummy is extracted - b/ is extracted - c/ is *not* created (the target, a/t, is not in the archive) - c/escape: c/ is created; escape is skipped (target, c/../../link_here, is not in archive) - c is not recreated as a directory - boom is not created (target is c/escape, which falls back to ..\..\link_here, which does not exist in archive)
pythongh-157266: Adjust test for Windows On Windows (no symlinks, no hardlinks), the behaviour is the same as without the fix in pythonGH-157266: - a/t/dummy is extracted - b/ is extracted - c/ is *not* created (the target, a/t, is not in the archive) - c/escape: c/ is created; escape is skipped (target, c/../../link_here, is not in archive) - c is not recreated as a directory - boom is not created (target is c/escape, which falls back to ..\..\link_here, which does not exist in archive)
pythongh-157266: Adjust test for Windows On Windows (no symlinks, no hardlinks), the behaviour is the same as without the fix in pythonGH-157266: - a/t/dummy is extracted - b/ is extracted - c/ is *not* created (the target, a/t, is not in the archive) - c/escape: c/ is created; escape is skipped (target, c/../../link_here, is not in archive) - c is not recreated as a directory - boom is not created (target is c/escape, which falls back to ..\..\link_here, which does not exist in archive)
pythongh-157266: Adjust test for Windows On Windows (no symlinks, no hardlinks), the behaviour is the same as without the fix in pythonGH-157266: - a/t/dummy is extracted - b/ is extracted - c/ is *not* created (the target, a/t, is not in the archive) - c/escape: c/ is created; escape is skipped (target, c/../../link_here, is not in archive) - c is not recreated as a directory - boom is not created (target is c/escape, which falls back to ..\..\link_here, which does not exist in archive)
pythongh-157266: Adjust test for Windows On Windows (no symlinks, no hardlinks), the behaviour is the same as without the fix in pythonGH-157266: - a/t/dummy is extracted - b/ is extracted - c/ is *not* created (the target, a/t, is not in the archive) - c/escape: c/ is created; escape is skipped (target, c/../../link_here, is not in archive) - c is not recreated as a directory - boom is not created (target is c/escape, which falls back to ..\..\link_here, which does not exist in archive)
pythongh-157266: Adjust test for Windows On Windows (no symlinks, no hardlinks), the behaviour is the same as without the fix in pythonGH-157266: - a/t/dummy is extracted - b/ is extracted - c/ is *not* created (the target, a/t, is not in the archive) - c/escape: c/ is created; escape is skipped (target, c/../../link_here, is not in archive) - c is not recreated as a directory - boom is not created (target is c/escape, which falls back to ..\..\link_here, which does not exist in archive)
pythongh-157266: Adjust test for Windows On Windows (no symlinks, no hardlinks), the behaviour is the same as without the fix in pythonGH-157266: - a/t/dummy is extracted - b/ is extracted - c/ is *not* created (the target, a/t, is not in the archive) - c/escape: c/ is created; escape is skipped (target, c/../../link_here, is not in archive) - c is not recreated as a directory - boom is not created (target is c/escape, which falls back to ..\..\link_here, which does not exist in archive)
CVE-2026-87910 / PSF-2026-40 (Medium): when tarfile cannot create a link and falls back to extracting the linked member as a copy, the extraction filter is consulted twice — once with the member's name replaced by the link's location, once under its own name. The first call's None (explicit rejection) was discarded, so a member a filter refused to see materialised at the link's location anyway (alert #8273, Grype, python 3.14.7 binary). The fix is merged upstream (python/cpython#157266, commit fb2f0bbc) but no released 3.14 image carries it. Following the established CVE-2026-15310 zipfile-backport pattern: - scripts/patch_cpython_tarfile_cve_2026_87910.py applies the reviewed 4-line pure-Python fix to the stdlib in builder-base and ldr (both stages build from the fresh base image), fails closed on source drift, and verifies at build time by reproducing the vulnerable fallback: a filter that allows secret.txt under its own name but rejects it under the hardlink's must leave nothing at the link's location. Verified against pristine 3.14.7: unpatched the copy materialises, patched it does not; ordinary hardlink extraction unaffected. - .grype.yaml: fix-state-unknown binary-CPE entry with the same removal condition as the zipfile backport (fixed 3.14 image ships). - tests/deployment/test_cpython_tarfile_backport.py: contract tests for hunk application, idempotency, drift refusal, plus the behavioral property on the image's 3.14 stdlib (skipped on the 3.12 dev venv, which predates this project's image scope). Resolves #8273.
- Dockerfile: correct the tarfile CVE comment — the upstream fix is merged on CPython main (python/cpython#157266, fb2f0bbc), not the 3.14 maintenance branch; the 3.14 backport (python/cpython#157307) is open. - test_cpython_tarfile_backport.py: skip the behavioral test based on whether the ambient tarfile actually carries the patch markers, not a version check plus two strings every 3.14.x contains; fix the module docstring's stale wording. - test_cpython_tarfile_backport.py: make the source-level test reject the variant that captures the first filter result but overwrites it before the None check, by asserting on the exact upstream hunk structure instead of marker presence plus a call count. - patch_cpython_tarfile_cve_2026_87910.py: print a clear notice (exit 0) when the target's tarfile already contains the fix; extend verify_runtime to exercise the accepted fallback-copy path, which the existing benign check never enters.
Uh oh!
There was an error while loading. Please reload this page.