@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.
@@ -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;
@@ -69,31 +69,26 @@ class LoggerService {
69
69
  "postalCode",
70
70
  "DireccionBeneficiario",
71
71
  ].map((field) => this.normalizeKey(field)));
72
- // Account identifiers. Truncated rather than replaced so a deposit can
73
- // still be matched to an account from the log line alone.
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.isSensitiveKey(key) &&
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) {
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@riocrypto/common-server",
3
- "version": "1.0.2901",
3
+ "version": "1.0.2903",
4
4
  "description": "",
5
5
  "main": "./build/index.js",
6
6
  "types": "./build/index.d.ts",