Skip to content

Commit 60af45c

Browse files
committed
ErrorMiddleware - Add sensitive data sanitization for error logging and response
1 parent da5bcf0 commit 60af45c

File tree

1 file changed

+42
-7
lines changed

1 file changed

+42
-7
lines changed

src/middleware/error.js

Lines changed: 42 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,37 @@
11
import {badRequest, HttpError} from "../utils/httpError.js";
22
import {env} from "../config/env.js";
33

4+
const sensitiveKeyPattern = /(authorization|cookie|token|session|secret|password|api[-_]?key|jwt)/i;
5+
6+
function sanitizeString(value) {
7+
let out = String(value || "");
8+
out = out.replace(/Bearer\s+[A-Za-z0-9\-._~+/]+=*/gi, "Bearer [REDACTED]");
9+
out = out.replace(/XBL3\.0\s+x=[^;]+;[^\s]+/gi, "XBL3.0 x=[REDACTED];[REDACTED]");
10+
out = out.replace(/(sessionticket|entitytoken|token|password|secret|api[_-]?key)=([^&\s]+)/gi, "$1=[REDACTED]");
11+
return out;
12+
}
13+
14+
function sanitizeValue(value, depth = 0) {
15+
if (value === null || value === undefined) return value;
16+
if (depth > 6) return "[TRUNCATED]";
17+
18+
const t = typeof value;
19+
if (t === "string") return sanitizeString(value);
20+
if (t === "number" || t === "boolean") return value;
21+
if (Array.isArray(value)) return value.map(v => sanitizeValue(v, depth + 1));
22+
if (t !== "object") return sanitizeString(value);
23+
24+
const out = {};
25+
for (const [key, v] of Object.entries(value)) {
26+
if (sensitiveKeyPattern.test(key)) {
27+
out[key] = "[REDACTED]";
28+
} else {
29+
out[key] = sanitizeValue(v, depth + 1);
30+
}
31+
}
32+
return out;
33+
}
34+
435
export function notFoundHandler(req, res, next) {
536
next(new HttpError(404, `Route ${req.method} ${req.originalUrl} not found`, undefined, "HTTP_404"));
637
}
@@ -11,17 +42,20 @@ export function errorHandler(err, req, res, next) {
1142
}
1243

1344
const status = err instanceof HttpError ? err.status : 500;
14-
const code = err.code || (err instanceof HttpError ? `HTTP_${status}` : "INTERNAL");
45+
const code = err instanceof HttpError ? (err.code || `HTTP_${status}`) : "INTERNAL";
46+
const safeMessage = status >= 500 ? "Internal Server Error" : sanitizeString(err.message || "Error");
47+
const safeDetails = sanitizeValue(err.details);
48+
const safeStack = err.stack ? sanitizeString(err.stack) : undefined;
1549

1650
const logPayload = {
1751
requestId: req.id,
1852
method: req.method,
1953
url: req.originalUrl,
2054
status,
2155
code,
22-
message: err.message,
23-
details: err.details,
24-
stack: err.stack
56+
message: sanitizeString(err.message || safeMessage),
57+
details: safeDetails,
58+
stack: safeStack
2559
};
2660

2761
try {
@@ -32,13 +66,14 @@ export function errorHandler(err, req, res, next) {
3266

3367
const body = {
3468
error: {
35-
code, message: err.message || "Internal Server Error"
69+
code,
70+
message: safeMessage
3671
}
3772
};
3873

3974
if (env.NODE_ENV !== "production") {
40-
if (err.details) body.error.details = err.details;
41-
if (err.stack) body.error.stack = err.stack;
75+
if (safeDetails !== undefined) body.error.details = safeDetails;
76+
if (safeStack) body.error.stack = safeStack;
4277
}
4378

4479
res.status(status).json(body);

0 commit comments

Comments
 (0)