T09 · Insecure Skill Coding Practices
- Location
scripts/file_lock.py:45- Finding
Active File Locks Can Be Forcibly Removed
- Content
View full analysis
Vulnerability Details
File Location:
scripts/file_lock.py, lines 45–61 and 64–72
Vulnerability Type: Unsafe stale-lock recovery and missing lock ownership validation
Risk Level: MediumVulnerable Code
python except (FileExistsError, OSError): if time.monotonic() >= deadline: # Timeout — attempt stale-lock recovery once self._remove_stale() try: self._fd = os.open( self.lock_path, os.O_CREAT | os.O_EXCL | os.O_WRONLY, ) return except (FileExistsError, OSError): raise FileLockTimeout( f'Could not acquire lock: {self.lock_path}' )python def release(self) -> None: if self._fd is not None: try: os.close(self._fd) except OSError: pass self._fd = None self._remove_stale() def _remove_stale(self) -> None: try: os.remove(self.lock_path) except OSError: passTechnical Analysis
The lock uses atomic exclusive file creation, but after the five-second timeout it assumes that any existing lock is stale and deletes it. The implementation does not inspect the lock's age, record the owning process, verify owner liveness, or associate the lock file with a unique ownership token.
Consequently, a waiting process can delete a lock that is still actively held. It can then create a replacement lock and enter the protected critical section while the original holder is still operating. The original holder's
release()method also removes the lock path unconditionally, even if that path now represents a replacement lock owned by another process.This defeats the mutual-exclusion guarantees relied upon by audit-log appends and scan-cache access.
Attack Path
- An audit worker acquires an audit-log or scan-cache lock.
- The worker remains in the protected section for longer than the configured five-second timeout, ...[truncated 1598 chars]
- Remediation
View remediation
Remediation Suggestions
-
Add lock ownership identity
- Generate a cryptographically random token when acquiring a lock.
- Write the token, process ID, creation timestamp, and optionally host identity into the lock file.
- Retain the token in the
FileLockinstance.
-
Validate ownership during release
- Before deleting the lock file, read and compare its token with the token held by the current instance.
- Never remove a lock whose ownership token does not match.
- Where possible, perform ownership verification and deletion atomically.
-
Use safe stale-lock recovery
- Do not classify a lock as stale solely because a waiter reached its timeout.
- Require the lock's recorded age to exceed a separately configured stale-lock threshold.
- Where supported, verify that the owning process is no longer alive.
- If owner liveness cannot be established safely, fail with
FileLockTimeoutrather than deleting the lock.
-
Prefer native locking mechanisms
- Use POSIX
fcntl.flockon Unix-like systems and an appropriate Windows locking primitive such asmsvcrt.locking. - Alternatively, use a maintained cross-platform file-locking library with ownership-safe stale-lock handling.
- Use POSIX
-
Harden cache updates
- Keep cache loading, modification, and saving inside one continuous lock scope.
- Write updated cache data to a temporary file in the same directory, flush and synchronize it, and atomically replace the destination.
-
Add concurrency tests
- Verify that a waiter cannot remove a live lock after timing out.
- Verify that an old owner cannot delete a replacement lock.
- Stress-test concurrent audit appends and cache updates with operations exceeding the timeout.
- Confirm that timeout failures preserve the active owner's lock and protected data.
-
