42 lines
1.9 KiB
JavaScript
42 lines
1.9 KiB
JavaScript
// ============================================================
|
|
// PHI redactor for audit log details
|
|
// ============================================================
|
|
// Audit details should describe *what happened*, not contain clinical
|
|
// text. This defensively strips obvious PHI patterns and caps length.
|
|
// ============================================================
|
|
|
|
var MAX_LEN = 500;
|
|
|
|
function redact(text) {
|
|
if (text == null) return text;
|
|
var s = typeof text === 'string' ? text : JSON.stringify(text);
|
|
|
|
// Credentials and tokens. These patterns intentionally run before PHI
|
|
// redaction so accidental pasted headers/URLs are removed from logs.
|
|
s = s.replace(/Authorization\s*:\s*Bearer\s+[A-Za-z0-9._~+/=-]+/gi, 'Authorization: Bearer [REDACTED]');
|
|
s = s.replace(/Bearer\s+[A-Za-z0-9._~+/=-]{20,}/gi, 'Bearer [REDACTED]');
|
|
s = s.replace(/\b[A-Za-z0-9_-]+\.[A-Za-z0-9_-]+\.[A-Za-z0-9_-]+\b/g, '[JWT]');
|
|
s = s.replace(/\b(api[_-]?key|password|secret|token|app[_-]?password)(\s*[=:]\s*)([^\s&"']+)/gi, '$1$2[REDACTED]');
|
|
s = s.replace(/(https?:\/\/)([^\s/@:]+):([^\s/@]+)@/gi, '$1[REDACTED]@');
|
|
|
|
// SSN
|
|
s = s.replace(/\b\d{3}-\d{2}-\d{4}\b/g, '[SSN]');
|
|
// US phone numbers
|
|
s = s.replace(/\b\(?\d{3}\)?[\s.-]\d{3}[\s.-]\d{4}\b/g, '[PHONE]');
|
|
// Email addresses (except domain-only)
|
|
s = s.replace(/\b[A-Z0-9._%+-]+@[A-Z0-9.-]+\.[A-Z]{2,}\b/gi, '[EMAIL]');
|
|
// Dates of birth / MRN-ish long digit runs
|
|
s = s.replace(/\b\d{2}\/\d{2}\/\d{4}\b/g, '[DATE]');
|
|
s = s.replace(/\b\d{8,}\b/g, '[ID]');
|
|
// Note-body signal: many newlines or very long prose → indicates the
|
|
// caller passed a clinical note as `details`. Truncate aggressively.
|
|
var newlines = (s.match(/\n/g) || []).length;
|
|
if (newlines > 4 || s.length > MAX_LEN * 2) {
|
|
s = s.slice(0, 120) + ' [TRUNCATED:possible-note-body]';
|
|
}
|
|
|
|
if (s.length > MAX_LEN) s = s.slice(0, MAX_LEN) + '…';
|
|
return s;
|
|
}
|
|
|
|
module.exports = { redact: redact };
|