T09 · Insecure Skill Coding Practices
- Location
SKILL.md:112- Finding
Predictable Shared Temporary Files Permit Draft Disclosure, File Clobbering, and Content Substitution
- Content
View full analysis
Vulnerability Details
File Location:
SKILL.md, lines 112-125
Vulnerability Type: Predictable and insecure temporary-file handling
Risk Level: MediumVulnerable 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
/tmpdirectory:/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:
- Symbolic-link file clobbering: A local process can pre-create `/tmp/devto_bod ...[truncated 3504 chars]
- Remediation
View remediation
Remediation Suggestions
-
Create an unpredictable private temporary directory. Use Python's
tempfile.TemporaryDirectory()ortempfile.NamedTemporaryFile()rather than fixed names under/tmp. -
Enforce restrictive permissions. Create temporary files with mode
0600and their containing directory with mode0700, independent of the user'sumask. -
Prevent symbolic-link attacks. Use atomic exclusive creation through
tempfile.mkstemp()or flags equivalent toO_CREAT | O_EXCL | O_NOFOLLOW. Do not reopen a predictable pathname without validating it. -
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.
-
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.
-
Delete sensitive files reliably. Remove temporary article files in a
finallyblock or rely on a scoped temporary directory that is deleted after publication, including when publication fails. -
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]-
