@loggerjs/processors 0.1.0 → 0.3.1
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 +2 -1
- package/dist/index.cjs +107 -19
- package/dist/index.cjs.map +1 -1
- package/dist/index.js +107 -19
- package/dist/index.js.map +1 -1
- package/dist/privacy-guard.d.ts.map +1 -1
- package/dist/redact.d.ts +2 -0
- package/dist/redact.d.ts.map +1 -1
- package/package.json +2 -2
package/dist/index.js
CHANGED
|
@@ -3,7 +3,12 @@ import { createBoundContext, createMiddleware, eventToRecord, incrementLoggerMet
|
|
|
3
3
|
function matchesKey(key, path, value, matchers) {
|
|
4
4
|
return matchers.some((matcher) => {
|
|
5
5
|
if (typeof matcher === "string") return matcher.toLowerCase() === key.toLowerCase();
|
|
6
|
-
if (matcher instanceof RegExp)
|
|
6
|
+
if (matcher instanceof RegExp) {
|
|
7
|
+
matcher.lastIndex = 0;
|
|
8
|
+
const keyMatches = matcher.test(key);
|
|
9
|
+
matcher.lastIndex = 0;
|
|
10
|
+
return keyMatches || matcher.test(path);
|
|
11
|
+
}
|
|
7
12
|
return matcher(key, path, value);
|
|
8
13
|
});
|
|
9
14
|
}
|
|
@@ -28,8 +33,10 @@ function redactValue(value, options, path = "", depth = 0, seen = /* @__PURE__ *
|
|
|
28
33
|
seen.set(value, out);
|
|
29
34
|
for (const [key, child] of Object.entries(input)) {
|
|
30
35
|
const childPath = path ? `${path}.${key}` : key;
|
|
31
|
-
if (matchesKey(key, childPath, child, options.keys ?? []) || pathMatches(childPath, options.paths ?? []))
|
|
32
|
-
|
|
36
|
+
if (matchesKey(key, childPath, child, options.keys ?? []) || pathMatches(childPath, options.paths ?? [])) {
|
|
37
|
+
if (options.remove) continue;
|
|
38
|
+
out[key] = options.replacement;
|
|
39
|
+
} else out[key] = redactValue(child, options, childPath, depth + 1, seen);
|
|
33
40
|
}
|
|
34
41
|
return out;
|
|
35
42
|
}
|
|
@@ -47,7 +54,8 @@ function redactProcessor(options = {}) {
|
|
|
47
54
|
"api_key"
|
|
48
55
|
],
|
|
49
56
|
paths: options.paths ?? [],
|
|
50
|
-
replacement: options.replacement ?? "[REDACTED]",
|
|
57
|
+
replacement: options.replacement ?? options.censor ?? "[REDACTED]",
|
|
58
|
+
remove: options.remove ?? false,
|
|
51
59
|
maxDepth: options.maxDepth ?? 8
|
|
52
60
|
};
|
|
53
61
|
return (event) => ({
|
|
@@ -998,21 +1006,87 @@ function passesLuhn(input) {
|
|
|
998
1006
|
}
|
|
999
1007
|
return sum % 10 === 0;
|
|
1000
1008
|
}
|
|
1001
|
-
const DEFAULT_PATTERNS = [
|
|
1002
|
-
|
|
1003
|
-
|
|
1004
|
-
|
|
1005
|
-
|
|
1006
|
-
{
|
|
1007
|
-
|
|
1008
|
-
|
|
1009
|
-
|
|
1010
|
-
|
|
1011
|
-
|
|
1012
|
-
|
|
1013
|
-
|
|
1009
|
+
const DEFAULT_PATTERNS = [{
|
|
1010
|
+
name: "bearer-token",
|
|
1011
|
+
pattern: /\bBearer\s+[A-Za-z0-9._~+/=-]+\b/g
|
|
1012
|
+
}, {
|
|
1013
|
+
name: "credit-card",
|
|
1014
|
+
pattern: /\b(?:\d[ -]*?){13,19}\b/g,
|
|
1015
|
+
validate: passesLuhn
|
|
1016
|
+
}];
|
|
1017
|
+
function isAsciiAlpha(code) {
|
|
1018
|
+
return code >= 65 && code <= 90 || code >= 97 && code <= 122;
|
|
1019
|
+
}
|
|
1020
|
+
function isAsciiAlphaNumeric(code) {
|
|
1021
|
+
return isAsciiAlpha(code) || code >= 48 && code <= 57;
|
|
1022
|
+
}
|
|
1023
|
+
function hasChar(code, chars) {
|
|
1024
|
+
return chars.includes(String.fromCharCode(code));
|
|
1025
|
+
}
|
|
1026
|
+
function isEmailLocalChar(code) {
|
|
1027
|
+
return isAsciiAlphaNumeric(code) || hasChar(code, "._%+-");
|
|
1028
|
+
}
|
|
1029
|
+
function isEmailDomainChar(code) {
|
|
1030
|
+
return isAsciiAlphaNumeric(code) || hasChar(code, "-.");
|
|
1031
|
+
}
|
|
1032
|
+
function hasValidEmailDomain(input, start, end) {
|
|
1033
|
+
let labelLength = 0;
|
|
1034
|
+
let dotCount = 0;
|
|
1035
|
+
let tldStart = -1;
|
|
1036
|
+
for (let index = start; index < end; index += 1) {
|
|
1037
|
+
if (input.charCodeAt(index) === 46) {
|
|
1038
|
+
if (labelLength === 0) return false;
|
|
1039
|
+
dotCount += 1;
|
|
1040
|
+
labelLength = 0;
|
|
1041
|
+
tldStart = index + 1;
|
|
1042
|
+
continue;
|
|
1043
|
+
}
|
|
1044
|
+
labelLength += 1;
|
|
1014
1045
|
}
|
|
1015
|
-
|
|
1046
|
+
if (dotCount === 0 || labelLength === 0 || tldStart < 0 || end - tldStart < 2) return false;
|
|
1047
|
+
for (let index = tldStart; index < end; index += 1) if (!isAsciiAlpha(input.charCodeAt(index))) return false;
|
|
1048
|
+
return true;
|
|
1049
|
+
}
|
|
1050
|
+
function redactEmailAddresses(input, path, options) {
|
|
1051
|
+
let output = "";
|
|
1052
|
+
let last = 0;
|
|
1053
|
+
let changed = false;
|
|
1054
|
+
let index = 0;
|
|
1055
|
+
while (index < input.length) {
|
|
1056
|
+
if (!isAsciiAlphaNumeric(input.charCodeAt(index))) {
|
|
1057
|
+
index += 1;
|
|
1058
|
+
continue;
|
|
1059
|
+
}
|
|
1060
|
+
const localStart = index;
|
|
1061
|
+
let cursor = index + 1;
|
|
1062
|
+
while (cursor < input.length && isEmailLocalChar(input.charCodeAt(cursor))) cursor += 1;
|
|
1063
|
+
if (input.charCodeAt(cursor) !== 64) {
|
|
1064
|
+
index = cursor + 1;
|
|
1065
|
+
continue;
|
|
1066
|
+
}
|
|
1067
|
+
const domainStart = cursor + 1;
|
|
1068
|
+
cursor = domainStart;
|
|
1069
|
+
while (cursor < input.length && isEmailDomainChar(input.charCodeAt(cursor))) cursor += 1;
|
|
1070
|
+
if (domainStart < cursor && hasValidEmailDomain(input, domainStart, cursor) && (cursor >= input.length || !isEmailLocalChar(input.charCodeAt(cursor)))) {
|
|
1071
|
+
output += input.slice(last, localStart);
|
|
1072
|
+
output += options.replacement;
|
|
1073
|
+
options.onRedact?.(path, "email");
|
|
1074
|
+
last = cursor;
|
|
1075
|
+
changed = true;
|
|
1076
|
+
index = cursor;
|
|
1077
|
+
continue;
|
|
1078
|
+
}
|
|
1079
|
+
index = domainStart;
|
|
1080
|
+
}
|
|
1081
|
+
if (!changed) return {
|
|
1082
|
+
value: input,
|
|
1083
|
+
changed: false
|
|
1084
|
+
};
|
|
1085
|
+
return {
|
|
1086
|
+
value: output + input.slice(last),
|
|
1087
|
+
changed: true
|
|
1088
|
+
};
|
|
1089
|
+
}
|
|
1016
1090
|
function matcherMatches(matcher, key, path, value) {
|
|
1017
1091
|
if (typeof matcher === "string") return matcher.toLowerCase() === key.toLowerCase();
|
|
1018
1092
|
if (matcher instanceof RegExp) {
|
|
@@ -1030,21 +1104,34 @@ function redact(path, reason, options) {
|
|
|
1030
1104
|
}
|
|
1031
1105
|
function guardString(input, path, options) {
|
|
1032
1106
|
let value = input;
|
|
1107
|
+
let changed = false;
|
|
1108
|
+
if (value.length > options.maxStringLength) {
|
|
1109
|
+
options.onRedact?.(path, "max-string-length");
|
|
1110
|
+
value = `${value.slice(0, options.maxStringLength)}${options.truncateSuffix}`;
|
|
1111
|
+
changed = true;
|
|
1112
|
+
}
|
|
1113
|
+
if (options.redactDefaultEmailPattern) {
|
|
1114
|
+
const emailResult = redactEmailAddresses(value, path, options);
|
|
1115
|
+
value = emailResult.value;
|
|
1116
|
+
changed ||= emailResult.changed;
|
|
1117
|
+
}
|
|
1033
1118
|
for (const item of options.patterns) {
|
|
1034
1119
|
item.pattern.lastIndex = 0;
|
|
1035
1120
|
value = value.replace(item.pattern, (match) => {
|
|
1036
1121
|
if (item.validate && !item.validate(match)) return match;
|
|
1037
1122
|
options.onRedact?.(path, item.name);
|
|
1123
|
+
changed = true;
|
|
1038
1124
|
return item.replacement ?? options.replacement;
|
|
1039
1125
|
});
|
|
1040
1126
|
}
|
|
1041
1127
|
if (value.length > options.maxStringLength) {
|
|
1042
1128
|
options.onRedact?.(path, "max-string-length");
|
|
1043
1129
|
value = `${value.slice(0, options.maxStringLength)}${options.truncateSuffix}`;
|
|
1130
|
+
changed = true;
|
|
1044
1131
|
}
|
|
1045
1132
|
return {
|
|
1046
1133
|
value,
|
|
1047
|
-
changed: value !== input
|
|
1134
|
+
changed: changed || value !== input
|
|
1048
1135
|
};
|
|
1049
1136
|
}
|
|
1050
1137
|
function guardValue(value, path, key, depth, options, seen) {
|
|
@@ -1123,6 +1210,7 @@ function privacyGuardProcessor(options = {}) {
|
|
|
1123
1210
|
denyKeys: options.denyKeys ?? DEFAULT_DENY_KEYS,
|
|
1124
1211
|
allowKeys: options.allowKeys ?? [],
|
|
1125
1212
|
patterns: options.patterns ?? DEFAULT_PATTERNS,
|
|
1213
|
+
redactDefaultEmailPattern: options.patterns === void 0,
|
|
1126
1214
|
replacement: options.replacement ?? "[REDACTED]",
|
|
1127
1215
|
maxDepth: Math.max(0, Math.floor(options.maxDepth ?? 8)),
|
|
1128
1216
|
maxStringLength: Math.max(1, Math.floor(options.maxStringLength ?? 8192)),
|