Skip to content

Commit c8d010e

Browse files
chore: Android SR - cleanup privacy settings (#341)
## Summary 1. Rename maskSensitive to maskBySemanticsKeywords and set default false 2. Changes default `maskText` to `false` 3. removes `maskAdditionalMatchers` (they might return later under different name and rework to be more universal) <!-- Ideally, there is an attached GitHub issue that will describe the "why". If relevant, use this section to call out any additional information you'd like to _highlight_ to the reviewer. --> ## How did you test this change? <!-- Frontend - Leave a screencast or a screenshot to visually describe the changes. --> ## Are there any deployment considerations? <!-- Backend - Do we need to consider migrations or backfilling data? --> <!-- CURSOR_SUMMARY --> --- > [!NOTE] > Streamlines Session Replay masking configuration and docs. > > - Renames `maskSensitive` to `maskBySemanticsKeywords` and wires it into `asMatchersList()`; default `false` > - Changes `PrivacyProfile.maskText` default to `false` > - Removes `maskAdditionalMatchers`; drops corresponding `addAll(...)` in `asMatchersList()` > - Restricts matcher properties (`viewsMatcher`, `xmlViewIdsMatcher`, `textInputMatcher`, `textMatcher`, `sensitiveMatcher`) to `internal` > - Minor KDoc tweaks and parameter order; `maskImageViews` remains, moved after lists > - README updates to show new toggles and example `PrivacyProfile` usage > > <sup>Written by [Cursor Bugbot](https://cursor.com/dashboard?tab=bugbot) for commit 987e957. This will update automatically on new commits. Configure [here](https://cursor.com/dashboard?tab=bugbot).</sup> <!-- /CURSOR_SUMMARY -->
1 parent 49f65f4 commit c8d010e

File tree

2 files changed

+19
-16
lines changed

2 files changed

+19
-16
lines changed

sdk/@launchdarkly/observability-android/README.md

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -238,6 +238,11 @@ import com.launchdarkly.observability.replay.plugin.SessionReplay
238238
val sessionReplay = SessionReplay(
239239
ReplayOptions(
240240
privacyProfile = PrivacyProfile(
241+
// Toggle built-in masking:
242+
// - maskTextInputs: masks text input fields (e.g. EditText / Compose text fields)
243+
maskTextInputs = true,
244+
// - maskText: masks non-input text targets
245+
maskText = false,
241246
// New settings:
242247
maskViews = listOf(
243248
// Masks targets by *exact* Android View class (does not match subclasses).

sdk/@launchdarkly/observability-android/lib/src/main/kotlin/com/launchdarkly/observability/replay/PrivacyProfile.kt

Lines changed: 14 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -14,23 +14,22 @@ import com.launchdarkly.observability.replay.masking.MaskTarget
1414
*
1515
* @param maskTextInputs Set to false to disable masking text input targets.
1616
* @param maskText Set to false to disable masking text targets.
17-
* @param maskSensitive Set to false to disable masking "sensitive" targets (password + keyword heuristics).
1817
* @param maskImageViews Set to true to mask [ImageView] targets by exact class match.
1918
* @param maskViews Additional Views to mask by exact class match (see [viewsMatcher]).
2019
* @param maskXMLViewIds Additional Views to mask by resource entry name (see [xmlViewIdsMatcher]).
21-
* Accepts `"@+id/foo"`, `"@id/foo"`, or `"foo"`.
22-
* @param maskAdditionalMatchers Additional custom matchers to apply.
20+
* accepts `"@+id/foo"`, `"@id/foo"`, or `"foo"`.
21+
* @param maskBySemanticsKeywords Set to true to enable masking of "sensitive" targets detected by
22+
* semantic keywords (password + keyword heuristics).
2323
**/
2424
data class PrivacyProfile(
2525
val maskTextInputs: Boolean = true,
26-
val maskText: Boolean = true,
27-
val maskSensitive: Boolean = true,
28-
// only for XML ImageViews
29-
val maskImageViews: Boolean = false,
26+
val maskText: Boolean = false,
3027
val maskViews: List<MaskViewRef> = emptyList(),
3128
val maskXMLViewIds: List<String> = emptyList(),
32-
val maskAdditionalMatchers: List<MaskMatcher> = emptyList(),
33-
) {
29+
// only for XML ImageViews
30+
val maskImageViews: Boolean = false,
31+
val maskBySemanticsKeywords: Boolean = false,
32+
) {
3433
private val viewClassSet = buildSet {
3534
addAll(maskViews.map { it.clazz })
3635
if (maskImageViews) add(ImageView::class.java)
@@ -56,16 +55,15 @@ data class PrivacyProfile(
5655
if (maskText) add(textMatcher)
5756
if (viewClassSet.isNotEmpty()) add(viewsMatcher)
5857
if (maskXMLViewIdSet.isNotEmpty()) add(xmlViewIdsMatcher)
59-
if (maskSensitive) add(sensitiveMatcher)
60-
addAll(maskAdditionalMatchers)
58+
if (maskBySemanticsKeywords) add(sensitiveMatcher)
6159
}
6260

6361
/**
6462
* Matches targets whose underlying Android View has an exact class match with [maskViews].
6563
*
6664
* Note: this uses `target.view.javaClass` equality; it does not match subclasses.
6765
*/
68-
val viewsMatcher: MaskMatcher = object : MaskMatcher {
66+
internal val viewsMatcher: MaskMatcher = object : MaskMatcher {
6967
override fun isMatch(target: MaskTarget): Boolean {
7068
return viewClassSet.contains(target.view.javaClass)
7169
}
@@ -78,7 +76,7 @@ data class PrivacyProfile(
7876
* IDs are compared using `resources.getResourceEntryName(view.id)`, so this only applies to
7977
* Views with a non-[View.NO_ID] id that resolves to a resource entry.
8078
*/
81-
val xmlViewIdsMatcher: MaskMatcher = object : MaskMatcher {
79+
internal val xmlViewIdsMatcher: MaskMatcher = object : MaskMatcher {
8280
fun View.idNameOrNull(): String? =
8381
if (id == View.NO_ID) null
8482
else runCatching { resources.getResourceEntryName(id) }.getOrNull()
@@ -94,7 +92,7 @@ data class PrivacyProfile(
9492
* This matcher will match most text inputs, but there may be special cases where it will
9593
* miss as we can't account for all possible future semantic properties.
9694
*/
97-
val textInputMatcher: MaskMatcher = object : MaskMatcher {
95+
internal val textInputMatcher: MaskMatcher = object : MaskMatcher {
9896
override fun isMatch(target: MaskTarget): Boolean {
9997
return target.isTextInput()
10098
}
@@ -104,7 +102,7 @@ data class PrivacyProfile(
104102
* This matcher will match most text, but there may be special cases where it will
105103
* miss as we can't account for all possible future semantic properties.
106104
*/
107-
val textMatcher: MaskMatcher = object : MaskMatcher {
105+
internal val textMatcher: MaskMatcher = object : MaskMatcher {
108106
override fun isMatch(target: MaskTarget): Boolean {
109107
return target.isText()
110108
}
@@ -114,7 +112,7 @@ data class PrivacyProfile(
114112
* This matcher will match all items having the semantic property
115113
* and all text or context descriptions that have substring matches with any of the [sensitiveKeywords]
116114
*/
117-
val sensitiveMatcher: MaskMatcher = object : MaskMatcher {
115+
internal val sensitiveMatcher: MaskMatcher = object : MaskMatcher {
118116
override fun isMatch(target: MaskTarget): Boolean {
119117
return target.isSensitive(sensitiveKeywords)
120118
}

0 commit comments

Comments
 (0)