VNX-862: Missing Authorization on a destructive or administrative handler
A Python route, Django view or Graphene resolver deletes records or serves an /admin path with no authorization decorator, dependency, in-handler check or project-wide guard.
| Field | Value |
|---|---|
| Rule ID | VNX-862 |
| Severity | High |
| SARIF level | error |
| Kind | sast |
| Languages | Python |
| CWE | CWE-862 |
| 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-authorization cwe-862 access-control |
| Source | internal/sast/rules/vnx-862.rego |
Overview
VNX-862 maps to CWE-862: Missing Authorization. It reports, on the def line, a Python request handler that deletes data or serves an /admin path with no authorization anywhere on its path.
Handlers understood: Flask/Quart/FastAPI/Sanic routes (@app.route, @bp.delete, @router.post …), Django function views in views/api modules (def view(request, ...)), and Graphene resolvers (def mutate(self, info, ...), def resolve_x(self, info, ...)). Sensitive means the body deletes records (ORM .delete(), db.session.delete(obj), delete_one/delete_many, SQL DELETE FROM/DROP TABLE/TRUNCATE) or the route path is under /admin.
Authorization that clears a handler: a decorator (login_required, roles_required, permission_required, jwt_required, user_passes_test, admin_required …), a FastAPI Depends/Security dependency that resolves a user, token, admin or permission, a check in the body (current_user, request.user, g.user, info.context.user, is_authenticated, has_perm, abort(403), PermissionDenied), a before_request hook that rejects unauthenticated requests anywhere in the project, a Django urls.py wrapping the view in login_required, LoginRequiredMiddleware, DRF DEFAULT_PERMISSION_CLASSES, or a Graphene auth middleware.
Not reported: routes on a FastAPI router or app built with dependencies=[Depends(...auth...)], or included with one by include_router; read-only handlers outside /admin, functions that are not handlers (model methods, CLI commands), test files, comments and strings. Authorization enforced by a reverse proxy, or by a decorator whose name does not say what it checks, is invisible to the rule.
Why This Matters
A delete endpoint without an authorization check lets anyone who can reach the URL destroy data: no stolen credential is needed, only the route. The same holds for admin pages and GraphQL mutations, which are often discovered through introspection.
What Gets Flagged
@app.route("/admin/users/<int:uid>/delete", methods=["POST"])
def delete_user(uid): # FLAGGED
User.query.filter_by(id=uid).delete()
@router.delete("/items/{item_id}")
async def delete_item(item_id: int, db: Session = Depends(get_db)): # FLAGGED
db.query(Item).filter(Item.id == item_id).delete()
class DeleteNote(graphene.Mutation):
def mutate(self, info, id): # FLAGGED
Note.query.filter_by(id=id).delete()
Not flagged: the same handler with @login_required, admin: User = Depends(get_current_admin), if post.author_id != current_user.id: abort(403), or a blueprint before_request that aborts with 403.
Remediation
- Put an authorization decorator or dependency on every handler that changes or deletes data.
- Check that the caller owns the record, or holds the required permission, before deleting it.
- Prefer a deny-by-default guard (a
before_requesthook,LoginRequiredMiddleware, DRF default permission classes) so a new route cannot be forgotten.
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-862
// nosec vnx-862
Or record an ignore rule: vulnetix ignore add --rule vnx-862 --reason "...". See Suppressing findings.