Skip to content

Redact sensitive information

Bird Eats Bug offers multiple approaches to automatically scrub sensitive information from data before it is uploaded, which are described below.

INFO

Currently, it is not possible to redact screenshots or video recordings.

Default redactions

The following data is redacted by default:

  • Network requests:

    • The Authorization header.
    • All the text after the term password.
    • Any word following the keyword Bearer.
    • Values following the keys access_token (or accessToken), refresh_token (or refreshToken), id_token (or idToken), decoded_token (or decodedToken).

    These rules are visible, changeable, and restorable in Workspace Settings -> General -> Data redaction.

  • Key presses:

    • In inputs with type="password"
    • In inputs with the autocomplete attribute with one of the following values:, current-password, new-password, one-time-code, cc-number, cc-csc, cc-exp.

Prevent that certain data is recorded

You can add a custom data-birdeatsbug="ignore" HTML attribute to any element on the page to instruct the browser extension and the SDK to ignore certain types of data:

  • Clicks on that specific element or its children.
  • Key presses on that specific element or its children.
  • DOM elements will only be recorded as empty elements with the same size as the original element.

The upside of this method is that software developers can exclude sensitive information from being recorded right when they are implementing features that include such data. The redaction will apply to any current or future Bird Eats Bug recorder, independent of its specific configuration. The downside is that source code changes are required, and that only the data points listed above are covered by this feature.

Redact data with regular expressions

Workspace Settings -> General -> Data redaction allows to configure regular expression patterns to redact sensitive information before a session is uploaded with the browser extension or the SDK. These regular expressions apply to all data visible in the Developer Tools of a recording. In comparison to the data-birdeatsbug="ignore" HTML attribute discussed above, DOM recordings are not affected by this redaction method, and it is not possible to redact which keys where pressed. Regular expressions are handy for reacting console logs and network requests though.

Regular expresssions should be as targeted / specific as possible to avoid over-redaction. All regular expressions added to the Data redaction workspace settings are applied to all data visible in the Developer Tools.

INFO

When a redacted string is less than 4 characters long, the redaction will still be returned as ****. This is to ensure that guessing the original data is not too easy for short values.

The regular expressions entered in the workspace settings should not contain a leading /, or trailing /, nor regular expression options. The values should only be the content of the expression. They will be automatically wrapped with / and the gm options (global, multi-line) when the patterns are passed to a JavaScript RegExp constructor prior to being executed.

Sometimes the value that should be redacted is known up front, or at least the value pattern. But usually there's a keyword indicating that the following, dynamic value of unknown characters and length should be redacted.

For instance, you'll want to redact the string following the term password. In these cases, use capturing groups to denote the values that should be replaced with * characters, and the parts of the regular expression outside to match the key terms. The same approach is used to redact the value in key-value pairs, e.g. in network request/response headers, or network request/response JSON.

This will become clearer if you read the examples below:

Redaction examples

Network request URLs:

Given a URL http://example.com/login?userName=test&password=insecure&otp=1234, the pattern =[^=&?]*+ will result in the redacted URL http://example.com/login?userName*****&password*********&otp*****.

To just redact the password query parameter on example.com/login, irrespective of query parameter order, we could also use a capture group like example\.com\/login.*password=([^&]*) to receive http://example.com/login?userName=test&password=********&otp=1234.

Network request / response headers

Assuming we have a network request with the request headers:

json
{
	"Access-Control-Allow-Origin": "*",
	"Authorization": "Bearer eySomeVeryLongJwtToken",
	"Content-Type": "application/javascript"
	// ... all the other headers
}

To redact the Authorization request header, we could use the pattern Authorization([^\;]+) to get:

json
{
	"Access-Control-Allow-Origin": "*",
	"Authorization": "*****************************",
	"Content-Type": "application/javascript"
	// ... all the other headers
}

Or let's take an imaginary reply with the response headers:

json
{
	"Cache-Control": "no-cache",
	"Set-Cookie": "some-token=eyMySuperSecretToken; Path=/; Expires=Fri, 09 Feb 2024 13:30:17 GMT; HttpOnly; Secure; SameSite=Strict"
	// ... all the other headers
}

To redact the token from the Cookie response header, we would configure the expression some-token=([^\;]+) to get:

json
{
	"Cache-Control": "no-cache",
	"Set-Cookie": "some-token=********************; Path=/; Expires=Fri, 09 Feb 2024 13:30:17 GMT; HttpOnly; Secure; SameSite=Strict"
	// ... all the other headers
}

Network request / response bodies

Imagine a network response body that contains a nested JSON object:

json
{
	"password": "insecure",
	"isSecret": true,
	"user": {
		"name": "bob",
		"isNewUser": false,
		"company": {
			"creditCardNumber": "4242 4242 4242 4242",
			"country": "Germany"
		}
	}
}

To get to the following redacted JSON:

json
{
	"password": "********",
	"isSecret": "****",
	"user": {
		"name": "****",
		"isNewUser": false,
		"company": {
			"creditCardNumber": "*******************",
			"country": "Germany"
		}
	}
}

... combine the redaction patterns password(.+), isSecret(.+), and creditCardNumber(.+). Every pattern applies to every key/value-pair in the JSON object, even the nested ones.

Note how the boolean value isSecret turned into 4 characters, which prevents understanding whether the value was false before redaction. The 3 letter name is now also 4 letters.

Click events

If we want to hide the text of a clicked element, and also some secret data- attributes, we could use the patterns data-temporary-password(.+) and password is "(.+)" to get from the click event:

json
{
	"tagName": "section",
	"attributes": {"data-temporary-password": "insecure", "class": "reset-password-section"},
	"innerText": "Registration successful. Your temporary new password is \"insecure\". Please enter your new password below."
}

...to a redacted click event:

json
{
	"tagName": "section",
	"attributes": {"data-temporary-password": "********", "class": "reset-password-section"},
	"innerText": "Registration successful. Your temporary new password is \"********\". Please enter your new password below."
}

Errors

Lastly, let's redact a user ID and the source file paths in the stacktrace of an uncaught error. Here we are using the patterns auth0\|(.+) and \/([^\/]+).js to redact the original event:

json
{
	"message": "Failed to update user auth0|email123",
	"stack": "TypeError: Failed to fetch\n    at https://example.com/assets/index-30vtPKVQ.js:13:3929\n    at window.fetch\n    at https://example.com/assets/UpdateUserName-PeaKgeUR.js:1:2513\n    at r (https://example.com/assets/index-30vtPKVQ.js:27:1426)"
}

...to the redacted error event:

json
{
	"message": "Failed to update user auth0|********",
	"stack": "TypeError: Failed to fetch\n    at https://example.com/assets/**************.js:13:3929\n    at window.fetch\n    at https://example.com/assets/***********************.js:1:2513\n    at r (https://example.com/assets/**************.js:27:1426)"
}

Additional data redaction possibilities in the SDK

The SDK allows to customize the options for DOM recording by passing options to the underlying library, rrweb. How to customize this option is described here. rrweb's privacy options are documented here.

With SDK, you have full programmatic control how the recorded data is mutated before it is uploaded. This code example show how to do that with the beforeUpload hook.

Disable recording of certain data types

Disabling all data recording of a certain data type like network requests is possible too. This is only a last resort though, as it also prevents the recording of data that can be useful for debugging.