Skip to main content

filelock-lts (lts-py39) — 🛡️ 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.19.1 (upstream tag for Python 3.9)
Python 3.9 only (>=3.9, <3.10)
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.9 users are permanently excluded from official security patches.

This package backports the complete fix to Python 3.9.


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

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

Pinning in requirements files

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

Pinning in pyproject.toml

[project]
dependencies = [
    "filelock-lts-py39==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-py39  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_py39', 'filelock_lts',
    'filelock-lts-py39', '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-py39 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-py39')
    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-py39
Patch files https://github.com/1minds3t/filelock-lts/tree/lts-py39/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.9 and currently lists filelock as a dependency, consider switching to filelock-lts-py39>=2026.22701.1 to ensure your users receive the patched version. The import API is 100% compatible — no code changes required.

Release files for filelock-lts-py39 2026.22701

For a detailed explanation of source distributions (sdists) and built distributions (wheels), please see the package formats documentation.

Source distribution (sdist)

Source distribution for filelock-lts-py39 2026.22701
File Size Uploaded
filelock_lts_py39-2026.22701.tar.gz 26.5 kB Details

Built distribution (wheel)

Table of built distributions (wheels) for filelock-lts-py39 2026.22701
File Interpreter ABI Platform
filelock_lts_py39-2026.22701-py3-none-any.whl Python 3 none any Details

Total release size: 48.1 kB

Release files / filelock_lts_py39-2026.22701.tar.gz

Download URL filelock_lts_py39-2026.22701.tar.gz
Size 26.5 kB
Tags Source
SHA-256 checksum
How to use checksums
6494983194bcf2c8c482f9498b1b3008b93fc26eaafb8ad7ce5bbe78b7743b4c
BLAKE2b-256 checksum
How to use checksums
42d0aa5dce0a55de398727a14c47b182eacb35529137eb314b95d24109b606e0
Upload date
Uploaded using Trusted Publishing?
What is trusted publishing?
Yes
Uploaded via twine/6.1.0 CPython/3.13.12

Provenance

Provenance describes where a file came from. On PyPI, provenance is shared via attestations, which provide a verifiable record of the build or publishing details. View details, limitations and caveats.

PyPI Publish Attestation

PyPI verified that this artifact, at this checksum, originated from the publisher listed below.

Signed by GitHub Actions, verified by PyPI on Apr 29, 2026.

Transparency log

Release files / filelock_lts_py39-2026.22701-py3-none-any.whl

Download URL filelock_lts_py39-2026.22701-py3-none-any.whl
Size 21.6 kB
Tags Python 3
SHA-256 checksum
How to use checksums
830a9e4081d153e9e22fefb1abaff0f24e222378e6d91b6cae9c23b82e8fea9b
BLAKE2b-256 checksum
How to use checksums
f2d9bf705e713ba6513f68c73872eee91bb146f548edf16f8ca4df19ce7800a4
Upload date
Uploaded using Trusted Publishing?
What is trusted publishing?
Yes
Uploaded via twine/6.1.0 CPython/3.13.12

Provenance

Provenance describes where a file came from. On PyPI, provenance is shared via attestations, which provide a verifiable record of the build or publishing details. View details, limitations and caveats.

PyPI Publish Attestation

PyPI verified that this artifact, at this checksum, originated from the publisher listed below.

Signed by GitHub Actions, verified by PyPI on Apr 29, 2026.

Transparency log

Release history Release notifications | RSS feed

This release

2026.22701 This release

2 release files

Anthropic, PBC Visionary sponsor Bloomberg Visionary sponsor Hudson River Trading Visionary sponsor Meta Visionary sponsor NVIDIA Visionary sponsor Microsoft Sustainability sponsor Depot Continuous Integration AWS Cloud computing and Security Sponsor Datadog Monitoring Fastly CDN Google Download Analytics Sentry Error logging StatusPage Status page