UNPKG

dataveil

Version:

A robust TypeScript library for masking sensitive data including card numbers, emails, passwords, phone numbers, and more.

151 lines (150 loc) 5.92 kB
"use strict"; Object.defineProperty(exports, "__esModule", { value: true }); exports.maskJSON = maskJSON; var maskCardNumber_1 = require("./maskCardNumber"); var maskEmail_1 = require("./maskEmail"); var maskPassword_1 = require("./maskPassword"); var maskPhoneNumber_1 = require("./maskPhoneNumber"); var maskUUID_1 = require("./maskUUID"); var maskSubstring_1 = require("./maskSubstring"); var utils_1 = require("../utils"); function maskJSON(json, fieldsToMask, options) { if (options === void 0) { options = {}; } if (!json || typeof json !== 'object') { throw new Error('JSON must be a valid object'); } if (fieldsToMask.length > 100) { throw new Error('Too many fields to mask (maximum: 100)'); } // Estimate object size to prevent memory exhaustion var jsonString = JSON.stringify(json); if (jsonString.length > 10 * 1024 * 1024) { // 10MB limit throw new Error('JSON object too large (maximum: 10MB)'); } var maskedJson = deepClone(json); fieldsToMask.forEach(function (fieldConfig) { var fieldPath; var fieldType; var fieldOptions; if (typeof fieldConfig === 'string') { fieldPath = sanitizeFieldPath(fieldConfig); fieldType = inferFieldType(fieldPath); fieldOptions = options; } else { fieldPath = sanitizeFieldPath(fieldConfig.path); fieldType = fieldConfig.type || inferFieldType(fieldPath); fieldOptions = fieldConfig.options ? Object.assign({}, options, fieldConfig.options) : options; } var value = (0, utils_1.getNestedField)(maskedJson, fieldPath); if (value && typeof value === 'string') { var maskedValue = void 0; try { switch (fieldType) { case 'card': maskedValue = (0, maskCardNumber_1.maskCardNumber)(value, fieldOptions); break; case 'email': maskedValue = (0, maskEmail_1.maskEmail)(value, fieldOptions); break; case 'phone': maskedValue = (0, maskPhoneNumber_1.maskPhoneNumber)(value, fieldOptions); break; case 'uuid': maskedValue = (0, maskUUID_1.maskUUID)(value, fieldOptions); break; case 'password': maskedValue = (0, maskPassword_1.maskPassword)(value, fieldOptions); break; default: maskedValue = (0, maskSubstring_1.maskSubstring)(value, value, fieldOptions); } (0, utils_1.setNestedField)(maskedJson, fieldPath, maskedValue); } catch (error) { // If specific masking fails, fall back to generic masking maskedValue = (0, maskSubstring_1.maskSubstring)(value, value, fieldOptions); (0, utils_1.setNestedField)(maskedJson, fieldPath, maskedValue); } } }); return maskedJson; } function deepClone(obj, depth, seen) { if (depth === void 0) { depth = 0; } if (seen === void 0) { seen = new WeakSet(); } if (depth > 100) { throw new Error('Maximum cloning depth exceeded - possible circular reference'); } if (obj !== null && typeof obj === 'object') { if (seen.has(obj)) { throw new Error('Circular reference detected'); } seen.add(obj); } if (obj === null || typeof obj !== 'object') return obj; if (obj instanceof Date) return new Date(obj.getTime()); if (Array.isArray(obj)) return obj.map(function (item) { return deepClone(item, depth + 1, seen); }); var cloned = {}; for (var key in obj) { if (key === '__proto__' || key === 'constructor' || key === 'prototype') { continue; // Skip dangerous keys } if (Object.prototype.hasOwnProperty.call(obj, key)) { cloned[key] = deepClone(obj[key], depth + 1, seen); } } return cloned; } function sanitizeFieldPath(fieldPath) { if (!fieldPath || typeof fieldPath !== 'string') { throw new Error('Field path must be a non-empty string'); } // Prevent dangerous paths var dangerousPatterns = [ '__proto__', 'constructor', 'prototype', '../', '..\\', 'eval', 'function' ]; var lowerPath = fieldPath.toLowerCase(); for (var _i = 0, dangerousPatterns_1 = dangerousPatterns; _i < dangerousPatterns_1.length; _i++) { var pattern = dangerousPatterns_1[_i]; if (lowerPath.includes(pattern)) { throw new Error("Dangerous field path detected: ".concat(pattern)); } } // Limit path depth var parts = fieldPath.split('.'); if (parts.length > 20) { throw new Error('Field path depth exceeds maximum allowed (20 levels)'); } // Validate each part for (var _a = 0, parts_1 = parts; _a < parts_1.length; _a++) { var part = parts_1[_a]; if (!part || !/^[a-zA-Z_$][a-zA-Z0-9_$]*$/.test(part)) { throw new Error("Invalid field path component: ".concat(part)); } } return fieldPath; } function inferFieldType(fieldPath) { var lowerPath = fieldPath.toLowerCase(); if (lowerPath.includes('card') || lowerPath.includes('credit')) return 'card'; if (lowerPath.includes('email')) return 'email'; if (lowerPath.includes('phone') || lowerPath.includes('mobile')) return 'phone'; if (lowerPath.includes('uuid') || lowerPath.includes('id')) return 'uuid'; if (lowerPath.includes('password') || lowerPath.includes('pwd')) return 'password'; return 'custom'; }