fastapi/full-stack-fastapi-template/backend

1 error, 3 warningsmaster

1 critical finding to fix first.

1 error and 3 warnings in the paths between 23 routes and 2 tables.

ROUTE FINDINGS4 of 23 · 11/11 rules
ROUTES
23
TABLES
2
FILES READ
19/19
RULES RUN
11/11

4 findings

1 critical2 medium1 low

  • Criticalhigh confidencePOST /private/users can reach user without authenticating.

    Anyone, signed in or not, can write to user (email, hashed_password) — personal or secret fields.

    Data reached
    user (write) · sensitive: email, hashed_password
    Missing
    Authentication before the handler reaches data, and a query scoped to the caller.
    Confidence
    No middleware, guard or wrapper is applied to this route anywhere in backend/app/api/routes/private.py.
    Path
    POST /private/users → user

    Prove it

    # No cookie, no Authorization header.
    curl -i -X POST 'https://YOUR_API/private/users' \
      -H 'content-type: application/json' -d '{}'
    # Expect 401. A 2xx means anyone on the internet can do this.

    Fix

    @app.post("/private/users")
    def handler(user: User = Depends(get_current_user)):
        # get_current_user raises HTTPException(401) when the token is missing
        ...

    Regression test

    def test_post_requires_auth(client):
        res = client.post("/private/users")
        assert res.status_code == 401

    Intended? This cannot be declared intentional: a public write is not a design choice the check will bless. Accept it as known debt: run wyro-check --update-baseline and commit the ledger. It stays visible and CI fails only on new findings.

    auth-before-data

  • Mediumlow confidencePOST /login/access-token has no validator or rate limiter attached.

    POST /login/access-token accepts any body at any rate. Malformed input reaches your handler, and nothing stops a script calling it in a loop.

    Missing
    Schema validation of the request body, and a rate limit.
    Confidence
    Inline validation (zod.parse, pydantic models) and platform rate limits are not always visible to the parser. Check before acting.

    Prove it

    curl -i -X POST 'https://YOUR_API/login/access-token' \
      -H 'content-type: application/json' -d '{"unexpected": {"nested": [1,2,3]}}'
    # Expect 422. A 2xx or 500 means the body is not validated.

    Fix

    class Body(BaseModel):
        name: constr(max_length=200)
    
    @app.post("/login/access-token")
    def handler(body: Body): ...

    Regression test

    def test_post_rejects_unexpected_body(client):
        res = client.post("/login/access-token", json={"unexpected": True})
        assert res.status_code == 422

    Intended? If validation and rate limiting happen upstream (an API gateway, Vercel Firewall), turn the rule off for the repo: { "policy": { "rules": { "public-entry-guarded": "off" } } } in wyro.json.

    public-entry-guarded

  • Mediumlow confidencePOST /password-recovery/{email} has no validator or rate limiter attached.

    POST /password-recovery/{email} accepts any body at any rate. Malformed input reaches your handler, and nothing stops a script calling it in a loop.

    Missing
    Schema validation of the request body, and a rate limit.
    Confidence
    Inline validation (zod.parse, pydantic models) and platform rate limits are not always visible to the parser. Check before acting.

    Prove it

    curl -i -X POST 'https://YOUR_API/password-recovery/1' \
      -H 'content-type: application/json' -d '{"unexpected": {"nested": [1,2,3]}}'
    # Expect 422. A 2xx or 500 means the body is not validated.

    Fix

    class Body(BaseModel):
        name: constr(max_length=200)
    
    @app.post("/password-recovery/{email}")
    def handler(body: Body): ...

    Regression test

    def test_post_rejects_unexpected_body(client):
        res = client.post("/password-recovery/1", json={"unexpected": True})
        assert res.status_code == 422

    Intended? If validation and rate limiting happen upstream (an API gateway, Vercel Firewall), turn the rule off for the repo: { "policy": { "rules": { "public-entry-guarded": "off" } } } in wyro.json.

    public-entry-guarded

  • Lowlow confidenceGET /utils/health-check has no validator or rate limiter attached.

    GET /utils/health-check can be called at any rate. Cheap to fix, rarely urgent on a read.

    Missing
    A rate limit.
    Confidence
    Inline validation (zod.parse, pydantic models) and platform rate limits are not always visible to the parser. Check before acting.

    Fix

    from slowapi import Limiter
    from slowapi.util import get_remote_address
    
    limiter = Limiter(key_func=get_remote_address)
    
    @app.get("/utils/health-check")
    @limiter.limit("60/minute")
    def handler(request: Request): ...

    Intended? If validation and rate limiting happen upstream (an API gateway, Vercel Firewall), turn the rule off for the repo: { "policy": { "rules": { "public-entry-guarded": "off" } } } in wyro.json.

    public-entry-guarded

Free account, no card. The repository opens as an editable graph.

Add this check to the README

wyro architecture badge
[![wyro architecture](https://wyro.in/api/badge/fastapi/full-stack-fastapi-template?path=backend)](https://wyro.in/scan/fastapi/full-stack-fastapi-template?path=backend)

It updates itself whenever the repository changes and links back to this report.

What this is

Wyro reads the repository’s routes and data models and checks the paths between them: whether a route can reach a table without passing a guard, whether a datastore holding personal data is exposed to a public read, whether an endpoint that issues credentials requires the credentials it issues.

It is rule-based, not a model. The same commit produces the same result every time, and it does not guess at business rules it cannot see. A rule with nothing to look at is reported as not having run — never as a pass.

This check runs on public source through GitHub’s own API and needs no account. A free account adds the editable architecture canvas, private repositories on paid plans, and a CI gate. No card required.

Maintain this repository? You can have this report removed, and a real vulnerability is disclosed to you privately before it is published. How public reports are handled.

fastapi/full-stack-fastapi-template/backend — 4 architecture findings | Wyro