Skip to content
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

Rename session replay redact options and APIs to mask #4373

Open
wants to merge 14 commits into
base: main
Choose a base branch
from
1 change: 1 addition & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@

- Speed up HTTP tracking for multiple requests in parallel (#4366)
- Slightly speed up SentryInAppLogic (#4370)
- Rename session replay `redact` options and APIs to `mask` (#4373)

## 8.37.0-beta.1

Expand Down
4 changes: 2 additions & 2 deletions Samples/iOS-ObjectiveC/iOS-ObjectiveC/AppDelegate.m
Original file line number Diff line number Diff line change
Expand Up @@ -29,8 +29,8 @@ - (BOOL)application:(UIApplication *)application
options.failedRequestStatusCodes = @[ httpStatusCodeRange ];

options.experimental.sessionReplay.quality = SentryReplayQualityMedium;
options.experimental.sessionReplay.redactAllText = true;
options.experimental.sessionReplay.redactAllImages = true;
options.experimental.sessionReplay.maskAllText = true;
options.experimental.sessionReplay.maskAllImages = true;
options.experimental.sessionReplay.sessionSampleRate = 0;
options.experimental.sessionReplay.onErrorSampleRate = 1;

Expand Down
2 changes: 1 addition & 1 deletion Samples/iOS-Swift/iOS-Swift/AppDelegate.swift
Original file line number Diff line number Diff line change
Expand Up @@ -44,7 +44,7 @@
options.debug = true

if #available(iOS 16.0, *), !args.contains("--disable-session-replay") {
options.experimental.sessionReplay = SentryReplayOptions(sessionSampleRate: 1, onErrorSampleRate: 1, redactAllText: true, redactAllImages: true)
options.experimental.sessionReplay = SentryReplayOptions(sessionSampleRate: 1, onErrorSampleRate: 1, maskAllText: true, maskAllImages: true)
options.experimental.sessionReplay.quality = .high
}

Expand Down Expand Up @@ -114,7 +114,7 @@
options.enableNetworkBreadcrumbs = !args.contains("--disable-network-breadcrumbs")
options.enableSwizzling = !args.contains("--disable-swizzling")
options.enableCrashHandler = !args.contains("--disable-crash-handler")
options.enableTracing = !args.contains("--disable-tracing")

Check warning on line 117 in Samples/iOS-Swift/iOS-Swift/AppDelegate.swift

View workflow job for this annotation

GitHub Actions / iOS-Swift UI Tests iPhone 15 (17.2)

'enableTracing' is deprecated: Use tracesSampleRate or tracesSampler instead

// because we run CPU for 15 seconds at full throttle, we trigger ANR issues being sent. disable such during benchmarks.
options.enableAppHangTracking = !isBenchmarking && !args.contains("--disable-anr-tracking")
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,6 @@ class SRRedactSampleViewController: UIViewController {
notRedactedView.backgroundColor = .green
notRedactedView.transform = CGAffineTransform(rotationAngle: 45 * .pi / 180.0)

SentrySDK.replay.ignoreView(notRedactedView)
SentrySDK.replay.maskView(notRedactedView)
}
}
4 changes: 2 additions & 2 deletions Samples/iOS-SwiftUI/iOS-SwiftUI/SwiftUIApp.swift
Original file line number Diff line number Diff line change
Expand Up @@ -11,8 +11,8 @@ struct SwiftUIApp: App {
options.tracesSampleRate = 1.0
options.profilesSampleRate = 1.0
options.experimental.sessionReplay.sessionSampleRate = 1.0
options.experimental.sessionReplay.redactAllImages = false
options.experimental.sessionReplay.redactAllText = false
options.experimental.sessionReplay.maskAllImages = false
options.experimental.sessionReplay.maskAllText = false
options.initialScope = { scope in
scope.injectGitInformation()
return scope
Expand Down
9 changes: 4 additions & 5 deletions Sources/Sentry/Public/SentryReplayApi.h
Original file line number Diff line number Diff line change
Expand Up @@ -15,19 +15,18 @@ NS_ASSUME_NONNULL_BEGIN
@interface SentryReplayApi : NSObject

/**
* Marks this view to be redacted during replays.
* Marks this view to be masked during replays.
*
* @warning This is an experimental feature and may still have bugs.
*/
- (void)redactView:(UIView *)view NS_SWIFT_NAME(redactView(_:));
- (void)maskView:(UIView *)view NS_SWIFT_NAME(maskView(_:));

/**
* Marks this view to be ignored during redact step of session replay.
* All its content will be visible in the replay.
* Marks this view to not be masked during redact step of session replay.
*
* @warning This is an experimental feature and may still have bugs.
*/
- (void)ignoreView:(UIView *)view NS_SWIFT_NAME(ignoreView(_:));
- (void)unmaskView:(UIView *)view NS_SWIFT_NAME(unmaskView(_:));

/**
* Pauses the replay.
Expand Down
8 changes: 4 additions & 4 deletions Sources/Sentry/SentryReplayApi.m
Original file line number Diff line number Diff line change
Expand Up @@ -10,14 +10,14 @@

@implementation SentryReplayApi

- (void)redactView:(UIView *)view
- (void)maskView:(UIView *)view
{
[SentryRedactViewHelper redactView:view];
[SentryRedactViewHelper maskView:view];
}

- (void)ignoreView:(UIView *)view
- (void)unmaskView:(UIView *)view
{
[SentryRedactViewHelper ignoreView:view];
[SentryRedactViewHelper unmaskView:view];
}

- (void)pause
Expand Down
8 changes: 4 additions & 4 deletions Sources/SentrySwiftUI/SentryReplayView.swift
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,7 @@ struct SentryReplayView: UIViewRepresentable {

func makeUIView(context: Context) -> UIView {
let result = SentryRedactView()
result.sentryReplayRedact()
result.sentryReplayMask()
return result
}

Expand All @@ -29,15 +29,15 @@ struct SentryReplayModifier: ViewModifier {
@available(iOS 13, macOS 10.15, tvOS 13, *)
public extension View {

/// Marks the view as containing sensitive information that should be redacted during replays.
/// Marks the view as containing sensitive information that should be masked during replays.
///
/// When this modifier is applied, any sensitive content within the view will be hidden or masked
/// When this modifier is applied, any sensitive content within the view will be masked
/// during session replays to ensure user privacy. This is useful for views containing personal
/// data or confidential information that shouldn't be visible when the replay is reviewed.
///
/// - Returns: A modifier that redacts sensitive information during session replays.
/// - Experiment: This is an experimental feature and may still have bugs.
func sentryReplayRedact() -> some View {
func sentryReplayMask() -> some View {
modifier(SentryReplayModifier())
}
}
Expand Down
8 changes: 4 additions & 4 deletions Sources/Swift/Extensions/UIViewExtensions.swift
Original file line number Diff line number Diff line change
Expand Up @@ -9,17 +9,17 @@ public extension UIView {
* Marks this view to be redacted during replays.
* - experiment: This is an experimental feature and may still have bugs.
*/
func sentryReplayRedact() {
SentryRedactViewHelper.redactView(self)
func sentryReplayMask() {
SentryRedactViewHelper.maskView(self)
}

/**
* Marks this view to be ignored during redact step
* of session replay. All its content will be visible in the replay.
* - experiment: This is an experimental feature and may still have bugs.
*/
func sentryReplayIgnore() {
SentryRedactViewHelper.ignoreView(self)
func sentryReplayUnmask() {
SentryRedactViewHelper.unmaskView(self)
}
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -51,15 +51,15 @@ public class SentryReplayOptions: NSObject, SentryRedactOptions {
*
* - note: The default is true
*/
public var redactAllText = true
public var maskAllText = true
vaind marked this conversation as resolved.
Show resolved Hide resolved

/**
* Indicates whether session replay should redact all non-bundled image
* in the app by drawing a black rectangle over it.
*
* - note: The default is true
*/
public var redactAllImages = true
public var maskAllImages = true

/**
* Indicates the quality of the replay.
Expand All @@ -73,15 +73,15 @@ public class SentryReplayOptions: NSObject, SentryRedactOptions {
* By default Sentry already mask text and image elements from UIKit
* Every child of a view that is redacted will also be redacted.
*/
public var redactViewClasses = [AnyClass]()
public var maskedViewClasses = [AnyClass]()

/**
* A list of custom UIView subclasses to be ignored
* during masking step of the session replay.
* The views of given classes will not be redacted but their children may be.
* This property has precedence over `redactViewTypes`.
*/
public var ignoreViewClasses = [AnyClass]()
public var unmaskedViewClasses = [AnyClass]()

/**
* Defines the quality of the session replay.
Expand Down Expand Up @@ -139,18 +139,18 @@ public class SentryReplayOptions: NSObject, SentryRedactOptions {
* - errorSampleRate Indicates the percentage in which a 30 seconds replay will be send with
* error events.
*/
public init(sessionSampleRate: Float = 0, onErrorSampleRate: Float = 0, redactAllText: Bool = true, redactAllImages: Bool = true) {
public init(sessionSampleRate: Float = 0, onErrorSampleRate: Float = 0, maskAllText: Bool = true, maskAllImages: Bool = true) {
self.sessionSampleRate = sessionSampleRate
self.onErrorSampleRate = onErrorSampleRate
self.redactAllText = redactAllText
self.redactAllImages = redactAllImages
self.maskAllText = maskAllText
self.maskAllImages = maskAllImages
}

convenience init(dictionary: [String: Any]) {
let sessionSampleRate = (dictionary["sessionSampleRate"] as? NSNumber)?.floatValue ?? 0
let onErrorSampleRate = (dictionary["errorSampleRate"] as? NSNumber)?.floatValue ?? 0
let redactAllText = (dictionary["redactAllText"] as? NSNumber)?.boolValue ?? true
let redactAllImages = (dictionary["redactAllImages"] as? NSNumber)?.boolValue ?? true
self.init(sessionSampleRate: sessionSampleRate, onErrorSampleRate: onErrorSampleRate, redactAllText: redactAllText, redactAllImages: redactAllImages)
let maskAllText = (dictionary["maskAllText"] as? NSNumber)?.boolValue ?? true
let maskAllImages = (dictionary["maskAllImages"] as? NSNumber)?.boolValue ?? true
self.init(sessionSampleRate: sessionSampleRate, onErrorSampleRate: onErrorSampleRate, maskAllText: maskAllText, maskAllImages: maskAllImages)
}
}
8 changes: 4 additions & 4 deletions Sources/Swift/Protocol/SentryRedactOptions.swift
Original file line number Diff line number Diff line change
Expand Up @@ -2,8 +2,8 @@ import Foundation

@objc
protocol SentryRedactOptions {
var redactAllText: Bool { get }
var redactAllImages: Bool { get }
var redactViewClasses: [AnyClass] { get }
var ignoreViewClasses: [AnyClass] { get }
var maskAllText: Bool { get }
var maskAllImages: Bool { get }
var maskedViewClasses: [AnyClass] { get }
var unmaskedViewClasses: [AnyClass] { get }
}
20 changes: 10 additions & 10 deletions Sources/Swift/Tools/UIRedactBuilder.swift
Original file line number Diff line number Diff line change
Expand Up @@ -62,11 +62,11 @@ class UIRedactBuilder {
init(options: SentryRedactOptions) {
var redactClasses = [AnyClass]()

if options.redactAllText {
if options.maskAllText {
brustolin marked this conversation as resolved.
Show resolved Hide resolved
redactClasses += [ UILabel.self, UITextView.self, UITextField.self ]
}

if options.redactAllImages {
if options.maskAllImages {
//this classes are used by SwiftUI to display images.
redactClasses += ["_TtCOCV7SwiftUI11DisplayList11ViewUpdater8Platform13CGDrawingView",
"_TtC7SwiftUIP33_A34643117F00277B93DEBAB70EC0697122_UIShapeHitTestingView",
Expand All @@ -89,11 +89,11 @@ class UIRedactBuilder {

redactClassesIdentifiers = Set(redactClasses.map({ ObjectIdentifier($0) }))

for type in options.ignoreViewClasses {
for type in options.unmaskedViewClasses {
self.ignoreClassesIdentifiers.insert(ObjectIdentifier(type))
}

for type in options.redactViewClasses {
for type in options.maskedViewClasses {
self.redactClassesIdentifiers.insert(ObjectIdentifier(type))
}
}
Expand Down Expand Up @@ -159,11 +159,11 @@ class UIRedactBuilder {
}

private func shouldIgnore(view: UIView) -> Bool {
return SentryRedactViewHelper.shouldIgnoreView(view) || containsIgnoreClass(type(of: view))
return SentryRedactViewHelper.shouldUnmask(view) || containsIgnoreClass(type(of: view))
}

private func shouldRedact(view: UIView) -> Bool {
if SentryRedactViewHelper.shouldRedactView(view) {
if SentryRedactViewHelper.shouldMaskView(view) {
return true
}
if let imageView = view as? UIImageView, containsRedactClass(UIImageView.self) {
Expand Down Expand Up @@ -257,19 +257,19 @@ class SentryRedactViewHelper: NSObject {
private static var associatedRedactObjectHandle: UInt8 = 0
private static var associatedIgnoreObjectHandle: UInt8 = 0

static func shouldRedactView(_ view: UIView) -> Bool {
static func shouldMaskView(_ view: UIView) -> Bool {
(objc_getAssociatedObject(view, &associatedRedactObjectHandle) as? NSNumber)?.boolValue ?? false
}

static func shouldIgnoreView(_ view: UIView) -> Bool {
static func shouldUnmask(_ view: UIView) -> Bool {
(objc_getAssociatedObject(view, &associatedIgnoreObjectHandle) as? NSNumber)?.boolValue ?? false
}

static func redactView(_ view: UIView) {
static func maskView(_ view: UIView) {
objc_setAssociatedObject(view, &associatedRedactObjectHandle, true, .OBJC_ASSOCIATION_ASSIGN)
}

static func ignoreView(_ view: UIView) {
static func unmaskView(_ view: UIView) {
objc_setAssociatedObject(view, &associatedIgnoreObjectHandle, true, .OBJC_ASSOCIATION_ASSIGN)
}
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -288,7 +288,7 @@ class SentrySessionReplayIntegrationTests: XCTestCase {
}

startSDK(sessionSampleRate: 1, errorSampleRate: 1) { options in
options.experimental.sessionReplay.redactViewClasses = [AnotherLabel.self]
options.experimental.sessionReplay.maskedViewClasses = [AnotherLabel.self]
}

let sut = try getSut()
Expand All @@ -301,7 +301,7 @@ class SentrySessionReplayIntegrationTests: XCTestCase {
}

startSDK(sessionSampleRate: 1, errorSampleRate: 1) { options in
options.experimental.sessionReplay.ignoreViewClasses = [AnotherLabel.self]
options.experimental.sessionReplay.unmaskedViewClasses = [AnotherLabel.self]
}

let sut = try getSut()
Expand Down
24 changes: 12 additions & 12 deletions Tests/SentryTests/UIRedactBuilderTests.swift
Original file line number Diff line number Diff line change
Expand Up @@ -6,16 +6,16 @@ import UIKit
import XCTest

class RedactOptions: SentryRedactOptions {
var redactViewClasses: [AnyClass]
var ignoreViewClasses: [AnyClass]
var redactAllText: Bool
var redactAllImages: Bool
var maskedViewClasses: [AnyClass]
var unmaskedViewClasses: [AnyClass]
var maskAllText: Bool
var maskAllImages: Bool

init(redactAllText: Bool = true, redactAllImages: Bool = true) {
self.redactAllText = redactAllText
self.redactAllImages = redactAllImages
redactViewClasses = []
ignoreViewClasses = []
self.maskAllText = redactAllText
self.maskAllImages = redactAllImages
maskedViewClasses = []
unmaskedViewClasses = []
}
}

Expand Down Expand Up @@ -197,7 +197,7 @@ class UIRedactBuilderTests: XCTestCase {

let sut = getSut()
let label = AnotherLabel(frame: CGRect(x: 20, y: 20, width: 40, height: 40))
SentrySDK.replay.ignoreView(label)
SentrySDK.replay.unmaskView(label)
rootView.addSubview(label)

let result = sut.redactRegionsFor(view: rootView)
Expand All @@ -210,7 +210,7 @@ class UIRedactBuilderTests: XCTestCase {

let sut = getSut()
let view = AnotherView(frame: CGRect(x: 20, y: 20, width: 40, height: 40))
SentrySDK.replay.redactView(view)
SentrySDK.replay.maskView(view)
rootView.addSubview(view)

let result = sut.redactRegionsFor(view: rootView)
Expand All @@ -223,7 +223,7 @@ class UIRedactBuilderTests: XCTestCase {

let sut = getSut()
let label = AnotherLabel(frame: CGRect(x: 20, y: 20, width: 40, height: 40))
label.sentryReplayIgnore()
label.sentryReplayUnmask()
rootView.addSubview(label)

let result = sut.redactRegionsFor(view: rootView)
Expand All @@ -236,7 +236,7 @@ class UIRedactBuilderTests: XCTestCase {

let sut = getSut()
let view = AnotherView(frame: CGRect(x: 20, y: 20, width: 40, height: 40))
view.sentryReplayRedact()
view.sentryReplayMask()
rootView.addSubview(view)

let result = sut.redactRegionsFor(view: rootView)
Expand Down
Loading