Safeguard
Supply Chain Security•

CPython tarfile CVE-2026-114583: tar_filter bypass, no release fix yet

CVE-2026-114583 lets a crafted tar escape tarfile's 'tar' filter. The fix is merged in CPython git but no Python release ships it yet. What to do now.

Safeguard Security Team
35 min read

CVE-2026-114583 is a medium severity flaw in CPython's tarfile module: a crafted tar archive can slip past tarfile.tar_filter and write files outside the extraction directory when code calls extractall(..., filter="tar"). Every CPython below 3.16.0 is affected. The fix is merged in git, but no downloadable Python release contains it yet.

That last sentence is the reason for this post. On 22 October 2026 the Python Security Response Team announced the CVE, and by 21:49 UTC that evening the fix had landed on the main, 3.15 and 3.14 branches. The same week, Python shipped expedited releases 3.15.1, 3.14.9 and 3.13.17 for an unrelated certificate problem. Those releases were tagged on 21 October, a day before the fix existed, so they do not contain it. The 3.13, 3.12 and 3.11 backports were still open pull requests when we checked on 24 October. If you are waiting for python3 --version to tell you that you are safe, there is nothing to wait for yet.

We already covered how the extraction filters work in our explainer on the earlier tarfile filter bypass, and compared the two filters in our guide to the data and tar filters, so this post does not re-teach them from scratch. It covers what is new: the CVE itself, exactly which commits fix it, why no release has it, how to tell whether a given interpreter is patched, and the safe ways to extract archives in the meantime. No exploitation has been reported, the CVSS vector is local, and an attacker needs you to extract an archive they supplied. Treat it as a real but bounded bug.

What happened

The flaw is a sibling of CVE-2026-7774, which the Python Security Response Team published in June 2026 for the stricter data filter. The June fix made data_filter refuse link members that resolve to the destination directory itself. It did not apply the same rule to tar_filter. On 21 October 2026, Stan Ulbrych opened a GitHub issue pointing that out, and the rest moved quickly.

