Skip to main content

Python 3.8 filelock with CVE-2025-68146 and CVE-2026-22701 security patches (symlink/TOCTOU attack prevention via O_NOFOLLOW)

Project description

filelock-lts (lts-py38) — 🛡️ PATCHED (Backport)

⚠️ Disclaimer: This project is not affiliated with, endorsed by, or associated with the official filelock maintainers. All patches and releases are independently maintained and provided on a best-effort basis to support legacy environments.

Metric Details
CVEs Fixed CVE-2025-68146 (HIGH) · CVE-2026-22701 (MODERATE)
Version 2026.22701.1
Base filelock 3.16.1 (upstream tag for Python 3.8)
Python 3.8 only (>=3.8, <3.9)
License Unlicense (Public Domain)

🚨 The Problem

filelock >= 3.20.1 contains the official fix for CVE-2025-68146 and CVE-2026-22701, but upstream requires Python >= 3.10. Python 3.8 users are permanently excluded from official security patches.

This package backports the complete fix to Python 3.8.


🔒 Defense-in-Depth

This package uses three hardening layers to protect patched files from being silently overwritten by pip's flat install model:

  1. Install-time prevention[conflicts] table in pyproject.toml signals incompatibility with upstream filelock to resolvers that support it. Enforced by pip ≥ 24.1 during dependency resolution; older pip versions will ignore this field silently.

  2. Runtime integrity check_check_clobber() runs at import time using two detection layers:

    • RECORD-based SHA256 verification (primary): reads pip's own dist-info/RECORD and verifies _unix.py, _windows.py, and _soft.py on disk still match the hashes recorded at install time. Catches silent overwrites even when no upstream dist-info is present.
    • Co-install detection (secondary): scans installed distributions for a bare filelock dist alongside this package. Detection is non-fatal — failures in the check will not interrupt imports.
  3. Documentation — install-order warnings, safe workflow guidance, and verification steps below.

Note: These layers reduce risk but cannot fully prevent file overwrites within pip's install model. Always verify patch integrity after installing new packages in the same environment. See Verifying Patch Integrity below.


📦 Installation

Fresh install (no existing filelock)

pip install filelock-lts-py38==2026.22701.1

If filelock is already installed — do this in order

# 1. Remove upstream filelock first
pip uninstall filelock -y

# 2. Install the patched version
pip install filelock-lts-py38==2026.22701.1

# 3. Verify patch integrity (see section below)

Why the order matters: Both packages install into the filelock/ namespace in site-packages. Whichever is installed last owns the files. If upstream filelock is installed after this package, it silently overwrites the patched _unix.py, _windows.py, and _soft.py, reintroducing the CVEs with no error or warning from pip.


⚠️ Staying Protected After Initial Install

The clobber risk

Any tool that declares Requires: filelock (without a version pin) may cause pip to install upstream filelock when that tool is installed, overwriting your patched files. This happens silently — pip does not warn when one package's files are overwritten by another.

Safe workflow

# After installing ANY new package into this environment, verify protection:
python -c "import filelock"
# If patched files were overwritten, you will see a RuntimeWarning.

# Or check which dist owns the filelock namespace:
pip show filelock
# Should show NO result, or only show filelock-lts-py38

# If upstream filelock crept back in:
pip uninstall filelock -y
pip install --force-reinstall filelock-lts-py38

Pinning in requirements files

# requirements.txt — pin explicitly to block pip from pulling upstream
filelock-lts-py38==2026.22701.1
# Do NOT also list 'filelock' — that will pull in the unpatched upstream

Pinning in pyproject.toml

[project]
dependencies = [
    "filelock-lts-py38==2026.22701.1",
    # Do NOT add 'filelock' here — it will clobber the patched version
]

✅ Verifying Patch Integrity

Quick check — dist ownership

python -c "
import importlib.metadata as m
for d in m.distributions():
    name = d.metadata.get('Name', '')
    if 'filelock' in name.lower():
        print(name, d.metadata.get('Version', ''))
"
# Expected: only 'filelock-lts-py38  2026.22701.1'
# If you see bare 'filelock  3.x.x' — upstream has been installed and may
# have overwritten the patched files.

Full integrity check — SHA256 against RECORD

This verifies the actual patched files on disk match the hashes pip recorded at install time. This is the same check _check_clobber() performs at import.

python -c "
import importlib.metadata as m, hashlib, base64, pathlib

LTS_NAMES = {
    'filelock_lts_py38', 'filelock_lts',
    'filelock-lts-py38', 'filelock-lts',
}
PATCHED = {'_unix.py', '_windows.py', '_soft.py'}

our_dist = None
for d in m.distributions():
    norm = (d.metadata.get('Name', '') or '').lower().replace('-', '_')
    if norm in LTS_NAMES:
        our_dist = d
        break

if our_dist is None:
    print('ERROR: filelock-lts-py38 dist-info not found')
    raise SystemExit(1)

