@ogcio/o11y-sdk-node
Version:
Opentelemetry standard instrumentation SDK for NodeJS based project
74 lines (73 loc) • 3.49 kB
JavaScript
import { BasicRedactor } from "./basic-redactor.js";
// iOS < 16.6 compatible: no lookbehind/lookahead
// Building blocks — composed programmatically to avoid regex complexity & duplication
const H = String.raw `[\da-f]{1,4}`; // single hex group
const PCT = String.raw `(?:%[\da-f]{2})`; // percent-encoded byte
const OCTET = String.raw `(?:25[0-5]|2[0-4]\d|[01]?\d\d?)`; // IPv4 octet 0-255
const IPV4_PATTERN = String.raw `${PCT}?${OCTET}(?:\.${OCTET}){3}${PCT}?`;
const IPV6_ALTERNATIVES = [
`(?:${H}:){7}${H}`, // full 8-group
`(?:${H}:){1,6}:${H}`, // :: after 1-6 groups + 1 trailing
`(?:${H}:){1,5}(?::${H}){1,2}`, // :: after 1-5 + 1-2 trailing
`(?:${H}:){1,4}(?::${H}){1,3}`, // :: after 1-4 + 1-3 trailing
`(?:${H}:){1,3}(?::${H}){1,4}`, // :: after 1-3 + 1-4 trailing
`(?:${H}:){1,2}(?::${H}){1,5}`, // :: after 1-2 + 1-5 trailing
`${H}:(?::${H}){1,6}`, // :: after 1 + 1-6 trailing
`::(?:${H}:){0,5}${H}`, // leading ::
`${H}::(?:${H}:){0,5}${H}`, // 1 group + ::
`(?:${H}:){2}::(?:${H}:){0,4}${H}`, // 2 groups + ::
`(?:${H}:){3}::(?:${H}:){0,3}${H}`, // 3 groups + ::
`(?:${H}:){4}::(?:${H}:){0,2}${H}`, // 4 groups + ::
`(?:${H}:){5}::(?:${H}:)?${H}`, // 5 groups + ::
`(?:${H}:){6}::${H}`, // 6 groups + ::
`(?:${H}:){1,7}:`, // trailing ::
String.raw `(?:${H}:){1,4}:${OCTET}(?:\.${OCTET}){3}`, // mixed IPv4 form
];
const IPV6_PATTERN = `${PCT}?(${IPV6_ALTERNATIVES.join("|")})${PCT}?`;
export class IpRedactor extends BasicRedactor {
static IPV4_REGEX = new RegExp(IPV4_PATTERN, "gi");
static IPV6_REGEX = new RegExp(IPV6_PATTERN, "gi");
redact(value) {
const counters = {};
// First pass: redact IPv4 with boundary checking
const tempValue = value.replace(IpRedactor.IPV4_REGEX, (match, ...args) => {
const offset = args.at(-2);
const before = offset > 0 ? value[offset - 1] : "";
const after = offset + match.length < value.length
? value[offset + match.length]
: "";
// Check if surrounded by non-digit characters
const isValidBefore = !before || !/\d/.test(before);
const isValidAfter = !after || !/\d/.test(after);
if (isValidBefore && isValidAfter) {
counters["IPv4"] = (counters["IPv4"] || 0) + 1;
return "[REDACTED IPV4]";
}
return match;
});
// Second pass: redact IPv6 with boundary checking
const redactedValue = tempValue.replace(IpRedactor.IPV6_REGEX, (match, ...args) => {
const offset = args.at(-2);
const before = offset > 0 ? tempValue[offset - 1] : "";
const after = offset + match.length < tempValue.length
? tempValue[offset + match.length]
: "";
// For IPv6:
// Reject if there's a colon before (indicating :::)
// Reject if there's a hex digit or colon after
const isValidBefore = !before || !/[\da-f:]/i.test(before);
const isValidAfter = !after || !/[\da-f:]/i.test(after);
if (isValidBefore && isValidAfter) {
counters["IPv6"] = (counters["IPv6"] || 0) + 1;
return "[REDACTED IPV6]";
}
return match;
});
return {
redactedValue: redactedValue,
context: {
counters,
},
};
}
}