Skip to main content

Python 3.7 filelock with CVE-2025-68146 and CVE-2026-22701 security patches (TOCTOU symlink attack prevention)

Project description

filelock-lts (lts-py37) — 🛡️ 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.12.2 (upstream tag for Python 3.7)
Python 3.7 only (>=3.7, <3.8)
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.7 users are permanently excluded from official security patches.

This package backports the complete fix to Python 3.7.


🔒 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-py37==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-py37==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-py37

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

Pinning in requirements files

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

Pinning in pyproject.toml

[project]
dependencies = [
    "filelock-lts-py37==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-py37  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_py37', 'filelock_lts',
    'filelock-lts-py37', '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-py37 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-py37')
    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-py37
Patch files https://github.com/1minds3t/filelock-lts/tree/lts-py37/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.7 and currently lists filelock as a dependency, consider switching to filelock-lts-py37>=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_py37-2026.22701.post1.tar.gz (17.3 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_py37-2026.22701.post1-py3-none-any.whl (17.4 kB view details)

Uploaded Python 3

File details

Details for the file filelock_lts_py37-2026.22701.post1.tar.gz.

File metadata

File hashes

Hashes for filelock_lts_py37-2026.22701.post1.tar.gz
Algorithm Hash digest
SHA256 b264d95a8ee28dbcf0f1211ebeabcf2b4a94c97d0dece19627b4a99ab14586bc
MD5 35705bb4ba66841bbbf3aaf0cf827f73
BLAKE2b-256 0fb547bcd81229807ea59c93f572861f8415eafd42a3a4e065229e17ddd93d67

See more details on using hashes here.

Provenance

The following attestation bundles were made for filelock_lts_py37-2026.22701.post1.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_py37-2026.22701.post1-py3-none-any.whl.

File metadata

File hashes

Hashes for filelock_lts_py37-2026.22701.post1-py3-none-any.whl
Algorithm Hash digest
SHA256 cd823f41e9e55258aa5e1cf08097e025092c4d5258496464eb535329ff1b7041
MD5 49d33c54bb809a3a0a51d43a8cd3d85a
BLAKE2b-256 a656ad0bfce4433c257dee828b8cd5446186e2b2c35c7586be39448ff5559240

See more details on using hashes here.

Provenance

The following attestation bundles were made for filelock_lts_py37-2026.22701.post1-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