filelock-lts (lts-py39) — 🛡️ PATCHED (Backport)
⚠️ Disclaimer: This project is not affiliated with, endorsed by, or associated with the official
filelockmaintainers. 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:
-
Install-time prevention —
[conflicts]table inpyproject.tomlsignals incompatibility with upstreamfilelockto resolvers that support it. Enforced by pip ≥ 24.1 during dependency resolution; older pip versions will ignore this field silently. -
Runtime integrity check —
_check_clobber()runs at import time using two detection layers:- RECORD-based SHA256 verification (primary): reads pip's own
dist-info/RECORDand verifies_unix.py,_windows.py, and_soft.pyon 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
filelockdist alongside this package. Detection is non-fatal — failures in the check will not interrupt imports.
- RECORD-based SHA256 verification (primary): reads pip's own
-
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 insite-packages. Whichever is installed last owns the files. If upstreamfilelockis 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_NOFOLLOWflag 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_NOFOLLOWguard applied toSoftFileLockviagetattrfallback for cross-platform safety.
Patch files and upstream diff analysis:
security/patches/— the exact diffs appliedsecurity/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
filelockas a dependency, consider switching tofilelock-lts-py39>=2026.22701.1to 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)
| File | Size | Uploaded | |
|---|---|---|---|
| filelock_lts_py39-2026.22701.tar.gz | 26.5 kB | Details |
Built distribution (wheel)
| File | Interpreter | ABI | Platform | Reset |
|---|---|---|---|---|
| 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 logRelease 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