55 lines
1.4 KiB
TypeScript
55 lines
1.4 KiB
TypeScript
import { Request, Response, NextFunction } from 'express';
|
|
|
|
export function maskPII(data: any): any {
|
|
if (typeof data === 'string') {
|
|
// Mask email
|
|
if (data.includes('@')) {
|
|
const [local, domain] = data.split('@');
|
|
return `${local.substring(0, 2)}***@${domain}`;
|
|
}
|
|
// Mask phone
|
|
if (/^\d{10,}$/.test(data.replace(/\D/g, ''))) {
|
|
const cleaned = data.replace(/\D/g, '');
|
|
return `***-***-${cleaned.slice(-4)}`;
|
|
}
|
|
// Mask SSN
|
|
if (/^\d{3}-\d{2}-\d{4}$/.test(data)) {
|
|
return `***-**-${data.slice(-4)}`;
|
|
}
|
|
}
|
|
|
|
if (Array.isArray(data)) {
|
|
return data.map(item => maskPII(item));
|
|
}
|
|
|
|
if (data && typeof data === 'object') {
|
|
const masked: any = {};
|
|
const sensitiveFields = ['ssn', 'taxId', 'accountNumber', 'routingNumber', 'creditCard', 'cvv'];
|
|
|
|
for (const [key, value] of Object.entries(data)) {
|
|
if (sensitiveFields.includes(key.toLowerCase())) {
|
|
masked[key] = '***';
|
|
} else {
|
|
masked[key] = maskPII(value);
|
|
}
|
|
}
|
|
return masked;
|
|
}
|
|
|
|
return data;
|
|
}
|
|
|
|
export function maskDataInResponse(req: Request, res: Response, next: NextFunction): void {
|
|
const originalJson = res.json.bind(res);
|
|
|
|
res.json = function (data: any) {
|
|
// Only mask in non-production or for non-admin users
|
|
if (process.env.NODE_ENV !== 'production' || (req as any).userRole !== 'ADMIN') {
|
|
data = maskPII(data);
|
|
}
|
|
return originalJson(data);
|
|
};
|
|
|
|
next();
|
|
}
|