jaraco.context Has a Path Traversal Vulnerability
Description
jaraco.context, an open-source software package that provides some useful decorators and context managers, has a Zip Slip path traversal vulnerability in the jaraco.context.tarball() function starting in version 5.2.0 and prior to version 6.1.0. The vulnerability may allow attackers to extract files outside the intended extraction directory when malicious tar archives are processed. The strip_first_component filter splits the path on the first / and extracts the second component, while allowing ../ sequences. Paths like dummy_dir/../../etc/passwd become ../../etc/passwd. Note that this suffers from a nested tarball attack as well with multi-level tar files such as dummy_dir/inner.tar.gz, where the inner.tar.gz includes a traversal dummy_dir/../../config/.env that also gets translated to ../../config/.env. Version 6.1.0 contains a patch for the issue.
Affected packages
Versions sourced from the GitHub Security Advisory.
| Package | Affected versions | Patched versions |
|---|---|---|
jaraco.contextPyPI | >= 5.2.0, < 6.1.0 | 6.1.0 |
Affected products
1- Range: v5.2.0, v5.3.0, v6.0.0, …
Patches
17b26a42b5257Merge pull request #18 from jaraco/security/extraction-safety
3 files changed · +84 −1
jaraco/context/__init__.py+11 −1 modified@@ -77,12 +77,19 @@ def tarball( try: req = urllib.request.urlopen(url) with tarfile.open(fileobj=req, mode='r|*') as tf: - tf.extractall(path=target_dir, filter=strip_first_component) + tf.extractall(path=target_dir, filter=_default_filter) yield target_dir finally: shutil.rmtree(target_dir) +def _compose_tarfile_filters(*filters): + def compose_two(f1, f2): + return lambda member, path: f1(f2(member, path), path) + + return functools.reduce(compose_two, filters, lambda member, path: member) + + def strip_first_component( member: tarfile.TarInfo, path, @@ -91,6 +98,9 @@ def strip_first_component( return member +_default_filter = _compose_tarfile_filters(tarfile.data_filter, strip_first_component) + + def _compose(*cmgrs): """ Compose any number of dependent context managers into a single one.
newsfragments/+a4f5dda0.feature.rst+1 −0 added@@ -0,0 +1 @@ +In tarfile.context, ensure that the default filter honors the data filter to avoid path traversal vulnerabilities.
tests/test_safety.py+72 −0 added@@ -0,0 +1,72 @@ +import io +import sys +import types +from contextlib import nullcontext as does_not_raise + +import pytest + +import jaraco.context +from jaraco.context import tarfile + + +def make_tarball_with(member): + tar_data = io.BytesIO() + with tarfile.open(fileobj=tar_data, mode='w') as tar: + tarinfo = tarfile.TarInfo(name=member.path) + content = f'content for {member.path}' + bin_content = content.encode('ascii') + tarinfo.size = len(bin_content) + tar.addfile(tarinfo, io.BytesIO(bin_content)) + + tar_data.seek(0) + return tar_data + + +cases = [ + types.SimpleNamespace( + path='dummy_dir/legitimate_file.txt', + expect=does_not_raise(), + ), + pytest.param( + types.SimpleNamespace( + path='dummy_dir/subdir/../legitimate_file.txt', + expect=does_not_raise(), + ), + marks=pytest.mark.skipif( + (3, 11) < sys.version_info < (3, 13), + reason='Fails with FileExistsError on Python 3.12', + ), + ), + types.SimpleNamespace( + path='dummy_dir/../../tmp/pwned_by_zipslip.txt', + expect=pytest.raises(tarfile.OutsideDestinationError), + ), + types.SimpleNamespace( + path='dummy_dir/../../../../home/pwned_home.txt', + expect=pytest.raises(tarfile.OutsideDestinationError), + ), + types.SimpleNamespace( + path='dummy_dir/../escaped.txt', + expect=pytest.raises(tarfile.OutsideDestinationError), + ), +] + + +@pytest.fixture(params=cases) +def tarfile_case(request): + with tarfile.open(fileobj=make_tarball_with(request.param), mode='r') as tf: + yield types.SimpleNamespace( + tarfile=tf, + expect=request.param.expect, + ) + + +def test_zipslip_exploit(tmp_path, tarfile_case): + """ + Ensure that protections from the default tarfile filter are applied. + """ + (member,) = tarfile_case.tarfile + with tarfile_case.expect: + tarfile_case.tarfile.extract( + member, path=tmp_path, filter=jaraco.context._default_filter + )
Vulnerability mechanics
Generated by null/stub on May 9, 2026. Inputs: CWE entries + fix-commit diffs from this CVE's patches. Citations validated against bundle.
References
6- github.com/advisories/GHSA-58pv-8j8x-9vj2ghsaADVISORY
- nvd.nist.gov/vuln/detail/CVE-2026-23949ghsaADVISORY
- github.com/jaraco/jaraco.context/blob/main/jaraco/context/__init__.pyghsax_refsource_MISCWEB
- github.com/jaraco/jaraco.context/commit/7b26a42b525735e4085d2e994e13802ea339d5f9ghsax_refsource_MISCWEB
- github.com/jaraco/jaraco.context/security/advisories/GHSA-58pv-8j8x-9vj2ghsax_refsource_CONFIRMWEB
- github.com/pypa/setuptools/blob/main/setuptools/_vendor/jaraco/context.pyghsax_refsource_MISCWEB
News mentions
0No linked articles in our index yet.