T09 · Insecure Skill Coding Practices
- Location
scripts/fetch_deadline_sources.py:98- Finding
Predictable Temporary Cache File Permits Symlink-Based File Overwrite
- Content
View full analysis
Vulnerability Details
File Location:
scripts/fetch_deadline_sources.py, lines 98-126
Vulnerability Type: Predictable temporary file and unsafe symlink handling
Risk Level: MediumVulnerable Code
python def default_cache_path() -> str: override = os.environ.get("AI_CONFERENCE_DEADLINE_RADAR_CACHE") if override: return override return os.path.join(tempfile.gettempdir(), "ai-conference-deadline-radar-cache.json") def load_cache(path: str) -> dict: try: with open(path, "r", encoding="utf-8") as fh: data = json.load(fh) return data if isinstance(data, dict) else {} except Exception: return {} def save_cache(cache: dict, path: str) -> None: try: parent = os.path.dirname(path) if parent: os.makedirs(parent, exist_ok=True) tmp = path + ".tmp" with open(tmp, "w", encoding="utf-8") as fh: json.dump(cache, fh, ensure_ascii=False) os.replace(tmp, path) except Exception: # Cache failure should never prevent the lookup result. returnTechnical Analysis
The default cache uses the globally predictable path
/tmp/ai-conference-deadline-radar-cache.json, and its staging file is always<cache-path>.tmp. The staging file is opened withopen(..., "w"), which follows symbolic links and truncates the resolved target. The implementation does not use exclusive creation, verify that the file is regular, reject symbolic links, or place the cache in a private per-user directory.On a shared system, another local user can predict the staging filename and create it as a symbolic link to a file writable by the victim. When the victim runs the helper, Python follows the link and replaces the target's contents with cache JSON.
The subsequent
os.replace()does not eliminate the vulnerability because the target has ...[truncated 2094 chars]- Remediation
View remediation
Remediation Suggestions
- Store the cache in a private per-user cache directory rather than a shared, predictable path. Create the directory with permissions limited to the current user.
- Create staging files with
tempfile.mkstemp()ortempfile.NamedTemporaryFile(delete=False, dir=cache_directory). These APIs generate unpredictable names and use exclusive creation. - Write through the securely returned file descriptor, flush the data, call
os.fsync()if durability is required, close the descriptor, and then useos.replace()for atomic publication. - Set cache and staging-file permissions to
0600. - Before replacing an existing cache, use
os.lstat()to reject symbolic links and non-regular files where supported. - Validate an environment-provided cache path and document that it must refer to a trusted, user-controlled directory.
- Do not silently suppress security-relevant cache-write failures. Report a concise warning to standard error while allowing the deadline lookup itself to continue.
- Add a regression test that pre-creates the staging path as a symbolic link and verifies that the linked target is not modified.
