@riocrypto/common-server 1.0.2710 → 1.0.2712
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
|
@@ -8,27 +8,29 @@ const axios_1 = __importDefault(require("axios"));
|
|
|
8
8
|
const logger_1 = __importDefault(require("../services/logger"));
|
|
9
9
|
function buildAxiosWithLogging() {
|
|
10
10
|
const axiosWithLogging = axios_1.default.create();
|
|
11
|
-
const
|
|
12
|
-
"
|
|
13
|
-
"
|
|
14
|
-
"
|
|
15
|
-
"
|
|
16
|
-
"
|
|
17
|
-
"
|
|
18
|
-
"
|
|
19
|
-
"
|
|
20
|
-
"
|
|
11
|
+
const sensitiveSubstrings = [
|
|
12
|
+
"key",
|
|
13
|
+
"token",
|
|
14
|
+
"secret",
|
|
15
|
+
"auth",
|
|
16
|
+
"sign",
|
|
17
|
+
"password",
|
|
18
|
+
"private",
|
|
19
|
+
"credential",
|
|
20
|
+
"passphrase",
|
|
21
|
+
"csrf",
|
|
21
22
|
];
|
|
22
23
|
function maskHeaderValue(value) {
|
|
23
|
-
return value.length >
|
|
24
|
-
? "*".repeat(
|
|
25
|
-
:
|
|
24
|
+
return value.length > 2
|
|
25
|
+
? "*".repeat(12) + value.slice(-2)
|
|
26
|
+
: "************";
|
|
26
27
|
}
|
|
27
|
-
// Masking function
|
|
28
|
+
// Masking function - masks any header containing a sensitive substring
|
|
28
29
|
function maskHeaders(headers) {
|
|
29
30
|
const maskedHeaders = Object.assign({}, headers);
|
|
30
31
|
Object.keys(maskedHeaders).forEach((header) => {
|
|
31
|
-
|
|
32
|
+
const lower = header.toLowerCase();
|
|
33
|
+
if (sensitiveSubstrings.some((sub) => lower.includes(sub))) {
|
|
32
34
|
maskedHeaders[header] = maskHeaderValue(maskedHeaders[header]);
|
|
33
35
|
}
|
|
34
36
|
});
|
|
@@ -2,10 +2,18 @@ import { Logger } from "winston";
|
|
|
2
2
|
declare class LoggerService {
|
|
3
3
|
private logger;
|
|
4
4
|
constructor();
|
|
5
|
+
private sensitiveSubstrings;
|
|
6
|
+
private isSensitiveKey;
|
|
5
7
|
private maskValue;
|
|
8
|
+
private deepMaskSensitiveKeys;
|
|
6
9
|
private maskSensitiveData;
|
|
7
10
|
private requestMethodFilter;
|
|
8
11
|
private handleCustomErrorFormat;
|
|
12
|
+
private sanitizeArg;
|
|
13
|
+
private sensitiveStringPatterns;
|
|
14
|
+
private maskSensitiveString;
|
|
15
|
+
private setupConsoleMasking;
|
|
16
|
+
private setupStreamMasking;
|
|
9
17
|
getLogger(): Logger;
|
|
10
18
|
initLogger(): Promise<void>;
|
|
11
19
|
}
|
package/build/services/logger.js
CHANGED
|
@@ -8,26 +8,44 @@ var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, ge
|
|
|
8
8
|
step((generator = generator.apply(thisArg, _arguments || [])).next());
|
|
9
9
|
});
|
|
10
10
|
};
|
|
11
|
+
var __rest = (this && this.__rest) || function (s, e) {
|
|
12
|
+
var t = {};
|
|
13
|
+
for (var p in s) if (Object.prototype.hasOwnProperty.call(s, p) && e.indexOf(p) < 0)
|
|
14
|
+
t[p] = s[p];
|
|
15
|
+
if (s != null && typeof Object.getOwnPropertySymbols === "function")
|
|
16
|
+
for (var i = 0, p = Object.getOwnPropertySymbols(s); i < p.length; i++) {
|
|
17
|
+
if (e.indexOf(p[i]) < 0 && Object.prototype.propertyIsEnumerable.call(s, p[i]))
|
|
18
|
+
t[p[i]] = s[p[i]];
|
|
19
|
+
}
|
|
20
|
+
return t;
|
|
21
|
+
};
|
|
11
22
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
12
23
|
const common_1 = require("@riocrypto/common");
|
|
13
24
|
const winston_1 = require("winston");
|
|
14
25
|
class LoggerService {
|
|
15
26
|
constructor() {
|
|
27
|
+
this.sensitiveSubstrings = [
|
|
28
|
+
"key",
|
|
29
|
+
"token",
|
|
30
|
+
"secret",
|
|
31
|
+
"auth",
|
|
32
|
+
"sign",
|
|
33
|
+
"password",
|
|
34
|
+
"private",
|
|
35
|
+
"credential",
|
|
36
|
+
"passphrase",
|
|
37
|
+
"csrf",
|
|
38
|
+
];
|
|
16
39
|
this.maskSensitiveData = (0, winston_1.format)((info) => {
|
|
17
40
|
var _a, _b, _c, _d, _e;
|
|
18
41
|
// Mask sensitive headers
|
|
19
42
|
if ((_b = (_a = info.meta) === null || _a === void 0 ? void 0 : _a.req) === null || _b === void 0 ? void 0 : _b.headers) {
|
|
20
43
|
// Use optional chaining
|
|
21
44
|
const headers = info.meta.req.headers;
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
];
|
|
27
|
-
headerKeysToMask.forEach((key) => {
|
|
28
|
-
if (headers[key]) {
|
|
29
|
-
// Type assertion might be needed if headers[key] is not strictly string
|
|
30
|
-
headers[key] = this.maskValue(String(headers[key]));
|
|
45
|
+
// Mask any header containing a sensitive substring
|
|
46
|
+
Object.keys(headers).forEach((headerKey) => {
|
|
47
|
+
if (this.isSensitiveKey(headerKey)) {
|
|
48
|
+
headers[headerKey] = this.maskValue(String(headers[headerKey]));
|
|
31
49
|
}
|
|
32
50
|
});
|
|
33
51
|
// Mask cookies specifically
|
|
@@ -144,22 +162,131 @@ class LoggerService {
|
|
|
144
162
|
if (typeof info.message === "object") {
|
|
145
163
|
info.message = error.message;
|
|
146
164
|
}
|
|
165
|
+
// Deep mask the error object to catch SDK errors with embedded configs/headers
|
|
147
166
|
if ((_c = info.meta) === null || _c === void 0 ? void 0 : _c.err) {
|
|
148
|
-
|
|
149
|
-
delete info.meta.err;
|
|
167
|
+
info.meta.err = this.deepMaskSensitiveKeys(info.meta.err);
|
|
150
168
|
}
|
|
151
169
|
}
|
|
170
|
+
// Deep mask the entire meta object to catch any remaining sensitive data
|
|
171
|
+
if (info.meta) {
|
|
172
|
+
const _d = info.meta, { req, res } = _d, rest = __rest(_d, ["req", "res"]);
|
|
173
|
+
// req/res are already handled above, deep mask everything else (error objects, SDK responses, etc.)
|
|
174
|
+
const masked = this.deepMaskSensitiveKeys(rest);
|
|
175
|
+
info.meta = Object.assign(Object.assign({}, info.meta), masked);
|
|
176
|
+
}
|
|
152
177
|
return info;
|
|
153
178
|
});
|
|
179
|
+
// Pre-compiled regex patterns for string masking
|
|
180
|
+
this.sensitiveStringPatterns = [
|
|
181
|
+
// Quoted keys: "Authorization":"value" or "api-key": "value"
|
|
182
|
+
new RegExp(`("(?:[^"]*(?:${this.sensitiveSubstrings.join("|")})[^"]*)"\\s*[:=]\\s*"?)([^",}\\s]+)`, "gi"),
|
|
183
|
+
// Unquoted keys: Authorization: value, apiKey=value, api_key: value
|
|
184
|
+
new RegExp(`((?:^|[\\s,;{])(?:[\\w-]*(?:${this.sensitiveSubstrings.join("|")})[\\w-]*)\\s*[:=]\\s*"?)([^",}\\s;]+)`, "gi"),
|
|
185
|
+
// Bearer tokens: Bearer <token>
|
|
186
|
+
/(\bBearer\s+)([^\s,;"]+)/gi,
|
|
187
|
+
];
|
|
154
188
|
this.logger = null;
|
|
189
|
+
this.setupConsoleMasking();
|
|
190
|
+
this.setupStreamMasking();
|
|
191
|
+
}
|
|
192
|
+
isSensitiveKey(key) {
|
|
193
|
+
const lower = key.toLowerCase();
|
|
194
|
+
return this.sensitiveSubstrings.some((sub) => lower.includes(sub));
|
|
155
195
|
}
|
|
156
196
|
maskValue(value, showLastFour = false) {
|
|
157
197
|
if (showLastFour) {
|
|
158
|
-
return value.length > 4 ? "*".repeat(12) + value.slice(-4) :
|
|
198
|
+
return value.length > 4 ? "*".repeat(12) + value.slice(-4) : "************";
|
|
199
|
+
}
|
|
200
|
+
return value.length > 2 ? "*".repeat(12) + value.slice(-2) : "************";
|
|
201
|
+
}
|
|
202
|
+
deepMaskSensitiveKeys(obj, seen = new WeakSet()) {
|
|
203
|
+
if (obj === null || obj === undefined)
|
|
204
|
+
return obj;
|
|
205
|
+
if (typeof obj !== "object")
|
|
206
|
+
return obj;
|
|
207
|
+
if (seen.has(obj))
|
|
208
|
+
return "[Circular]";
|
|
209
|
+
seen.add(obj);
|
|
210
|
+
if (Array.isArray(obj)) {
|
|
211
|
+
return obj.map((item) => this.deepMaskSensitiveKeys(item, seen));
|
|
212
|
+
}
|
|
213
|
+
const result = {};
|
|
214
|
+
for (const key of Object.keys(obj)) {
|
|
215
|
+
const value = obj[key];
|
|
216
|
+
if (this.isSensitiveKey(key) && typeof value === "string") {
|
|
217
|
+
result[key] = this.maskValue(value);
|
|
218
|
+
}
|
|
219
|
+
else if (typeof value === "object" && value !== null) {
|
|
220
|
+
result[key] = this.deepMaskSensitiveKeys(value, seen);
|
|
221
|
+
}
|
|
222
|
+
else {
|
|
223
|
+
result[key] = value;
|
|
224
|
+
}
|
|
225
|
+
}
|
|
226
|
+
return result;
|
|
227
|
+
}
|
|
228
|
+
sanitizeArg(arg) {
|
|
229
|
+
if (arg === null || arg === undefined)
|
|
230
|
+
return arg;
|
|
231
|
+
if (typeof arg === "string") {
|
|
232
|
+
return this.maskSensitiveString(arg);
|
|
159
233
|
}
|
|
160
|
-
|
|
161
|
-
|
|
234
|
+
if (arg instanceof Error) {
|
|
235
|
+
const masked = this.deepMaskSensitiveKeys(Object.assign(Object.assign({}, arg), { message: arg.message, stack: arg.stack }));
|
|
236
|
+
const sanitizedError = new Error(this.maskSensitiveString(masked.message));
|
|
237
|
+
sanitizedError.stack = this.maskSensitiveString(masked.stack || "");
|
|
238
|
+
Object.assign(sanitizedError, masked);
|
|
239
|
+
return sanitizedError;
|
|
240
|
+
}
|
|
241
|
+
if (typeof arg === "object") {
|
|
242
|
+
return this.deepMaskSensitiveKeys(arg);
|
|
243
|
+
}
|
|
244
|
+
return arg;
|
|
245
|
+
}
|
|
246
|
+
maskSensitiveString(str) {
|
|
247
|
+
let result = str;
|
|
248
|
+
for (const pattern of this.sensitiveStringPatterns) {
|
|
249
|
+
pattern.lastIndex = 0;
|
|
250
|
+
result = result.replace(pattern, (_match, prefix, value) => {
|
|
251
|
+
if (value.length > 2) {
|
|
252
|
+
return `${prefix}************${value.slice(-2)}`;
|
|
253
|
+
}
|
|
254
|
+
return `${prefix}************`;
|
|
255
|
+
});
|
|
162
256
|
}
|
|
257
|
+
return result;
|
|
258
|
+
}
|
|
259
|
+
setupConsoleMasking() {
|
|
260
|
+
const originalConsole = {
|
|
261
|
+
log: console.log.bind(console),
|
|
262
|
+
error: console.error.bind(console),
|
|
263
|
+
warn: console.warn.bind(console),
|
|
264
|
+
info: console.info.bind(console),
|
|
265
|
+
};
|
|
266
|
+
const sanitize = (...args) => args.map((arg) => this.sanitizeArg(arg));
|
|
267
|
+
console.log = (...args) => originalConsole.log(...sanitize(...args));
|
|
268
|
+
console.error = (...args) => originalConsole.error(...sanitize(...args));
|
|
269
|
+
console.warn = (...args) => originalConsole.warn(...sanitize(...args));
|
|
270
|
+
console.info = (...args) => originalConsole.info(...sanitize(...args));
|
|
271
|
+
}
|
|
272
|
+
setupStreamMasking() {
|
|
273
|
+
const originalStdoutWrite = process.stdout.write.bind(process.stdout);
|
|
274
|
+
const originalStderrWrite = process.stderr.write.bind(process.stderr);
|
|
275
|
+
const maskChunk = (chunk) => {
|
|
276
|
+
if (typeof chunk === "string") {
|
|
277
|
+
return this.maskSensitiveString(chunk);
|
|
278
|
+
}
|
|
279
|
+
if (Buffer.isBuffer(chunk)) {
|
|
280
|
+
return Buffer.from(this.maskSensitiveString(chunk.toString("utf8")));
|
|
281
|
+
}
|
|
282
|
+
return chunk;
|
|
283
|
+
};
|
|
284
|
+
process.stdout.write = (chunk, ...args) => {
|
|
285
|
+
return originalStdoutWrite(maskChunk(chunk), ...args);
|
|
286
|
+
};
|
|
287
|
+
process.stderr.write = (chunk, ...args) => {
|
|
288
|
+
return originalStderrWrite(maskChunk(chunk), ...args);
|
|
289
|
+
};
|
|
163
290
|
}
|
|
164
291
|
getLogger() {
|
|
165
292
|
if (!this.logger) {
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@riocrypto/common-server",
|
|
3
|
-
"version": "1.0.
|
|
3
|
+
"version": "1.0.2712",
|
|
4
4
|
"description": "",
|
|
5
5
|
"main": "./build/index.js",
|
|
6
6
|
"types": "./build/index.d.ts",
|
|
@@ -28,7 +28,7 @@
|
|
|
28
28
|
"@google-cloud/secret-manager": "^5.3.0",
|
|
29
29
|
"@google-cloud/storage": "^6.9.5",
|
|
30
30
|
"@hyperdx/node-opentelemetry": "^0.7.0",
|
|
31
|
-
"@riocrypto/common": "^1.0.
|
|
31
|
+
"@riocrypto/common": "^1.0.2510",
|
|
32
32
|
"@types/express": "^4.17.13",
|
|
33
33
|
"axios": "^1.7.4",
|
|
34
34
|
"crypto-js": "^4.2.0",
|