T09 · Insecure Skill Coding Practices
- Location
scripts/script.sh:35- Finding
Predictable Shared Temporary File Allows Symlink-Based File Overwrite
- Content
View full analysis
Vulnerability Details
File Location:
scripts/script.sh, lines 8, 35-36, 72, 101, and 132
Vulnerability Type: Predictable and unsafe temporary-file handling
Risk Level: MediumThe script stores its diagnostic pattern database at the fixed, globally predictable path
/tmp/debug-patterns.json. It opens this path for writing on every invocation without securely creating the file, verifying ownership, or rejecting symbolic links.bash PATTERNS_FILE="/tmp/debug-patterns.json" init_patterns() { python3 << 'PYEOF' import json patterns = [ # Static diagnostic patterns omitted for brevity ] with open("/tmp/debug-patterns.json", "w") as f: json.dump(patterns, f) print(f"Loaded {len(patterns)} error patterns") PYEOF }The same shared path is subsequently opened for reading in all three processing functions:
python with open("/tmp/debug-patterns.json") as f: patterns = json.load(f)Technical Analysis
/tmpis commonly writable by every local user. Although its sticky bit normally prevents users from deleting one another's files, it does not make a predictable filename safe. An attacker who creates/tmp/debug-patterns.jsonbefore the victim runs the Skill can control what filesystem object that path references.Python's
open("/tmp/debug-patterns.json", "w")follows symbolic links and truncates the resolved target before writing. The script does not use exclusive creation, inspect the path withlstat, verify file ownership, or place the file in a private per-process directory. Consequently, a malicious local user can create the predictable path as a symbolic link to another file writable by the victim. When the victim invokes any command,init_patternsfollows that link, truncates the target, and replaces its contents with JSON.The shared path also creates a race condition between concurrent invocations. One process may truncate or rewrite the file whi ...[truncated 1736 chars]
- Remediation
View remediation
Remediation Suggestions
Prefer eliminating the temporary file entirely. The patterns are static and can remain in memory within each Python invocation or be stored as a read-only resource inside the Skill package.
If a temporary file is required:
- Create a private, unpredictable directory using
mktemp -d. - Set a restrictive
umask, such asumask 077, before creating files. - Store the JSON file inside that private directory.
- Register a shell
trapto remove the directory on normal exit and interruption. - Pass the generated path explicitly to Python rather than embedding a fixed
/tmppath. - Use exclusive file creation where appropriate and reject symbolic links.
- Ensure every read and write uses the same private path.
- Avoid sharing mutable temporary state between concurrent invocations.
Example hardening approach:
bash umask 077 TMP_DIR="$(mktemp -d "${TMPDIR:-/tmp}/debugger.XXXXXXXX")" PATTERNS_FILE="$TMP_DIR/debug-patterns.json" trap 'rm -rf -- "$TMP_DIR"' EXIT HUP INT TERM export PATTERNS_FILEThe Python code should then retrieve the private path from the environment:
python import json import os patterns_file = os.environ["PATTERNS_FILE"] with open(patterns_file, "x", encoding="utf-8") as f: json.dump(patterns, f)For later reads, open that same private path in read-only mode. Keeping the patterns entirely in memory remains the safest and simplest solution.
- Create a private, unpredictable directory using
