@datdm198x/safe-logger 1.0.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/README.md +52 -0
- package/dist/index.cjs +248 -0
- package/dist/index.d.cts +20 -0
- package/dist/index.d.ts +20 -0
- package/dist/index.js +220 -0
- package/package.json +35 -0
package/README.md
ADDED
|
@@ -0,0 +1,52 @@
|
|
|
1
|
+
# Safe Logger
|
|
2
|
+
|
|
3
|
+
A lightweight TypeScript utility to sanitize sensitive information from objects before logging, preventing potential security risks from leaking credentials in logs.
|
|
4
|
+
|
|
5
|
+
## Features
|
|
6
|
+
|
|
7
|
+
- Recursive object sanitization.
|
|
8
|
+
- Automatic detection of sensitive fields based on keys (passwords, tokens, API keys, credit cards, PII, etc.).
|
|
9
|
+
- Configurable masking strategies for different data types.
|
|
10
|
+
|
|
11
|
+
## Installation
|
|
12
|
+
|
|
13
|
+
```bash
|
|
14
|
+
npm install @datdm198x/safe-logger
|
|
15
|
+
```
|
|
16
|
+
|
|
17
|
+
## Usage
|
|
18
|
+
|
|
19
|
+
```typescript
|
|
20
|
+
import { safeLog } from '@datdm198x/safe-logger';
|
|
21
|
+
|
|
22
|
+
const sensitiveData = {
|
|
23
|
+
username: 'john_doe',
|
|
24
|
+
password: 'my-super-secret-password',
|
|
25
|
+
api_key: 'sk_live_1234567890abcdef',
|
|
26
|
+
email: 'john.doe@gmail.com'
|
|
27
|
+
};
|
|
28
|
+
|
|
29
|
+
const sanitizedData = safeLog(sensitiveData);
|
|
30
|
+
console.log(sanitizedData);
|
|
31
|
+
/*
|
|
32
|
+
Output:
|
|
33
|
+
{
|
|
34
|
+
username: 'john_doe',
|
|
35
|
+
password: '********',
|
|
36
|
+
api_key: '********cdef',
|
|
37
|
+
email: 'j******e@gmail.com'
|
|
38
|
+
}
|
|
39
|
+
*/
|
|
40
|
+
```
|
|
41
|
+
|
|
42
|
+
## Development
|
|
43
|
+
|
|
44
|
+
### Build
|
|
45
|
+
```bash
|
|
46
|
+
npm run build
|
|
47
|
+
```
|
|
48
|
+
|
|
49
|
+
### Testing
|
|
50
|
+
```bash
|
|
51
|
+
npm test
|
|
52
|
+
```
|
package/dist/index.cjs
ADDED
|
@@ -0,0 +1,248 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
var __defProp = Object.defineProperty;
|
|
3
|
+
var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
|
|
4
|
+
var __getOwnPropNames = Object.getOwnPropertyNames;
|
|
5
|
+
var __hasOwnProp = Object.prototype.hasOwnProperty;
|
|
6
|
+
var __export = (target, all) => {
|
|
7
|
+
for (var name in all)
|
|
8
|
+
__defProp(target, name, { get: all[name], enumerable: true });
|
|
9
|
+
};
|
|
10
|
+
var __copyProps = (to, from, except, desc) => {
|
|
11
|
+
if (from && typeof from === "object" || typeof from === "function") {
|
|
12
|
+
for (let key of __getOwnPropNames(from))
|
|
13
|
+
if (!__hasOwnProp.call(to, key) && key !== except)
|
|
14
|
+
__defProp(to, key, { get: () => from[key], enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable });
|
|
15
|
+
}
|
|
16
|
+
return to;
|
|
17
|
+
};
|
|
18
|
+
var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod);
|
|
19
|
+
|
|
20
|
+
// src/index.ts
|
|
21
|
+
var index_exports = {};
|
|
22
|
+
__export(index_exports, {
|
|
23
|
+
maskValue: () => maskValue,
|
|
24
|
+
safeLog: () => safeLog
|
|
25
|
+
});
|
|
26
|
+
module.exports = __toCommonJS(index_exports);
|
|
27
|
+
|
|
28
|
+
// src/safe-log.ts
|
|
29
|
+
var DEFAULT_MASK_CHAR = "*";
|
|
30
|
+
function normalizeKey(key) {
|
|
31
|
+
return key.trim().toLowerCase().replace(/[\s._-]/g, "");
|
|
32
|
+
}
|
|
33
|
+
function maskValue(value, options = {}) {
|
|
34
|
+
const {
|
|
35
|
+
visiblePercent = 0.2,
|
|
36
|
+
minVisible = 2,
|
|
37
|
+
maskChar = DEFAULT_MASK_CHAR
|
|
38
|
+
} = options;
|
|
39
|
+
if (!value) {
|
|
40
|
+
return value;
|
|
41
|
+
}
|
|
42
|
+
const length = value.length;
|
|
43
|
+
const visible = Math.max(
|
|
44
|
+
minVisible,
|
|
45
|
+
Math.floor(length * visiblePercent)
|
|
46
|
+
);
|
|
47
|
+
if (visible >= length) {
|
|
48
|
+
return value;
|
|
49
|
+
}
|
|
50
|
+
return maskChar.repeat(length - visible) + value.slice(length - visible);
|
|
51
|
+
}
|
|
52
|
+
function maskPassword() {
|
|
53
|
+
return "********";
|
|
54
|
+
}
|
|
55
|
+
function maskEmail(email) {
|
|
56
|
+
const parts = email.split("@");
|
|
57
|
+
if (parts.length !== 2) {
|
|
58
|
+
return maskValue(email);
|
|
59
|
+
}
|
|
60
|
+
const name = parts[0] ?? "";
|
|
61
|
+
const domain = parts[1] ?? "";
|
|
62
|
+
if (name.length <= 2) {
|
|
63
|
+
return `**@${domain}`;
|
|
64
|
+
}
|
|
65
|
+
return name[0] + "*".repeat(name.length - 2) + name[name.length - 1] + "@" + domain;
|
|
66
|
+
}
|
|
67
|
+
function maskPhone(phone) {
|
|
68
|
+
const digits = phone.replace(/\D/g, "");
|
|
69
|
+
if (digits.length <= 4) {
|
|
70
|
+
return phone;
|
|
71
|
+
}
|
|
72
|
+
return "*".repeat(digits.length - 4) + digits.slice(-4);
|
|
73
|
+
}
|
|
74
|
+
function maskCreditCard(card) {
|
|
75
|
+
return maskValue(card.replace(/\s/g, ""), {
|
|
76
|
+
visiblePercent: 0.25,
|
|
77
|
+
minVisible: 4
|
|
78
|
+
});
|
|
79
|
+
}
|
|
80
|
+
function maskToken(token) {
|
|
81
|
+
return maskValue(token, {
|
|
82
|
+
visiblePercent: 0.1,
|
|
83
|
+
minVisible: 4
|
|
84
|
+
});
|
|
85
|
+
}
|
|
86
|
+
var FIELD_MASKERS = /* @__PURE__ */ new Map([
|
|
87
|
+
["password", maskPassword],
|
|
88
|
+
["passwd", maskPassword],
|
|
89
|
+
["pwd", maskPassword],
|
|
90
|
+
["pass", maskToken],
|
|
91
|
+
["email", maskEmail],
|
|
92
|
+
["phone", maskPhone],
|
|
93
|
+
["creditcard", maskCreditCard],
|
|
94
|
+
["cvv", () => "***"],
|
|
95
|
+
// AWS
|
|
96
|
+
["awsaccesskeyid", maskToken],
|
|
97
|
+
["awssecretaccesskey", maskToken],
|
|
98
|
+
["awsregion", maskToken],
|
|
99
|
+
["smtpusername", maskToken],
|
|
100
|
+
["smtppassword", maskToken],
|
|
101
|
+
["sessmtpusername", maskToken],
|
|
102
|
+
["sessmtppassword", maskToken],
|
|
103
|
+
// Security & Auth
|
|
104
|
+
["clientid", maskToken],
|
|
105
|
+
["clientsecret", maskToken],
|
|
106
|
+
["jwtsecret", maskToken],
|
|
107
|
+
["encryptionkey", maskToken],
|
|
108
|
+
["sshkey", maskToken],
|
|
109
|
+
["rsakey", maskToken],
|
|
110
|
+
["masterkey", maskToken],
|
|
111
|
+
["salt", maskToken],
|
|
112
|
+
["encryptionpassword", maskPassword],
|
|
113
|
+
["dbpassword", maskPassword],
|
|
114
|
+
["dbpass", maskPassword],
|
|
115
|
+
["connectionstring", maskToken],
|
|
116
|
+
["connstr", maskToken],
|
|
117
|
+
["ldapbindpassword", maskPassword],
|
|
118
|
+
["stripekey", maskToken],
|
|
119
|
+
["stripesecret", maskToken],
|
|
120
|
+
["sendgridapikey", maskToken],
|
|
121
|
+
["slackwebhookurl", maskToken],
|
|
122
|
+
["ssn", maskToken],
|
|
123
|
+
["socialsecuritynumber", maskToken],
|
|
124
|
+
["nationalid", maskToken],
|
|
125
|
+
["passportnumber", maskToken],
|
|
126
|
+
["driverslicense", maskToken],
|
|
127
|
+
["dob", maskToken],
|
|
128
|
+
["dateofbirth", maskToken],
|
|
129
|
+
["cardnumber", maskCreditCard],
|
|
130
|
+
["cvc", () => "***"],
|
|
131
|
+
["accountnumber", maskToken],
|
|
132
|
+
["routingnumber", maskToken],
|
|
133
|
+
// Misc
|
|
134
|
+
["username", maskToken],
|
|
135
|
+
["userid", maskToken],
|
|
136
|
+
["sessionid", maskToken],
|
|
137
|
+
["ip", maskToken],
|
|
138
|
+
["ipaddress", maskToken],
|
|
139
|
+
["macaddress", maskToken],
|
|
140
|
+
["jwt", maskToken],
|
|
141
|
+
["token", maskToken],
|
|
142
|
+
["accesstoken", maskToken],
|
|
143
|
+
["refreshtoken", maskToken],
|
|
144
|
+
["authorization", maskToken],
|
|
145
|
+
["apikey", maskToken],
|
|
146
|
+
["secret", maskToken],
|
|
147
|
+
["cookie", maskToken],
|
|
148
|
+
["privatekey", maskToken],
|
|
149
|
+
// Database connection strings
|
|
150
|
+
["mongouri", maskToken],
|
|
151
|
+
["redisuri", maskToken],
|
|
152
|
+
["mysqluri", maskToken],
|
|
153
|
+
["postgresqluri", maskToken],
|
|
154
|
+
["pgsql", maskToken]
|
|
155
|
+
]);
|
|
156
|
+
var SENSITIVE_KEYWORDS = [
|
|
157
|
+
"password",
|
|
158
|
+
"passwd",
|
|
159
|
+
"pwd",
|
|
160
|
+
"pass",
|
|
161
|
+
"token",
|
|
162
|
+
"jwt",
|
|
163
|
+
"bearer",
|
|
164
|
+
"secret",
|
|
165
|
+
"apikey",
|
|
166
|
+
"accesskey",
|
|
167
|
+
"authorization",
|
|
168
|
+
"cookie",
|
|
169
|
+
"creditcard",
|
|
170
|
+
"cardnumber",
|
|
171
|
+
"cvv",
|
|
172
|
+
"cvc",
|
|
173
|
+
"otp",
|
|
174
|
+
"privatekey",
|
|
175
|
+
"clientsecret",
|
|
176
|
+
"clientid",
|
|
177
|
+
"accesstoken",
|
|
178
|
+
"refreshtoken",
|
|
179
|
+
"username",
|
|
180
|
+
"userid",
|
|
181
|
+
"sessionid",
|
|
182
|
+
"ipaddress",
|
|
183
|
+
"encryption",
|
|
184
|
+
"ssh",
|
|
185
|
+
"rsa",
|
|
186
|
+
"master",
|
|
187
|
+
"salt",
|
|
188
|
+
"connection",
|
|
189
|
+
"conn",
|
|
190
|
+
"ldap",
|
|
191
|
+
"stripe",
|
|
192
|
+
"sendgrid",
|
|
193
|
+
"slack",
|
|
194
|
+
"ssn",
|
|
195
|
+
"socialsecurity",
|
|
196
|
+
"national",
|
|
197
|
+
"passport",
|
|
198
|
+
"drivers",
|
|
199
|
+
"dob",
|
|
200
|
+
"birth",
|
|
201
|
+
"account",
|
|
202
|
+
"routing"
|
|
203
|
+
];
|
|
204
|
+
function isSensitiveKey(key) {
|
|
205
|
+
const normalized = normalizeKey(key);
|
|
206
|
+
return SENSITIVE_KEYWORDS.some(
|
|
207
|
+
(keyword) => normalized.includes(keyword)
|
|
208
|
+
);
|
|
209
|
+
}
|
|
210
|
+
function sanitize(value) {
|
|
211
|
+
if (value === null || value === void 0) {
|
|
212
|
+
return value;
|
|
213
|
+
}
|
|
214
|
+
if (Array.isArray(value)) {
|
|
215
|
+
return value.map(sanitize);
|
|
216
|
+
}
|
|
217
|
+
if (typeof value !== "object") {
|
|
218
|
+
return value;
|
|
219
|
+
}
|
|
220
|
+
const obj = value;
|
|
221
|
+
return Object.fromEntries(
|
|
222
|
+
Object.entries(obj).map(([key, value2]) => {
|
|
223
|
+
if (typeof value2 === "string") {
|
|
224
|
+
const normalized = normalizeKey(key);
|
|
225
|
+
const masker = FIELD_MASKERS.get(normalized);
|
|
226
|
+
if (masker) {
|
|
227
|
+
return [key, masker(value2)];
|
|
228
|
+
}
|
|
229
|
+
if (isSensitiveKey(normalized)) {
|
|
230
|
+
return [key, maskToken(value2)];
|
|
231
|
+
}
|
|
232
|
+
return [key, value2];
|
|
233
|
+
}
|
|
234
|
+
if (value2 && typeof value2 === "object") {
|
|
235
|
+
return [key, sanitize(value2)];
|
|
236
|
+
}
|
|
237
|
+
return [key, value2];
|
|
238
|
+
})
|
|
239
|
+
);
|
|
240
|
+
}
|
|
241
|
+
function safeLog(value) {
|
|
242
|
+
return sanitize(value);
|
|
243
|
+
}
|
|
244
|
+
// Annotate the CommonJS export names for ESM import in node:
|
|
245
|
+
0 && (module.exports = {
|
|
246
|
+
maskValue,
|
|
247
|
+
safeLog
|
|
248
|
+
});
|
package/dist/index.d.cts
ADDED
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* ============================================================================
|
|
3
|
+
* Safe Logger Utility
|
|
4
|
+
* ============================================================================
|
|
5
|
+
*/
|
|
6
|
+
interface MaskOptions {
|
|
7
|
+
visiblePercent?: number;
|
|
8
|
+
minVisible?: number;
|
|
9
|
+
maskChar?: string;
|
|
10
|
+
}
|
|
11
|
+
/**
|
|
12
|
+
* Generic masking
|
|
13
|
+
*/
|
|
14
|
+
declare function maskValue(value: string, options?: MaskOptions): string;
|
|
15
|
+
/**
|
|
16
|
+
* Public API
|
|
17
|
+
*/
|
|
18
|
+
declare function safeLog<T>(value: T): T;
|
|
19
|
+
|
|
20
|
+
export { maskValue, safeLog };
|
package/dist/index.d.ts
ADDED
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* ============================================================================
|
|
3
|
+
* Safe Logger Utility
|
|
4
|
+
* ============================================================================
|
|
5
|
+
*/
|
|
6
|
+
interface MaskOptions {
|
|
7
|
+
visiblePercent?: number;
|
|
8
|
+
minVisible?: number;
|
|
9
|
+
maskChar?: string;
|
|
10
|
+
}
|
|
11
|
+
/**
|
|
12
|
+
* Generic masking
|
|
13
|
+
*/
|
|
14
|
+
declare function maskValue(value: string, options?: MaskOptions): string;
|
|
15
|
+
/**
|
|
16
|
+
* Public API
|
|
17
|
+
*/
|
|
18
|
+
declare function safeLog<T>(value: T): T;
|
|
19
|
+
|
|
20
|
+
export { maskValue, safeLog };
|
package/dist/index.js
ADDED
|
@@ -0,0 +1,220 @@
|
|
|
1
|
+
// src/safe-log.ts
|
|
2
|
+
var DEFAULT_MASK_CHAR = "*";
|
|
3
|
+
function normalizeKey(key) {
|
|
4
|
+
return key.trim().toLowerCase().replace(/[\s._-]/g, "");
|
|
5
|
+
}
|
|
6
|
+
function maskValue(value, options = {}) {
|
|
7
|
+
const {
|
|
8
|
+
visiblePercent = 0.2,
|
|
9
|
+
minVisible = 2,
|
|
10
|
+
maskChar = DEFAULT_MASK_CHAR
|
|
11
|
+
} = options;
|
|
12
|
+
if (!value) {
|
|
13
|
+
return value;
|
|
14
|
+
}
|
|
15
|
+
const length = value.length;
|
|
16
|
+
const visible = Math.max(
|
|
17
|
+
minVisible,
|
|
18
|
+
Math.floor(length * visiblePercent)
|
|
19
|
+
);
|
|
20
|
+
if (visible >= length) {
|
|
21
|
+
return value;
|
|
22
|
+
}
|
|
23
|
+
return maskChar.repeat(length - visible) + value.slice(length - visible);
|
|
24
|
+
}
|
|
25
|
+
function maskPassword() {
|
|
26
|
+
return "********";
|
|
27
|
+
}
|
|
28
|
+
function maskEmail(email) {
|
|
29
|
+
const parts = email.split("@");
|
|
30
|
+
if (parts.length !== 2) {
|
|
31
|
+
return maskValue(email);
|
|
32
|
+
}
|
|
33
|
+
const name = parts[0] ?? "";
|
|
34
|
+
const domain = parts[1] ?? "";
|
|
35
|
+
if (name.length <= 2) {
|
|
36
|
+
return `**@${domain}`;
|
|
37
|
+
}
|
|
38
|
+
return name[0] + "*".repeat(name.length - 2) + name[name.length - 1] + "@" + domain;
|
|
39
|
+
}
|
|
40
|
+
function maskPhone(phone) {
|
|
41
|
+
const digits = phone.replace(/\D/g, "");
|
|
42
|
+
if (digits.length <= 4) {
|
|
43
|
+
return phone;
|
|
44
|
+
}
|
|
45
|
+
return "*".repeat(digits.length - 4) + digits.slice(-4);
|
|
46
|
+
}
|
|
47
|
+
function maskCreditCard(card) {
|
|
48
|
+
return maskValue(card.replace(/\s/g, ""), {
|
|
49
|
+
visiblePercent: 0.25,
|
|
50
|
+
minVisible: 4
|
|
51
|
+
});
|
|
52
|
+
}
|
|
53
|
+
function maskToken(token) {
|
|
54
|
+
return maskValue(token, {
|
|
55
|
+
visiblePercent: 0.1,
|
|
56
|
+
minVisible: 4
|
|
57
|
+
});
|
|
58
|
+
}
|
|
59
|
+
var FIELD_MASKERS = /* @__PURE__ */ new Map([
|
|
60
|
+
["password", maskPassword],
|
|
61
|
+
["passwd", maskPassword],
|
|
62
|
+
["pwd", maskPassword],
|
|
63
|
+
["pass", maskToken],
|
|
64
|
+
["email", maskEmail],
|
|
65
|
+
["phone", maskPhone],
|
|
66
|
+
["creditcard", maskCreditCard],
|
|
67
|
+
["cvv", () => "***"],
|
|
68
|
+
// AWS
|
|
69
|
+
["awsaccesskeyid", maskToken],
|
|
70
|
+
["awssecretaccesskey", maskToken],
|
|
71
|
+
["awsregion", maskToken],
|
|
72
|
+
["smtpusername", maskToken],
|
|
73
|
+
["smtppassword", maskToken],
|
|
74
|
+
["sessmtpusername", maskToken],
|
|
75
|
+
["sessmtppassword", maskToken],
|
|
76
|
+
// Security & Auth
|
|
77
|
+
["clientid", maskToken],
|
|
78
|
+
["clientsecret", maskToken],
|
|
79
|
+
["jwtsecret", maskToken],
|
|
80
|
+
["encryptionkey", maskToken],
|
|
81
|
+
["sshkey", maskToken],
|
|
82
|
+
["rsakey", maskToken],
|
|
83
|
+
["masterkey", maskToken],
|
|
84
|
+
["salt", maskToken],
|
|
85
|
+
["encryptionpassword", maskPassword],
|
|
86
|
+
["dbpassword", maskPassword],
|
|
87
|
+
["dbpass", maskPassword],
|
|
88
|
+
["connectionstring", maskToken],
|
|
89
|
+
["connstr", maskToken],
|
|
90
|
+
["ldapbindpassword", maskPassword],
|
|
91
|
+
["stripekey", maskToken],
|
|
92
|
+
["stripesecret", maskToken],
|
|
93
|
+
["sendgridapikey", maskToken],
|
|
94
|
+
["slackwebhookurl", maskToken],
|
|
95
|
+
["ssn", maskToken],
|
|
96
|
+
["socialsecuritynumber", maskToken],
|
|
97
|
+
["nationalid", maskToken],
|
|
98
|
+
["passportnumber", maskToken],
|
|
99
|
+
["driverslicense", maskToken],
|
|
100
|
+
["dob", maskToken],
|
|
101
|
+
["dateofbirth", maskToken],
|
|
102
|
+
["cardnumber", maskCreditCard],
|
|
103
|
+
["cvc", () => "***"],
|
|
104
|
+
["accountnumber", maskToken],
|
|
105
|
+
["routingnumber", maskToken],
|
|
106
|
+
// Misc
|
|
107
|
+
["username", maskToken],
|
|
108
|
+
["userid", maskToken],
|
|
109
|
+
["sessionid", maskToken],
|
|
110
|
+
["ip", maskToken],
|
|
111
|
+
["ipaddress", maskToken],
|
|
112
|
+
["macaddress", maskToken],
|
|
113
|
+
["jwt", maskToken],
|
|
114
|
+
["token", maskToken],
|
|
115
|
+
["accesstoken", maskToken],
|
|
116
|
+
["refreshtoken", maskToken],
|
|
117
|
+
["authorization", maskToken],
|
|
118
|
+
["apikey", maskToken],
|
|
119
|
+
["secret", maskToken],
|
|
120
|
+
["cookie", maskToken],
|
|
121
|
+
["privatekey", maskToken],
|
|
122
|
+
// Database connection strings
|
|
123
|
+
["mongouri", maskToken],
|
|
124
|
+
["redisuri", maskToken],
|
|
125
|
+
["mysqluri", maskToken],
|
|
126
|
+
["postgresqluri", maskToken],
|
|
127
|
+
["pgsql", maskToken]
|
|
128
|
+
]);
|
|
129
|
+
var SENSITIVE_KEYWORDS = [
|
|
130
|
+
"password",
|
|
131
|
+
"passwd",
|
|
132
|
+
"pwd",
|
|
133
|
+
"pass",
|
|
134
|
+
"token",
|
|
135
|
+
"jwt",
|
|
136
|
+
"bearer",
|
|
137
|
+
"secret",
|
|
138
|
+
"apikey",
|
|
139
|
+
"accesskey",
|
|
140
|
+
"authorization",
|
|
141
|
+
"cookie",
|
|
142
|
+
"creditcard",
|
|
143
|
+
"cardnumber",
|
|
144
|
+
"cvv",
|
|
145
|
+
"cvc",
|
|
146
|
+
"otp",
|
|
147
|
+
"privatekey",
|
|
148
|
+
"clientsecret",
|
|
149
|
+
"clientid",
|
|
150
|
+
"accesstoken",
|
|
151
|
+
"refreshtoken",
|
|
152
|
+
"username",
|
|
153
|
+
"userid",
|
|
154
|
+
"sessionid",
|
|
155
|
+
"ipaddress",
|
|
156
|
+
"encryption",
|
|
157
|
+
"ssh",
|
|
158
|
+
"rsa",
|
|
159
|
+
"master",
|
|
160
|
+
"salt",
|
|
161
|
+
"connection",
|
|
162
|
+
"conn",
|
|
163
|
+
"ldap",
|
|
164
|
+
"stripe",
|
|
165
|
+
"sendgrid",
|
|
166
|
+
"slack",
|
|
167
|
+
"ssn",
|
|
168
|
+
"socialsecurity",
|
|
169
|
+
"national",
|
|
170
|
+
"passport",
|
|
171
|
+
"drivers",
|
|
172
|
+
"dob",
|
|
173
|
+
"birth",
|
|
174
|
+
"account",
|
|
175
|
+
"routing"
|
|
176
|
+
];
|
|
177
|
+
function isSensitiveKey(key) {
|
|
178
|
+
const normalized = normalizeKey(key);
|
|
179
|
+
return SENSITIVE_KEYWORDS.some(
|
|
180
|
+
(keyword) => normalized.includes(keyword)
|
|
181
|
+
);
|
|
182
|
+
}
|
|
183
|
+
function sanitize(value) {
|
|
184
|
+
if (value === null || value === void 0) {
|
|
185
|
+
return value;
|
|
186
|
+
}
|
|
187
|
+
if (Array.isArray(value)) {
|
|
188
|
+
return value.map(sanitize);
|
|
189
|
+
}
|
|
190
|
+
if (typeof value !== "object") {
|
|
191
|
+
return value;
|
|
192
|
+
}
|
|
193
|
+
const obj = value;
|
|
194
|
+
return Object.fromEntries(
|
|
195
|
+
Object.entries(obj).map(([key, value2]) => {
|
|
196
|
+
if (typeof value2 === "string") {
|
|
197
|
+
const normalized = normalizeKey(key);
|
|
198
|
+
const masker = FIELD_MASKERS.get(normalized);
|
|
199
|
+
if (masker) {
|
|
200
|
+
return [key, masker(value2)];
|
|
201
|
+
}
|
|
202
|
+
if (isSensitiveKey(normalized)) {
|
|
203
|
+
return [key, maskToken(value2)];
|
|
204
|
+
}
|
|
205
|
+
return [key, value2];
|
|
206
|
+
}
|
|
207
|
+
if (value2 && typeof value2 === "object") {
|
|
208
|
+
return [key, sanitize(value2)];
|
|
209
|
+
}
|
|
210
|
+
return [key, value2];
|
|
211
|
+
})
|
|
212
|
+
);
|
|
213
|
+
}
|
|
214
|
+
function safeLog(value) {
|
|
215
|
+
return sanitize(value);
|
|
216
|
+
}
|
|
217
|
+
export {
|
|
218
|
+
maskValue,
|
|
219
|
+
safeLog
|
|
220
|
+
};
|
package/package.json
ADDED
|
@@ -0,0 +1,35 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "@datdm198x/safe-logger",
|
|
3
|
+
"version": "1.0.0",
|
|
4
|
+
"description": "Safe logger utility",
|
|
5
|
+
"main": "dist/index.js",
|
|
6
|
+
"types": "dist/index.d.ts",
|
|
7
|
+
"files": [
|
|
8
|
+
"dist"
|
|
9
|
+
],
|
|
10
|
+
"scripts": {
|
|
11
|
+
"test": "vitest run",
|
|
12
|
+
"build": "tsup src/index.ts --format cjs,esm --dts",
|
|
13
|
+
"prepare": "npm run build"
|
|
14
|
+
},
|
|
15
|
+
"repository": {
|
|
16
|
+
"type": "git",
|
|
17
|
+
"url": "git+https://github.com/dat-dao-c2c/safe-logger.git"
|
|
18
|
+
},
|
|
19
|
+
"keywords": [],
|
|
20
|
+
"author": "",
|
|
21
|
+
"license": "ISC",
|
|
22
|
+
"type": "module",
|
|
23
|
+
"bugs": {
|
|
24
|
+
"url": "https://github.com/dat-dao-c2c/safe-logger/issues"
|
|
25
|
+
},
|
|
26
|
+
"homepage": "https://github.com/dat-dao-c2c/safe-logger#readme",
|
|
27
|
+
"dependencies": {
|
|
28
|
+
"tsup": "^8.5.1",
|
|
29
|
+
"typescript": "^6.0.3"
|
|
30
|
+
},
|
|
31
|
+
"devDependencies": {
|
|
32
|
+
"@types/node": "^26.0.1",
|
|
33
|
+
"vitest": "^4.1.9"
|
|
34
|
+
}
|
|
35
|
+
}
|