VNX-694: Two route handlers registered under the same identifier
Reports a Flask/FastAPI route decorator or a Django path()/re_path()/url() entry that repeats a route already registered in the same file, so only one of the two handlers is ever dispatched.
| Field | Value |
|---|---|
| Rule ID | VNX-694 |
| Severity | Low |
| SARIF level | note |
| Kind | sast |
| Languages | Python |
| CWE | CWE-694 |
| CAPEC | CAPEC-66 |
| ATT&CK | T1190 |
| CWSS vector | CWSS:1.0/TI:L/AP:RU/AL:S/IC:N/FC:LT/RP:RU/RL:S/AV:L/AS:N/IN:A/SC:A/BI:L/DI:H/EX:L/EC:N/P:L |
| Tags | duplicate-identifier routing cwe-694 |
| Source | internal/sast/rules/vnx-694.rego |
Overview
VNX-694 maps to CWE-694: Use of Multiple Resources with Duplicate Identifier. A URL route is the identifier a web framework uses to pick a handler. When two handlers are registered under the same route, the dispatcher reaches only one of them; the other is dead code. The dangerous version is a second handler that adds an authorisation check and is never called.
The rule reports, at the second and later registration in a Python file:
- a Flask/FastAPI/Starlette route decorator (
@app.route,@bp.route,@router.get/post/put/patch/delete/head/options,@app.api_route,@app.websocket) on the same receiver with the same arguments (path, methods and options, with quote style and spacing normalised) as an earlier one; - a Django
path(),re_path()orurl()entry with the same route string as an earlier entry.
Not reported: the same path with different methods, the same path on different routers or blueprints, repeated include() prefixes, registrations in different files, test files, docstrings, comments and string literals. Decorators broken over several lines are not compared.
What Gets Flagged
@app.route("/admin/users")
def list_users():
return render_users()
@app.route("/admin/users") # FLAGGED: never dispatched
def list_users_checked():
if not session.get("is_admin"):
abort(403)
return render_users()
urlpatterns = [
path("orders/<int:pk>/", views.order_detail),
path("orders/<int:pk>/", views.order_detail_secure), # FLAGGED
]
What Is Not Flagged
@app.route("/login", methods=["GET"])
def login_form(): ...
@app.route("/login", methods=["POST"])
def login_submit(): ...
urlpatterns = [
path("api/", include("billing.urls")),
path("api/", include("accounts.urls")),
]
Remediation
- Delete the handler that is not meant to serve the route, or give each a distinct path or method.
- Keep authorisation in a decorator or middleware so a duplicate cannot silently drop it.
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-694
// nosec vnx-694
Or record an ignore rule: vulnetix ignore add --rule vnx-694 --reason "...". See Suppressing findings.