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 review a Flask API with several deliberate security flaws and produce both a written list of issues and a corrected file. The scorer checks security_issues.md for correct identification of problems and restaurant_api_fixed.py for correct remediation of each flaw.",
"type": "weighted_checklist",
"checklist": [
{
"name": "Wildcard CORS identified",
"description": "security_issues.md explicitly identifies the CORS wildcard origins='*' as a security problem.",
"max_score": 6
},
{
"name": "Hardcoded secret identified",
"description": "security_issues.md identifies the hardcoded SECRET_KEY as a security problem.",
"max_score": 6
},
{
"name": "Debug mode identified",
"description": "security_issues.md identifies app.debug = True as unsafe for production.",
"max_score": 5
},
{
"name": "Permissive rate limit identified",
"description": "security_issues.md flags the 1000-per-minute global default as excessively permissive.",
"max_score": 5
},
{
"name": "Missing per-route limits identified",
"description": "security_issues.md notes that the POST, PATCH, and DELETE routes have no per-route rate limits.",
"max_score": 5
},
{
"name": "CORS fixed with env var",
"description": "In restaurant_api_fixed.py, CORS is initialized with origins from os.getenv('ALLOWED_ORIGINS', 'http://localhost:3000').split(',') or equivalent, not with '*'.",
"max_score": 10
},
{
"name": "SECRET_KEY from env",
"description": "In restaurant_api_fixed.py, SECRET_KEY is loaded from os.getenv('SECRET_KEY') and not hardcoded.",
"max_score": 10
},
{
"name": "SECRET_KEY startup guard",
"description": "In restaurant_api_fixed.py, there is a startup check that raises a RuntimeError (or equivalent) if SECRET_KEY is missing outside TESTING mode.",
"max_score": 8
},
{
"name": "Global rate limit corrected",
"description": "In restaurant_api_fixed.py, the global default_limits is set to 200 per hour or less (not 1000 per minute).",
"max_score": 8
},
{
"name": "POST route limited",
"description": "In restaurant_api_fixed.py, the POST /orders route has a @limiter.limit(...) decorator with a limit stricter than the global default.",
"max_score": 7
},
{
"name": "PATCH route limited",
"description": "In restaurant_api_fixed.py, the PATCH /orders/<id> route has a @limiter.limit(...) decorator with a limit stricter than the global default.",
"max_score": 7
},
{
"name": "DELETE route limited",
"description": "In restaurant_api_fixed.py, the DELETE /orders/<id> route has a @limiter.limit(...) decorator with a limit stricter than the global default.",
"max_score": 7
},
{
"name": "Debug mode disabled",
"description": "In restaurant_api_fixed.py, app.debug = True is removed or changed to False and no debug=True appears in app.run().",
"max_score": 6
},
{
"name": "Routes preserved",
"description": "All four original routes (GET /menu, POST /orders, DELETE /orders/<id>, PATCH /orders/<id>) are still present and functional in restaurant_api_fixed.py.",
"max_score": 5
},
{
"name": "Both files present",
"description": "Both security_issues.md and restaurant_api_fixed.py exist as files.",
"max_score": 5
}
]
}evals
scenario-1
scenario-2
scenario-3
scenario-4
scenario-5
scenario-6
scenario-7
scenario-8
skills
flask-security-basics
verifiers