@bugspotter/sdk
Version:
Professional bug reporting SDK with screenshots, session replay, and automatic error capture for web applications
255 lines (254 loc) • 9.27 kB
JavaScript
"use strict";
/**
* PII Detection and Sanitization Utility - REFACTORED
* Follows SOLID, DRY, and KISS principles
*/
Object.defineProperty(exports, "__esModule", { value: true });
exports.Sanitizer = exports.validatePattern = exports.getPatternsByCategory = exports.getPattern = exports.createPatternConfig = exports.PatternBuilder = exports.PATTERN_CATEGORIES = exports.PATTERN_PRESETS = exports.DEFAULT_PATTERNS = void 0;
exports.createSanitizer = createSanitizer;
const sanitize_patterns_1 = require("./sanitize-patterns");
var sanitize_patterns_2 = require("./sanitize-patterns");
Object.defineProperty(exports, "DEFAULT_PATTERNS", { enumerable: true, get: function () { return sanitize_patterns_2.DEFAULT_PATTERNS; } });
Object.defineProperty(exports, "PATTERN_PRESETS", { enumerable: true, get: function () { return sanitize_patterns_2.PATTERN_PRESETS; } });
Object.defineProperty(exports, "PATTERN_CATEGORIES", { enumerable: true, get: function () { return sanitize_patterns_2.PATTERN_CATEGORIES; } });
Object.defineProperty(exports, "PatternBuilder", { enumerable: true, get: function () { return sanitize_patterns_2.PatternBuilder; } });
Object.defineProperty(exports, "createPatternConfig", { enumerable: true, get: function () { return sanitize_patterns_2.createPatternConfig; } });
Object.defineProperty(exports, "getPattern", { enumerable: true, get: function () { return sanitize_patterns_2.getPattern; } });
Object.defineProperty(exports, "getPatternsByCategory", { enumerable: true, get: function () { return sanitize_patterns_2.getPatternsByCategory; } });
Object.defineProperty(exports, "validatePattern", { enumerable: true, get: function () { return sanitize_patterns_2.validatePattern; } });
/**
* Pattern Manager - SRP: Handles pattern initialization and storage
*/
class PatternManager {
constructor(selectedPatterns, customPatterns) {
this.patterns = new Map();
this.initializePatterns(selectedPatterns, customPatterns);
}
initializePatterns(selectedPatterns, customPatterns) {
// Resolve preset to pattern names
let patternNames;
if (typeof selectedPatterns === 'string') {
// It's a preset name like 'all', 'minimal', etc.
patternNames = sanitize_patterns_1.PATTERN_PRESETS[selectedPatterns];
}
else {
// It's an array of pattern names
patternNames = selectedPatterns.filter((p) => {
return p !== 'custom';
});
}
// Get pattern definitions and sort by priority
const patternDefs = patternNames.map((name) => {
return sanitize_patterns_1.DEFAULT_PATTERNS[name];
});
const sortedPatterns = (0, sanitize_patterns_1.getPatternsByPriority)(patternDefs);
// Add built-in patterns in priority order
sortedPatterns.forEach((patternDef) => {
this.patterns.set(patternDef.name, patternDef.regex);
});
// Add custom patterns (convert to PatternDefinition format)
customPatterns.forEach((custom) => {
this.patterns.set(custom.name, custom.regex);
});
}
getPatterns() {
return this.patterns;
}
}
/**
* String Sanitizer - SRP: Handles string-level PII detection and replacement
*/
class StringSanitizer {
constructor(patterns) {
this.patterns = patterns;
}
sanitize(value) {
if (typeof value !== 'string') {
return value;
}
let sanitized = value;
this.patterns.forEach((regex, name) => {
const patternType = name.toUpperCase();
sanitized = sanitized.replace(regex, `[REDACTED-${patternType}]`);
});
return sanitized;
}
}
/**
* Value Sanitizer - SRP: Handles recursive object/array traversal
*/
class ValueSanitizer {
constructor(stringSanitizer) {
this.stringSanitizer = stringSanitizer;
}
sanitize(value) {
// Handle null/undefined
if (value == null) {
return value;
}
// Handle strings
if (typeof value === 'string') {
return this.stringSanitizer.sanitize(value);
}
// Handle arrays
if (Array.isArray(value)) {
return value.map((item) => {
return this.sanitize(item);
});
}
// Handle objects
if (typeof value === 'object') {
return this.sanitizeObject(value);
}
// Return primitives as-is
return value;
}
sanitizeObject(obj) {
const sanitized = {};
for (const [key, val] of Object.entries(obj)) {
const sanitizedKey = this.stringSanitizer.sanitize(key);
sanitized[sanitizedKey] = this.sanitize(val);
}
return sanitized;
}
}
/**
* Element Matcher - SRP: Handles DOM element exclusion logic
*/
class ElementMatcher {
constructor(excludeSelectors) {
this.excludeSelectors = excludeSelectors;
}
shouldExclude(element) {
if (!element || !this.excludeSelectors.length) {
return false;
}
return this.excludeSelectors.some((selector) => {
try {
return element.matches(selector);
}
catch (_a) {
return false;
}
});
}
}
/**
* Main Sanitizer - Facade pattern: Coordinates all sanitization operations
* SOLID: Open/Closed - easily extensible without modification
*/
class Sanitizer {
constructor(config) {
var _a, _b, _c, _d;
this.enabled = (_a = config.enabled) !== null && _a !== void 0 ? _a : true;
if (!this.enabled) {
// Create no-op implementations when disabled
this.stringSanitizer = new StringSanitizer(new Map());
this.valueSanitizer = new ValueSanitizer(this.stringSanitizer);
this.elementMatcher = new ElementMatcher([]);
return;
}
// Default to 'all' preset if no patterns specified
const selectedPatterns = (_b = config.patterns) !== null && _b !== void 0 ? _b : 'all';
const customPatterns = (_c = config.customPatterns) !== null && _c !== void 0 ? _c : [];
const excludeSelectors = (_d = config.excludeSelectors) !== null && _d !== void 0 ? _d : [];
const patternManager = new PatternManager(selectedPatterns, customPatterns);
this.stringSanitizer = new StringSanitizer(patternManager.getPatterns());
this.valueSanitizer = new ValueSanitizer(this.stringSanitizer);
this.elementMatcher = new ElementMatcher(excludeSelectors);
}
/**
* Guard clause helper - DRY principle
*/
guardDisabled(value) {
return this.enabled ? undefined : value;
}
/**
* Sanitize any value (string, object, array, etc.)
*/
sanitize(value) {
const guarded = this.guardDisabled(value);
if (guarded !== undefined) {
return guarded;
}
return this.valueSanitizer.sanitize(value);
}
/**
* Sanitize console arguments - KISS: delegates to generic sanitize
*/
sanitizeConsoleArgs(args) {
const guarded = this.guardDisabled(args);
if (guarded !== undefined) {
return guarded;
}
return args.map((arg) => {
return this.sanitize(arg);
});
}
/**
* Sanitize network data - KISS: uses generic sanitize with type safety
*/
sanitizeNetworkData(data) {
const guarded = this.guardDisabled(data);
if (guarded !== undefined) {
return guarded;
}
return this.sanitize(data);
}
/**
* Sanitize error - KISS: uses generic sanitize with type safety
*/
sanitizeError(error) {
const guarded = this.guardDisabled(error);
if (guarded !== undefined) {
return guarded;
}
return this.sanitize(error);
}
/**
* Check if element should be excluded
*/
shouldExclude(element) {
if (!this.enabled) {
return false;
}
return this.elementMatcher.shouldExclude(element);
}
/**
* Sanitize DOM text node
*/
sanitizeTextNode(text, element) {
const guarded = this.guardDisabled(text);
if (guarded !== undefined) {
return guarded;
}
if (this.elementMatcher.shouldExclude(element)) {
return text;
}
return this.stringSanitizer.sanitize(text);
}
/**
* Detect PII patterns in text without sanitizing
* Returns a map of pattern names to match counts
*/
detectPII(text) {
const detections = new Map();
if (!this.enabled || !text) {
return detections;
}
for (const [name, regex] of this.stringSanitizer['patterns']) {
const matches = text.match(new RegExp(regex, 'g'));
if (matches && matches.length > 0) {
detections.set(name, matches.length);
}
}
return detections;
}
}
exports.Sanitizer = Sanitizer;
/**
* Factory function to create a Sanitizer instance
*/
function createSanitizer(config) {
return new Sanitizer(config);
}