T09 · Insecure Skill Coding Practices
Warning
- Location
- SKILL.md:112
- Finding
- Predictable Shared Temporary Files Permit Draft Disclosure, File Clobbering, and Content Substitution## Vulnerability Details **File Location**: `SKILL.md`, lines 112-125 **Vulnerability Type**: Predictable and insecure temporary-file handling **Risk Level**: Medium ### Vulnerable Code ```bash # Write article content to temp JSON file python3 -c " import json with open('/tmp/devto_body.md') as f: body = f.read() with open('/tmp/devto_body.json', 'w') as f: json.dump(body, f) " # Use JXA to read the file and publish osascript -l JavaScript -e ' var chrome = Application("Google Chrome"); var tab = chrome.windows[0].activeTab; var body = JSON.parse($.NSString.alloc.initWithContentsOfFileEncodingError("/tmp/devto_body.json", $.NSUTF8StringEncoding, null).js); tab.execute({javascript: "(async()=>{try{var csrf=document.querySelector(\"meta[name=csrf-token]\").getAttribute(\"content\");var resp=await fetch(\"/articles\",{method:\"POST\",headers:{\"Content-Type\":\"application/json\",\"X-CSRF-Token\":csrf},credentials:\"include\",body:JSON.stringify({article:{title:\"YOUR TITLE\",body_markdown:" + JSON.stringify(body) + ",tags:[\"tag1\",\"tag2\"],published:true}})});var r=await resp.json();document.title=r.current_state_path?\"OK:\"+r.current_state_path:\"ERR:\"+JSON.stringify(r)}catch(e){document.title=\"ERR:\"+e.message}})()"}); ' ``` ### Technical Analysis The file-based publishing workflow uses fixed, globally predictable paths under the shared `/tmp` directory: - `/tmp/devto_body.md` - `/tmp/devto_body.json` The output file is opened with Python's ordinary write mode without secure exclusive creation, ownership validation, file-type validation, explicit restrictive permissions, or protection against symbolic links. The subsequent JXA process reopens the same predictable path without confirming that it still refers to the file created by the publishing workflow. This creates several local attack opportunities: 1. **Symbolic-link file clobbering:** A local process can pre-create `/tmp/devto_bod ...[truncated 3504 chars]
- Remediation
- ## Remediation Suggestions 1. **Create an unpredictable private temporary directory.** Use Python's `tempfile.TemporaryDirectory()` or `tempfile.NamedTemporaryFile()` rather than fixed names under `/tmp`. 2. **Enforce restrictive permissions.** Create temporary files with mode `0600` and their containing directory with mode `0700`, independent of the user's `umask`. 3. **Prevent symbolic-link attacks.** Use atomic exclusive creation through `tempfile.mkstemp()` or flags equivalent to `O_CREAT | O_EXCL | O_NOFOLLOW`. Do not reopen a predictable pathname without validating it. 4. **Validate before reading.** If the file must be passed between Python and JXA, verify that it is a regular file, owned by the current user, not a symbolic link, and has restrictive permissions. Prefer retaining an open file descriptor where practical. 5. **Minimize the race window.** Generate the unpredictable path and immediately pass that exact path to JXA. Avoid separate commands that expose a stable pathname between writing and reading. 6. **Delete sensitive files reliably.** Remove temporary article files in a `finally` block or rely on a scoped temporary directory that is deleted after publication, including when publication fails. 7. **Avoid disk staging when possible.** Pass the article through standard input, a protected pipe, or another in-memory mechanism to prevent persistent plaintext drafts. A safer Python implementation should follow this pattern: ```python import json import os import tempfile source_path = "/path/to/user-selected/article.md" with open(source_path, "r", encoding="utf-8") as source: body = source.read() fd, temporary_path = tempfile.mkstemp( prefix="devto-", suffix=".json" ) try: os.fchmod(fd, 0o600) with os.fdopen(fd, "w", encoding="utf-8") as temporary_file: json.dump(body, temporary_file) # Pass temporary_path directly to the JXA process. # Validate ownership ...[truncated 286 chars]
