@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 sensitiveHeaders = [
12
- "Authorization",
13
- "x-api-key",
14
- "Api-Key",
15
- "x-cluster-api-key",
16
- "X-API-KEY",
17
- "CB-ACCESS-KEY",
18
- "CB-ACCESS-SIGN",
19
- "CB-ACCESS-TIMESTAMP",
20
- "CB-ACCESS-PASSPHRASE",
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 > 4
24
- ? "*".repeat(value.length - 4) + value.slice(-4)
25
- : value;
24
+ return value.length > 2
25
+ ? "*".repeat(12) + value.slice(-2)
26
+ : "************";
26
27
  }
27
- // Masking function to selectively mask headers
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
- if (sensitiveHeaders.includes(header)) {
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
  }
@@ -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
- const headerKeysToMask = [
23
- "x-api-key",
24
- "x-admin-api-key",
25
- "x-cluster-api-key",
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
- // Use optional chaining
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) : value;
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
- else {
161
- return "************";
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.2710",
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.2509",
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",