VNX-392: Missing Report of Error Condition
A Python web handler catches an exception and returns an error body (jsonify, JsonResponse, HttpResponse, Response, Sanic json or a dict) without an error status, so the client receives 200 OK for a failed request.
| Field | Value |
|---|---|
| Rule ID | VNX-392 |
| Severity | Low |
| SARIF level | note |
| Kind | sast |
| Languages | Python |
| CWE | CWE-392 |
| 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 | error-handling cwe-392 |
| Source | internal/sast/rules/vnx-392.rego |
Overview
VNX-392 maps to CWE-392: Missing Report of Error Condition. It reports, inside an except block, a return of jsonify(...), JsonResponse(...), JSONResponse(...), HttpResponse(...), Response(...) or make_response(...), or of a dict literal in a Flask, FastAPI, Django or Starlette module, whose content says the request failed (error, fail, invalid, not found, denied, unauthorized, forbidden, exception, missing, or the exception itself) and which sets no error status: no status=/status_code= argument and no (body, 4xx) tuple, or one that explicitly says 200 (status=200, (body, 200), HTTPStatus.OK). A call broken across lines is read as one statement.
Not reported: a response that sets a status, abort() or raise, a re-rendered HTML template (a form shown again with its error), a dict returned from a module that is not a web handler, test files, comments and strings.
Why This Matters
Clients, proxies, retries, monitoring and caches decide success by the status code, not by reading the body. An error answered with 200 is cached as a good response, never retried, never alerted on, and treated as success by any client that does not parse the body. Writing 'status': '500' into the JSON does not change what is sent on the wire.
What Gets Flagged
try:
order.save()
except ValueError as e:
return jsonify({"error": str(e)}) # FLAGGED
except Cart.DoesNotExist:
return JsonResponse({'message': 'failed', 'status': '500'}) # FLAGGED
Not flagged:
except ValueError as e:
return jsonify(error=str(e)), 400
except OSError:
return JsonResponse({"error": "storage unavailable"}, status=503)
Remediation
- Return the matching 4xx/5xx status with the error body:
return jsonify(error=...), 400,JsonResponse(..., status=400). - Or raise an HTTP exception (
abort(404),HTTPException) and let the framework produce the response.
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-392
// nosec vnx-392
Or record an ignore rule: vulnetix ignore add --rule vnx-392 --reason "...". See Suppressing findings.