DEV Community

Mohammad Waseem
Mohammad Waseem

Posted on

Preventing Leaking PII in Test Environments with JavaScript: A Practical Approach

In modern development workflows, safeguarding Personally Identifiable Information (PII) during testing is critical to maintaining user privacy and compliance with data protection regulations. However, many teams face challenges, especially when test environments inadvertently leak PII due to lax controls or inadequate documentation. As a Lead QA Engineer, I encountered a scenario where sensitive test data was being exposed through unfiltered logs and debugging tools, risking exposure and non-compliance.

Given the complexity and the urgency, my approach focused on implementing an automated, code-centric solution using JavaScript—without relying on extensive documentation or external tools. This post details the steps I took to identify, mask, and prevent PII leakage effectively.

Understanding the Problem

The first challenge was to understand how PII was leaking. Common sources included:

  • Logs that printed user data for debugging
  • Client-side data structures sent to third-party services
  • Unfiltered error reports or crash dumps

In our case, logs stored sensitive information like email addresses, phone numbers, and addresses. Because there was no proper documentation on data flow, I needed a dynamic way to scan and sanitize data at runtime.

Implementing Data Masking with JavaScript

The core strategy was to intercept data before it leaves the application, particularly at points where data is logged or transmitted. JavaScript’s ability to override functions and filter data on the fly proved invaluable.

Step 1: Create a Data Sanitization Function

I developed a reusable sanitizer that scans an object for PII fields based on known attribute names.

const piiFields = ['email', 'phone', 'address', 'ssn', 'name'];

function maskPII(data) {
  if (Array.isArray(data)) {
    return data.map(maskPII);
  }
  if (typeof data === 'object' && data !== null) {
    const sanitized = {};
    Object.keys(data).forEach(key => {
      if (piiFields.includes(key.toLowerCase())) {
        sanitized[key] = 'REDACTED';
      } else {
        sanitized[key] = maskPII(data[key]);
      }
    });
    return sanitized;
  }
  return data;
}
Enter fullscreen mode Exit fullscreen mode

This function replaces sensitive fields with 'REDACTED'.

Step 2: Override Console Logging

To prevent PII from leaking via logs, I override the default console.log to sanitize data before printing:

const originalConsoleLog = console.log;
console.log = function(...args) {
  const sanitizedArgs = args.map(arg => {
    if (typeof arg === 'object') {
      return maskPII(arg);
    }
    return arg;
  });
  originalConsoleLog.apply(console, sanitizedArgs);
};
Enter fullscreen mode Exit fullscreen mode

This substitution ensures that whenever data is logged for debugging, sensitive info is masked.

Step 3: Intercept Data Send Functions

Similarly, for network requests, I patched the fetch API to sanitize payloads:

const originalFetch = window.fetch;
window.fetch = function(input, init = {}) {
  if (init.body && typeof init.body === 'string') {
    try {
      const bodyData = JSON.parse(init.body);
      init.body = JSON.stringify(maskPII(bodyData));
    } catch (e) {
      // Non-JSON payloads, skip masking
    }
  }
  return originalFetch(input, init);
};
Enter fullscreen mode Exit fullscreen mode

This approach ensures that even in network requests, PII is redacted dynamically.

Monitoring and Alerts

Since margin for error remains high without proper documentation, I added runtime monitoring within the application's test suite. Using custom reporters or middleware, I logged whenever actual PII fields slipped through or were not masked, correlating it with specific code paths.

Conclusion

In environments lacking documentation, the combination of dynamic code patching and runtime data masking offers a robust safeguard against PII leaks. By overriding console functions and network primitives, sensitive data is sanitized at the source, reducing risk without significant refactoring.

Ensuring that such measures are integrated into continuous testing environments and complemented by proper data handling policies is essential for maintaining compliance and protecting user privacy.

This approach highlights how proactive, code-based controls in JavaScript can serve as a first line of defense amid documentation gaps, providing a scalable safeguard for sensitive data in test environments.


🛠️ QA Tip

To test this safely without using real user data, I use TempoMail USA.

Top comments (0)