Security essentials for Flask APIs — CORS, Talisman security headers, rate
99
94%
Does it follow best practices?
Impact
100%
1.17xAverage score across 10 eval scenarios
Passed
No known issues
{
"context": "The agent was asked to harden a minimal Flask app for production by adding CORS, security headers, rate limiting, a safe secret key, and disabling debug mode. The scorer checks app.py and requirements.txt for correct implementation of each security measure.",
"type": "weighted_checklist",
"checklist": [
{
"name": "CORS extension used",
"description": "app.py imports and uses Flask-CORS (flask_cors) to configure CORS, not a manual header approach.",
"max_score": 8
},
{
"name": "No wildcard CORS",
"description": "CORS is NOT configured with origins='*' or resources={r'/*': {'origins': '*'}}. Only explicit origins are allowed.",
"max_score": 8
},
{
"name": "CORS env var origins",
"description": "Allowed origins are read from the ALLOWED_ORIGINS environment variable (e.g., os.getenv('ALLOWED_ORIGINS', 'http://localhost:5173').split(',')).",
"max_score": 7
},
{
"name": "Talisman / security headers",
"description": "Flask-Talisman (flask_talisman) is imported and initialized on the app to add security headers.",
"max_score": 10
},
{
"name": "Rate limiter initialized",
"description": "Flask-Limiter is imported, a Limiter instance is created using get_remote_address as the key function, and it is initialized with the app.",
"max_score": 8
},
{
"name": "Global rate limit 200/hour",
"description": "The Limiter is configured with a default limit of '200 per hour' (or equivalent).",
"max_score": 7
},
{
"name": "POST endpoint rate limited",
"description": "The POST /api/items route has a @limiter.limit(...) decorator with a limit stricter than 200/hour.",
"max_score": 8
},
{
"name": "DELETE endpoint rate limited",
"description": "The DELETE /api/items/<id> route has a @limiter.limit(...) decorator with a limit stricter than 200/hour.",
"max_score": 8
},
{
"name": "SECRET_KEY from env",
"description": "app.config['SECRET_KEY'] is set from os.getenv('SECRET_KEY') or equivalent, NOT from a hardcoded string.",
"max_score": 10
},
{
"name": "Startup check for SECRET_KEY",
"description": "The app raises a RuntimeError (or equivalent) at startup if SECRET_KEY is not set and the app is not in TESTING mode.",
"max_score": 8
},
{
"name": "Debug mode disabled",
"description": "app.debug is not set to True and app.run() does not pass debug=True. The hardcoded app.debug = True has been removed or set to False.",
"max_score": 8
},
{
"name": "requirements.txt present",
"description": "A requirements.txt file is present and lists flask-cors, flask-talisman, and flask-limiter (or their equivalent package names).",
"max_score": 10
}
]
}evals
scenario-1
scenario-2
scenario-3
scenario-4
scenario-5
scenario-6
scenario-7
scenario-8
skills
flask-security-basics
verifiers