T09 · Insecure Skill Coding Practices
- Location
SKILL.md:19- Finding
Predictable Temporary File Path Permits Symlink-Based File Clobbering
- Content
View full analysis
Vulnerability Details
File Location:
SKILL.md, line 19; repeated at line 84
Vulnerability Type: Unsafe temporary-file handling
Risk Level: MediumVulnerable code at line 19:
bash # Save PNG echo "$RESULT" | python3 -c "import json,sys,base64; d=json.load(sys.stdin); open('/tmp/diagram.png','wb').write(base64.b64decode(d['png']))"Repeated vulnerable code at line 84:
bash echo "$RESULT" | python3 -c "import json,sys,base64; d=json.load(sys.stdin); open('/tmp/diagram.png','wb').write(base64.b64decode(d['png'])); print(d['editUrl'])"Technical Analysis
The documented workflow writes rendered image data to the fixed, globally predictable path
/tmp/diagram.png. Python's standardopen()operation follows symbolic links and opens the destination with truncation when usingwbmode.On a multi-user system, another local process can create
/tmp/diagram.pngas a symbolic link to a file writable by the account running the Skill. When the workflow executes, the linked target is truncated and replaced with bytes returned by the remote rendering service. Separate concurrent Skill invocations can also overwrite or read one another's output because they share the same filename.The base64 operation does not constitute decoded-command execution: it only converts the API's
pngfield into bytes and writes those bytes to a file. No decoded content is executed. Likewise, the reviewed file does not contain acurl | bashpipeline or other remote-script execution. The security issue is limited to unsafe handling of the output file.Attack Path
- A local attacker with access to the shared
/tmpdirectory predicts the documented output path. - Before the Skill runs, the attacker creates
/tmp/diagram.pngas a symbolic link to a target file writable by the Skill's operating-system account. - The Skill submits diagram data to the hosted rendering API and receives a base64-encode ...[truncated 1416 chars]
- A local attacker with access to the shared
- Remediation
View remediation
Remediation Suggestions
Replace the fixed pathname with an exclusively created, unpredictable temporary file. Python's
tempfilemodule is preferred:bash OUTPUT_PATH=$(echo "$RESULT" | python3 -c " import base64 import json import os import sys import tempfile data = json.load(sys.stdin) png = base64.b64decode(data['png'], validate=True) with tempfile.NamedTemporaryFile( mode='wb', prefix='excalidraw-', suffix='.png', delete=False, dir='/tmp' ) as output: output.write(png) print(output.name) ")Additional hardening measures should include:
- Create files atomically and exclusively rather than checking whether a path exists before opening it.
- Use a private temporary directory created with
tempfile.TemporaryDirectory()ormktemp -d, with permissions restricted to the current account. - Return the generated unique path to the message-sending step instead of assuming
/tmp/diagram.png. - Remove the temporary artifact after it has been sent, preferably in a cleanup handler.
- Validate the base64 response with
validate=Trueand impose a reasonable decoded-size limit to prevent malformed or excessively large responses. - Apply the same correction to both occurrences at lines 19 and 84.
