rollbar / rollbar.js

Error tracking and logging from Javascript to Rollbar

Home Page:https://docs.rollbar.com/docs/javascript

Geek Repo:Geek Repo

Github PK Tool:Github PK Tool

Scrubbing not working when object referenced multiple times in item metadata

mikekreeki opened this issue · comments

We noticed on our errors that value scrubbing is not working sometimes. I was able to dig through the scrubbing logic and I believe the issue is the following.

Imagine in case of a failed request to the server we have request object which we log into Rollbar together with the error as extra. This object contains sensitive data. We also log the response we received from the server. The sensitive data will be in these keys on item occurrence:

body.trace.extra.request.password
body.trace.extra.response.request.password

The important bit is that the same request object is referenced twice in the tree which is scrubbed before sending to Rollbar API.

The first time the request object is encountered while scrubber is traversing the tree, it is scrubbed correctly and replaced by new object with scrubbed values here https://github.com/rollbar/rollbar.js/blob/master/src/utility/traverse.js#L26-L35.

The second time request object is encountered, scrubbing is skipped because we have encountered it before https://github.com/rollbar/rollbar.js/blob/master/src/utility/traverse.js#L9-L11.

It seems to me that the "seen" optimization causes this issue and leakage of sensitive data to Rollbar, because new object is created when some value is scrubbed, therefore https://github.com/rollbar/rollbar.js/blob/master/src/utility/traverse.js#L10 cannot return the old (unscrubbed) one on the next encounter.

I'm not able to reproduce this so far. If request is the same object both times, password gets scrubbed the first time and is still scrubbed the second time because request is the same object. If request is a different object, it gets scrubbed both times.

Example:
scrubFields: ['password']

  const customData = {}
  const request = { password: 'foo' }
  const response = {}
  response.request = request;
  customData.request = request;
  customData.response = response;

In the payload:

"extra": {
        "request": {
          "password": "********"
        }, 
        "response": {
          "request": {
            "password": "********"
          }
        }

Can you give more details to set this up?

Hi @waltjones, thanks for looking this. I tried to put together a reproducible scenario (sorry for a little bit hacky code) and run it in browser console in our app. Your code looks the same to me as mine.

var scrubPayload = window.Rollbar.client.notifier.transforms.find(t => t.name === 'scrubPayload');

var request = {
  password: 'foo'
}

var item = { 
  data: {
    request,
    response: { request }
  }
}

scrubPayload(item, { scrubFields: ['password'] }, (_, scrubbed) => console.log(scrubbed));

Result:

{ 
  data: {
    request: {password: "********"},
    response: { 
      request: {password: "foo"}
    }
  }
}

Tested on 2.15.0. Let me test it with the most recent version.

Modified code for 2.19.4

var request = {
  password: 'foo'
}

var item = { 
  data: {
    request,
    response: { request }
  }
}

window.Rollbar.scrub(item, ['password'], []);

Same result as above.

Scrubber does not seem to ever mutate the object, that's the important bit, a new request object is created with scrubbed values instead, but never remembered anywhere in traverse function so it cannot be reused on the second encounter, instead the original object (unscrubbed) is kept because scrubbing is skipped this time by "seen" logic in the traverse function. Thats still how it looks to me.

Thank you. Your example, when pasted into the console, did repro the issue. But I kept seeing the redaction succeed when running the full SDK. It turns out the custom data object gets deep cloned when the item is created.

https://github.com/rollbar/rollbar.js/blob/v2.19.4/src/utility.js#L441

This caused the objects to no longer be the same object, and the scrub was successful.

I've opened a PR with just the test case for now.

Great! We're not using scrub function directly though as shown in the example above. We logs stuff normally via Rollbar(err, data) which also produces the issue I describe going the whole SDK, but does not show in a trivial example. Let me followup with a reproducible case which looks more like the real code from our app.

Here is a more real-world example, still pretty contrived, but more or less what we do (we log request/response objects from superagent library).

window.superagent.post('https://reqres.in/api/unknown/1', { password: 'foo' }).end((_err, res) => {
  var err = new Error('Request failed');

  var errData = {
    err,
    req: res.req,
    res,
  };

  var rollbar = new window.rollbar({ 
    accessToken: "POST_CLIENT_ITEM_ACCESS_TOKEN",
  })

  rollbar.error(err, errData);
})

Runnable version here https://playcode.io/717565/.

The request to log the error is fired and in network tab you can see the unscrubbed password.

Screenshot 2020-12-23 at 09 47 16

Could be caused by some specificity of superagent objects as well.

That makes sense. It was just a coincidence that I was testing with a part of the payload that was already cloned before reaching the scrubbing transform.