Skip to content

Commit 8ec220e

Browse files
authored
Merge 4cfb596 into 41834f1
2 parents 41834f1 + 4cfb596 commit 8ec220e

File tree

11 files changed

+233
-90
lines changed

11 files changed

+233
-90
lines changed

CHANGELOG.md

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,11 @@
11
# Changelog
22

3+
## Unreleased
4+
5+
### Fixes
6+
7+
- Disable SessionSentryReplayIntegration if the environment is unsafe [#6573]
8+
39
## 8.57.0
410

511
> [!Warning]

Sentry.xcodeproj/project.pbxproj

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1012,6 +1012,7 @@
10121012
F41362112E1C55AF00B84443 /* SentryScopePersistentStore+Tags.swift in Sources */ = {isa = PBXBuildFile; fileRef = F41362102E1C55AF00B84443 /* SentryScopePersistentStore+Tags.swift */; };
10131013
F41362132E1C566100B84443 /* SentryScopePersistentStore+User.swift in Sources */ = {isa = PBXBuildFile; fileRef = F41362122E1C566100B84443 /* SentryScopePersistentStore+User.swift */; };
10141014
F41362152E1C568400B84443 /* SentryScopePersistentStore+Context.swift in Sources */ = {isa = PBXBuildFile; fileRef = F41362142E1C568400B84443 /* SentryScopePersistentStore+Context.swift */; };
1015+
F426748D2EB11E7900E09150 /* SentryReplayApiTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = F42674872EB11E7000E09150 /* SentryReplayApiTests.swift */; };
10151016
F443DB272E09BE8C009A9045 /* LoadValidatorTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = F443DB262E09BE8C009A9045 /* LoadValidatorTests.swift */; };
10161017
F44858132E03579D0013E63B /* SentryCrashDynamicLinker+Test.h in Headers */ = {isa = PBXBuildFile; fileRef = F44858122E0357940013E63B /* SentryCrashDynamicLinker+Test.h */; };
10171018
F451FAA62E0B304E0050ACF2 /* LoadValidator.swift in Sources */ = {isa = PBXBuildFile; fileRef = F451FAA52E0B304E0050ACF2 /* LoadValidator.swift */; };
@@ -2376,6 +2377,7 @@
23762377
F41362102E1C55AF00B84443 /* SentryScopePersistentStore+Tags.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "SentryScopePersistentStore+Tags.swift"; sourceTree = "<group>"; };
23772378
F41362122E1C566100B84443 /* SentryScopePersistentStore+User.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "SentryScopePersistentStore+User.swift"; sourceTree = "<group>"; };
23782379
F41362142E1C568400B84443 /* SentryScopePersistentStore+Context.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "SentryScopePersistentStore+Context.swift"; sourceTree = "<group>"; };
2380+
F42674872EB11E7000E09150 /* SentryReplayApiTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SentryReplayApiTests.swift; sourceTree = "<group>"; };
23792381
F443DB262E09BE8C009A9045 /* LoadValidatorTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = LoadValidatorTests.swift; sourceTree = "<group>"; };
23802382
F44858122E0357940013E63B /* SentryCrashDynamicLinker+Test.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = "SentryCrashDynamicLinker+Test.h"; sourceTree = "<group>"; };
23812383
F451FAA52E0B304E0050ACF2 /* LoadValidator.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = LoadValidator.swift; sourceTree = "<group>"; };
@@ -4418,6 +4420,7 @@
44184420
D80694C12B7CC85800B820E6 /* SessionReplay */ = {
44194421
isa = PBXGroup;
44204422
children = (
4423+
F42674872EB11E7000E09150 /* SentryReplayApiTests.swift */,
44214424
D4D0E1E22E9D040800358814 /* SentrySessionReplayEnvironmentCheckerTests.swift */,
44224425
D49480D22DC23E8E00A3B6E9 /* SentryReplayTypeTests.swift */,
44234426
D80694C22B7CC86E00B820E6 /* SentryReplayEventTests.swift */,
@@ -6371,6 +6374,7 @@
63716374
D4CA34832E378C9900E92A61 /* SentryArrayTests.swift in Sources */,
63726375
7B05A61824A4D14A00EF211D /* SentrySessionGeneratorTests.swift in Sources */,
63736376
D8CB742B294B1DD000A5F964 /* SentryUIApplicationTests.swift in Sources */,
6377+
F426748D2EB11E7900E09150 /* SentryReplayApiTests.swift in Sources */,
63746378
63FE720920DA66EC00CDBAE8 /* XCTestCase+SentryCrash.m in Sources */,
63756379
D8918B222849FA6D00701F9A /* SentrySDKIntegrationTestsBase.swift in Sources */,
63766380
620078782D3906BF0022CB67 /* SentryCodableTests.swift in Sources */,

Sources/Sentry/SentryReplayApi.m

Lines changed: 7 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -56,8 +56,14 @@ - (void)start SENTRY_DISABLE_THREAD_SANITIZER("double-checked lock produce false
5656
replayIntegration = (SentrySessionReplayIntegration *)[SentrySDKInternal.currentHub
5757
getInstalledIntegration:SentrySessionReplayIntegration.class];
5858
if (replayIntegration == nil) {
59-
SENTRY_LOG_DEBUG(@"[Session Replay] Initializing replay integration");
6059
SentryOptions *currentOptions = SentrySDKInternal.currentHub.client.options;
60+
if (![SentrySessionReplayIntegration shouldEnableForOptions:currentOptions]) {
61+
SENTRY_LOG_ERROR(@"[Session Replay] Session replay is disabled due to "
62+
@"environment potentially causing PII leaks.");
63+
return;
64+
}
65+
SENTRY_LOG_DEBUG(@"[Session Replay] Initializing replay integration");
66+
6167
replayIntegration =
6268
[[SentrySessionReplayIntegration alloc] initForManualUse:currentOptions];
6369

Sources/Sentry/SentrySessionReplayIntegration.m

Lines changed: 23 additions & 27 deletions
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,7 @@
1010
# import "SentryEvent+Private.h"
1111
# import "SentryFileManager.h"
1212
# import "SentryHub+Private.h"
13+
# import "SentryInternalDefines.h"
1314
# import "SentryLogC.h"
1415
# import "SentryOptions.h"
1516
# import "SentryRateLimits.h"
@@ -52,7 +53,6 @@ - (void)newSceneActivate;
5253
@implementation SentrySessionReplayIntegration {
5354
BOOL _startedAsFullSession;
5455
SentryReplayOptions *_replayOptions;
55-
SentryExperimentalOptions *_experimentalOptions;
5656
id<SentryNSNotificationCenterWrapper> _notificationCenter;
5757
id<SentryRateLimits> _rateLimits;
5858
id<SentryViewScreenshotProvider> _currentScreenshotProvider;
@@ -64,7 +64,14 @@ @implementation SentrySessionReplayIntegration {
6464
// replay absolutely needs segment 0 to make replay work.
6565
BOOL _rateLimited;
6666
id<SentryCurrentDateProvider> _dateProvider;
67-
id<SentrySessionReplayEnvironmentCheckerProvider> _environmentChecker;
67+
}
68+
69+
+ (BOOL)shouldEnableForOptions:(SentryOptions *)options
70+
{
71+
return [SentrySessionReplay
72+
shouldEnableSessionReplayWithEnvironmentChecker:SentryDependencyContainer.sharedInstance
73+
.sessionReplayEnvironmentChecker
74+
experimentalOptions:options.experimental];
6875
}
6976

7077
- (instancetype)init
@@ -77,39 +84,34 @@ - (instancetype)initForManualUse:(nonnull SentryOptions *)options
7784
{
7885
if (self = [super init]) {
7986
[self setupWith:options.sessionReplay
80-
experimentalOptions:options.experimental
8187
enableTouchTracker:options.enableSwizzling
8288
enableViewRendererV2:options.sessionReplay.enableViewRendererV2
8389
enableFastViewRendering:options.sessionReplay.enableFastViewRendering];
84-
[self startWithOptions:options.sessionReplay
85-
experimentalOptions:options.experimental
86-
fullSession:YES];
90+
[self startWithOptions:options.sessionReplay fullSession:YES];
8791
}
8892
return self;
8993
}
9094

9195
- (BOOL)installWithOptions:(nonnull SentryOptions *)options
9296
{
93-
if ([super installWithOptions:options] == NO) {
97+
if ([super installWithOptions:options] == NO ||
98+
[SentrySessionReplayIntegration shouldEnableForOptions:options] == NO) {
9499
return NO;
95100
}
96101

97102
[self setupWith:options.sessionReplay
98-
experimentalOptions:options.experimental
99103
enableTouchTracker:options.enableSwizzling
100104
enableViewRendererV2:options.sessionReplay.enableViewRendererV2
101105
enableFastViewRendering:options.sessionReplay.enableFastViewRendering];
102106
return YES;
103107
}
104108

105109
- (void)setupWith:(SentryReplayOptions *)replayOptions
106-
experimentalOptions:(SentryExperimentalOptions *)experimentalOptions
107110
enableTouchTracker:(BOOL)touchTracker
108111
enableViewRendererV2:(BOOL)enableViewRendererV2
109112
enableFastViewRendering:(BOOL)enableFastViewRendering
110113
{
111114
_replayOptions = replayOptions;
112-
_experimentalOptions = experimentalOptions;
113115
_rateLimits = SentryDependencyContainer.sharedInstance.rateLimits;
114116
_dateProvider = SentryDependencyContainer.sharedInstance.dateProvider;
115117

@@ -140,7 +142,6 @@ - (void)setupWith:(SentryReplayOptions *)replayOptions
140142

141143
_notificationCenter = SentryDependencyContainer.sharedInstance.notificationCenterWrapper;
142144
_dateProvider = SentryDependencyContainer.sharedInstance.dateProvider;
143-
_environmentChecker = SentryDependencyContainer.sharedInstance.sessionReplayEnvironmentChecker;
144145

145146
// We use the dispatch queue provider as a factory to create the queues, but store the queues
146147
// directly in this instance, so they get deallocated when the integration is deallocated.
@@ -338,9 +339,7 @@ - (void)runReplayForAvailableWindow
338339
if ([SentryDependencyContainer.sharedInstance.application getWindows].count > 0) {
339340
SENTRY_LOG_DEBUG(@"[Session Replay] Running replay for available window");
340341
// If a window its already available start replay right away
341-
[self startWithOptions:_replayOptions
342-
experimentalOptions:_experimentalOptions
343-
fullSession:_startedAsFullSession];
342+
[self startWithOptions:_replayOptions fullSession:_startedAsFullSession];
344343
} else if (@available(iOS 13.0, tvOS 13.0, *)) {
345344
SENTRY_LOG_DEBUG(
346345
@"[Session Replay] Waiting for a scene to be available to started the replay");
@@ -360,27 +359,22 @@ - (void)newSceneActivate
360359
removeObserver:self
361360
name:UISceneDidActivateNotification
362361
object:nil];
363-
[self startWithOptions:_replayOptions
364-
experimentalOptions:_experimentalOptions
365-
fullSession:_startedAsFullSession];
362+
[self startWithOptions:_replayOptions fullSession:_startedAsFullSession];
366363
}
367364
}
368365

369366
- (void)startWithOptions:(SentryReplayOptions *)replayOptions
370-
experimentalOptions:(SentryExperimentalOptions *)experimentalOptions
371367
fullSession:(BOOL)shouldReplayFullSession
372368
{
373369
SENTRY_LOG_DEBUG(@"[Session Replay] Starting session");
374370
[self startWithOptions:replayOptions
375-
experimentalOptions:experimentalOptions
376371
screenshotProvider:_currentScreenshotProvider ?: _viewPhotographer
377372
breadcrumbConverter:_currentBreadcrumbConverter
378373
?: [[SentrySRDefaultBreadcrumbConverter alloc] init]
379374
fullSession:shouldReplayFullSession];
380375
}
381376

382377
- (void)startWithOptions:(SentryReplayOptions *)replayOptions
383-
experimentalOptions:(SentryExperimentalOptions *)experimentalOptions
384378
screenshotProvider:(id<SentryViewScreenshotProvider>)screenshotProvider
385379
breadcrumbConverter:(id<SentryReplayBreadcrumbConverter>)breadcrumbConverter
386380
fullSession:(BOOL)shouldReplayFullSession
@@ -415,16 +409,14 @@ - (void)startWithOptions:(SentryReplayOptions *)replayOptions
415409

416410
SentryDisplayLinkWrapper *displayLinkWrapper = [[SentryDisplayLinkWrapper alloc] init];
417411
self.sessionReplay = [[SentrySessionReplay alloc] initWithReplayOptions:replayOptions
418-
experimentalOptions:experimentalOptions
419412
replayFolderPath:docs
420413
screenshotProvider:screenshotProvider
421414
replayMaker:replayMaker
422415
breadcrumbConverter:breadcrumbConverter
423416
touchTracker:_touchTracker
424417
dateProvider:_dateProvider
425418
delegate:self
426-
displayLinkWrapper:displayLinkWrapper
427-
environmentChecker:_environmentChecker];
419+
displayLinkWrapper:displayLinkWrapper];
428420

429421
[self.sessionReplay
430422
startWithRootView:[SentryDependencyContainer.sharedInstance.application getWindows]
@@ -456,15 +448,19 @@ - (nullable NSURL *)replayDirectory
456448
return [dir URLByAppendingPathComponent:SENTRY_REPLAY_FOLDER];
457449
}
458450

459-
- (void)saveCurrentSessionInfo:(SentryId *)sessionId
451+
- (void)saveCurrentSessionInfo:(SentryId *_Nullable)sessionId
460452
path:(NSString *)path
461453
options:(SentryReplayOptions *)options
462454
{
463455
SENTRY_LOG_DEBUG(@"[Session Replay] Saving current session info for session: %@ to path: %@",
464456
sessionId, path);
465-
NSDictionary *info =
466-
[[NSDictionary alloc] initWithObjectsAndKeys:sessionId.sentryIdString, @"replayId",
467-
path.lastPathComponent, @"path", @(options.onErrorSampleRate), @"errorSampleRate", nil];
457+
NSMutableDictionary *info = [NSMutableDictionary new];
458+
if (sessionId != nil) {
459+
[info setObject:SENTRY_UNWRAP_NULLABLE(SentryId, sessionId).sentryIdString
460+
forKey:@"replayId"];
461+
}
462+
[info setObject:path.lastPathComponent forKey:@"path"];
463+
[info setObject:@(options.onErrorSampleRate) forKey:@"errorSampleRate"];
468464

469465
NSData *data = [SentrySerializationSwift dataWithJSONObject:info];
470466

Sources/Sentry/include/HybridPublic/SentrySessionReplayIntegration.h

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -45,6 +45,12 @@ NS_ASSUME_NONNULL_BEGIN
4545

4646
- (void)hideMaskPreview;
4747

48+
/**
49+
* Verifies the device environment and options and returns wether it is safe to enable
50+
* SessionReplay or not
51+
*/
52+
+ (BOOL)shouldEnableForOptions:(SentryOptions *)options;
53+
4854
@end
4955
#endif // SENTRY_TARGET_REPLAY_SUPPORTED
5056
NS_ASSUME_NONNULL_END

Sources/Swift/Integrations/SessionReplay/SentrySessionReplay.swift

Lines changed: 19 additions & 20 deletions
Original file line numberDiff line numberDiff line change
@@ -30,14 +30,12 @@ import UIKit
3030
private(set) var isSessionPaused = false
3131

3232
private let replayOptions: SentryReplayOptions
33-
private let experimentalOptions: SentryExperimentalOptions
3433
private let replayMaker: SentryReplayVideoMaker
3534
private let displayLink: SentryReplayDisplayLinkWrapper
3635
private let dateProvider: SentryCurrentDateProvider
3736
private let touchTracker: SentryTouchTracker?
3837
private let lock = NSLock()
3938
public var replayTags: [String: Any]?
40-
private let environmentChecker: SentrySessionReplayEnvironmentCheckerProvider
4139

4240
var isRunning: Bool {
4341
displayLink.isRunning()
@@ -48,19 +46,16 @@ import UIKit
4846

4947
public init(
5048
replayOptions: SentryReplayOptions,
51-
experimentalOptions: SentryExperimentalOptions,
5249
replayFolderPath: URL,
5350
screenshotProvider: SentryViewScreenshotProvider,
5451
replayMaker: SentryReplayVideoMaker,
5552
breadcrumbConverter: SentryReplayBreadcrumbConverter,
5653
touchTracker: SentryTouchTracker?,
5754
dateProvider: SentryCurrentDateProvider,
5855
delegate: SentrySessionReplayDelegate,
59-
displayLinkWrapper: SentryReplayDisplayLinkWrapper,
60-
environmentChecker: SentrySessionReplayEnvironmentCheckerProvider
56+
displayLinkWrapper: SentryReplayDisplayLinkWrapper
6157
) {
6258
self.replayOptions = replayOptions
63-
self.experimentalOptions = experimentalOptions
6459
self.dateProvider = dateProvider
6560
self.delegate = delegate
6661
self.screenshotProvider = screenshotProvider
@@ -69,27 +64,31 @@ import UIKit
6964
self.replayMaker = replayMaker
7065
self.breadcrumbConverter = breadcrumbConverter
7166
self.touchTracker = touchTracker
72-
self.environmentChecker = environmentChecker
7367
}
7468

7569
deinit { displayLink.invalidate() }
70+
71+
static public func shouldEnableSessionReplay(environmentChecker: SentrySessionReplayEnvironmentCheckerProvider, experimentalOptions: SentryExperimentalOptions) -> Bool {
72+
// Detect if we are running on iOS 26.0 with Liquid Glass and disable session replay.
73+
// This needs to be done until masking for session replay is properly supported, as it can lead
74+
// to PII leaks otherwise.
75+
if environmentChecker.isReliable() {
76+
return true
77+
}
78+
guard experimentalOptions.enableSessionReplayInUnreliableEnvironment else {
79+
SentrySDKLog.fatal("[Session Replay] Detected environment potentially causing PII leaks, disabling Session Replay. To override this mechanism, set `options.experimental.enableSessionReplayInUnreliableEnvironment` to `true`")
80+
return false
81+
}
82+
SentrySDKLog.warning("[Session Replay] Detected environment potentially causing PII leaks, but `options.experimental.enableSessionReplayInUnreliableEnvironment` is set to `true`, ignoring and enabling Session Replay.")
7683

84+
return true
85+
}
86+
7787
public func start(rootView: UIView, fullSession: Bool) {
7888
SentrySDKLog.debug("[Session Replay] Starting session replay with full session: \(fullSession)")
79-
guard !isRunning else {
89+
guard !isRunning else {
8090
SentrySDKLog.debug("[Session Replay] Session replay is already running, not starting again")
81-
return
82-
}
83-
84-
// Detect if we are running on iOS 26.0 with Liquid Glass and disable session replay.
85-
// This needs to be done until masking for session replay is properly supported, as it can lead
86-
// to PII leaks otherwise.
87-
if !environmentChecker.isReliable() {
88-
guard experimentalOptions.enableSessionReplayInUnreliableEnvironment else {
89-
SentrySDKLog.fatal("[Session Replay] Detected environment potentially causing PII leaks, disabling Session Replay. To override this mechanism, set `options.experimental.enableSessionReplayInUnreliableEnvironment` to `true`")
90-
return
91-
}
92-
SentrySDKLog.warning("[Session Replay] Detected environment potentially causing PII leaks, but `options.experimental.enableSessionReplayInUnreliableEnvironment` is set to `true`, ignoring and enabling Session Replay.")
91+
return
9392
}
9493

9594
displayLink.link(withTarget: self, selector: #selector(newFrame(_:)))

0 commit comments

Comments
 (0)