Skip to main content

Lightweight validation for Flask request args and form data

Project description

🚀 flask-request-validate

Lightweight, decorator-based input validation for Flask apps


🚧 Pre-release Feedback Wanted

I'm looking for feedback before publishing to PyPI:

👉 https://github.com/blainekwilson/flask-request-validate/discussions

Would especially appreciate input on API design and validation patterns.


🎯 Why this exists

If you are building Flask apps, there are established frameworks you should consider first.

  • 🧱 Server-side rendered HTML apps → use Flask-WTF
  • 🔌 REST APIs → use Marshmallow or Pydantic

👉 In practice, a large number of Flask applications still don’t use a structured validation framework and rely on ad hoc validation instead.

👉 flask-request-validate exists to provide a lightweight, auditable alternative for those cases.


✨ What makes this different

  • ✅ Decorator-based validation — no forms, no schemas
  • ✅ Works with query strings + form data
  • ✅ Field-level error handling
  • ✅ Custom error responses (HTML, JSON, anything)
  • 🔥 Security audit tool to detect unprotected routes

⚡ Quick example

from flask import Flask, request
import flask_request_validate as fv

app = Flask(__name__)

@app.route("/submit", methods=["POST"])
@fv.validate({
    "args": {
        "st": {"required": True, "rules": fv.US_STATE}
    },
    "form": {
        "zip": {"required": False, "rules": fv.US_ZIP}
    }
})
def submit():
    return f"State: {request.args['st']}"

🧠 Error handling (simple → advanced)

Default (HTML response)

@fv.validate(schema)
def route():
    ...

Custom error handler (recommended)

def json_error_handler(result):
    return {"errors": result["errors"]}, 400

@fv.validate(schema, on_error=json_error_handler)
def route():
    ...

Field-level errors

{
  "errors": {
    "zip": ["Invalid ZIP code"],
    "st": ["Invalid US state"]
  }
}

🔐 Built-in Security Audit

Find routes that accept input without validation.

Run it:

python -m audit_security app:app

What it detects

  • ✅ Routes protected with @validate
  • ⚪ Routes explicitly excluded
  • ❌ Routes missing validation (potential risk)

Example output

🔍 Flask Validate Security Audit Report
==================================================
📊 OVERALL SUMMARY:
   Total routes analyzed: 42
   ✅ Protected routes: 38
   ⚪ Excluded routes: 2
   ❌ Unprotected routes: 2
   🔒 Security Score: 95.2%

❌ UNPROTECTED ROUTES:
   🚨 POST /api/admin (high priority)
   ℹ️  GET / (low priority)

🧩 When to use this

Use flask-validate when:

  • You want simple, explicit validation and you aren't using an existing framework

🚫 When NOT to use this

  • UI apps → use Flask-WTF
  • API-first apps → use Pydantic or Marshmallow

📦 Installation (development)

pip install -e .

Planned: PyPI release as flask-request-validate


🛡️ Security-first design

  • Inspired by OWASP validation guidance
  • Built to reduce common input validation mistakes
  • Includes runtime auditing for missing protections

🔐 Security Response Headers (enabled by default)

flask-validate now applies several security HTTP response headers by default to every Flask response: How it works

  • Headers applied: Content-Security-Policy, X-Frame-Options, X-Content-Type-Options, Referrer-Policy, Permissions-Policy, and Strict-Transport-Security (configurable).

How to customize

  • Global defaults (process-wide): mutate fv.SECURITY_HEADER_DEFAULTS before creating your Flask app.
  • Per-app override: set app.config['FLASK_REQUEST_VALIDATE_SECURITY_HEADERS'] to a dict of header overrides before running the app.
  • Per-route override: pass security_headers to the @fv.validate(..., security_headers=...) decorator for route-level control.

Server header stripping

  • The package attempts to remove/blank the Server header to avoid leaking server/version information. It installs WSGI middleware that filters Server from response headers and also monkeypatches Werkzeug's handler version string when possible.
  • Note: some development servers may still emit an empty Server: header; removing the header entirely is best handled at the HTTP server/proxy layer (nginx/Gunicorn, etc.).

Auto-initialization and init_app

  • By default flask_request_validate.init_app(app) is called automatically for every Flask app created after the module is imported (we monkeypatch Flask.__init__ to call init_app). You can still call fv.init_app(app) explicitly — it is idempotent.
  • init_app wraps app.wsgi_app with the Server-header-stripping middleware and registers the after_request hook that applies headers and (optionally) injects CSRF tokens.

