T09 · Insecure Skill Coding Practices
- Location
scripts/yield_scout.py:24- Finding
Predictable Shared Temporary Cache Enables Data Poisoning and Symlink-Based File Overwrite
- Content
View full analysis
Vulnerability Details
File Location:
scripts/yield_scout.py, lines 24 and 73–85
Vulnerability Type: Unsafe temporary-file handling
Risk Level: MediumVulnerable code:
python CACHE_PATH = os.path.join(tempfile.gettempdir(), "yield_scout_pools.json")python def load_pools(force=False): if not force and os.path.exists(CACHE_PATH): age = time.time() - os.path.getmtime(CACHE_PATH) if age < CACHE_TTL: with open(CACHE_PATH, "r") as f: return json.load(f) print("Fetching pool data from DeFiLlama (this may take a moment)...", file=sys.stderr) data = fetch_json(POOLS_URL, timeout=120) pools = data.get("data", data) if isinstance(data, dict) else data with open(CACHE_PATH, "w") as f: json.dump(pools, f) return poolsTechnical Analysis
The pool cache is stored under a fixed, predictable name in the system-wide temporary directory. The implementation does not verify that the cache is a regular file, is owned by the current user, has restrictive permissions, or is not a symbolic link.
When the file's modification time is less than 15 minutes old, its JSON content is trusted as pool data without checking its origin or integrity. A local attacker who can write to the shared temporary directory can therefore provide fabricated pool records. Those records can influence APY rankings, TVL values, risk labels, and breakeven recommendations.
The refresh path opens the same predictable location using normal write mode. This follows symbolic links and truncates the resolved target before writing JSON. A local attacker may pre-create the cache path as a symbolic link to another file. Exploitation is limited to targets writable by the account running the Skill; the code does not independently grant elevated privileges.
Attack Path
Cache-poisoning path:
- A local attacker creates `/tmp/yield_sco ...[truncated 1868 chars]
- Remediation
View remediation
Remediation Suggestions
- Store cached data in a private, per-user cache directory rather than a shared temporary directory. Create the directory with mode
0700. - Create cache files with mode
0600and verify that existing files are owned by the current user. - Reject symbolic links and non-regular files before reading. Where supported, open files using
os.open()withO_NOFOLLOW. - Avoid check-then-open sequences based on
exists()andgetmtime(), which are vulnerable to time-of-check/time-of-use races. - Write refreshed data to a securely created temporary file in the private cache directory, flush and optionally
fsync()it, then atomically replace the cache usingos.replace(). - Validate the cached JSON structure and expected field types before using it.
- Consider recording and verifying an integrity digest or trusted metadata if cache authenticity must be protected from same-user processes.
- On validation, ownership, permission, or file-type failure, discard the cache and securely retrieve a fresh copy.
- Store cached data in a private, per-user cache directory rather than a shared temporary directory. Create the directory with mode
