debug-mode-production-anti-pattern
SKILL.md
Debug Mode in Production Anti-Pattern
Severity: High
Summary
Debug mode in production exposes sensitive system information and creates backdoors. Occurs when development settings remain enabled in deployment. Common in AI-generated code that hardcodes debug flags or fails to differentiate environments.
The Anti-Pattern
This anti-pattern manifests in two primary ways:
- Hardcoded Debug Flags: Global flag
DEBUG = Truenever changes, so the application runs in debug mode in all environments. - Unprotected Debug Endpoints: Debug routes (
/debug/env,/_debug/sql) included in production builds provide attack vectors.
BAD Code Example
# VULNERABLE: Hardcoded debug flag and unprotected debug routes
import os
from flask import Flask, jsonify
app = Flask(__name__)
app.config['DEBUG'] = True # Hardcoded debug mode
@app.route("/")
def index():
return "Welcome!"
# This debug route exposes all environment variables, including potential secrets.
# It should never be present in a production environment.
@app.route("/debug/env")
def debug_env():
if app.config['DEBUG']:
return jsonify(os.environ.copy())
return "Not in debug mode."
if __name__ == "__main__":
app.run()
GOOD Code Example
# SECURE: Environment-based configuration and conditional routes
import os
from flask import Flask, jsonify
app = Flask(__name__)
# Load configuration from the environment. Default to 'production'.
APP_ENV = os.environ.get('APP_ENV', 'production')
app.config['DEBUG'] = APP_ENV == 'development'
@app.route("/")
def index():
return "Welcome!"
# This debug route is now conditionally registered and will only exist
# if the application is explicitly run in a development environment.
if app.config['DEBUG']:
@app.route("/debug/env")
def debug_env():
return jsonify(os.environ.copy())
# It's also a good practice to add a startup check to prevent accidental
# deployment of debug mode to production.
if APP_ENV == 'production' and app.config['DEBUG']:
raise ValueError("FATAL: Debug mode is enabled in a production environment. Aborting.")
if __name__ == "__main__":
app.run()
JavaScript/Node.js Examples
BAD:
// VULNERABLE: Hardcoded debug flag in Express
const express = require('express');
const app = express();
// Hardcoded debug mode
const DEBUG = true;
app.get('/', (req, res) => {
res.send('Welcome!');
});
// Debug route exposes environment variables
app.get('/debug/env', (req, res) => {
if (DEBUG) {
res.json(process.env);
} else {
res.send('Not in debug mode.');
}
});
app.listen(3000);
GOOD:
// SECURE: Environment-based configuration
const express = require('express');
const app = express();
// Load from environment, default to production
const APP_ENV = process.env.APP_ENV || 'production';
const DEBUG = APP_ENV === 'development';
app.get('/', (req, res) => {
res.send('Welcome!');
});
// Conditionally register debug route
if (DEBUG) {
app.get('/debug/env', (req, res) => {
res.json(process.env);
});
}
// Startup check prevents production debug mode
if (APP_ENV === 'production' && DEBUG) {
throw new Error('FATAL: Debug mode enabled in production. Aborting.');
}
app.listen(3000);
Java/Spring Boot Examples
BAD:
// VULNERABLE: Hardcoded debug in application.properties
// application.properties:
// debug=true
// logging.level.root=DEBUG
@RestController
public class DebugController {
@Value("${debug}")
private boolean debug;
@GetMapping("/debug/env")
public Map<String, String> debugEnv() {
if (debug) {
return System.getenv();
}
return Map.of("error", "Not in debug mode");
}
}
GOOD:
// SECURE: Profile-based configuration
// application-dev.properties:
// debug=true
// application-prod.properties:
// debug=false
@RestController
@Profile("dev") // Only register in development profile
public class DebugController {
@GetMapping("/debug/env")
public Map<String, String> debugEnv() {
return System.getenv();
}
}
// Application startup check
@Component
public class EnvironmentValidator implements ApplicationRunner {
@Value("${spring.profiles.active:prod}")
private String activeProfile;
@Value("${debug:false}")
private boolean debug;
@Override
public void run(ApplicationArguments args) {
if ("prod".equals(activeProfile) && debug) {
throw new IllegalStateException(
"FATAL: Debug mode enabled in production. Aborting."
);
}
}
}
Detection
Python/Flask/Django:
DEBUG = Truein source codedebug=Truein Flask configDEBUG = Truein Django settings.py- Debug routes:
@app.route("/debug/
JavaScript/Node.js/Express:
const DEBUG = truein source codeprocess.env.NODE_ENV !== 'production'checks missing- Debug middleware always enabled
- Routes:
app.get('/debug/
Java/Spring Boot:
debug=truein application.propertieslogging.level.root=DEBUGin production- Debug endpoints without
@Profile("dev") spring.devtools.restart.enabled=truein prod
PHP:
error_reporting(E_ALL)in productiondisplay_errors = Onin php.iniAPP_DEBUG=truein .env
Configuration Files:
.envfiles withDEBUG=true- YAML configs with
debug: true - JSON configs with
"debug": true
Search Patterns:
- Grep:
DEBUG.*=.*[Tt]rue|debug.*:.*true|\/debug\/|process\.env\.NODE_ENV - Development dependencies in production builds
- Stack traces exposed in error responses
- Verbose error messages with file paths
Prevention
- Use environment variables to control debug mode and other environment-specific settings.
- Never hardcode
DEBUG = True. - Conditionally register debug routes so they are not included in production builds.
- Implement a startup check in the application that aborts if it detects debug mode is enabled in a production environment.
- Use separate configuration files for each environment (development, staging, production) to avoid overlap.
- Review your CI/CD pipeline to ensure that the correct environment variables are being injected and that development artifacts are excluded from the final build.
Testing for Debug Mode
Manual Testing:
- Check environment variables:
echo $DEBUG,echo $APP_ENV - Access debug endpoints:
/debug,/_debug,/debug/env - Trigger errors and check for stack traces
- Review HTTP headers for debug information (X-Debug, Server versions)
Automated Testing:
- Static Analysis: Semgrep, Bandit (Python), ESLint, SonarQube
- Configuration Scanning: Detect hardcoded
DEBUG = Truein code - Runtime Testing: Burp Suite, OWASP ZAP to find debug endpoints
- CI/CD Checks: Fail builds with debug flags enabled
Example Test:
# Test that debug mode is disabled in production
def test_debug_disabled_in_production():
import os
os.environ['APP_ENV'] = 'production'
# This should raise ValueError
with pytest.raises(ValueError, match="Debug mode is enabled in a production environment"):
import app # Import triggers startup check
CI/CD Pipeline Check:
# .github/workflows/deploy.yml
- name: Verify No Debug Mode
run: |
if grep -r "DEBUG.*=.*True" app/; then
echo "ERROR: Hardcoded DEBUG=True found"
exit 1
fi
if [ "$APP_ENV" = "production" ] && [ "$DEBUG" = "true" ]; then
echo "ERROR: Debug mode enabled for production deployment"
exit 1
fi
Remediation Steps
- Identify debug configurations - Use detection patterns above
- Check current environment - Determine if debug mode is active
- Create environment-based config - Use environment variables
- Remove hardcoded flags - Replace
DEBUG = Truewith env lookup - Conditional debug routes - Register only in development
- Add startup checks - Abort if debug mode in production
- Test the fix - Verify debug disabled in production config
- Update CI/CD - Add validation to deployment pipeline
Related Security Patterns & Anti-Patterns
- Verbose Error Messages Anti-Pattern: A common consequence of running in debug mode.
- Hardcoded Secrets Anti-Pattern: Secrets are often exposed through debug information.
- Missing Security Headers Anti-Pattern: Can provide defense-in-depth by controlling how browsers handle content.
References
Weekly Installs
4
Repository
igbuend/grimbardGitHub Stars
4
First Seen
Jan 20, 2026
Security Audits
Installed on
claude-code4
codex4
cursor4
opencode4
trae-cn3
antigravity3