Exports

  • fv.SECURITY_HEADER_DEFAULTS: module-level dict of header defaults (exported so callers can mutate before app creation).

Tests and examples

  • Test coverage for headers is in tests/test_security_headers.py.
  • Examples: examples/sample_login.py (now demonstrates CSRF injection), examples/example_override_header.py.

Automatic CSRF injection and validation

  • Default: Automatic CSRF injection/validation is enabled by default via fv.AUTO_CSRF_DEFAULT = True.

    • Disable globally before creating apps: set fv.AUTO_CSRF_DEFAULT = False.
    • Disable per-app: app.config['FLASK_REQUEST_VALIDATE_AUTO_CSRF'] = False.
  • Sessions required: You must set app.secret_key (or configure a session backend) for server-side token storage. The library only injects/enforces CSRF when a session secret is available.

app.secret_key = 'a-secure-secret'

What it does when enabled

  • Injects a hidden input named flask_request_validate_csrf_token into any HTML response that contains </form>.
  • Stores a server-side token in the user's session using a randomized session key (prefixed with fv_csrf_). Tokens are single-use and consumed on successful validation.
  • For inbound form submissions (application/x-www-form-urlencoded or multipart/form-data) on routes decorated with @fv.validate(...), the decorator will validate the submitted token against session-stored tokens.

Notes & guidance

  • Tokens are single-use and stored under randomized session keys to avoid predictable session layout.
  • If you prefer custom behavior (different field name, TTL, persistent tokens), disable AUTO_CSRF_DEFAULT and implement your own lifecycle.

Check-unprotected-routes secret-key detection

  • fv.check_unprotected_routes(app) now detects whether app.secret_key is set and will emit a warning if sessions are not configured (CSRF injection/validation will not function). The returned status dict includes the boolean key secret_key_set.

🧭 Roadmap

  • form field validation
  • query string validation
  • audit for unprotected endpoints
  • throw error if additional fields found
  • support error function callback and HTML output
  • CSRF Token Support (by default)
  • Security Response Headers (by default)
  • PyPI release
  • Authentication and Authorization
  • Custom rule extensions
  • Enhanced reporting + CLI options

🤝 Contributing


⭐ Why this project stands out

Most validation libraries focus on:

  • full frameworks
  • API schemas

This one focuses on Flask applications with no validation frameworks.

And adds something most don’t:

🔥 Runtime detection of missing validation (security auditing)

Project details


Download files

Download the file for your platform. If you're not sure which to choose, learn more about installing packages.

Source Distribution

flask_request_validate-0.1.2.tar.gz (31.8 kB view details)

Uploaded Source

Built Distribution

If you're not sure about the file name format, learn more about wheel file names.

flask_request_validate-0.1.2-py3-none-any.whl (22.7 kB view details)

Uploaded Python 3

File details

Details for the file flask_request_validate-0.1.2.tar.gz.

File metadata

  • Download URL: flask_request_validate-0.1.2.tar.gz
  • Upload date:
  • Size: 31.8 kB
  • Tags: Source
  • Uploaded using Trusted Publishing? No
  • Uploaded via: twine/6.1.0 CPython/3.8.2

File hashes

Hashes for flask_request_validate-0.1.2.tar.gz
Algorithm Hash digest
SHA256 bfbbda4db3994a134ff0f7ba7e57e4598901454dc1f176c7a27a66e06b35f43c
MD5 cd801c80694a94413b3e7813c4c8caa2
BLAKE2b-256 174b54262a7c7f1013ee42f764cff60e6690c9091ddddbfd46b49563809b5a35

See more details on using hashes here.

File details

Details for the file flask_request_validate-0.1.2-py3-none-any.whl.

File metadata

File hashes

Hashes for flask_request_validate-0.1.2-py3-none-any.whl
Algorithm Hash digest
SHA256 a068e6663412ee3059c99e53d4c6d018204e98dd62a021514a5aec032bc8320c
MD5 18f0bd5a5512785037f70f08e560fd29
BLAKE2b-256 36988aaee44a2a963dd7fa325d7283c9bb0f9fba96c22c8686f4e69197e85daf

See more details on using hashes here.

Supported by

AWS Cloud computing and Security Sponsor Datadog Monitoring Depot Continuous Integration Fastly CDN Google Download Analytics Pingdom Monitoring Sentry Error logging StatusPage Status page