Repro Steps and Auto-masking Screenshots

How to automatically mask the screens reported in Repro Steps

Repro Steps is your go-to tool for understanding what led to a bug or crash in your app. This guide will help you make the most of Repro Steps.

What are "Repro Steps"?

Repro Steps capture and present all the actions a user takes in your app before encountering a bug or crash. These steps are grouped by the app view, and you can view user steps alongside relevant screenshots. It's like having a play-by-play visual and written record of the user's actions leading to the problem.

Repro steps captured by Instabug

Repro steps captured by Instabug

Where can you find “Repro Steps”?

To find the Repro Steps, simply scroll down below the bug details. If you are on the crash reporting tool, you will find them in the occurrences page of a crash below the crash details. Check the screenshot below to find them

Location of Reprosteps inside a Crash Report

Location of Reprosteps inside a Crash Report

Location of Reprosteps inside a Bug Report

Location of Reprosteps inside a Bug Report

Understanding the Setup

Repro Steps are organized by the different screens in your app that users interact with. Each screen view visited by the user contains a set of chronological steps, giving you a clear picture of what led to the reported issue.

Understanding Logged Data and Captured Events

Logged data captures user interactions, such as swipes, scrolls, taps, and more. It provides insights into how users engage with your app. The captured events track app states, including background/foreground transitions, activity changes, and memory warnings. To know more about the logged data, you can find the details here.

How can you enable Repro steps

Repro Steps is by default enabled with screenshots for bug reporting, and enabled without screenshots for crash reporting. So do you need to change anything here? If yes, you can use the following properties:

Instabug.setReproStepsFor(.all, with: .enable)
[Instabug setReproStepsFor:IBGIssueTypeAll withMode:IBGUserStepsModeEnable];

You can change the value of the Issue Type.

//Bug Reporting and Crash Reporting  
.all  
//Bug Reporting Only  
.bug  
//Crash Reporting Only  
.crash
//Bug Reporting and Crash Reporting
IBGIssueTypeAll
//Bug Reporting Only
IBGIssueTypeBug
//Crash Reporting Only
IBGIssueTypeCrash

You can change the value of the User Steps Mode.

//Enable with Screenshots  
.enable  
//Enable with NO Screenshots  
.enabledWithNoScreenshots  
//Disable  
.disable
//Enable with Screenshots
IBGUserStepsModeEnable
//Enable with NO Screenshots
IBGUserStepsModeEnabledWithNoScreenshots
//Disable
IBGUserStepsModeDisable

Examples

Enable with Screenshots for both Bug Reporting and Crash Reporting:

Instabug.setReproStepsFor(.all, with: .enable)
[Instabug setReproStepsFor:IBGIssueTypeAll withMode:IBGUserStepsModeEnable];

Enable with No Screenshots for Crash Reporting.

Instabug.setReproStepsFor(.crash, with: .enabledWithNoScreenshots)
[Instabug setReproStepsFor:IBGIssueTypeCrash withMode:IBGUserStepsModeEnabledWithNoScreenshots];

Completely disable for both Bug Reporting and Crash Reporting.

Instabug.setReproStepsFor(.all, with: .disable)
[Instabug setReproStepsFor:IBGIssueTypeAll withMode:IBGUserStepsModeDisable];

User Privacy

We understand user privacy is of key to you, specially having our SDK on production. In this guide, we will show you how to mark views containing sensitive information as private. By doing so, these views will be masked on screenshots captured to your users' devices (before being sent to Instabug servers) and automatically appear with a black overlay (over whole screen, labels, texts or media) in any screenshot on Instabug's dashboard to protect your user’s sensitive data.

📘

