VNX-209: Generation of Error Message Containing Sensitive Information
Detects stack traces and database error messages sent to the client: traces in Python, Java, JavaScript/TypeScript, Go and C# responses, PHP die/echo of mysqli_error and friends, caught database exception messages in Python, Java and C# output, and configuration that turns detailed error pages on (Spring include-stacktrace, PHP display_errors, ASP.NET customErrors Off).
| Field | Value |
|---|---|
| Rule ID | VNX-209 |
| Severity | Medium |
| SARIF level | warning |
| Kind | sast |
| Languages | Java, JavaScript / TypeScript, Python, PHP, Go, C# |
| CWE | CWE-209 |
| CAPEC | CAPEC-66 |
| ATT&CK | T1190 |
| CWSS vector | CWSS:1.0/TI:M/AP:RU/AL:S/IC:N/FC:LT/RP:RU/RL:S/AV:L/AS:N/IN:A/SC:A/BI:M/DI:H/EX:M/EC:N/P:C |
| Tags | error-handling stack-trace information-exposure cwe-209 |
| Source | internal/sast/rules/vnx-209.rego |
Overview
VNX-209 maps to CWE-209: Generation of Error Message Containing Sensitive Information. A stack trace tells an attacker the file layout, framework and library versions and the code path behind a failure; a database error message gives away SQL text, table and column names and the driver in use, which is exactly what is needed to shape an injection.
The rule reports the trace or message reaching the client, not its existence:
| Language | Reported |
|---|---|
| Python | traceback.format_exc(), format_exception(), format_tb(), format_stack() passed to jsonify, make_response, HttpResponse, JsonResponse, JSONResponse, Response, render_template, abort, self.write (directly, or through a variable or dict assigned above); returned bare, or stored in an error dict that is returned, in a module that imports a web framework; traceback.print_exc(file=self.wfile); str(e) of a caught sqlite3.Error, psycopg2 error, DatabaseError, OperationalError, IntegrityError or SQLAlchemyError in a response |
| Java | e.printStackTrace(response.getWriter()) or into a writer taken from the response where e is not the exception of the enclosing catch block (a parameter, a field); e.printStackTrace(new PrintWriter(sw)) then sw.toString() in a body or a controller return; ExceptionUtils.getStackTrace(e) in a return, body or writer; the getMessage() of a caught SQLException/DataAccessException/PSQLException/PersistenceException written to output or returned. A caught exception written to the servlet response from inside its catch block is reported by VNX-536, not here |
| JavaScript, TypeScript | err.stack inside res.send/json/end/write/render (also a res / .status() / .json() chain split over lines), ctx.body or reply.send, also in a template literal, an object, or through a variable |
| PHP | die/exit/echo/print of mysqli_error(), mysqli_connect_error(), mysql_error(), pg_last_error(), $db->error, $db->connect_error, getTraceAsString() or a caught PDOException message; ini_set('display_errors', 1); display_errors = On in php.ini/.htaccess |
| Go | debug.Stack(), or the buffer filled by runtime.Stack(buf, ...), written to the http.ResponseWriter or a gin context |
| C# | ex.StackTrace or ex.ToString() returned by Content/Ok/StatusCode/Problem or Response.Write; the Message of a caught SqlException/SQLiteException/DbException returned the same way or assigned to a WebForms control Text |
| Config | Spring Boot server.error.include-stacktrace=always, include-exception=true; ASP.NET <customErrors mode="Off">, <httpErrors errorMode="Detailed"> |
What Gets Flagged
return jsonify({"error": "failed", "trace": traceback.format_exc()}), 500 # FLAGGED
except sqlite3.Error as exc:
return jsonify({"error": str(exc)}), 500 # FLAGGED
res.status(500).send(err.stack); // FLAGGED
n := runtime.Stack(buf, false)
w.Write(buf[:n]) // FLAGGED
return ResponseEntity.status(500).body(ExceptionUtils.getStackTrace(e)); // FLAGGED
} catch (SQLException sqle) {
return failed(this).output(sqle.getMessage()).build(); // FLAGGED
$result = mysqli_query($conn, $query) or die('<pre>' . mysqli_error($conn) . '</pre>'); // FLAGGED
server.error.include-stacktrace=always # FLAGGED
What Is Not Flagged
- Traces and messages written to a logger,
stderr,error_log()orconsole.error, with a generic message returned to the client. traceback.print_exc()ande.printStackTrace()with no argument (they write to stderr).- Messages of exceptions other than database ones (
IllegalArgumentException.getMessage(),lblError.Text = ex.Messageon a genericException). - A trace returned from a helper in a module that imports no web framework (a crash file, a
logging.Formatter.formatException). - A stack selected only outside production:
stack: process.env.NODE_ENV === 'production' ? undefined : err.stack. - A trace or message that reaches the response through a helper function or another file.
- A caught exception written to the servlet response inside its catch block (
catch (Exception e) { e.printStackTrace(response.getWriter()); },out.println(sqle.getMessage())): reported by VNX-536, the servlet form of this weakness. display_errorsset to0/Off,include-stacktrace=neveroron_param,customErrors mode="RemoteOnly".- The patterns in comments or string literals, and test code.
Remediation
Log the full error server-side and return a generic message with a correlation id.
except Exception:
log.exception("order lookup failed", extra={"request_id": rid})
return jsonify({"error": "internal error", "request_id": rid}), 500
References
- CWE-209: Generation of Error Message Containing Sensitive Information
- OWASP: Improper Error Handling
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-209
// nosec vnx-209
Or record an ignore rule: vulnetix ignore add --rule vnx-209 --reason "...". See Suppressing findings.