Skip to content

feat: non-nullable privacy setting #2382

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -79,7 +79,8 @@ class ScreenshotEventProcessor implements EventProcessor {
var recorder = ScreenshotRecorder(
ScreenshotRecorderConfig(
quality: _options.screenshot.screenshotQuality),
_options);
_options,
isReplayRecorder: false);

Uint8List? _screenshotData;

Expand Down
21 changes: 13 additions & 8 deletions flutter/lib/src/screenshot/recorder.dart
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,6 @@ import 'package:flutter/rendering.dart';
import 'package:meta/meta.dart';

import '../../sentry_flutter.dart';
import 'masking_config.dart';
import 'recorder_config.dart';
import 'widget_filter.dart';

Expand All @@ -21,13 +20,19 @@ class ScreenshotRecorder {
WidgetFilter? _widgetFilter;
bool warningLogged = false;

ScreenshotRecorder(this.config, this.options) {
/// TODO: Rewrite when default redaction value are synced with SS & SR
final SentryMaskingConfig maskingConfig =
(options.experimental.privacy ?? SentryPrivacyOptions())
.buildMaskingConfig();

if (maskingConfig.length > 0) {
// TODO: remove in the next major release, see recorder_test.dart.
@visibleForTesting
bool get hasWidgetFilter => _widgetFilter != null;

// TODO: remove [isReplayRecorder] parameter in the next major release, see _SentryFlutterExperimentalOptions.
ScreenshotRecorder(this.config, this.options,
{bool isReplayRecorder = true}) {
// see `options.experimental.privacy` docs for details
final privacyOptions = isReplayRecorder
? options.experimental.privacyForReplay
: options.experimental.privacyForScreenshots;
final maskingConfig = privacyOptions?.buildMaskingConfig();
if (maskingConfig != null && maskingConfig.length > 0) {
_widgetFilter = WidgetFilter(maskingConfig, options.logger);
}
}
Expand Down
21 changes: 0 additions & 21 deletions flutter/lib/src/sentry_flutter.dart
Original file line number Diff line number Diff line change
Expand Up @@ -99,8 +99,6 @@ mixin SentryFlutter {
// ignore: invalid_use_of_internal_member
runZonedGuardedOnError: runZonedGuardedOnError,
);
// TODO: Remove when we synced SS and SR configurations and have a single default configuration
_setRedactionOptions(options);

if (_native != null) {
// ignore: invalid_use_of_internal_member
Expand Down Expand Up @@ -245,25 +243,6 @@ mixin SentryFlutter {
options.sdk = sdk;
}

/// Masking behaviour
/// - If only Screenshot is enabled: masking is disabled by default.
/// - If both Screenshot and Session Replay are enabled: masking is enabled for both by default.
/// - If the user explicitly sets masking to false: masking is disabled for both features.
/// We don't want to break the existing screenshot integration which is not masked by default.
/// The plan is to unify screenshot and replay masking with the next major release.
static void _setMaskingOptions(SentryFlutterOptions options) {
if (options.experimental.privacy != null) {
return;
} else if (options.screenshot.attachScreenshot == true &&
!options.experimental.replay.isEnabled) {
options.experimental.privacy = SentryPrivacyOptions()
..maskAllText = false
..maskAllImages = false;
} else {
options.experimental.privacy = SentryPrivacyOptions();
}
}

/// Reports the time it took for the screen to be fully displayed.
/// This requires the [SentryFlutterOptions.enableTimeToFullDisplayTracing] option to be set to `true`.
static Future<void> reportFullyDisplayed() async {
Expand Down
28 changes: 27 additions & 1 deletion flutter/lib/src/sentry_flutter_options.dart
Original file line number Diff line number Diff line change
Expand Up @@ -395,5 +395,31 @@ class _SentryFlutterExperimentalOptions {
final replay = SentryReplayOptions();

/// Privacy configuration for masking sensitive data in the Screenshot and Session Replay.
SentryPrivacyOptions? privacy;
/// Screen content masking redaction is:
/// - enabled by default for SessionReplay
/// - disabled by default for screenshots captured with events.
/// In order to redact screenshots captured with events, access or change
/// this property in your application: `options.experimental.privacy`.
/// Doing so will indicate that you want to configure privacy settings and
/// will enable screenshot redaction alongside the default replay redaction.
/// Note: this will change in a future SDK major release to enable screenshot
/// redaction by default for all captures.
SentryPrivacyOptions get privacy {
// If the user explicitly sets the privacy setting, we use that.
// Otherwise, we use the default settings, which is no redaction for screenshots
// and full redaction for session replay.
// This property must only by accessed by user code otherwise it defeats the purpose.
_privacy ??= SentryPrivacyOptions();
return _privacy!;
}

/// TODO: remove when default redaction value are synced with SS & SR in the next major release
SentryPrivacyOptions? _privacy;

@meta.internal
SentryPrivacyOptions? get privacyForScreenshots => _privacy;

@meta.internal
SentryPrivacyOptions get privacyForReplay =>
_privacy ?? SentryPrivacyOptions();
}
27 changes: 27 additions & 0 deletions flutter/test/replay/recorder_test.dart
Original file line number Diff line number Diff line change
Expand Up @@ -55,6 +55,33 @@ void main() async {
await _Fixture.create(tester, quality: SentryScreenshotQuality.low);
expect(fixture.capture(), completion('427x854'));
});

// TODO: remove in the next major release, see _SentryFlutterExperimentalOptions.
group('Widget filter is used based on config or application', () {
test('Uses widget filter by default for Replay', () {
final sut = ScreenshotRecorder(
ScreenshotRecorderConfig(),
defaultTestOptions(),
);
expect(sut.hasWidgetFilter, isTrue);
});

test('Does not use widget filter by default for Screenshots', () {
final sut = ScreenshotRecorder(
ScreenshotRecorderConfig(), defaultTestOptions(),
isReplayRecorder: false);
expect(sut.hasWidgetFilter, isFalse);
});

test(
'Uses widget filter for Screenshots when privacy configured explicitly',
() {
final sut = ScreenshotRecorder(ScreenshotRecorderConfig(),
defaultTestOptions()..experimental.privacy.maskAllText = false,
isReplayRecorder: false);
expect(sut.hasWidgetFilter, isTrue);
});
});
}

class _Fixture {
Expand Down
Loading