Repro steps screenshots are disabled by default on crash reporting.

  1. Identify the View: Determine which view in your app contains sensitive information that should be kept private. For example, a payment details screen or a user's personal profile.

  2. Identify Sensitive Information: Determine which information on each view that you need to hide. Do you want to hide the whole screen? text fields? media? labels?

  3. Deciding on the content of the screenshots
    Instabug provides you with multiple ways to easily protect your users sensitive data, whether you want to hide the content of a whole specific screen or certain text fields, labels and media, we got you covered!

    1. Auto masking
      You can automatically mask sensitive data when screenshots are captured, while protecting the user's privacy by default using auto masking feature. Keep in mind that this feature affects both Bug and Crash Reporting.

      1. Masking text inputs
        You can use this to hide any text fields captured on all your screenshots

        Instabug.setAutoMaskScreenshots([.textInputs])
        
        [Instabug setAutoMaskScreenshots: IBGAutoMaskScreenshotOptionTextInputs];
        
      2. Masking labels
        This can be utilized to hide text labels including buttons and titles

        Instabug.setAutoMaskScreenshots([.labels])
        
        [Instabug setAutoMaskScreenshots: IBGAutoMaskScreenshotOptionLabels];
        
      3. Masking images and videos
        If your users might be sharing private images or videos on the app, in which you wouldn't want access to, you can utilize the following property to hide it.

        Instabug.setAutoMaskScreenshots([.media])
        
        [Instabug setAutoMaskScreenshots: IBGAutoMaskScreenshotOptionMedia];
        
      4. Masking nothing
        You can also disable auto-masking all together if you need using the following property

        Instabug.setAutoMaskScreenshots([.maskNothing])
        
        [Instabug setAutoMaskScreenshots: IBGAutoMaskScreenshotOptionMaskNothing];
        
      5. Mix and match
        You can combine different masking options according to your needs. For example, if you want to mask both text Inputs and labels, you can use the following API

        Instabug.setAutoMaskScreenshots([.textInputs, .labels])
        
    2. Hide a whole screen
      In case you need to add an extra layer of privacy and prevent sensitive information from being captured in screenshots. This property will hide the overall screen when the view appears in a screenshot

      view.instabug_privateView = true
      
      view.instabug_privateView = YES;
      
Example of private view

Example of private view


Set Private View

You can use this API to set any particular view as private so that it is always hidden in screenshots.

view.instabug_privateView = true
view.instabug_privateView = YES;
//Should be added in the activity with the view, takes any number of views
Instabug.addPrivateViews(view1, view2, view3);
//Should be added in the activity with the view, takes any number of views
Instabug.addPrivateViews(view1, view2, view3)
//Add the following tag to the view you'd like to hide
<Text ref={c => this.textView = c} style={styles.welcome}>This is a private view!</Text>

//Then call the following API
Instabug.setPrivateView(this.textView);

You can also remove a view from the list of private views using this API.

//Should be added in the activity with the view, takes any number of views
Instabug.removePrivateViews(view1, view2, view3);
//Should be added in the activity with the view, takes any number of views
Instabug.removePrivateViews(view1, view2, view3)

Network Masking

Instabug automatically logs all network requests performed by your app from the start of the session. Requests details, along with their responses, are sent with each report. Instabug will also show you an alert at the top of the bug report in your dashboard when network requests have timed-out or taken too long to complete. Note that the maximum number of network logs sent with each report is 1,000.

An example of network request logs in the Instabug dashboard

An example of network request logs in the Instabug dashboard

Logging HttpUrlConnection requests for Android

To log network requests, use InstabugNetworkLog then use the following method at the HttpUrlConnection, requestBody and responseBody referenced here.

Importing Network Logger for React Native

First, you'll need to import the Network Logger in order to be able to start logging network requests. You can use the statement referenced here to import it.

Omitting Requests

You can omit requests from being logged based on their request or response details. You can also specify an expression to be evaluated against every request and response to determine if the request should be included in logs or not.

let path = "/products"
let requestPredicate = NSPredicate(format: "URL.path MATCHES %@", path)
let responsePredicate = NSPredicate(format: "statusCode >= %d AND statusCode <= %d", 200, 399)
NetworkLogger.setNetworkLoggingRequestFilterPredicate(requestPredicate, responseFilterPredicate: responsePredicate)
NSString *path = @"/products";
NSPredicate *requestPredicate = [NSPredicate predicateWithFormat:@"URL.path MATCHES %@", path];
NSPredicate *responsePredicate = [NSPredicate predicateWithFormat:@"statusCode >= %d AND statusCode <= %d", 200, 399];
[IBGNetworkLogger setNetworkLoggingRequestFilterPredicate:requestPredicate 
                                responseFilterPredicate:responsePredicate];

// The code above excludes all requests made to URLs that have the /products path.
// It also excludes all responses with success and redirection status codes, 
// thus only including requests with 4xx and 5xx responses.

NetworkLogger.omitLog((data) =>
// you can also use other parts of data for filtering (e.g.: responseBody, requestBody)
	return data.url.contains('/payment');
);

// The code above excludes all requests made to any URL containing /payment. You also use other parts of data for filtering (e.g.: responseBody, requestBody).
NetworkLogger.setRequestFilterExpression('network.requestHeaders[\'accept\'] === \'application/json\'')

