skills/verbose-error-messages-anti-pattern/SKILL.md
Security anti-pattern for verbose error messages (CWE-209). Use when generating or reviewing code that handles errors, exceptions, or generates user-facing error responses. Detects stack trace exposure and detailed error information leakage to users.
npx skillsauth add igbuend/grimbard verbose-error-messages-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
Applications expose internal information (stack traces, database errors, file paths, configuration details) in error messages, enabling attackers to understand architecture, identify vulnerabilities, and craft targeted attacks. Suppress detailed errors in production.
The anti-pattern is presenting raw exception messages or system errors directly to end-users.
# VULNERABLE: The application exposes raw database errors and stack traces to the user.
from flask import Flask, request, jsonify
import sqlite3
app = Flask(__name__)
@app.route("/user_profile")
def user_profile():
user_id = request.args.get("id")
try:
conn = sqlite3.connect("database.db")
cursor = conn.cursor()
# Imagine 'users' table does not exist or 'id' column is missing.
# This will throw a `sqlite3.OperationalError`.
cursor.execute(f"SELECT username, email FROM users WHERE id = {user_id}")
user_data = cursor.fetchone()
conn.close()
if user_data:
return jsonify({"username": user_data[0], "email": user_data[1]})
else:
return "User not found", 404
except Exception as e:
# CRITICAL FLAW: The raw exception message is returned directly to the user.
# This could include SQL query details, table names, column names,
# or even parts of the application's code structure in a stack trace.
#
# Example output for an attacker:
# "OperationalError: no such table: users"
# "Traceback (most recent call last):
# File "/app/main.py", line 15, in user_profile
# cursor.execute(f"SELECT username, email FROM users WHERE id = {user_id}")
# sqlite3.OperationalError: no such table: users"
return f"An internal server error occurred: {str(e)}", 500
# SECURE: Generic error messages are returned to the user, with detailed logging internally.
from flask import Flask, request, jsonify
import sqlite3
import logging
import traceback # To capture stack traces for internal logging
app = Flask(__name__)
logging.basicConfig(level=logging.ERROR, format='%(asctime)s - %(levelname)s - %(message)s')
@app.route("/user_profile_secure")
def user_profile_secure():
user_id = request.args.get("id")
try:
conn = sqlite3.connect("database.db")
cursor = conn.cursor()
cursor.execute("SELECT username, email FROM users WHERE id = ?", (user_id,))
user_data = cursor.fetchone()
conn.close()
if user_data:
return jsonify({"username": user_data[0], "email": user_data[1]})
else:
return "User not found", 404
except Exception as e:
# 1. Log full exception details internally for debugging.
# Include unique error ID for correlation with user reports.
error_id = str(uuid.uuid4())
logging.error(f"Error ID: {error_id}. Detailed error: {e}\n{traceback.format_exc()}")
# 2. Return generic error message to end-user.
# Include error ID for support reference.
return jsonify({
"error": "An internal server error occurred.",
"message": "Please try again later. If the problem persists, contact support with Error ID: " + error_id
}), 500
# Another example: Authentication error messages should be generic to prevent user enumeration.
@app.route("/login")
def login():
username = request.form.get("username")
password = request.form.get("password")
if not authenticate_user(username, password):
# BAD: "User not found" or "Incorrect password". This tells attacker if username exists.
# GOOD: Generic "Invalid credentials" message for both username not found and wrong password.
return jsonify({"message": "Invalid credentials"}), 401
return jsonify({"message": "Login successful"}), 200
try...except blocks or global error handlers. Check what information is returned to the user in the except block.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.