CtrlK
BlogDocsLog inGet started
Tessl Logo

tessl-labs/flask-security-basics

Security essentials for Flask APIs — CORS, Talisman security headers, rate

99

1.17x
Quality

94%

Does it follow best practices?

Impact

100%

1.17x

Average score across 10 eval scenarios

SecuritybySnyk

Passed

No known issues

Overview
Quality
Evals
Security
Files

criteria.jsonevals/scenario-2/

{
  "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

tile.json