skills/insecure-temp-files-anti-pattern/SKILL.md
Security anti-pattern for insecure temporary files (CWE-377). Use when generating or reviewing code that creates temporary files, handles file caching, or processes uploads through temp storage. Detects predictable paths, insecure permissions, and missing cleanup.
npx skillsauth add igbuend/grimbard insecure-temp-files-anti-patternInstall this skill globally with one command. Works with Claude Code, Cursor, and Windsurf.
3 of 9 scanners reported clean
Some scanners were skipped, did not run, or reported a non-clean status. Review each row below.
Severity: Medium
Insecure temporary file creation exposes three attack vectors: predictable file names enabling symlink attacks, insecure permissions allowing unauthorized access, and missing cleanup leaving sensitive data on disk. Attackers exploit these to read sensitive data, inject malicious content, or cause denial of service. AI-generated code frequently suggests simplistic file handling vulnerable to these attacks.
Never create temporary files without securing their location, naming, permissions, and lifecycle management.
Using a predictable name for a temporary file creates a race condition. An attacker can guess the file name and create a symbolic link (symlink) at that location pointing to a sensitive system file. When the application writes to its "temporary" file, it is actually overwriting the linked file.
# VULNERABLE: Predictable temporary file name in a shared directory.
import os
def process_user_data(user_id, data):
# The filename is easy for an attacker to guess.
temp_path = f"/tmp/userdata_{user_id}.txt"
# Attacker's action (done before this code runs):
# ln -s /etc/passwd /tmp/userdata_123.txt
# When the application writes to the temp file for user 123,
# it is actually overwriting the system's password file.
with open(temp_path, "w") as f:
f.write(data)
# ... processing logic ...
os.remove(temp_path)
# SECURE: Use a library function that creates a securely named temporary file.
import tempfile
def process_user_data(user_id, data):
# `tempfile.mkstemp()` creates a temporary file with a random, unpredictable name
# and returns a low-level file handle and the path.
# It also ensures the file is created with secure permissions (0600 on Unix).
fd, temp_path = tempfile.mkstemp(prefix="userdata_", suffix=".txt")
try:
with os.fdopen(fd, 'w') as f:
f.write(data)
# ... processing logic ...
finally:
# Always ensure the file is cleaned up.
os.remove(temp_path)
Creating a temporary file with default permissions can make it world-readable, allowing other users on the system to access its contents. Failing to delete the temporary file after use means that sensitive data may be left behind on the disk.
# VULNERABLE: World-readable permissions and no cleanup.
import uuid
def generate_report(data):
# The name is random, but the permissions are not secure.
temp_path = f"/tmp/{uuid.uuid4()}.pdf"
# `open` with mode 'w' often uses default permissions like 0644,
# which means other users on the system can read the file.
with open(temp_path, "w") as f:
f.write(data) # Sensitive report data is written.
return temp_path # The path is returned, but the file is never deleted.
# SECURE: Guaranteed cleanup using a context manager.
import tempfile
def generate_report(data):
# `NamedTemporaryFile` creates a file that is automatically deleted
# when the context manager is exited.
with tempfile.NamedTemporaryFile(mode='w', suffix='.pdf', delete=True) as temp_f:
# The file has a secure name and permissions.
temp_f.write(data)
temp_f.flush()
# You can use `temp_f.name` to get the path and pass it to other functions.
result = send_file_to_storage(temp_f.name)
# The temporary file is automatically and reliably deleted here,
# even if an error occurs inside the `with` block.
return result
JavaScript/Node.js:
// VULNERABLE: Predictable name and no cleanup
const fs = require('fs');
const path = require('path');
function processUpload(userId, data) {
const tempPath = `/tmp/upload_${userId}.dat`; // Predictable!
fs.writeFileSync(tempPath, data); // World-readable by default
// ... processing ...
// File never deleted!
return tempPath;
}
// SECURE: Use tmp module with automatic cleanup
const tmp = require('tmp');
function processUpload(userId, data) {
// Creates file with mode 0600 (owner read/write only)
const tempFile = tmp.fileSync({ prefix: 'upload-', postfix: '.dat' });
try {
fs.writeFileSync(tempFile.name, data);
// ... processing ...
return processFile(tempFile.name);
} finally {
tempFile.removeCallback(); // Guaranteed cleanup
}
}
Java:
// VULNERABLE: Predictable name in shared directory
public void processData(String userId, byte[] data) throws IOException {
File tempFile = new File("/tmp/data_" + userId + ".tmp"); // Predictable!
Files.write(tempFile.toPath(), data); // Default permissions may be insecure
// ... processing ...
// No cleanup - file persists!
}
// SECURE: Use Files.createTempFile with try-with-resources
import java.nio.file.*;
import java.nio.file.attribute.*;
public void processData(String userId, byte[] data) throws IOException {
// Create with restricted permissions (owner only)
Set<PosixFilePermission> perms = PosixFilePermissions.fromString("rw-------");
FileAttribute<Set<PosixFilePermission>> attr =
PosixFilePermissions.asFileAttribute(perms);
Path tempFile = Files.createTempFile("data-", ".tmp", attr);
try {
Files.write(tempFile, data);
// ... processing ...
} finally {
Files.deleteIfExists(tempFile); // Guaranteed cleanup
}
}
Go:
// VULNERABLE: Predictable path and missing cleanup
func processData(userID string, data []byte) error {
tempPath := fmt.Sprintf("/tmp/data_%s.tmp", userID) // Predictable!
if err := os.WriteFile(tempPath, data, 0644); err != nil { // World-readable!
return err
}
// ... processing ...
// No cleanup!
return nil
}
// SECURE: Use os.CreateTemp with defer cleanup
import "os"
func processData(userID string, data []byte) error {
// Creates file with mode 0600 automatically
tempFile, err := os.CreateTemp("", "data-*.tmp")
if err != nil {
return err
}
defer os.Remove(tempFile.Name()) // Guaranteed cleanup
defer tempFile.Close()
if _, err := tempFile.Write(data); err != nil {
return err
}
// ... processing ...
return nil
}
rg 'open\s*\(\s*["\']/(tmp|var/tmp)/'rg 'File\.createTempFile|mktemp|tmpfile' (check if used correctly)rg 'f"/tmp/{user_id}' 'f"/tmp/{username}'rg 'new File\("/tmp/" \+ userId'rg 'os\.chmod.*0o[67]' (world-readable/writable)os.umask(0o077) or tempfile usagerg 'open\(' | rg -v 'with|try.*finally|NamedTemporaryFile'defer f.Close() (Go) or using (C#)tempfile in Python or Files.createTempFile in Java. These libraries are designed to handle naming and permissions securely.try...finally blocks or language features like context managers (with in Python) to guarantee deletion.io.BytesIO in Python) instead of temporary files if the data is small enough to fit in memory.development
Security anti-pattern for Cross-Site Scripting vulnerabilities (CWE-79). Use when generating or reviewing code that renders HTML, handles user input in web pages, uses innerHTML/document.write, or builds dynamic web content. Covers Reflected, Stored, and DOM-based XSS. AI code has 86% XSS failure rate.
development
Security anti-pattern for XPath injection vulnerabilities (CWE-643). Use when generating or reviewing code that queries XML documents, constructs XPath expressions, or handles user input in XML operations. Detects unescaped quotes and special characters in XPath queries.
development
Security anti-pattern for weak password hashing (CWE-327, CWE-759). Use when generating or reviewing code that stores or verifies user passwords. Detects use of MD5, SHA1, SHA256 without salt, or missing password hashing entirely. Recommends bcrypt, Argon2, or scrypt.
development
Security anti-pattern for weak encryption (CWE-326, CWE-327). Use when generating or reviewing code that encrypts data, handles encryption keys, or uses cryptographic modes. Detects DES, ECB mode, static IVs, and custom crypto implementations.