// The code above excludes all requests made to URLs that have request header accept set to application/json.

Obfuscating Data

Requests

You can obfuscate sensitive user data, such as authentication tokens, in requests without filtering out the whole request.

NetworkLogger.setRequestObfuscationHandler { (request) -> URLRequest in
    var myRequest: NSMutableURLRequest = request as! NSMutableURLRequest
    let urlString = request.url?.absoluteString
    urlString = obfuscateAuthenticationTokenInString()
    let obfuscatedURL = URL(string: urlString)
    myRequest.url = obfuscatedURL
    return myRequest.copy() as! URLRequest
}

IBGNetworkLogger.requestObfuscationHandler = ^NSURLRequest * _Nonnull(NSURLRequest * _Nonnull request) {
    NSMutableURLRequest *myRequest = [request mutableCopy];
    NSString *urlString = request.URL.absoluteString;
    urlString = [self obfuscateAuthenticationTokenInString:urlString];
    NSURL *obfuscatedURL = [NSURL URLWithString:urlString];
    myRequest.URL = obfuscatedURL;
    return myRequest;
};

NetworkLogger.obfuscateLog((data) {
  data.requestHeaders.remove('Authorization');
  return data;
});

NetworkLogger.setNetworkDataObfuscationHandler(async (networkData) => {
  networkData.requestHeaders = {
    'Content-Type': 'application/json',
    'TestHeader': 'some-header2'
  };
  return networkData;
});

Responses

As with requests, the response object, as well as the response data, can be modified for obfuscation purposes before they are logged.

NetworkLogger.setResponseObfuscationHandler { (data, response, completion) in
    if let data = data {
        let modifiedData = self.modify(data: data)
        let modifiedResponse = self.modify(response: response)
        completion(modifiedData, modifiedResponse)
    }
}

[IBGNetworkLogger setResponseObfuscationHandler:^(NSData * _Nullable responseData,
                                                  NSURLResponse * _Nonnull response,
                                                  NetworkObfuscationCompletionBlock _Nonnull returnBlock) {
    NSData *modifiedData = [self obfuscateData:responseData];
    NSURLResponse *modifiedResponse = [self obfuscateResponse:response];
    returnBlock(modifiedData, modifiedResponse);
}];

NetworkLogger.obfuscateLog((data) {
  data.responseBody = '';
  return data;
});

NetworkLogger.setNetworkDataObfuscationHandler(async (networkData) => {
  networkData.response = {};
  return networkData;
});

Modifying Requests for Android

In the event that you need to modify a network request prior to sending it to the dashboard, you can follow the below steps:

  1. Create a NetworkLogListener object and modify the captured network log as shown below.

    val networkLogListener = NetworkLogListener { networkLog: NetworkLogSnapshot ->
        // Modify the received networkLog parameter
        return@NetworkLogListener networkLog
    }
    
    
    NetworkLogListener networkLogListener = new NetworkLogListener() {
        @Override
        public NetworkLogSnapshot onNetworkLogCaptured(NetworkLogSnapshot networkLog) {
            // Modify the received networkLog parameter
            return networkLog;
            
            // To exclude the network trace from being captured, return null
            // return null;
        }
    };
    
    
  2. Register the created NetworkLogListener to your InstabugOkHttpInterceptor object. This can be done through two different methods:

    1. Pass it to the Constructor:

      val instabugOkhttpInterceptor = InstabugOkhttpInterceptor(networkLogListener)
      
    2. Call registerNetworkLogsListener method on InstabugOkhttpInterceptor object.

      val instabugOkhttpInterceptor = InstabugOkhttpInterceptor()
      instabugOkhttpInterceptor.registerNetworkLogsListener(networkLogListener)
      
      // For the modifications to reflect in APM as well, you can use the below network logs listener
      APM.registerNetworkLogsListener(networkLogListener)
      
      

In case you need to remove the network listener, you can use the below method:

instabugOkhttpInterceptor.removeNetworkLogsListener()

// Use the below API to remove APM's network logs listener
APM.registerNetworkLogsListener(null)

instabugOkhttpInterceptor.removeNetworkLogsListener();

// Use the below API to remove APM's network logs listener
APM.registerNetworkLogsListener(null);

📘

Note: Protecting sensitive information and respecting user privacy are vital. By following these steps, you'll create a secure and trustworthy environment for your users.