Skip to content

Instantly share code, notes, and snippets.

@farhad-taran
Last active April 20, 2023 20:31
Show Gist options
  • Save farhad-taran/1b8ed40a75a7fb5ad9a68dbb23a2ee5f to your computer and use it in GitHub Desktop.
Save farhad-taran/1b8ed40a75a7fb5ad9a68dbb23a2ee5f to your computer and use it in GitHub Desktop.
Selectively masking nested properties for PII and GDPR purposes

we need to mask some specific fields in an object because they are personal identifying info, we would like to do this selectively because in large objects it is possible that nested properties might have the same name but they might not be required to be hidden.

the following function uses recursion to traverse the properties of an object and it accumulates the path to each property, this way the consumer can pass in the paths that it wants hidden and upon detecting these paths it proceeds to hide them.

export default function maskProperties(
  obj: Record<string, any>,
  pathsToMask: string[],
  accumulatedPath = '',
) {
  for (const property in obj) {
    if (obj.hasOwnProperty(property)) {
      const currentPath = accumulatedPath + '.' + property;
      if (pathsToMask.includes(currentPath)) {
        obj[property] = '[HIDDEN]';
      }
      if (typeof obj[property] == 'object') {
        maskProperties(obj[property], pathsToMask, currentPath);
      }
    }
  }
}

usage:

const data = {
          account: {
            tenant: 'some',
            email: '[email protected]',
            phone: 1234567,
            names: [
              { givenName: 'Jason', familyName: 'Mays', type: 'primary' },
            ],
          },
        };
        
maskProperties(data, ['.payload.account.email', '.payload.account.phone', '.payload.account.names']);

result:

{
  payload: {
    account: {
      tenant: "some",
      email: "[HIDDEN]",
      phone: "[HIDDEN]",
      names: "[HIDDEN]"
    }
  }
}

If however you would like to mask all properties in an object you could use the following:

function mask(propValue) {
    if (!propValue) return propValue;
    const regex = /(?<!^).(?!$)/g;
    return propValue.toString().replace(regex, '*');
  }

  function doMasking(obj: Record<string, any>) {
    for (const property in obj) {
      if (Object.prototype.hasOwnProperty.call(obj, property)) {
        if (typeof obj[property] == 'object') {
          doMasking(obj[property]);
        } else {
          obj[property] = mask(obj[property]);
        }
      }
    }
  }

  export function maskProperties(obj: Record<string, any> | undefined) {
    if (!obj) {
      return undefined;
    }
    const copy = { ...obj };
    doMasking(copy);
    return copy;
  }
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment