T09 · Insecure Skill Coding Practices
Warning
- Location
- skills/causal-md/scripts/backfill_email.py:167
- Finding
- Predictable Temporary File Exposes Email History and Permits Symlink-Based File Overwrite<![CDATA[ ## Vulnerability Details **File Location**: `skills/causal-md/scripts/backfill_email.py`, lines 167-172 **Vulnerability Type**: Predictable temporary file, insecure permissions, missing cleanup, and symlink following **Risk Level**: Medium ### Vulnerable Code ```python # Write to temp and process tmp_path = "/tmp/gog_sent_emails.json" with open(tmp_path, "w") as f: json.dump(sent_emails, f) return backfill_from_json(tmp_path, log_path, all_emails) ``` ### Technical Analysis The email backfill process writes exported email data to the fixed path `/tmp/gog_sent_emails.json`. The file is opened with normal write mode, which: - Does not create the file exclusively. - Follows an existing symbolic link. - Uses permissions derived from the process umask rather than explicitly enforcing owner-only access. - Leaves the exported data on disk after processing. - Reuses the same globally predictable path for every invocation. The subprocess invocation itself uses an argument array without `shell=True`, so no command-injection vulnerability was identified. The vulnerability arises from how its sensitive output is subsequently stored. On a shared system, another local account can predict and monitor this path. If the resulting permissions allow access, that account may read the retained email export. An attacker may also pre-create the path as a symbolic link. If operating-system symlink protections do not block the operation and the victim can write to the target, opening the path with `"w"` truncates and overwrites the target with JSON. ### Attack Path 1. A local attacker determines that the victim uses the email backfill script. 2. The attacker monitors `/tmp/gog_sent_emails.json` or pre-creates it as a symbolic link to another file writable by the victim. 3. The victim executes `backfill_email.py --days ...`. 4. The script retrieves sent-email records through `gog`. 5. The script opens the predictable path in truncating write mode and writes the ex ...[truncated 919 chars]
- Remediation
- <![CDATA[ ## Remediation Suggestions Avoid creating a temporary file because `sent_emails` is already available in memory. Refactor the processing function to accept parsed records directly. If a temporary file is genuinely required: 1. Use `tempfile.NamedTemporaryFile` or `tempfile.TemporaryDirectory`. 2. Create the file with exclusive, race-resistant semantics. 3. Enforce owner-only mode `0600`. 4. Keep the temporary file inside a directory accessible only to the current user. 5. Delete it in a `finally` block, including when JSON parsing or backfill processing fails. 6. Do not reuse a fixed filename. 7. Avoid following attacker-controlled symbolic links. Example: ```python import os import tempfile tmp_path = None try: with tempfile.NamedTemporaryFile( mode="w", prefix="gog_sent_emails_", suffix=".json", delete=False, encoding="utf-8", ) as tmp: tmp_path = tmp.name os.chmod(tmp_path, 0o600) json.dump(sent_emails, tmp) return backfill_from_json(tmp_path, log_path, all_emails) finally: if tmp_path: try: os.unlink(tmp_path) except FileNotFoundError: pass ``` The preferred design is to eliminate the temporary export entirely. ]]>
