VNX-306: Missing Authentication for Critical Function
A Python (Flask, FastAPI, Quart) route with an administrative path registered with no authentication decorator, dependency or check. The same defect in JavaScript, TypeScript and Go is VNX-425.
| Field | Value |
|---|---|
| Rule ID | VNX-306 |
| Severity | High |
| SARIF level | error |
| Kind | sast |
| Languages | Python |
| CWE | CWE-306 |
| CAPEC | CAPEC-66 |
| ATT&CK | T1190 |
| CWSS vector | CWSS:1.0/TI:H/AP:A/AL:A/IC:N/FC:LT/RP:N/RL:A/AV:I/AS:N/IN:A/SC:A/BI:H/DI:H/EX:H/EC:N/P:H |
| Tags | missing-auth critical-function cwe-306 |
| Source | internal/sast/rules/vnx-306.rego |
Overview
VNX-306 maps to CWE-306: Missing Authentication for Critical Function. It reports an administrative Python route registered with nothing in front of it. A path is administrative when one of its segments is admin, administrator(s), administration, adminpanel/adminarea/adminconsole/admindashboard/adminapi, backoffice, superuser, sysadmin, staff or manage/management.
Reported:
- a Flask, Quart or FastAPI route decorator (
@app.route,@bp.get/post/put/patch/delete,@router.api_route,@app.websocket, also with the path on the line after an open decorator) when none of the other decorators is an auth decorator (login_required,*_required,roles_*, a name with auth, login, admin, role, permission, jwt, token, protect or guard), the route has nodependencies=, the function takes noSecurity()parameter and noDepends()on an auth-named dependency, and the first 15 lines of the view never test the user (current_user,g.user,session[...],request.authorization,is_admin,abort(401/403),HTTPException(401/403)), in a file with nobefore_requesthook and noAPIRouter(dependencies=...); app.add_url_rule("/admin/...", view_func=f)wherefis defined in the same file with no such decorator or check.
Why This Matters
Admin routes delete users, change configuration and reset data. Without authentication they are reachable by anyone who guesses or finds the path, and admin paths are among the first that scanners try.
What Gets Flagged
# FLAGGED
@app.route("/admin/users/<int:user_id>/delete", methods=["POST"])
def delete_user(user_id):
...
# FLAGGED: Depends(get_db) opens a session, it does not authenticate
@router.delete("/api/admin/tenants/{tenant_id}")
async def drop_tenant(tenant_id: str, db=Depends(get_db)):
...
What Is Not Flagged
@app.route("/admin/users")followed by@login_required,async def drop_tenant(..., admin=Depends(require_admin)),dependencies=[Depends(require_admin)].- A view that checks the user in its first lines, and files with a
before_requesthook or router-wide dependencies. - The pages an administrator uses before logging in (
/admin/login,/admin/logout,/admin/forgot-password,/admin/static,/admin/health). - Django
admin.site.urls(the Django admin enforces staff login), non-administrative paths, test files and comments.
Reported by other rules, so one route is one finding:
- An administrative Express, Koa, Fastify or Go route with nothing in front of it is reported by VNX-425.
- A Spring or JAX-RS handler that
@PermitAllopens to everyone is reported by VNX-935.
Remediation
- Put an authentication and role check in front of every admin route: a decorator or a FastAPI dependency.
- Prefer protecting the whole admin prefix in one place (a blueprint
before_request, a router withdependencies=[...]) so a new route cannot be added unprotected.
References
Suppressing this rule
Put a nosec comment on the line, or on line 1 to cover the whole file. Use the comment marker of the file’s language (#, //, -- or ;):
# nosec vnx-306
// nosec vnx-306
Or record an ignore rule: vulnetix ignore add --rule vnx-306 --reason "...". See Suppressing findings.