skills/unrestricted-file-upload-anti-pattern/SKILL.md
Security anti-pattern for unrestricted file upload vulnerabilities (CWE-434). Use when generating or reviewing code that handles file uploads, processes user-submitted files, or stores uploaded content. Detects missing extension, MIME type, and size validation.
npx skillsauth add igbuend/grimbard unrestricted-file-upload-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: Critical
Applications accept user-uploaded files without validating type, content, or size, enabling attackers to upload malicious scripts or executables. Leads to remote code execution (web shells), server compromise, or denial-of-service (disk exhaustion).
The anti-pattern is accepting uploaded files without validating type, content, and size.
# VULNERABLE: No validation of file type, content, or size.
from flask import Flask, request
import os
UPLOAD_FOLDER = '/var/www/uploads' # This directory might be accessible by the web server.
app = Flask(__name__)
app.config['UPLOAD_FOLDER'] = UPLOAD_FOLDER
@app.route('/upload', methods=['POST'])
def upload_file():
if 'file' not in request.files:
return 'No file part', 400
file = request.files['file']
if file.filename == '':
return 'No selected file', 400
# CRITICAL FLAW: The application takes the filename as is and saves the file.
# An attacker can upload a file named `shell.php` with PHP code.
# If the `UPLOAD_FOLDER` is web-accessible and PHP is executed,
# the attacker achieves Remote Code Execution.
filename = file.filename
file.save(os.path.join(app.config['UPLOAD_FOLDER'], filename))
return f'File {filename} uploaded successfully', 200
# Attack Scenario:
# 1. Attacker crafts a PHP file named `shell.php` containing `<?php system($_GET['cmd']); ?>`.
# 2. Attacker uploads `shell.php` via this endpoint.
# 3. Attacker accesses `http://your-app.com/uploads/shell.php?cmd=ls%20-la`
# and can now execute arbitrary commands on the server.
# SECURE: Implement a multi-layered validation approach for file uploads.
from flask import Flask, request, jsonify
import os
import uuid # For generating unique filenames
from magic import from_buffer # `python-magic` for magic byte detection
UPLOAD_FOLDER = '/var/www/safe_uploads' # Store files outside the web root.
ALLOWED_EXTENSIONS = {'png', 'jpg', 'jpeg', 'gif', 'pdf'}
MAX_FILE_SIZE = 5 * 1024 * 1024 # 5 MB
app = Flask(__name__)
app.config['UPLOAD_FOLDER'] = UPLOAD_FOLDER
def allowed_file(filename):
return '.' in filename and \
filename.rsplit('.', 1)[1].lower() in ALLOWED_EXTENSIONS
@app.route('/upload/secure', methods=['POST'])
def upload_file_secure():
if 'file' not in request.files:
return jsonify({'error': 'No file part'}), 400
file = request.files['file']
if file.filename == '':
return jsonify({'error': 'No selected file'}), 400
if file:
# 1. Validate file extension (allowlist approach).
if not allowed_file(file.filename):
return jsonify({'error': 'File type not allowed'}), 400
# 2. Validate file size.
file.seek(0, os.SEEK_END)
file_length = file.tell()
file.seek(0)
if file_length > MAX_FILE_SIZE:
return jsonify({'error': 'File too large'}), 400
# 3. Validate actual MIME type using magic bytes (more reliable than Content-Type header).
file_buffer = file.read(1024) # Read a chunk for magic byte detection
file.seek(0) # Reset file pointer
actual_mime = from_buffer(file_buffer, mime=True)
if actual_mime not in ['image/png', 'image/jpeg', 'image/gif', 'application/pdf']:
return jsonify({'error': f'Invalid file content type: {actual_mime}'}), 400
# 4. Generate a unique and safe filename. Never use the original filename directly.
original_extension = file.filename.rsplit('.', 1)[1].lower()
safe_filename = str(uuid.uuid4()) + '.' + original_extension
# 5. Store the file in a secure location, preferably outside the web root.
file.save(os.path.join(app.config['UPLOAD_FOLDER'], safe_filename))
return jsonify({'message': f'File {safe_filename} uploaded successfully'}), 200
Content-Type HTTP header, which is easily spoofed..png, .jpg, .pdf). Never use blocklists.Content-Type header.../) in the filename to write files to unintended locations.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.