@diia-inhouse/diia-logger 4.3.29 → 4.4.0
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/dist/config.js +30 -8
- package/dist/redactors/email.js +10 -0
- package/dist/redactors/phone.js +17 -0
- package/dist/trimmer.js +5 -2
- package/package.json +3 -3
- package/src/config.ts +12 -7
- package/src/interfaces/index.ts +3 -0
- package/src/redactors/email.ts +13 -0
- package/src/redactors/phone.ts +25 -0
- package/src/trimmer.ts +11 -5
package/dist/config.js
CHANGED
|
@@ -2,6 +2,14 @@ import lodash from "lodash";
|
|
|
2
2
|
import { LogLevel } from "@diia-inhouse/types";
|
|
3
3
|
//#region src/config.ts
|
|
4
4
|
const { merge } = lodash;
|
|
5
|
+
const freeTextFields = [
|
|
6
|
+
"value",
|
|
7
|
+
"text",
|
|
8
|
+
"textItems",
|
|
9
|
+
"label",
|
|
10
|
+
"shortText",
|
|
11
|
+
"accessibilityDescription"
|
|
12
|
+
];
|
|
5
13
|
const defaultOptions = {
|
|
6
14
|
logLevel: LogLevel.INFO,
|
|
7
15
|
maxArrayLength: 100,
|
|
@@ -28,7 +36,6 @@ const defaultOptions = {
|
|
|
28
36
|
"patronymic_name",
|
|
29
37
|
"passportSeries",
|
|
30
38
|
"passportNumber",
|
|
31
|
-
"email",
|
|
32
39
|
"addressOfRegistration",
|
|
33
40
|
"addressOfBirth",
|
|
34
41
|
"birthDay",
|
|
@@ -36,13 +43,10 @@ const defaultOptions = {
|
|
|
36
43
|
"date_birth",
|
|
37
44
|
"fio",
|
|
38
45
|
"passport",
|
|
39
|
-
"phone",
|
|
40
46
|
"address",
|
|
41
47
|
"birthplace",
|
|
42
48
|
"fullName",
|
|
43
49
|
"full_name",
|
|
44
|
-
"phoneNumber",
|
|
45
|
-
"extraPhoneNumber",
|
|
46
50
|
"refreshToken",
|
|
47
51
|
"token",
|
|
48
52
|
"fName",
|
|
@@ -96,11 +100,8 @@ const defaultOptions = {
|
|
|
96
100
|
"partnerFullName",
|
|
97
101
|
"residenceAddress",
|
|
98
102
|
"displayName",
|
|
99
|
-
"personalPhone",
|
|
100
103
|
"content",
|
|
101
104
|
"subjAddress",
|
|
102
|
-
"subjPhone",
|
|
103
|
-
"subjEmail",
|
|
104
105
|
"subjDrfoCode",
|
|
105
106
|
"oldLastName",
|
|
106
107
|
"oldFirstName",
|
|
@@ -140,6 +141,19 @@ const defaultOptions = {
|
|
|
140
141
|
"paymentPurpose",
|
|
141
142
|
"subject",
|
|
142
143
|
"value"
|
|
144
|
+
],
|
|
145
|
+
fieldsToRedactEmail: [
|
|
146
|
+
"email",
|
|
147
|
+
"subjEmail",
|
|
148
|
+
...freeTextFields
|
|
149
|
+
],
|
|
150
|
+
fieldsToRedactPhone: [
|
|
151
|
+
"phone",
|
|
152
|
+
"phoneNumber",
|
|
153
|
+
"extraPhoneNumber",
|
|
154
|
+
"personalPhone",
|
|
155
|
+
"subjPhone",
|
|
156
|
+
...freeTextFields
|
|
143
157
|
]
|
|
144
158
|
}
|
|
145
159
|
};
|
|
@@ -152,7 +166,15 @@ const toInternalLoggerOptions = (options = {}) => {
|
|
|
152
166
|
fields: new Set(redact.fields),
|
|
153
167
|
paths: new Set(redact.paths),
|
|
154
168
|
fieldsToRedactFullname: new Set(redact.fieldsToRedactFullname),
|
|
155
|
-
fieldsToRedactItn: new Set(redact.fieldsToRedactItn)
|
|
169
|
+
fieldsToRedactItn: new Set(redact.fieldsToRedactItn),
|
|
170
|
+
fieldsToRedactEmail: new Set(redact.fieldsToRedactEmail),
|
|
171
|
+
fieldsToRedactPhone: new Set(redact.fieldsToRedactPhone),
|
|
172
|
+
fieldsToScan: new Set([
|
|
173
|
+
...redact.fieldsToRedactFullname || [],
|
|
174
|
+
...redact.fieldsToRedactItn || [],
|
|
175
|
+
...redact.fieldsToRedactEmail || [],
|
|
176
|
+
...redact.fieldsToRedactPhone || []
|
|
177
|
+
])
|
|
156
178
|
}
|
|
157
179
|
};
|
|
158
180
|
};
|
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
//#region src/redactors/email.ts
|
|
2
|
+
const EMAIL_PATTERN = /([\w.%+-]+)@([\w.-]+\.[a-z]{2,})/gi;
|
|
3
|
+
function redactEmail(text) {
|
|
4
|
+
if (!text.includes("@")) return text;
|
|
5
|
+
return text.replace(EMAIL_PATTERN, (_match, local, domain) => {
|
|
6
|
+
return `${local.length >= 2 ? local[0] : ""}***@${domain}`;
|
|
7
|
+
});
|
|
8
|
+
}
|
|
9
|
+
//#endregion
|
|
10
|
+
export { redactEmail };
|
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
//#region src/redactors/phone.ts
|
|
2
|
+
const PHONE_CANDIDATE = /\+?\d[\d\s().-]{7,18}\d/g;
|
|
3
|
+
const nationalDigits = 10;
|
|
4
|
+
const internationalDigits = 12;
|
|
5
|
+
const subscriberDigits = 7;
|
|
6
|
+
function redactPhone(text) {
|
|
7
|
+
return text.replace(PHONE_CANDIDATE, (match) => maskPhone(match));
|
|
8
|
+
}
|
|
9
|
+
function maskPhone(match) {
|
|
10
|
+
const digits = match.replace(/\D/g, "");
|
|
11
|
+
const isNational = digits.length === nationalDigits && digits.startsWith("0");
|
|
12
|
+
const isInternational = digits.length === internationalDigits;
|
|
13
|
+
if (!isNational && !isInternational) return match;
|
|
14
|
+
return `${match.startsWith("+") ? "+" : ""}${digits.slice(0, -subscriberDigits)}${"*".repeat(subscriberDigits - 1)}${digits.slice(-1)}`;
|
|
15
|
+
}
|
|
16
|
+
//#endregion
|
|
17
|
+
export { redactPhone };
|
package/dist/trimmer.js
CHANGED
|
@@ -1,6 +1,8 @@
|
|
|
1
1
|
import { toInternalLoggerOptions } from "./config.js";
|
|
2
|
+
import { redactEmail } from "./redactors/email.js";
|
|
2
3
|
import { redactFullName } from "./redactors/fullName.js";
|
|
3
4
|
import { redactItn } from "./redactors/itn.js";
|
|
5
|
+
import { redactPhone } from "./redactors/phone.js";
|
|
4
6
|
import lodash from "lodash";
|
|
5
7
|
//#region src/trimmer.ts
|
|
6
8
|
const { isObject } = lodash;
|
|
@@ -32,8 +34,7 @@ function trimObject(opts, node, depth) {
|
|
|
32
34
|
}
|
|
33
35
|
output[key] = trimWalker(opts, value, depth + 1);
|
|
34
36
|
if (typeof value === "string" || Array.isArray(value)) {
|
|
35
|
-
if (opts.redact.
|
|
36
|
-
if (opts.redact.fieldsToRedactItn?.has(key)) output[key] = redactionWalker(opts, key, output[key]);
|
|
37
|
+
if (opts.redact.fieldsToScan.has(key)) output[key] = redactionWalker(opts, key, output[key]);
|
|
37
38
|
continue;
|
|
38
39
|
}
|
|
39
40
|
}
|
|
@@ -62,6 +63,8 @@ const redactionWalker = (opts, key, value) => {
|
|
|
62
63
|
if (typeof value !== "string") return value;
|
|
63
64
|
if (opts.redact.fieldsToRedactFullname?.has(key)) value = redactFullName(value);
|
|
64
65
|
if (opts.redact.fieldsToRedactItn?.has(key)) value = redactItn(value);
|
|
66
|
+
if (opts.redact.fieldsToRedactEmail?.has(key)) value = redactEmail(value);
|
|
67
|
+
if (opts.redact.fieldsToRedactPhone?.has(key)) value = redactPhone(value);
|
|
65
68
|
return value;
|
|
66
69
|
};
|
|
67
70
|
const trimmer = (opts) => {
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@diia-inhouse/diia-logger",
|
|
3
|
-
"version": "4.
|
|
3
|
+
"version": "4.4.0",
|
|
4
4
|
"type": "module",
|
|
5
5
|
"description": "Logger package",
|
|
6
6
|
"main": "dist/index.js",
|
|
@@ -44,8 +44,8 @@
|
|
|
44
44
|
"@diia-inhouse/configs": "7.0.1",
|
|
45
45
|
"@diia-inhouse/eslint-plugin": "1.9.8",
|
|
46
46
|
"@diia-inhouse/oxc-config": "1.11.0",
|
|
47
|
-
"@diia-inhouse/types": "14.
|
|
48
|
-
"@diia-inhouse/utils": "6.0.
|
|
47
|
+
"@diia-inhouse/types": "14.1.0",
|
|
48
|
+
"@diia-inhouse/utils": "6.0.37",
|
|
49
49
|
"@types/lodash": "4.17.24",
|
|
50
50
|
"@vitest/coverage-v8": "4.1.5",
|
|
51
51
|
"@vitest/ui": "4.1.5",
|
package/src/config.ts
CHANGED
|
@@ -7,6 +7,8 @@ import { InternalLoggerOptions } from './interfaces/index.js'
|
|
|
7
7
|
// oxlint-disable-next-line typescript/unbound-method
|
|
8
8
|
const { merge } = lodash
|
|
9
9
|
|
|
10
|
+
const freeTextFields = ['value', 'text', 'textItems', 'label', 'shortText', 'accessibilityDescription']
|
|
11
|
+
|
|
10
12
|
export const defaultOptions: LoggerOptions = {
|
|
11
13
|
logLevel: LogLevel.INFO,
|
|
12
14
|
maxArrayLength: 100,
|
|
@@ -33,7 +35,6 @@ export const defaultOptions: LoggerOptions = {
|
|
|
33
35
|
'patronymic_name',
|
|
34
36
|
'passportSeries',
|
|
35
37
|
'passportNumber',
|
|
36
|
-
'email',
|
|
37
38
|
'addressOfRegistration',
|
|
38
39
|
'addressOfBirth',
|
|
39
40
|
'birthDay',
|
|
@@ -41,13 +42,10 @@ export const defaultOptions: LoggerOptions = {
|
|
|
41
42
|
'date_birth',
|
|
42
43
|
'fio',
|
|
43
44
|
'passport',
|
|
44
|
-
'phone',
|
|
45
45
|
'address',
|
|
46
46
|
'birthplace',
|
|
47
47
|
'fullName',
|
|
48
48
|
'full_name',
|
|
49
|
-
'phoneNumber',
|
|
50
|
-
'extraPhoneNumber',
|
|
51
49
|
'refreshToken',
|
|
52
50
|
'token',
|
|
53
51
|
'fName',
|
|
@@ -101,11 +99,8 @@ export const defaultOptions: LoggerOptions = {
|
|
|
101
99
|
'partnerFullName',
|
|
102
100
|
'residenceAddress',
|
|
103
101
|
'displayName',
|
|
104
|
-
'personalPhone',
|
|
105
102
|
'content',
|
|
106
103
|
'subjAddress',
|
|
107
|
-
'subjPhone',
|
|
108
|
-
'subjEmail',
|
|
109
104
|
'subjDrfoCode',
|
|
110
105
|
'oldLastName',
|
|
111
106
|
'oldFirstName',
|
|
@@ -141,6 +136,8 @@ export const defaultOptions: LoggerOptions = {
|
|
|
141
136
|
'documents',
|
|
142
137
|
],
|
|
143
138
|
fieldsToRedactItn: ['label', 'paymentPurpose', 'subject', 'value'],
|
|
139
|
+
fieldsToRedactEmail: ['email', 'subjEmail', ...freeTextFields],
|
|
140
|
+
fieldsToRedactPhone: ['phone', 'phoneNumber', 'extraPhoneNumber', 'personalPhone', 'subjPhone', ...freeTextFields],
|
|
144
141
|
},
|
|
145
142
|
}
|
|
146
143
|
|
|
@@ -155,6 +152,14 @@ export const toInternalLoggerOptions = (options: LoggerOptions = {}): InternalLo
|
|
|
155
152
|
paths: new Set(redact.paths),
|
|
156
153
|
fieldsToRedactFullname: new Set(redact.fieldsToRedactFullname),
|
|
157
154
|
fieldsToRedactItn: new Set(redact.fieldsToRedactItn),
|
|
155
|
+
fieldsToRedactEmail: new Set(redact.fieldsToRedactEmail),
|
|
156
|
+
fieldsToRedactPhone: new Set(redact.fieldsToRedactPhone),
|
|
157
|
+
fieldsToScan: new Set([
|
|
158
|
+
...(redact.fieldsToRedactFullname || []),
|
|
159
|
+
...(redact.fieldsToRedactItn || []),
|
|
160
|
+
...(redact.fieldsToRedactEmail || []),
|
|
161
|
+
...(redact.fieldsToRedactPhone || []),
|
|
162
|
+
]),
|
|
158
163
|
},
|
|
159
164
|
}
|
|
160
165
|
}
|
package/src/interfaces/index.ts
CHANGED
|
@@ -6,5 +6,8 @@ export interface InternalLoggerOptions extends Required<Omit<LoggerOptions, 'red
|
|
|
6
6
|
paths: Set<string>
|
|
7
7
|
fieldsToRedactFullname: Set<string>
|
|
8
8
|
fieldsToRedactItn: Set<string>
|
|
9
|
+
fieldsToRedactEmail: Set<string>
|
|
10
|
+
fieldsToRedactPhone: Set<string>
|
|
11
|
+
fieldsToScan: Set<string>
|
|
9
12
|
}
|
|
10
13
|
}
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
const EMAIL_PATTERN = /([\w.%+-]+)@([\w.-]+\.[a-z]{2,})/gi
|
|
2
|
+
|
|
3
|
+
export function redactEmail(text: string): string {
|
|
4
|
+
if (!text.includes('@')) {
|
|
5
|
+
return text
|
|
6
|
+
}
|
|
7
|
+
|
|
8
|
+
return text.replace(EMAIL_PATTERN, (_match, local: string, domain: string) => {
|
|
9
|
+
const prefix = local.length >= 2 ? local[0] : ''
|
|
10
|
+
|
|
11
|
+
return `${prefix}***@${domain}`
|
|
12
|
+
})
|
|
13
|
+
}
|
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
// Liberal candidate: optional '+', then a digit-led run that may contain spaces,
|
|
2
|
+
// parentheses, dots and dashes. Validated by digit count in maskPhone.
|
|
3
|
+
const PHONE_CANDIDATE = /\+?\d[\d\s().-]{7,18}\d/g
|
|
4
|
+
|
|
5
|
+
const nationalDigits = 10
|
|
6
|
+
const internationalDigits = 12
|
|
7
|
+
const subscriberDigits = 7
|
|
8
|
+
|
|
9
|
+
export function redactPhone(text: string): string {
|
|
10
|
+
return text.replace(PHONE_CANDIDATE, (match) => maskPhone(match))
|
|
11
|
+
}
|
|
12
|
+
|
|
13
|
+
function maskPhone(match: string): string {
|
|
14
|
+
const digits = match.replace(/\D/g, '')
|
|
15
|
+
const isNational = digits.length === nationalDigits && digits.startsWith('0')
|
|
16
|
+
const isInternational = digits.length === internationalDigits
|
|
17
|
+
if (!isNational && !isInternational) {
|
|
18
|
+
return match
|
|
19
|
+
}
|
|
20
|
+
|
|
21
|
+
// Keep the country/operator code and the last digit; mask the subscriber number.
|
|
22
|
+
const prefix = match.startsWith('+') ? '+' : ''
|
|
23
|
+
|
|
24
|
+
return `${prefix}${digits.slice(0, -subscriberDigits)}${'*'.repeat(subscriberDigits - 1)}${digits.slice(-1)}`
|
|
25
|
+
}
|
package/src/trimmer.ts
CHANGED
|
@@ -5,8 +5,10 @@ import { LoggerOptions } from '@diia-inhouse/types'
|
|
|
5
5
|
|
|
6
6
|
import { toInternalLoggerOptions } from './config.js'
|
|
7
7
|
import { InternalLoggerOptions } from './interfaces/index.js'
|
|
8
|
+
import { redactEmail } from './redactors/email.js'
|
|
8
9
|
import { redactFullName } from './redactors/fullName.js'
|
|
9
10
|
import { redactItn } from './redactors/itn.js'
|
|
11
|
+
import { redactPhone } from './redactors/phone.js'
|
|
10
12
|
|
|
11
13
|
// oxlint-disable-next-line typescript/unbound-method
|
|
12
14
|
const { isObject } = lodash
|
|
@@ -54,11 +56,7 @@ function trimObject(opts: InternalLoggerOptions, node: object, depth: number): o
|
|
|
54
56
|
output[key] = trimWalker(opts, value, depth + 1)
|
|
55
57
|
|
|
56
58
|
if (typeof value === 'string' || Array.isArray(value)) {
|
|
57
|
-
if (opts.redact.
|
|
58
|
-
output[key] = redactionWalker(opts, key, output[key])
|
|
59
|
-
}
|
|
60
|
-
|
|
61
|
-
if (opts.redact.fieldsToRedactItn?.has(key)) {
|
|
59
|
+
if (opts.redact.fieldsToScan.has(key)) {
|
|
62
60
|
output[key] = redactionWalker(opts, key, output[key])
|
|
63
61
|
}
|
|
64
62
|
|
|
@@ -135,6 +133,14 @@ const redactionWalker = (opts: InternalLoggerOptions, key: string, value: string
|
|
|
135
133
|
value = redactItn(value)
|
|
136
134
|
}
|
|
137
135
|
|
|
136
|
+
if (opts.redact.fieldsToRedactEmail?.has(key)) {
|
|
137
|
+
value = redactEmail(value)
|
|
138
|
+
}
|
|
139
|
+
|
|
140
|
+
if (opts.redact.fieldsToRedactPhone?.has(key)) {
|
|
141
|
+
value = redactPhone(value)
|
|
142
|
+
}
|
|
143
|
+
|
|
138
144
|
return value
|
|
139
145
|
}
|
|
140
146
|
|