VYPR
High severityOSV Advisory· Published Jan 20, 2026· Updated Jan 20, 2026

jaraco.context Has a Path Traversal Vulnerability

CVE-2026-23949

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.

PackageAffected versionsPatched versions
jaraco.contextPyPI
>= 5.2.0, < 6.1.06.1.0

Affected products

1

Patches

1
7b26a42b5257

Merge pull request #18 from jaraco/security/extraction-safety

https://github.com/jaraco/jaraco.contextJason R. CoombsJan 13, 2026via ghsa
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

News mentions

0

No linked articles in our index yet.