Skip to content

Commit

Permalink
feat(flutter): update screenshots and session replay with masking opt…
Browse files Browse the repository at this point in the history
…ions (#11930)

* add masking options

* minor arrangement changes

* add specific intro for screenshot and session replay

* Update docs/platforms/flutter/session-replay/index.mdx

Co-authored-by: Ivan Dlugos <[email protected]>

---------

Co-authored-by: Martin Haintz <[email protected]>
Co-authored-by: Ivan Dlugos <[email protected]>
  • Loading branch information
3 people authored Dec 9, 2024
1 parent fdbdf00 commit 6e67955
Show file tree
Hide file tree
Showing 3 changed files with 75 additions and 39 deletions.
45 changes: 6 additions & 39 deletions docs/platforms/flutter/session-replay/index.mdx
Original file line number Diff line number Diff line change
Expand Up @@ -57,50 +57,17 @@ Sampling allows you to control how much of your website's traffic will result in

Sampling starts as soon as a session begins. The `sessionSampleRate` is then evaluated. If the session is sampled, replay recording will start immediately. If not, `onErrorSampleRate` will be evaluated. If the session is sampled at this point, the replay will be buffered and will only be uploaded to Sentry if an error occurs.

## Privacy
## Redact Session Replay via `masking`

The SDK is recording and aggressively redacting (masking) all `Text`, `EditableText`, and `Image` widgets.
Masking in the Sentry Flutter SDK is based on Widget *types*, e.g. `Image`, not the string representation of the type (i.e. we check whether
a `widgetInstance` should be masked by checking `if (widgetInstance is Image)` instead of `if (widgetInstance.runtimeType == 'Image')`).
This means we can ensure masking works regardless of obfuscation in release builds and also works for subclasses.
However, it also means we can only automatically mask widgets that are part of the Flutter SDK itself.
By default, the SDK is recording and aggressively redacting (masking) all `Text`, `EditableText`, and `Image` widgets for <PlatformLink to="/session-replay/">`Session Replay`</PlatformLink>. To modify or disable this behavior, use the `options.experimental.privacy` parameter.

<Alert level="warning">
We cannot mask widgets defined in various 3rd-party packages (because the type is not known in the Sentry Flutter SDK),
even though many should be masked.

Therefore, you need to consider the widgets your application uses and ensure they're masked correctly with custom masking rules.
Examples of widgets that usually should be masked include (but are not limited to): VideoPlayer, WebView, Chart, etc.
Modifying this parameter will also affect `masking` for
<PlatformLink to="/enriching-events/screenshots/">`Screenshots`</PlatformLink>
.
</Alert>

You can tune this and add custom masking rules to fit your needs by adjusting the configuration in `options.experimental.replay`.
For example, you can explicitly mask or unmask widgets by type,
or you can even have a callback to decide whether a specific widget instance should be masked:

```dart
options.experimental.replay.mask<IconButton>();
options.experimental.replay.unmask<Image>();
options.experimental.replay.maskCallback<Text>(
(Element element, Text widget) =>
(widget.data?.contains('secret') ?? false)
? SentryMaskingDecision.mask
: SentryMaskingDecision.continueProcessing);
```

You can find more details in the documentation for each method.

<Note>

If you find that data isn't being redacted with the default settings, please let us know by creating a [GitHub issue](https://github.com/getsentry/sentry-dart/issues/new?template=BUG_REPORT.yml).

</Note>

To disable redaction altogether (not to be used on applications with sensitive data):

```dart
options.experimental.replay.maskAllText = false;
options.experimental.replay.maskAllImages = false;
```
<PlatformContent includePath="replay/privacy-configuration" />

## Error Linking

Expand Down
11 changes: 11 additions & 0 deletions platform-includes/enriching-events/attach-screenshots/flutter.mdx
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,17 @@ await SentryFlutter.init(
);
```

## Redact Screenshots via `masking`

The masking feature is by default disabled for Screenshots. To enable masking, use the `options.experimental.privacy` parameter.

<Alert level="warning">
Modifying this parameter will also affect `masking` for{" "}
<PlatformLink to="/session-replay/">`Session Replay`</PlatformLink>.
</Alert>

<PlatformContent includePath="replay/privacy-configuration" />

## Filtering Screenshots

You can filter your screenshots by using the `beforeScreenshot` callback, which is called before attaching a screenshot to an event. By default, the callback returns `true` which means that all screenshots are attached.
Expand Down
58 changes: 58 additions & 0 deletions platform-includes/replay/privacy-configuration/flutter.mdx
Original file line number Diff line number Diff line change
@@ -0,0 +1,58 @@
Masking in the Sentry Flutter SDK is based on Widget _types_, e.g. `Image`, not the string representation of the type (i.e. we check whether
a `widgetInstance` should be masked by checking `if (widgetInstance is Image)` instead of `if (widgetInstance.runtimeType == 'Image')`).
This means we can ensure masking works regardless of obfuscation in release builds and also works for subclasses.
However, it also means we can only automatically mask widgets that are part of the Flutter SDK itself.

<Alert level="warning">
We cannot mask widgets defined in various 3rd-party packages (because the type is not known in the Sentry Flutter SDK),
even though many should be masked.

Therefore, you need to consider the widgets your application uses and ensure they're masked correctly with custom masking rules.
Examples of widgets that usually should be masked include (but are not limited to): VideoPlayer, WebView, Chart, etc.

</Alert>

You can tune this and add custom masking rules to fit your needs by adjusting the configuration in `options.experimental.privacy`.
For example, you can explicitly mask or unmask widgets by type,
or you can even have a callback to decide whether a specific widget instance should be masked:

```dart
options.privacy.mask<IconButton>();
options.privacy.unmask<Image>();
options.privacy.maskCallback<Text>(
(Element element, Text widget) =>
(widget.data?.contains('secret') ?? false)
? SentryMaskingDecision.mask
: SentryMaskingDecision.continueProcessing);
```

### `maskAllText`

Mask all text content. Draws a rectangle of text bounds with text color on top. Currently, only `Text` and `EditableText` Widgets are masked.

### `maskAllImages`

Mask content of all images. Draws a rectangle of image bounds with image's dominant color on top. Currently, only `Image` widgets are masked.

### `maskAssetImages`

Mask asset images coming from the root asset bundle.

### `mask<T extends Widget>()`

Mask given widget type `T` (or subclasses of `T`) in the screenshot. Note: masking rules are called in the order they're added so if a previous rule already makes a decision, this rule won't be called.

You can find more details in the documentation for each method.

<Note>

If you find that data isn't being masked with the default settings, please let us know by creating a [GitHub issue](https://github.com/getsentry/sentry-dart/issues/new?template=BUG_REPORT.yml).

</Note>

To disable masking for <PlatformLink to="/enriching-events/screenshots/">`Screenshots`</PlatformLink> and <PlatformLink to="/session-replay/">`Session Replay`</PlatformLink> (not to be used on applications with sensitive data):

```dart
options.experimental.privacy.maskAllText = false;
options.experimental.privacy.maskAllImages = false;
```

0 comments on commit 6e67955

Please sign in to comment.