import filelock as fl
pkg_dir = pathlib.Path(fl.__file__).parent
record = our_dist.read_text('RECORD')
ok, failed = [], []

for line in record.splitlines():
    parts = line.split(',')
    if len(parts) < 2:
        continue
    filename = pathlib.Path(parts[0].strip()).name
    recorded = parts[1].strip()
    if filename not in PATCHED or not recorded.startswith('sha256:'):
        continue
    actual = pkg_dir / filename
    if not actual.exists():
        failed.append(f'{filename}: MISSING')
        continue
    digest = base64.urlsafe_b64encode(
        hashlib.sha256(actual.read_bytes()).digest()
    ).rstrip(b'=').decode()
    if digest == recorded[7:]:
        ok.append(filename)
    else:
        failed.append(f'{filename}: HASH MISMATCH')

for f in ok:
    print(f'  ✅ {f}')
for f in failed:
    print(f'  ❌ {f}')

if failed:
    print('\nPatch integrity FAILED — reinstall filelock-lts-py38')
    raise SystemExit(1)
else:
    print('\nAll patched files verified against RECORD.')
"

⚙️ What Was Patched

CVE-2025-68146_unix.py, _windows.py

  • Unix: os.O_NOFOLLOW flag enforced during lock file creation, blocking symlink traversal at the kernel level.
  • Windows: Explicit reparse point detection via kernel32.GetFileAttributesW (ctypes), refusing lock acquisition if the target is a symlink or directory junction.

CVE-2026-22701_soft.py

  • O_NOFOLLOW guard applied to SoftFileLock via getattr fallback for cross-platform safety.

Patch files and upstream diff analysis:

  • security/patches/ — the exact diffs applied
  • security/analysis/ — justification for each change

🔗 Links

Resource URL
Source (this branch) https://github.com/1minds3t/filelock-lts/tree/lts-py38
Patch files https://github.com/1minds3t/filelock-lts/tree/lts-py38/security/patches
CVE-2025-68146 https://nvd.nist.gov/vuln/detail/CVE-2025-68146
CVE-2026-22701 https://www.cve.org/CVERecord?id=CVE-2026-22701
Upstream filelock https://github.com/tox-dev/py-filelock

Note for package maintainers: If your package targets Python 3.8 and currently lists filelock as a dependency, consider switching to filelock-lts-py38>=2026.22701.1 to ensure your users receive the patched version. The import API is 100% compatible — no code changes required.

Project details


Download files

Download the file for your platform. If you're not sure which to choose, learn more about installing packages.

Source Distribution

filelock_lts_py38-2026.22701.tar.gz (26.7 kB view details)

Uploaded Source

Built Distribution

If you're not sure about the file name format, learn more about wheel file names.

filelock_lts_py38-2026.22701-py3-none-any.whl (21.8 kB view details)

Uploaded Python 3

File details

Details for the file filelock_lts_py38-2026.22701.tar.gz.

File metadata

  • Download URL: filelock_lts_py38-2026.22701.tar.gz
  • Upload date:
  • Size: 26.7 kB
  • Tags: Source
  • Uploaded using Trusted Publishing? Yes
  • Uploaded via: twine/6.1.0 CPython/3.13.12

File hashes

Hashes for filelock_lts_py38-2026.22701.tar.gz
Algorithm Hash digest
SHA256 eaf2b9f3cc400903ee4425d7c590253ce05698616bf3523c26037dc7882a3116
MD5 64d52cf65f521c8a7d66da1bac21e97a
BLAKE2b-256 2185a1c126c6836326140a552dc1d2a7670a91a5c8afad4b3ae6fc4a75a96f90

See more details on using hashes here.

Provenance

The following attestation bundles were made for filelock_lts_py38-2026.22701.tar.gz:

Publisher: publish.yml on 1minds3t/filelock-lts

Attestations: Values shown here reflect the state when the release was signed and may no longer be current.

File details

Details for the file filelock_lts_py38-2026.22701-py3-none-any.whl.

File metadata

File hashes

Hashes for filelock_lts_py38-2026.22701-py3-none-any.whl
Algorithm Hash digest
SHA256 34dd333ec465fd792055aa677a3050be196c5f03cd4a20f3e5f053bdd5aa0eee
MD5 361d98f1ed2b13c6aca43c5faf268017
BLAKE2b-256 8d98e68dbc342c9d3a7f8dfa9a9bed3f819a742e4d80b797652f98a35e6e58fb

See more details on using hashes here.

Provenance

The following attestation bundles were made for filelock_lts_py38-2026.22701-py3-none-any.whl:

Publisher: publish.yml on 1minds3t/filelock-lts

Attestations: Values shown here reflect the state when the release was signed and may no longer be current.

Supported by

AWS Cloud computing and Security Sponsor Datadog Monitoring Depot Continuous Integration Fastly CDN Google Download Analytics Pingdom Monitoring Sentry Error logging StatusPage Status page