autotel
Version:
Write Once, Observe Anywhere
173 lines (171 loc) • 5.58 kB
JavaScript
;
// src/attribute-redacting-processor.ts
var REDACTOR_PATTERNS = {
// Value patterns (match content in attribute values)
email: /\b[A-Za-z0-9._%+-]+@[A-Za-z0-9.-]+\.[A-Za-z]{2,}\b/gi,
phone: /\b\d{3}[-.]?\d{3}[-.]?\d{4}\b/g,
ssn: /\b\d{3}[-]?\d{2}[-]?\d{4}\b/g,
creditCard: /\b\d{4}[- ]?\d{4}[- ]?\d{4}[- ]?\d{4}\b/g,
bearerToken: /Bearer\s+[A-Za-z0-9._~+/=-]+/gi,
apiKeyInValue: /(?:api[_-]?key|apikey|api_secret)[=:][\s"']*[A-Za-z0-9_-]+/gi,
jwt: /eyJ[A-Za-z0-9_-]*\.eyJ[A-Za-z0-9_-]*\.[A-Za-z0-9_-]*/g,
// Key patterns (match attribute names - redacts entire value)
sensitiveKey: /^(password|passwd|pwd|secret|token|api[_-]?key|auth|credential|private[_-]?key|authorization)$/i
};
var DEFAULT_VALUE_PATTERNS = [
{ name: "email", pattern: REDACTOR_PATTERNS.email },
{ name: "phone", pattern: REDACTOR_PATTERNS.phone },
{ name: "ssn", pattern: REDACTOR_PATTERNS.ssn },
{ name: "creditCard", pattern: REDACTOR_PATTERNS.creditCard }
];
var REDACTOR_PRESETS = {
/**
* Default preset - covers common PII patterns
* Detects: emails, phone numbers, SSNs, credit cards
* Redacts keys: password, secret, token, apiKey, auth, credential
*/
default: {
keyPatterns: [REDACTOR_PATTERNS.sensitiveKey],
valuePatterns: DEFAULT_VALUE_PATTERNS,
replacement: "[REDACTED]"
},
/**
* Strict preset - more aggressive redaction for high-security environments
* Includes everything in default plus: Bearer tokens, JWTs, API keys in values
*/
strict: {
keyPatterns: [REDACTOR_PATTERNS.sensitiveKey, /bearer/i, /jwt/i],
valuePatterns: [
...DEFAULT_VALUE_PATTERNS,
{ name: "bearerToken", pattern: REDACTOR_PATTERNS.bearerToken },
{ name: "apiKeyInValue", pattern: REDACTOR_PATTERNS.apiKeyInValue },
{ name: "jwt", pattern: REDACTOR_PATTERNS.jwt }
],
replacement: "[REDACTED]"
},
/**
* PCI-DSS preset - focused on payment card industry compliance
* Redacts: credit card numbers, CVV-like patterns, card-related keys
*/
"pci-dss": {
keyPatterns: [/card/i, /cvv/i, /cvc/i, /pan/i, /expir/i, /ccn/i],
valuePatterns: [
{ name: "creditCard", pattern: REDACTOR_PATTERNS.creditCard }
],
replacement: "[REDACTED]"
}
};
function resolveConfig(config) {
if (typeof config === "string") {
const preset = REDACTOR_PRESETS[config];
if (!preset) {
throw new Error(
`Unknown attribute redactor preset: "${config}". Available presets: ${Object.keys(REDACTOR_PRESETS).join(", ")}`
);
}
return preset;
}
return config;
}
function createRedactorFromConfig(config) {
if (config.redactor) {
return config.redactor;
}
const keyPatterns = config.keyPatterns ?? [];
const valuePatterns = config.valuePatterns ?? [];
const defaultReplacement = config.replacement ?? "[REDACTED]";
return (key, value) => {
for (const pattern of keyPatterns) {
pattern.lastIndex = 0;
if (pattern.test(key)) {
return defaultReplacement;
}
}
if (typeof value !== "string") {
if (Array.isArray(value)) {
return value.map((item) => {
if (typeof item === "string") {
return redactStringValue(
item,
valuePatterns,
defaultReplacement
);
}
return item;
});
}
return value;
}
return redactStringValue(value, valuePatterns, defaultReplacement);
};
}
function redactStringValue(value, patterns, defaultReplacement) {
let result = value;
for (const { pattern, replacement } of patterns) {
pattern.lastIndex = 0;
result = result.replaceAll(pattern, replacement ?? defaultReplacement);
}
return result;
}
function createRedactedSpan(span, redactor) {
const redactedAttributes = {};
for (const [key, value] of Object.entries(span.attributes)) {
if (value !== void 0) {
redactedAttributes[key] = redactor(key, value);
}
}
return new Proxy(span, {
get(target, prop) {
if (prop === "attributes") {
return redactedAttributes;
}
const value = Reflect.get(target, prop);
if (typeof value === "function") {
return value.bind(target);
}
return value;
}
});
}
function createAttributeRedactor(config) {
return createRedactorFromConfig(resolveConfig(config));
}
var AttributeRedactingProcessor = class {
wrappedProcessor;
redactor;
constructor(wrappedProcessor, options) {
this.wrappedProcessor = wrappedProcessor;
const config = resolveConfig(options.redactor);
this.redactor = createRedactorFromConfig(config);
}
/**
* Pass through onStart unchanged - attributes aren't finalized yet
*/
onStart(span, parentContext) {
this.wrappedProcessor.onStart(span, parentContext);
}
/**
* Redact attributes and forward to wrapped processor
*/
onEnd(span) {
try {
const redactedSpan = createRedactedSpan(span, this.redactor);
this.wrappedProcessor.onEnd(redactedSpan);
} catch {
this.wrappedProcessor.onEnd(span);
}
}
forceFlush() {
return this.wrappedProcessor.forceFlush();
}
shutdown() {
return this.wrappedProcessor.shutdown();
}
};
exports.AttributeRedactingProcessor = AttributeRedactingProcessor;
exports.REDACTOR_PATTERNS = REDACTOR_PATTERNS;
exports.REDACTOR_PRESETS = REDACTOR_PRESETS;
exports.createAttributeRedactor = createAttributeRedactor;
exports.createRedactedSpan = createRedactedSpan;
//# sourceMappingURL=chunk-ELW34S4C.cjs.map
//# sourceMappingURL=chunk-ELW34S4C.cjs.map