T09 · Insecure Skill Coding Practices
- Location
scripts/cache.py:87- Finding
Predictable Temporary Cache File Enables Symlink-Based File Overwrite
- Content
View full analysis
Vulnerability Details
File Location:
scripts/cache.py, lines 87–93
Vulnerability Type: Predictable temporary file with unsafe symlink handling
Risk Level: Mediumpython global _CACHE _CACHE = cache path = _cache_path() tmp = path + ".tmp" with open(tmp, "w", encoding="utf-8") as f: json.dump(cache, f, ensure_ascii=False, separators=(",", ":")) os.replace(tmp, path)Technical Analysis
The cache writer derives a predictable temporary filename,
fund_cache.json.tmp, and opens it with ordinary write mode. This operation follows symbolic links and truncates the linked target before writing JSON data. It does not use exclusive creation,O_NOFOLLOW, or a securely randomized temporary filename.The final
os.replace()provides atomic replacement for the cache destination, but it does not protect the precedingopen()operation. Therefore, a local attacker who can create or replace files in thescriptsdirectory can point the temporary path at another file writable by the Agent account.Attack Path
- A local attacker obtains write access to the project’s
scriptsdirectory. - The attacker creates
scripts/fund_cache.json.tmpas a symbolic link to a target file writable by the account running the Skill. - The victim invokes a query that builds, updates, or appends data to the cache.
save()opens the predictable temporary path in write mode and follows the symbolic link.- The target file is truncated and overwritten with cache JSON.
os.replace()then attempts to move the symlink path to the cache destination; regardless of that final result, the linked target has already been modified.
Impact Assessment
Exploitation requires local write access to the Skill directory and does not independently provide privilege escalation. If that prerequisite is met, the attacker can corrupt or overwrite an arbitrary file that is writable by the Agent process.
The resu ...[truncated 399 chars]
- A local attacker obtains write access to the project’s
- Remediation
View remediation
Remediation Suggestions
Create the temporary file securely and atomically in the same directory as the destination:
- Use
tempfile.NamedTemporaryFile()ortempfile.mkstemp()withdir=os.path.dirname(path)and a randomized filename. - Apply restrictive permissions, such as mode
0o600. - Do not follow symbolic links. If using
os.open(), combineO_CREAT,O_EXCL, andO_NOFOLLOWwhere supported. - Write and flush the JSON content, then call
os.fsync()when durability is required. - Atomically replace the destination using
os.replace(). - Remove the temporary file in a
finallyblock if any operation fails. - Ensure the cache directory is not writable by untrusted users.
Example hardening pattern:
python import json import os import tempfile def save(cache): global _CACHE path = _cache_path() directory = os.path.dirname(path) or "." tmp_path = None try: fd, tmp_path = tempfile.mkstemp( dir=directory, prefix=".fund_cache.", suffix=".tmp", ) os.chmod(tmp_path, 0o600) with os.fdopen(fd, "w", encoding="utf-8") as f: json.dump(cache, f, ensure_ascii=False, separators=(",", ":")) f.flush() os.fsync(f.fileno()) os.replace(tmp_path, path) tmp_path = None _CACHE = cache finally: if tmp_path is not None: try: os.unlink(tmp_path) except FileNotFoundError: pass- Use
