@loggerjs/processors 0.3.0 → 0.5.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/index.cjs CHANGED
@@ -1007,21 +1007,87 @@ function passesLuhn(input) {
1007
1007
  }
1008
1008
  return sum % 10 === 0;
1009
1009
  }
1010
- const DEFAULT_PATTERNS = [
1011
- {
1012
- name: "email",
1013
- pattern: /\b[A-Z0-9._%+-]+@[A-Z0-9.-]+\.[A-Z]{2,}\b/gi
1014
- },
1015
- {
1016
- name: "bearer-token",
1017
- pattern: /\bBearer\s+[A-Za-z0-9._~+/=-]+\b/g
1018
- },
1019
- {
1020
- name: "credit-card",
1021
- pattern: /\b(?:\d[ -]*?){13,19}\b/g,
1022
- validate: passesLuhn
1010
+ const DEFAULT_PATTERNS = [{
1011
+ name: "bearer-token",
1012
+ pattern: /\bBearer\s+[A-Za-z0-9._~+/=-]+\b/g
1013
+ }, {
1014
+ name: "credit-card",
1015
+ pattern: /\b(?:\d[ -]*?){13,19}\b/g,
1016
+ validate: passesLuhn
1017
+ }];
1018
+ function isAsciiAlpha(code) {
1019
+ return code >= 65 && code <= 90 || code >= 97 && code <= 122;
1020
+ }
1021
+ function isAsciiAlphaNumeric(code) {
1022
+ return isAsciiAlpha(code) || code >= 48 && code <= 57;
1023
+ }
1024
+ function hasChar(code, chars) {
1025
+ return chars.includes(String.fromCharCode(code));
1026
+ }
1027
+ function isEmailLocalChar(code) {
1028
+ return isAsciiAlphaNumeric(code) || hasChar(code, "._%+-");
1029
+ }
1030
+ function isEmailDomainChar(code) {
1031
+ return isAsciiAlphaNumeric(code) || hasChar(code, "-.");
1032
+ }
1033
+ function hasValidEmailDomain(input, start, end) {
1034
+ let labelLength = 0;
1035
+ let dotCount = 0;
1036
+ let tldStart = -1;
1037
+ for (let index = start; index < end; index += 1) {
1038
+ if (input.charCodeAt(index) === 46) {
1039
+ if (labelLength === 0) return false;
1040
+ dotCount += 1;
1041
+ labelLength = 0;
1042
+ tldStart = index + 1;
1043
+ continue;
1044
+ }
1045
+ labelLength += 1;
1023
1046
  }
1024
- ];
1047
+ if (dotCount === 0 || labelLength === 0 || tldStart < 0 || end - tldStart < 2) return false;
1048
+ for (let index = tldStart; index < end; index += 1) if (!isAsciiAlpha(input.charCodeAt(index))) return false;
1049
+ return true;
1050
+ }
1051
+ function redactEmailAddresses(input, path, options) {
1052
+ let output = "";
1053
+ let last = 0;
1054
+ let changed = false;
1055
+ let index = 0;
1056
+ while (index < input.length) {
1057
+ if (!isAsciiAlphaNumeric(input.charCodeAt(index))) {
1058
+ index += 1;
1059
+ continue;
1060
+ }
1061
+ const localStart = index;
1062
+ let cursor = index + 1;
1063
+ while (cursor < input.length && isEmailLocalChar(input.charCodeAt(cursor))) cursor += 1;
1064
+ if (input.charCodeAt(cursor) !== 64) {
1065
+ index = cursor + 1;
1066
+ continue;
1067
+ }
1068
+ const domainStart = cursor + 1;
1069
+ cursor = domainStart;
1070
+ while (cursor < input.length && isEmailDomainChar(input.charCodeAt(cursor))) cursor += 1;
1071
+ if (domainStart < cursor && hasValidEmailDomain(input, domainStart, cursor) && (cursor >= input.length || !isEmailLocalChar(input.charCodeAt(cursor)))) {
1072
+ output += input.slice(last, localStart);
1073
+ output += options.replacement;
1074
+ options.onRedact?.(path, "email");
1075
+ last = cursor;
1076
+ changed = true;
1077
+ index = cursor;
1078
+ continue;
1079
+ }
1080
+ index = domainStart;
1081
+ }
1082
+ if (!changed) return {
1083
+ value: input,
1084
+ changed: false
1085
+ };
1086
+ return {
1087
+ value: output + input.slice(last),
1088
+ changed: true
1089
+ };
1090
+ }
1025
1091
  function matcherMatches(matcher, key, path, value) {
1026
1092
  if (typeof matcher === "string") return matcher.toLowerCase() === key.toLowerCase();
1027
1093
  if (matcher instanceof RegExp) {
@@ -1039,21 +1105,34 @@ function redact(path, reason, options) {
1039
1105
  }
1040
1106
  function guardString(input, path, options) {
1041
1107
  let value = input;
1108
+ let changed = false;
1109
+ if (value.length > options.maxStringLength) {
1110
+ options.onRedact?.(path, "max-string-length");
1111
+ value = `${value.slice(0, options.maxStringLength)}${options.truncateSuffix}`;
1112
+ changed = true;
1113
+ }
1114
+ if (options.redactDefaultEmailPattern) {
1115
+ const emailResult = redactEmailAddresses(value, path, options);
1116
+ value = emailResult.value;
1117
+ changed ||= emailResult.changed;
1118
+ }
1042
1119
  for (const item of options.patterns) {
1043
1120
  item.pattern.lastIndex = 0;
1044
1121
  value = value.replace(item.pattern, (match) => {
1045
1122
  if (item.validate && !item.validate(match)) return match;
1046
1123
  options.onRedact?.(path, item.name);
1124
+ changed = true;
1047
1125
  return item.replacement ?? options.replacement;
1048
1126
  });
1049
1127
  }
1050
1128
  if (value.length > options.maxStringLength) {
1051
1129
  options.onRedact?.(path, "max-string-length");
1052
1130
  value = `${value.slice(0, options.maxStringLength)}${options.truncateSuffix}`;
1131
+ changed = true;
1053
1132
  }
1054
1133
  return {
1055
1134
  value,
1056
- changed: value !== input
1135
+ changed: changed || value !== input
1057
1136
  };
1058
1137
  }
1059
1138
  function guardValue(value, path, key, depth, options, seen) {
@@ -1132,6 +1211,7 @@ function privacyGuardProcessor(options = {}) {
1132
1211
  denyKeys: options.denyKeys ?? DEFAULT_DENY_KEYS,
1133
1212
  allowKeys: options.allowKeys ?? [],
1134
1213
  patterns: options.patterns ?? DEFAULT_PATTERNS,
1214
+ redactDefaultEmailPattern: options.patterns === void 0,
1135
1215
  replacement: options.replacement ?? "[REDACTED]",
1136
1216
  maxDepth: Math.max(0, Math.floor(options.maxDepth ?? 8)),
1137
1217
  maxStringLength: Math.max(1, Math.floor(options.maxStringLength ?? 8192)),