@riocrypto/common-server 1.0.2901 → 1.0.2903
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.
- package/build/services/logger.d.ts +4 -0
- package/build/services/logger.js +43 -22
- package/package.json +1 -1
|
@@ -7,7 +7,11 @@ declare class LoggerService {
|
|
|
7
7
|
private normalizeKey;
|
|
8
8
|
private fullyRedactedBodyFields;
|
|
9
9
|
private lastFourBodyFields;
|
|
10
|
+
private clearTextExactBodyFields;
|
|
10
11
|
private attributionBodyFields;
|
|
12
|
+
private bodySensitiveSubstrings;
|
|
13
|
+
private bodyCredentialFields;
|
|
14
|
+
private isSensitiveBodyKey;
|
|
11
15
|
private redactBodyFields;
|
|
12
16
|
redactLogBody: (source: any, propName: string) => any;
|
|
13
17
|
private maskValue;
|
package/build/services/logger.js
CHANGED
|
@@ -69,31 +69,26 @@ class LoggerService {
|
|
|
69
69
|
"postalCode",
|
|
70
70
|
"DireccionBeneficiario",
|
|
71
71
|
].map((field) => this.normalizeKey(field)));
|
|
72
|
-
//
|
|
73
|
-
//
|
|
72
|
+
// Pull-capable account identifiers, truncated to the last four. An ACH debit
|
|
73
|
+
// can be initiated from a routing and account number pair, and a SEPA direct
|
|
74
|
+
// debit from an IBAN, so these are closer to a credential than to an address.
|
|
75
|
+
//
|
|
76
|
+
// CLABE and CCI are deliberately absent. SPEI and CCE are credit-push only,
|
|
77
|
+
// so a destination account cannot be drained by anyone who reads it, and the
|
|
78
|
+
// full value is the evidence that a settlement went where it was instructed
|
|
79
|
+
// to. Truncating it would cost real traceability to buy very little.
|
|
74
80
|
this.lastFourBodyFields = new Set([
|
|
75
|
-
"CLABE",
|
|
76
81
|
"IBAN",
|
|
77
82
|
"accountNumber",
|
|
78
83
|
"routingNumber",
|
|
79
|
-
"cuentaOrdenante",
|
|
80
|
-
"cuentaBeneficiario",
|
|
81
|
-
"cuentaBeneficiario2",
|
|
82
|
-
"cuentaBaaS",
|
|
83
|
-
"cuentaOrigen",
|
|
84
|
-
"originCLABE",
|
|
85
|
-
"destinationCLABE",
|
|
86
|
-
"senderCLABE",
|
|
87
|
-
"receiverCLABE",
|
|
88
84
|
"originAccountNumber",
|
|
89
|
-
"CCI",
|
|
90
|
-
"CCIV",
|
|
91
|
-
"CCI_Gene",
|
|
92
|
-
"CCIOrigen",
|
|
93
|
-
"CCIDestino",
|
|
94
|
-
"cciBeneficiario",
|
|
95
|
-
"originCCI",
|
|
96
85
|
].map((field) => this.normalizeKey(field)));
|
|
86
|
+
// Matched on literal spelling, not the normalised key, because the two
|
|
87
|
+
// spellings mean different things. Fintoc puts a CLABE in account_number
|
|
88
|
+
// (see createTransfer in fintoc-client), which is push-only and should stay
|
|
89
|
+
// readable, while bank-accounts uses accountNumber for US accounts, which is
|
|
90
|
+
// pull-capable and stays truncated above.
|
|
91
|
+
this.clearTextExactBodyFields = new Set(["account_number"]);
|
|
97
92
|
// Authenticated-identity attribution, logged deliberately as a security
|
|
98
93
|
// control. They contain "auth", so the credential substring rule would
|
|
99
94
|
// swallow them now that it walks bodies too.
|
|
@@ -106,6 +101,24 @@ class LoggerService {
|
|
|
106
101
|
"adminAuthEmail",
|
|
107
102
|
"authPhoneNumber",
|
|
108
103
|
].map((field) => this.normalizeKey(field)));
|
|
104
|
+
// sensitiveSubstrings is written for headers and config objects, where "key"
|
|
105
|
+
// means API key. In a request body it nearly always means identifier
|
|
106
|
+
// instead -- tracking_key, idempotencyKey, uniqueCompositeKey, tradeKey --
|
|
107
|
+
// and "sign" matches cosigner. Both are dropped here so the reconciliation
|
|
108
|
+
// layer stays readable, and the two real credentials they were catching are
|
|
109
|
+
// listed by name below. Headers, console output and stdout are untouched and
|
|
110
|
+
// keep the full list.
|
|
111
|
+
this.bodySensitiveSubstrings = [
|
|
112
|
+
"token",
|
|
113
|
+
"secret",
|
|
114
|
+
"auth",
|
|
115
|
+
"password",
|
|
116
|
+
"private",
|
|
117
|
+
"credential",
|
|
118
|
+
"passphrase",
|
|
119
|
+
"csrf",
|
|
120
|
+
];
|
|
121
|
+
this.bodyCredentialFields = new Set(["apiKey", "signature"].map((field) => this.normalizeKey(field)));
|
|
109
122
|
// express-winston filter for services that whitelist whole bodies. It hands
|
|
110
123
|
// over either the parsed object or the raw string depending on the content
|
|
111
124
|
// type it saw, and it calls this once per whitelisted field as well as once
|
|
@@ -279,6 +292,15 @@ class LoggerService {
|
|
|
279
292
|
normalizeKey(key) {
|
|
280
293
|
return key.toLowerCase().replace(/[_-]/g, "");
|
|
281
294
|
}
|
|
295
|
+
isSensitiveBodyKey(key) {
|
|
296
|
+
const normalized = this.normalizeKey(key);
|
|
297
|
+
if (this.attributionBodyFields.has(normalized))
|
|
298
|
+
return false;
|
|
299
|
+
if (this.bodyCredentialFields.has(normalized))
|
|
300
|
+
return true;
|
|
301
|
+
const lower = key.toLowerCase();
|
|
302
|
+
return this.bodySensitiveSubstrings.some((sub) => lower.includes(sub));
|
|
303
|
+
}
|
|
282
304
|
redactBodyFields(value, seen = new WeakSet(), depth = 0) {
|
|
283
305
|
if (depth > 8 || value === null || value === undefined)
|
|
284
306
|
return value;
|
|
@@ -303,12 +325,11 @@ class LoggerService {
|
|
|
303
325
|
result[key] = "[REDACTED]";
|
|
304
326
|
}
|
|
305
327
|
else if (this.lastFourBodyFields.has(normalized) &&
|
|
328
|
+
!this.clearTextExactBodyFields.has(key) &&
|
|
306
329
|
(typeof entry === "string" || typeof entry === "number")) {
|
|
307
330
|
result[key] = this.maskValue(String(entry), true);
|
|
308
331
|
}
|
|
309
|
-
else if (this.
|
|
310
|
-
!this.attributionBodyFields.has(normalized) &&
|
|
311
|
-
typeof entry === "string") {
|
|
332
|
+
else if (this.isSensitiveBodyKey(key) && typeof entry === "string") {
|
|
312
333
|
result[key] = this.maskValue(entry);
|
|
313
334
|
}
|
|
314
335
|
else if (typeof entry === "object" && entry !== null) {
|