T09 · Insecure Skill Coding Practices
- Location
scripts/refresh_usage.sh:35- Finding
Predictable Temporary Script Enables File Overwrite and Local Code Execution
- Content
View full analysis
Vulnerability Details
File Location:
scripts/refresh_usage.sh, lines 35–62
Vulnerability Type: Predictable and insecure temporary executable file
Risk Level: HighVulnerable Code
bash cat > /tmp/refresh_usage.js << 'EOF' import { readFileSync } from 'fs'; import { join } from 'path'; import { homedir } from 'os'; const configPath = join(homedir(), 'web/AIClient-2-API/configs/usage-cache.json'); console.log('Reading current usage cache...'); try { const data = JSON.parse(readFileSync(configPath, 'utf8')); console.log('✅ Cache file is accessible'); console.log('📊 Last update:', data.timestamp); // The actual refresh happens automatically in the background // We just need to wait for the next cycle console.log(''); console.log('💡 Usage data refreshes automatically every few minutes.'); console.log(' Check again in a moment with: bash scripts/check_usage.sh'); } catch (error) { console.error('❌ Error reading cache:', error.message); } EOF node /tmp/refresh_usage.js rm /tmp/refresh_usage.jsTechnical Analysis
The script creates executable JavaScript at the fixed, globally predictable path
/tmp/refresh_usage.js. It does not securely reserve the filename, verify that the path is a regular file owned by the current user, prevent symbolic-link traversal, or place the file in a private directory.This produces two related local attack opportunities:
- Symbolic-link file overwrite: A local attacker can create
/tmp/refresh_usage.jsas a symbolic link to another file writable by the victim. The shell redirection then follows that link and truncates or overwrites its target. - Time-of-check/time-of-use replacement: There is a window between writing the file and invoking
nodeduring which another local user may replace the temporary script. Node would then execute attacker-controlled JavaScript under the victim's account.
Removing the predictable path afterward does not prev ...[truncated 1353 chars]
- Symbolic-link file overwrite: A local attacker can create
- Remediation
View remediation
Remediation Suggestions
Avoid creating a temporary JavaScript file. The cache-reading operation can be implemented directly in shell with
jq, or JavaScript can be supplied to Node through standard input so that no executable file is exposed in/tmp.If a temporary file is unavoidable:
- Create a private temporary directory using
mktemp -d. - Restrict permissions with
umask 077. - write the script only inside that private directory.
- Install a cleanup trap immediately after directory creation.
- Quote every generated path.
- Do not run this script with elevated privileges.
Example hardening pattern:
bash umask 077 TMP_DIR=$(mktemp -d) || exit 1 trap 'rm -rf -- "$TMP_DIR"' EXIT HUP INT TERM SCRIPT_FILE="$TMP_DIR/refresh_usage.js" cat > "$SCRIPT_FILE" <<'EOF' // JavaScript content EOF node -- "$SCRIPT_FILE"A file-free approach is preferable because it eliminates the temporary-path race entirely.
- Create a private temporary directory using