Date and time (UTC, 2026)EventSource
4 May, 14:47CVE-2026-7774 reserved by the PSF for the data_filter bypassCVE record CVE-2026-7774
7 May, 09:18Issue gh-149486 opened: tarfile.data_filter does not handle symlinks with empty namesGitHub issue 149486
4 June, 14:21CVE-2026-7774 published, CVSS 4.0 score 6.9CVE record CVE-2026-7774
21 October, 05:55v3.15.1 taggedCPython tag v3.15.1
21 October, 06:02v3.14.9 taggedCPython tag v3.14.9
21 October, 13:48v3.13.17 taggedCPython tag v3.13.17
21 October, 18:34Discourse announcement of 3.15.1, 3.14.9 and 3.13.17, an expedited release for the bundled pip and certifi updatediscuss.python.org
21 October, 18:54Issue gh-160097 opened: tarfile.tar_filter does not handle symlinks with empty namesGitHub issue 160097
21 October, 18:58CVE-2026-114583 reserved by the PSFCVE record CVE-2026-114583
21 October, 19:00Fix pull request gh-160098 openedGitHub PR 160098
22 October, 20:23gh-160098 merged to main as commit e7e0b3523aGitHub commit e7e0b35
22 October, 20:23Backport PRs opened for 3.15 (#160339), 3.14 (#160340), 3.13 (#160341), 3.12 (#160342) and 3.11 (#160343)GitHub PRs
22 October, 20:35Security announcement mailed to security-announce@python.org (sent 21:35 +0100)Python security-announce
22 October, 20:57CVE-2026-114583 published by the PSFCVE record
22 October, 21:193.14 backport merged as 0901ad4ffbGitHub commit 0901ad4
22 October, 21:40Release announcement for 3.15.1, 3.14.9 and 3.13.17 forwarded to oss-securityoss-security 2026/10/22/29
22 October, 21:493.15 backport merged as c6ada40177GitHub commit c6ada40
22 October, 22:46CVE announcement forwarded to oss-securityoss-security 2026/10/22/33
23 October, 00:36CVE record last updatedCVE record
24 October3.13, 3.12 and 3.11 backports still open; no release tag contains the fixGitHub PRs 160341 to 160343, CPython tags

First, the expedited releases and the CVE are separate events that happened to land in the same 48 hours. The releases exist because of the eMudhra and emSign certificate incident, not because of tarfile. Second, the fix reached the 3.15 and 3.14 branches within an hour and a half of landing on main, which usually means the next scheduled bugfix releases of those lines will carry it. The older lines are in security only mode and get source only releases, so their timing is less predictable.

Who is affected

The CVE record lists one affected range for CPython: everything from version 0 up to, but not including, 3.16.0. In practice the bug only matters where two things are true at once:

  1. Your code extracts a tar archive that someone else could have crafted.
  2. That extraction uses the tar filter, by passing filter="tar", passing filter=tarfile.tar_filter, setting extraction_filter to it, or calling shutil.unpack_archive(..., filter="tar").

If you use the default filter on 3.14 or later, which is data, or you pass filter="data" on a runtime that already has the CVE-2026-7774 fix, this CVE is not your problem. If you pass filter="fully_trusted" or run an old runtime with no filter at all, you have a much bigger and older problem than this CVE, because nothing is checking paths.

Release status by branch

All of the following comes from the CPython repository as of 24 October 2026. "Contains fix" means the fix commit is an ancestor of the release tag.

BranchStatus (devguide)Newest release tagContains fix?Fix commit on branchBackport PR
main (3.16)in developmentv3.16.0a1 (13 Oct)No, fix is 282 commits ahead of the tage7e0b3523ad6ba8472873dda594cf4021a16f8b5gh-160098 (merged)
3.15bugfixv3.15.1 (tagged 21 Oct)No, fix is 62 commits aheadc6ada40177436e4e2533a7b252082b5f0c043477#160339 (merged)
3.14bugfixv3.14.9 (tagged 21 Oct)No, fix is 52 commits ahead0901ad4ffb22605f12b705d1c1197f0a01f0ba0b#160340 (merged)
3.13securityv3.13.17 (tagged 21 Oct)Nonone yet#160341 (open)
3.12securityv3.12.15Nonone yet#160342 (open)
3.11securityv3.11.17Nonone yet#160343 (open)
3.10end of lifev3.10.22Nononenone opened

The "commits ahead" figures come from GitHub's compare view between each tag and the fix commit, which reported the fix as strictly ahead with zero commits behind. That is a clean way to prove a tag does not contain a commit, and you can repeat it yourself with the commands in the detection section.

Who is most likely to be exposed

Teams pick filter="tar" on purpose when the data filter is too strict, because tar keeps more Unix semantics such as links and permission bits. The likeliest users are package build and install tooling, backup and restore tools, CI steps that unpack build artifacts or caches, container and root filesystem builders, and services or plugin loaders that unpack uploaded bundles.

The risk grows with privilege. The CVE text is explicit that the out of bounds writes happen "subject to the permissions of the extracting process". An extraction that runs as root in a build container can overwrite anything in that container. The same extraction under a dedicated unprivileged user can only write where that user can.

How it works, in plain language

The tar filter is a function that tarfile calls on every archive member before writing it. Its job is to refuse members whose final location would land outside the destination directory, to strip leading slashes, to refuse absolute paths and to clear dangerous permission bits. It looks at each member one at a time.

The weakness is ordering. Tar archives can contain links, and links change what later paths mean. Suppose an early member is a link whose own name normalises to the destination directory itself, for example an empty name or a name that collapses to .. If that member is allowed through, the destination is replaced by a link that points somewhere else. Every later member is then checked against a path that looks fine, but the writes follow the link to wherever it points. The check is correct for each member on its own and wrong for the sequence.

CVE-2026-7774 closed this for data_filter in June by refusing any link member that resolved to the destination. That check sat inside the part of the code that only runs for the data filter. tar_filter shares most of the code but skipped that branch, so the same trick still worked there.

The fix in gh-160098 moves the check into the code that both filters share, and it compares the normalised member name with the current directory marker rather than comparing resolved paths. Any hard link or symbolic link whose name refers to the destination directory now raises OutsideDestinationError under either filter. The updated documentation adds that such members are refused even on platforms without symlink support. You do not need a malicious sample to protect yourself; the defensive checks below do not depend on one.

Technical details

Everything in this section comes from the CVE record, the security announcement, the CPython pull request and commits, and the CPython documentation, all fetched on 24 October 2026.

Identifiers and scores

FieldCVE-2026-114583CVE-2026-7774 (sibling)
AssignerPSFPSF
Reserved2026-10-21T18:58:26Z2026-05-04T14:47:51Z
Published2026-10-22T20:57:36Z2026-06-04T14:21:23Z
Affected componentCPython, module tarfileCPython, module tarfile
Affected range in recordversion 0 to before 3.16.0before 3.10.21; 3.11.0 to before 3.11.16; 3.12.0 to before 3.12.14; 3.13.0 to before 3.13.14; 3.14.0 to before 3.14.6; 3.15.0a1 to before 3.15.0b2
Functiontarfile.tar_filter (filter="tar")tarfile.data_filter (filter="data")
CVSS 4.0 score6.9 MEDIUM6.9
CVSS 4.0 vectorCVSS:4.0/AV:L/AC:L/AT:N/PR:N/UI:N/VC:N/VI:H/VA:N/SC:N/SI:N/SA:NCVSS:4.0/AV:N/AC:L/AT:N/PR:N/UI:A/VC:N/VI:H/VA:N/SC:N/SI:N/SA:N
Issuegh-160097gh-149486
Fix PRgh-160098gh-149487
Credits in recordStan Ulbrych (coordinator), Ethan Furman (remediation reviewer)not reviewed here

The two vectors differ even though the scores match. The new record scores the attack vector as local with no user interaction, while the June record used network with active user interaction. Both say the impact is integrity only: files can be written, nothing is read or crashed by the bug itself. If your service accepts tarballs over the network and extracts them automatically, your own exposure looks more like the June vector; score it in your context.

The fix commits

BranchCommitMerged (UTC)Via
maine7e0b3523ad6ba8472873dda594cf4021a16f8b52026-10-22 20:23:08gh-160098
3.140901ad4ffb22605f12b705d1c1197f0a01f0ba0b2026-10-22 21:19:59#160340, cherry picked from e7e0b35
3.15c6ada40177436e4e2533a7b252082b5f0c0434772026-10-22 21:49:35#160339, cherry picked from e7e0b35
3.13not merged#160341, opened 2026-10-22 20:23:33
3.12not merged#160342, opened 2026-10-22 20:23:39
3.11not merged#160343, opened 2026-10-22 20:23:45

The pull request title is "Reject links replacing the destination in tarfile.tar_filter". On main it touched four files, 20 lines added and 7 removed:

FileLines addedLines removedWhat changed
Lib/tarfile.py65The destination check moved from the data only branch of _get_filtered_attrs to the shared path
Lib/test/test_tarfile.py42The existing test_link_at_destination test now runs for both tar and data
Doc/library/tarfile.rst50New bullet under tar_filter documenting the refusal
Misc/NEWS.d/next/Security/2026-10-21-19-58-53.gh-issue-160097.-opfAL.rst50Changelog entry tagged cve: CVE-2026-114583

The core of the change is small enough to read in full. This is the added code from the fix commit, shown so you can recognise it in a patched interpreter; it is defensive code, not an attack:

    # A link member named after the destination directory itself
    # would replace it with a (sym)link, redirecting the destination
    # for all subsequent members.
    if ((member.islnk() or member.issym())
            and os.path.normpath(name) == os.curdir):
        raise OutsideDestinationError(member, target_path)

The removed code was a check of target_path == dest_path that only ran when the function was handling the data filter. Because the new check uses os.path.normpath(name) == os.curdir, names such as an empty string or a path that collapses back to the current directory are all caught, including on platforms that cannot create symlinks.

Release tags checked

TagTag commitTag date (UTC)
v3.16.0a122d79d98402026-10-13 15:05
v3.15.1c37e74cb6b2026-10-21 05:55
v3.14.9ead82c07db2026-10-21 06:02
v3.13.1764e080d3812026-10-21 13:48

The expedited releases upgraded the bundled pip to 26.2.2, which includes certifi 2026.10.19, according to the release announcement. The announcement gives the pip wheel hash as sha256=fd7f4dfa8190e014013b9f08f3e973f4e4c26ffd179a204895533a7254813a70. That hash is for pip, not for any tarfile change. It is listed here only so nobody mistakes the release for a tarfile fix.

Exceptions and behaviour after the fix

On a patched runtime, a link member whose name refers to the destination raises tarfile.OutsideDestinationError, a subclass of tarfile.FilterError. What happens next depends on TarFile.errorlevel. The documentation says a filter error aborts the extraction when errorlevel is 1 or more, which is the default, while errorlevel=0 logs the error, skips the member and carries on. Leave it at the default for untrusted input. Members already written stay on disk, which is one reason to extract into a fresh directory and discard it on any error.

Indicators of compromise

None have been published. The security announcement, the CVE record and the oss-security forward list no hashes, archive names, domains or IP addresses, and no exploitation has been reported. If you want something to hunt for in your own logs, the useful signal is the exception itself after you patch: an OutsideDestinationError from an extraction that used to succeed means someone sent you an archive with a link member aimed at the destination. That is worth an alert, not a retry.

Why there is a fix but no release

It is common to see a CVE with a merged fix and no release, but this case is unusually clean to explain.

CPython ships release lines on a schedule. The 3.15 and 3.14 lines are in bugfix mode and get regular binary releases. The 3.13, 3.12 and 3.11 lines are in security mode and get source only releases when the release manager decides there is enough to ship. The tarfile fix landed after all of the current releases had been tagged, because those releases were rushed out for the certifi update on 21 October. So the earliest releases that can contain the fix are the next 3.15.x and 3.14.x, which have not been tagged, and the next security releases of 3.13, 3.12 and 3.11, which depend on backport PRs that were still open on 24 October.

Your operating system vendor may move faster or slower than CPython. Linux distributions often backport security patches into their own builds without changing the upstream version number, so a distribution's python3 might be patched while still reporting 3.12.x. The only way to know is to check the code or the distribution's tracker, which the next section covers.

How to check whether you are exposed

There are two questions: does your code use the tar filter on untrusted input, and does your interpreter contain the fix. Answer the first one first, because if the answer is no you can stop.

Step 1: find call sites

Search your repositories for the patterns that select the tar filter.

# Explicit tar filter in extractall, extract or unpack_archive calls
grep -rnE "filter\s*=\s*['\"]tar['\"]" --include='*.py' .
grep -rn "tarfile.tar_filter" --include='*.py' .
grep -rn "extraction_filter" --include='*.py' .

# All extraction calls, to review by hand
grep -rnE "\.extractall\(|\.extract\(|unpack_archive\(" --include='*.py' .

On Windows PowerShell the equivalent is:

Get-ChildItem -Recurse -Include *.py | Select-String -Pattern "filter\s*=\s*['""]tar['""]","tar_filter","extraction_filter"

For each hit, write down three facts: where the archive comes from, which user the process runs as, and where the destination directory is. A hit that extracts a user upload or a file fetched from the internet is the one to fix first.

Do not forget dependencies. Your own code may never call tarfile, while a build backend, a model loader or a plugin manager in your virtual environment does. Run the same grep over site-packages:

python -c "import site; print(site.getsitepackages())"
grep -rlE "filter\s*=\s*['\"]tar['\"]|tar_filter" "$(python -c 'import site; print(site.getsitepackages()[0])')"

Step 2: check whether a given interpreter has the fix

Version numbers will not help until releases ship, and distribution builds may carry backports. The most reliable check is to look for the new code in the interpreter's own tarfile module:

python3 - <<'EOF'
import inspect, sys, tarfile
src = inspect.getsource(tarfile._get_filtered_attrs)
patched = "normpath(name) == os.curdir" in src
print(sys.version.split()[0], "CVE-2026-114583 fix present:", patched)
EOF

This reads a private function, so treat it as a diagnostic, not an API. If a distribution applies the fix with different wording, the string check can give a false "not present". In that case confirm with the distribution's security tracker or changelog.

Run it inside every image you build, not only on your laptop:

docker run --rm python:3.14-slim python3 -c "import inspect,tarfile;print('normpath(name) == os.curdir' in inspect.getsource(tarfile._get_filtered_attrs))"

To confirm that a CPython checkout or release tag lacks the fix, use git directly:

git clone --filter=blob:none https://github.com/python/cpython.git && cd cpython
git merge-base --is-ancestor 0901ad4ffb22605f12b705d1c1197f0a01f0ba0b v3.14.9 && echo "fixed" || echo "not in v3.14.9"
git merge-base --is-ancestor c6ada40177436e4e2533a7b252082b5f0c043477 v3.15.1 && echo "fixed" || echo "not in v3.15.1"

Once new releases are tagged, the same command against the new tag tells you whether it carries the fix.

Step 3: check the CVE-2026-7774 baseline

The interim advice below leans on the data filter, which is only trustworthy against this class of bug on a runtime that has the June fix. Per the CVE-2026-7774 record, those are 3.10.21 or later on 3.10, 3.11.16, 3.12.14, 3.13.14, 3.14.6 and 3.15.0b2 or later. Check quickly:

python3 -c "import sys; print(sys.version_info[:3])"

Compare the result with that list. If you are below it, upgrading to the newest release in your line is worth doing today for CVE-2026-7774 alone, even though it will not fix CVE-2026-114583.

Platform notes

PlatformHow to see the versionHow to get a patched build
Debian and Ubuntudpkg -l 'python3*' | grep ^iiWatch the distribution security tracker for CVE-2026-114583 and apply updates with apt-get upgrade
RHEL, Fedora and derivativesrpm -qa 'python3*'Watch the distribution advisories; dnf upgrade python3 once an update lists the CVE
Alpineapk info -v | grep python3apk upgrade python3 once the aports package carries the patch
python.org installers on macOS and Windowspython3 --version or py -0pNo installer contains the fix yet; use the interim mitigations
Official Docker python imagesdocker run --rm IMAGE python3 --versionImages are rebuilt from upstream releases, so expect the fix after the next 3.14.x and 3.15.x releases
uv managed Pythonsuv python listPrebuilt interpreters follow upstream releases; check for a newer patch release after one ships

What to do now

Here is a practical order of operations. Steps 1 to 3 are quick and safe; steps 4 and 5 take more care.

  1. Inventory call sites. Run the greps above over your code and installed dependencies and list every tar filter use with its input source and process user.
  2. Switch to the data filter where you can. If the only reason you used tar was habit or caution about breakage, try filter="data" on a runtime that has the CVE-2026-7774 fix. It refuses links that leave the destination and links aimed at the destination itself. Run your tests; many workloads never notice the difference.
  3. Drop privileges and isolate. Never extract untrusted archives as root. Run the step as a dedicated user that can only write to a scratch directory, or inside a container with a read only root filesystem and one writable volume. This does not stop the bug, but it limits where a redirected write can land.
  4. Add the upstream check yourself. Where you must keep tar semantics, wrap tar_filter with the same check the fix adds. The next section shows how.
  5. Patch pinned runtimes. If you build your own Python, cherry pick the fix commit into your build. The change to Lib/tarfile.py is 6 lines added and 5 removed, and the 3.14 and 3.15 cherry picks applied cleanly upstream.
  6. Plan the upgrade. Subscribe to python.org release announcements and your distribution's tracker, and schedule image rebuilds for the day the patched releases ship.
  7. Alert on the exception. Log and alert on OutsideDestinationError and other FilterError subclasses from extraction code, and quarantine the archive that caused it.

A safer extraction pattern for any Python

The function below is a defensive wrapper you can drop into a codebase today. It adds the upstream CVE-2026-114583 check in front of tar_filter, extracts into a fresh temporary directory, and only moves the result into place if everything succeeded. It never builds or ships an archive; it only refuses dangerous members.

import os
import shutil
import tarfile
import tempfile

def _name_for_check(member):
    # tar_filter strips leading slashes before checking; do the same.
    return member.name.lstrip("/" + os.sep)

def tar_filter_cve_2026_114583(member, dest_path):
    """tarfile.tar_filter plus the check added upstream in gh-160098."""
    if member.islnk() or member.issym():
        if os.path.normpath(_name_for_check(member)) == os.curdir:
            raise tarfile.OutsideDestinationError(
                member, os.path.realpath(dest_path))
    return tarfile.tar_filter(member, dest_path)

def extract_untrusted(archive_path, final_dir, allow_links=True):
    """Extract into a new temp dir, then move into place on success."""
    work = tempfile.mkdtemp(prefix="extract-")
    try:
        with tarfile.open(archive_path) as tf:
            def check(member, path):
                if not allow_links and (member.islnk() or member.issym()):
                    raise tarfile.FilterError(member)
                return tar_filter_cve_2026_114583(member, path)
            tf.extractall(work, filter=check)
        os.makedirs(os.path.dirname(os.path.abspath(final_dir)), exist_ok=True)
        shutil.move(work, final_dir)
    except Exception:
        shutil.rmtree(work, ignore_errors=True)
        raise

A few notes on using it:

  • On a runtime that already has the fix, the extra check is redundant and harmless. You can remove it after you upgrade.
  • If your archives do not need links at all, call extract_untrusted(path, dest, allow_links=False). Refusing every link is the simplest defence against the whole family of link tricks, including this one and CVE-2026-7774.
  • The function relies on tarfile.tar_filter existing, which means a runtime with PEP 706 filters. If hasattr(tarfile, "tar_filter") is false, the runtime is far too old and needs an upgrade before anything else.
  • shutil.move of a directory into a path that already exists nests it inside. Make sure final_dir does not exist yet, or remove it first.

Pre-scanning without extracting

Some teams prefer to reject a suspicious archive before writing anything. You can list members and refuse the archive if any link has a name that normalises to the current directory, or if any link target is absolute:

import os
import tarfile

def suspicious_links(archive_path):
    findings = []
    with tarfile.open(archive_path) as tf:
        for m in tf.getmembers():
            if m.islnk() or m.issym():
                name = os.path.normpath(m.name.lstrip("/" + os.sep))
                if name == os.curdir or os.path.isabs(m.linkname):
                    findings.append((m.name, m.linkname))
    return findings

This is a complement, not a replacement. The extraction filter remains the real control, because it runs at the moment of writing.

Patching a self built CPython

If you compile Python yourself, for example for a base image or an embedded product, apply the upstream commit to your source tree:

cd cpython-3.14-source
curl -sL https://github.com/python/cpython/commit/0901ad4ffb22605f12b705d1c1197f0a01f0ba0b.patch -o cve-2026-114583.patch
git apply --check cve-2026-114583.patch && git apply cve-2026-114583.patch
./configure --enable-optimizations && make -j"$(nproc)"
./python -m test test_tarfile

Use the 3.15 commit c6ada40177 for a 3.15 tree and the main commit for a 3.16 development tree. For 3.13, 3.12 and 3.11, the open backport PRs are the best reference; review the diff of the PR for your branch before applying, since it has not been merged yet. Running test_tarfile afterwards confirms the upstream test for both filters passes on your build without you needing to construct any archive.

If you cannot rebuild Python, apply the check in your application instead. The most dependable way is to pass the wrapper function from the previous section directly as filter= at every call site. Replacing the tarfile.tar_filter module attribute at startup is tempting, but whether a call that passes the string "tar" picks up the replacement depends on how your Python version maps filter names to functions, so do not rely on it without testing on your exact runtime.

Common mistakes

Treating "medium" as "ignore". The score is medium because exploitation needs a crafted archive and the impact is writes only. If your service extracts uploads as root, a write anywhere in the container is serious in your context. Score your own deployment.

Switching to data on an old runtime. The data filter only resists this trick on runtimes with the CVE-2026-7774 fix. On 3.12.13, for example, both filters are bypassable. Check the baseline first.

Fixing your code and forgetting your dependencies. Build backends, plugin managers and model loaders inside site-packages can call tarfile with their own filter choice. Grep them too, and check their issue trackers for updates.

Reusing a destination directory. Extracting into a directory that already holds files means a failed extraction leaves a mix of old and new content. Always extract into a fresh directory and move it into place only on success.

Catching and ignoring filter errors. Some code wraps extraction in a broad try and carries on. After you patch, a FilterError is a signal that someone sent you a hostile archive. Log it and stop.

What changed since first reports

Early coverage tied the expedited releases to 22 October, the day their oss-security forward went out at 21:40 UTC. The Discourse announcement is timestamped 21 October 18:34 UTC and the tags are dated 21 October, so the releases predate the fix by more than a day.

Issue gh-160097 first listed only the main PR; it now lists gh-160098 plus backports #160339 to #160343, and it stays open while backports are pending. The CVE record was updated at 00:36 UTC on 23 October to reference all three merged fix commits, but still gives a single affected range below 3.16.0, so scanners will flag every CPython until per branch fixed versions are added.

The tarfile page on docs.python.org already shows the new tar_filter bullet because the docs build from the branch. That is not proof your installed Python behaves that way.

Open questions

  • When will patched releases ship? No date has been announced for 3.15.2 or 3.14.10, and the security only branches have no schedule. Watch python.org and the release managers' announcements.
  • Will 3.10 get a fix? The devguide lists 3.10 as end of life, and no backport PR for 3.10 was opened. If you still run 3.10, plan on either the interim mitigations or a migration.
  • What will distributions do? We found no distribution advisory for CVE-2026-114583 at the time of writing. Check your vendor's tracker.
  • Will the CVE record gain per branch fixed versions? The June sibling record has them; this one does not yet.

What would have helped

Know where you extract archives. A searchable list of code that calls extractall, extract or unpack_archive, with the filter it uses and where its input comes from, turns this CVE into a ten minute review instead of an all hands search.

Prefer the strictest filter you can live with. The data filter exists so that most code never has to think about link tricks. Teams that chose tar should write down why, so that the decision gets revisited when something like this lands.

Extract with least privilege. Running extraction as a dedicated unprivileged user, in a fresh directory, inside a sandbox with a read only root filesystem, would have kept a redirected write from touching anything important. This defence works against bugs nobody has found yet.

Be able to patch a runtime without waiting for a release, and rebuild fast. Teams that build their own Python, or that build images from a pinned source, can apply a six line fix the day it merges. Teams that only consume binary releases have to wait or mitigate in application code. Having the build pipeline and test suite ready for a cherry pick, and being able to rebuild every image that bundles CPython in a day, is the difference.

Where Safeguard fits

Safeguard's scanner builds SBOMs and dependency inventories for repositories, container images and SBOM files, for example with safeguard scan --dir ., safeguard scan --image <name> or safeguard scan --sbom <file>, writing results as JSON under ./output. Where an SBOM records the CPython runtime, such as a python3 package in a container image, the platform shows which projects contain that component and version, and matches it against vulnerability databases as those databases add this CVE. That answers "which images ship which Python" quickly once you need it.

The Safeguard CLI's assistant can do the code side of the work with your approval: search a repository for filter="tar" call sites, add the wrapper from this post, switch a call to filter="data", and run your tests, asking before each command and file edit under the /permissions mode you choose. In CI, safeguard exec runs the same kind of task non interactively.

Where Safeguard does not help: it does not write CPython's release schedule, and it cannot make a runtime fix exist in a binary that has not been built. It does not inspect archives at runtime or block a malicious tarball from being extracted; that is your application's filter and your sandbox. Reachability analysis is built for dependency code paths and should not be read as proof that a given stdlib call is or is not exercised. Safeguard would not have prevented this vulnerability.

Zero-Day Discovery and Gold

Zero-Day Discovery looks for previously unknown flaws before a CVE exists, in an agentic mode and a deterministic scanner mode, and issues findings as SGZ advisories before they reach public CVE databases. It covers seven package ecosystems. The CPython standard library is a language runtime, not a package in one of those ecosystems, so it is outside what Zero-Day Discovery covers.

Gold Open Source at gold.safeguard.sh is free and needs no login. It gives CVE intelligence with CVSS, EPSS, KEV status, affected and fixed versions and a recommended safe version, plus a free read API and a CI gate GitHub Action. Gold Registry at registry.safeguard.sh offers hardened drop in components and container images held to zero exploitable critical or high CVEs, with SLSA L3 or higher provenance and Sigstore signatures. We make no claim that a patched Python build for CVE-2026-114583 exists in Gold. The habit these tools support is the one that matters here: pull patched, signed components from a curated source and rebuild fast when the fix lands.

FAQ

Is CVE-2026-114583 being exploited?

No exploitation has been reported. The security announcement, the CVE record and the oss-security forward mention none, and the CVE is not marked as exploited in the record. The vector is local: an attacker needs your code to extract an archive they control.

Which Python version fixes CVE-2026-114583?

None yet. As of 24 October 2026, the fix is merged on main and on the 3.15 and 3.14 branches, but the newest tags, v3.15.1, v3.14.9 and v3.13.17, were all created before the fix. The 3.13, 3.12 and 3.11 backports are open pull requests.

I use the default filter. Am I affected?

On Python 3.14 and later the default is data, which is not affected by this CVE as long as you have the CVE-2026-7774 fix (3.14.6 or later on 3.14). Before 3.14, the documentation says the default was equivalent to fully_trusted, which means no path checks at all. If you run an older line without an explicit filter, pass filter="data" explicitly; that is a bigger fix than this CVE.

Does shutil.unpack_archive count?

Yes, if you pass filter="tar". The shutil documentation says the filter argument is passed to the underlying unpacking function, which for tar formats is tarfile. Zip files do not accept a filter and are not covered by this CVE.

Is switching to filter="data" enough?

On a runtime with the CVE-2026-7774 fix, yes for this bug, because data_filter already refuses links aimed at the destination. It also refuses links that point outside the destination and device files, and it changes some permissions, so test your workload. If those semantics break you, use the wrapper in this post with tar_filter.

Why is the CVSS vector different from CVE-2026-7774?

The June record used a network vector with active user interaction; the October record uses a local vector with no user interaction. Both score 6.9 and both rate integrity impact high with no confidentiality or availability impact.

Does this affect Windows?

The fix documentation says link members aimed at the destination are now refused even on platforms that do not support symbolic links. The CVE record does not limit the affected platforms. Apply the same mitigations on Windows.

Do I need to rotate anything?

Only if you find evidence that an untrusted archive was extracted with filter="tar" in a place where a redirected write could have changed configuration, code or credentials. In that case treat the host or container as you would after any arbitrary file write: rebuild from known good images and rotate secrets that were writable or readable by the process.

Fix it without waiting for upstream

This CVE is the textbook case of the second gap below: the fix is public and merged, but no release you can download contains it. The steps here are about the open source components you build and ship; for the CPython runtime itself, the interim mitigations above and a rebuild once releases land are what close the gap.

Whatever the vulnerability, most of the risk sits in two gaps. The first is the time between a fix being published and that fix actually running in your production builds. The second, and worse, is the time before any fix exists at all, while a maintainer works out a patch and a release schedule. Waiting through both gaps is a habit, not a requirement. Safeguard gives teams three ways to close them.

1. Autonomous remediation: pull requests, not tickets

When a fixed version exists, Safeguard's Griffin models open a tested, policy gated pull request that moves the affected dependency to the safe version. You can run it on one repository or in bulk across every project that carries the vulnerable version. Remediation covers eight categories, including dependencies, licenses, security posture and findings. Reachability analysis ranks the vulnerable code paths your application actually calls first, so the first pull requests you merge are the ones that matter most. Autonomous remediation ships switched off, with every category set to Manual, so your team decides how much runs on its own and what still needs a human approval.

2. Gold Open Source: the safe version in seconds, free

gold.safeguard.sh is free and needs no login. Search any CVE or package to see affected and fixed versions, CVSS, EPSS, CISA KEV status and a recommended safe version, or use the "Am I affected?" checker. The same data is available through a free read API, README status badges and RSS threat feeds. The safeguard-sh/gold-scan-action GitHub Action can fail a build on a known exploited vulnerability, on any known vulnerability, or never, so a version you have just removed cannot quietly come back in next week's merge.

3. Gold Registry: start from components that are already fixed

registry.safeguard.sh provides hardened, drop in replacements for upstream packages and container images. Every Gold artifact is held continuously to zero exploitable critical or high CVEs, zero malware and a completed license check, with SLSA L3 or higher provenance, a Sigstore signature and a public evidence page you can hand to an auditor. An artifact loses its Gold badge if it regresses. Teams can point their own package directory at the registry so hardened equivalents resolve without changing how developers install anything.

When upstream has no fix yet, or will not ship one, Safeguard first works to get the fix accepted upstream. If the issue is severe or upstream will not move, Safeguard builds a fork, fixes the vulnerability there and publishes a hardened Gold build, so you are not stuck on a maintainer's release schedule. You can also request a hardened, compatibility tested build of a specific package. This fork and patch flow is in early access today, so ask which packages it covers before you plan around it.

Your situationWhat to use
A fixed version existsAutonomous remediation opens the upgrade pull request; Gold Open Source confirms the safe version
You need to know today whether you are affectedGold Open Source lookup and "Am I affected?" checker, free and without login
You want vulnerable versions to stop shippingThe Gold scan GitHub Action in CI, plus Safeguard policies and gates
Upstream has no fix yet, or will not ship oneA custom Gold request; fork and patch (early access)
You want to start clean on new projectsPull hardened components and images from Gold Registry

One honest limit: when the vulnerable code lives in a vendor appliance, a hosted service or an operating system runtime you do not build yourself, the fix still has to come from that vendor or distribution. The steps above apply to the open source dependencies and container images you build and ship, which is where most teams carry the largest and least tracked share of their exposure.

See how Safeguard would have helped

None of these demos shows this CVE; each shows a real flow on another example, and the notes explain how it applies here.

The CLI demo fixes a lodash CVE: the assistant reads the finding, asks before each command, edits a file, installs and runs the tests, and pushes nothing. For CVE-2026-114583 you would run the same kind of session in a Python repository and ask it to find every filter="tar" call, add the wrapper from this post or switch to filter="data", and run pytest, approving each step.

Click through it at your own pace, or open it full screen.

The desktop app demo sends work to your computer, picks a folder, and asks you to Approve or Deny each command and file edit, ending with an upgrade done and tests passing. Here that could be the Dockerfile change that moves a base image to the next patched Python release once it ships, or the cherry pick of the fix into a self built interpreter.

Click through it at your own pace, or open it full screen.

The runner demo sets up a runner on your own machine, approves it with its device code, and scans from the terminal. For this CVE you would use it on a build host to scan the images that bundle CPython and see which Python runtime versions each one ships.

Click through it at your own pace, or open it full screen.

To try it on your own code, download Safeguard and follow the CLI docs.

For more context, see our explainer on the earlier CPython tarfile filter bypass and our roundup of October 2026 security incidents and what would have helped.

Quick check

You upgraded every Python image to 3.14.9 this week. Are you protected from CVE-2026-114583?

No. 3.14.9 was tagged on 21 October, before the fix merged on 22 October. It fixes the pip and certifi issue it was released for. Until a later 3.14 release ships, use the data filter or the wrapper check, and extract as an unprivileged user.

Your code calls extractall(dest) with no filter on Python 3.14.7. Do you need to act for this CVE?

Not for this CVE. On 3.14 the default filter is data, and 3.14.7 already includes the CVE-2026-7774 fix, so the destination link check applies. Review any call that passes filter="tar" instead.

Sources

Last reviewed: 24 October 2026. This is a developing story; check the linked advisories for the latest status.

Never miss an update

Weekly insights on software supply chain security, delivered to your inbox.

Self-healing security runs on Safeguard.

Your first fix PR is minutes away.

No sales call required, even your agent can complete the purchase over MCP.