T09 · Insecure Skill Coding Practices
Error
- Location
- bin/agentsource.py:115
- Finding
- Predictable and Insecure Temporary Files Expose Sensitive Prospect Data<![CDATA[ ## Vulnerability Details **File Location**: `bin/agentsource.py`, lines 115–129 **Vulnerability Type**: Predictable temporary files, non-atomic file creation, and insufficient file permissions **Risk Level**: High ### Vulnerable Code ```python def make_temp_path(command: str) -> pathlib.Path: ts = int(time.time()) return TEMP_DIR / f"agentsource_{ts}_{command}.json" def write_result(command: str, data: dict) -> pathlib.Path: path = make_temp_path(command) path.write_text(json.dumps(data, indent=2, default=str)) print(str(path)) return path def write_error(command: str, error_msg: str, error_code: str = None, http_status: int = None) -> pathlib.Path: ``` The same predictable path construction and unrestricted `write_text()` operation are also used when error files are written. ### Technical Analysis The CLI stores API responses and imported CSV content directly in the shared `/tmp` directory. A filename consists only of the current timestamp in seconds and a predictable command name, such as: ```text /tmp/agentsource_1750000000_fetch.json ``` This construction has several security weaknesses: 1. **Predictable filenames:** A local attacker can calculate likely paths from the current time and known command names. 2. **Non-atomic creation:** `Path.write_text()` opens an existing pathname rather than securely creating a new, exclusive file. 3. **Symbolic-link following:** If an attacker creates the expected pathname as a symbolic link, the CLI follows it and overwrites the link target, provided the victim account can write to that target. 4. **No explicit restrictive permissions:** The resulting mode depends on the process umask. Under a common `022` umask, files are created as `0644` and may be readable by other local users. 5. **Filename collisions:** Two invocations of the same command during the same second use the same path and can overwrite or corrupt each other's results. 6. **No explicit cleanup: ...[truncated 1809 chars]
- Remediation
- <![CDATA[ ## Remediation Suggestions 1. Use `tempfile.mkstemp()` or `tempfile.NamedTemporaryFile(delete=False)` to generate cryptographically unpredictable names and create files atomically. 2. Store results in a private per-user directory with mode `0700` rather than directly in shared `/tmp`. 3. Create each result file with mode `0600`, independent of the caller's umask. 4. Use exclusive creation semantics and reject symbolic links. On supported platforms, use `O_CREAT | O_EXCL | O_NOFOLLOW`. 5. Write to an exclusively created temporary file, flush and optionally `fsync()` it, and then atomically rename it when complete. 6. Add a defined retention policy and cleanup command rather than relying solely on operating-system cleanup. 7. Avoid embedding predictable timestamps as the only uniqueness source. A suitable pattern is: ```python import os import tempfile PRIVATE_TEMP_DIR = pathlib.Path(tempfile.gettempdir()) / f"agentsource-{os.getuid()}" PRIVATE_TEMP_DIR.mkdir(mode=0o700, parents=True, exist_ok=True) PRIVATE_TEMP_DIR.chmod(0o700) fd, name = tempfile.mkstemp( prefix=f"agentsource_{command}_", suffix=".json", dir=PRIVATE_TEMP_DIR, text=True, ) try: os.fchmod(fd, 0o600) with os.fdopen(fd, "w", encoding="utf-8") as output: json.dump(data, output, indent=2, default=str) finally: pass ``` ]]>
