vibe-py-no-input-validation
Pack: vibe-frontend Default severity: MAJOR Languages: Python Lifecycle: beta Confidence: 0.75
What it catches
State-changing (POST / PUT / PATCH / DELETE) Flask / FastAPI route handlers that read raw request input (request.get_json(), request.form, request.args, request.values, request.data, await request.json()) in a file that imports no recognised schema validator — pydantic, marshmallow, cerberus, wtforms / flask_wtf, voluptuous, trafaret, flask_restful, rest_framework. Python analogue of `vibe-no-input-validation`.
Why it matters
Unvalidated user input flows straight into the handler and DB calls — injection, shape confusion, mass assignment. Vibe-coded Flask apps almost always go straight from request.get_json() to the DB call. FastAPI handlers that declare a Pydantic model parameter are clean by construction and never match.
Example — flagged
@app.route("/posts", methods=["POST"])
def create_post():
data = request.get_json() # no validation anywhere in the file
post = Post(title=data["title"], body=data["body"])
db.session.add(post)Example — not flagged
from pydantic import BaseModel, ValidationError
class CreatePost(BaseModel):
title: str
body: str
@app.route("/posts", methods=["POST"])
def create_post():
try:
payload = CreatePost.model_validate(request.get_json())
except ValidationError:
return {"error": "invalid input"}, 400Also not flagged: GET-only routes, FastAPI handlers taking a Pydantic model parameter, and test files (tests/, test_*.py, conftest.py).
Suggested fix
Define a schema and validate the payload before use (pydantic shown above; marshmallow / cerberus / wtforms equally accepted). On FastAPI, declare the model as the handler parameter instead of reading await request.json().
Suppression
# codemore-ignore-next-line: vibe-py-no-input-validation
@app.route("/webhook", methods=["POST"])Implementation
Tree-sitter-python AST scopes decorated route handlers (@app.route(..., methods=[...]), @app.post(...), @api_view([...])); a regex confirms a raw request read inside the handler; a file-level import scan looks for validator evidence. As with the TS rule, a validator import is treated as evidence of intent — "imported marshmallow but never called .load()" is a known false negative.
Source: `shared/packs/vibe-frontend/vibe-py-no-input-validation.ts` Fixtures: `corpus/rules/vibe-py-no-input-validation/`