1

I have a function which loops over the keys of an object and looks for sensitive keys like email, key, password, apiKey, secrets, secret and userKey.

If it finds any that have a value, it redacts the value.

However, its failing sometimes with an error like:

"RangeError: Maximum call stack size exceeded"

Whats causing the endless recursion??

const deepObjectRedactor = obj => {
  const sensitiveKeys = [
    'email',
    'key',
    'password',
    'apiKey',
    'secrets',
    'secret',
    'userKey'
  ];
  Object.keys(obj).forEach(key => {
    if (
      sensitiveKeys.map(e => e.toLowerCase()).includes(key.toLowerCase()) &&
      obj[key]
    ) {
      obj[key] = '**********';
      return;
    }

    if (obj[key] && typeof obj[key] === 'object') {
      deepObjectRedactor(obj[key]);
    }
  });
};


// function invoking the redactor

const customRedactorFormat = info => {
  if (info.message && typeof info.message === 'object') {
    deepObjectRedactor(info.message);
  }
  return info;
});
6
  • it means that it reached the memory limit. Commented May 21, 2021 at 7:13
  • 2
    in recursion base case isn't being met. Commented May 21, 2021 at 7:13
  • 2
    sensitiveKeys.find instead of sensitiveKeys.map might be one of the problem. Commented May 21, 2021 at 7:14
  • 1
    Might be, that there's a cycle in the object somewhere. As your function does not track already seen objects, you would recursive infinitely. Also your condition is quite awkward, why not simple: sensitiveKeys.includes(key.toLowerCase());? sensitiveKeys is a constant in your function. If you want them to be all lower case, define them as all lower case. Instead of lower casing them over and over and over. Commented May 21, 2021 at 7:17
  • 2
    @nishkaush can you provide a simplified variant of your object Commented May 21, 2021 at 7:22

1 Answer 1

1

You can write a generic map that works for objects and arrays. And then write redact as a specialization of map -

function map (t, f)
{ switch (t?.constructor)
  { case Array:
      return t.map((v, k) => f(k, map(v, f)))
    case Object:
      return Object.fromEntries(
        Object.entries(t).map(([k, v]) => [k, f(k, map(v, f))])
      )
    default:
      return t
  }
}

const redact = (t, keys = new Set) =>
  map(t, (k, v) => keys.has(k) ? "*****" : v)
  
const data =
  [ { user: 1, cc: 1234, password: "foo" }
  , { nested: [ { a: 1, pin: 999 }, { b: 2, pin: 333 } ] }
  , { deeply: [ { nested: [ { user: 2, password: "here" } ] } ] }
  ]
  
console.log(redact(data, new Set(["cc", "password", "pin"])))

[
  {
    "user": 1,
    "cc": "*****",
    "password": "*****"
  },
  {
    "nested": [
      {
        "a": 1,
        "pin": "*****"
      },
      {
        "b": 2,
        "pin": "*****"
      }
    ]
  },
  {
    "deeply": [
      {
        "nested": [
          {
            "user": 2,
            "password": "*****"
          }
        ]
      }
    ]
  }
]
Sign up to request clarification or add additional context in comments.

1 Comment

Code is much cleaner than OP's, but OP's code has the exact same result on your sample data object. Im fairly certain their issue lies in the Object they are trying to recurse over (likely a circular reference). jsfiddle.net/o6fhbLxr

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.