fix(session-replay-privacy): require complete mask-subtree discovery - #19
Draft
posthog[bot] wants to merge 1 commit into
Draft
fix(session-replay-privacy): require complete mask-subtree discovery#19posthog[bot] wants to merge 1 commit into
posthog[bot] wants to merge 1 commit into
Conversation
Flutter's shipped mask-rect tree walk silently dropped matched nodes; the spec described how masks are painted but never stated that discovery must be complete. Generated-By: PostHog Code Task-Id: 7d7cb7ed-0d67-4718-ada0-2b2cd585f3a6
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Why
posthog-flutter#500 ("mask every element that matched a masking rule") fixed a shipped privacy leak in the mobile screenshot-masking pipeline (both iOS and Android — Flutter's Dart-side
ImageMaskPainterpaints masks itself, so both platforms shared the bug).ElementData.extractRects(), which collects the rectangles painted black over each screenshot, was not fully recursive: it walked a root's direct children, and grandchildren only when a child itself had more than one child — dropping the child in that branch instead of also visiting it.Concretely: a
PostHogMaskWidgetwrapping several matched children lost its own mask rect. Anything inside the wrapper that didn't independently match a rule (an image withmaskAllImagesoff, spacing, a decoration) recorded unmasked, despite the developer explicitly wrapping that subtree to be masked. Verified live on both iOS and Android in the PR: before the fix, a wrapper aroundText/ unmasked-widget /Textmasked only the two texts, leaving the widget between them fully visible in the recording.openspec/specs/session-replay-privacy/spec.mdbehavior item 6 describes how masks get painted into screenshots but never states completeness — that every matching element/subtree must be included, not just the first match a traversal happens to reach. This is a real spec gap the shipped bug fell into, not a documented-then-violated rule.What this PR does
Canonical session-replay-privacy behaviorrequirement (viaopenspec/changes/archive/2026-07-31-fix-replay-mask-subtree-completeness/, following this repo's propose→archive convention): a masked subtree with a non-matching child in the middle must still be fully masked.openspec validate --specs --strictpasses.Uncertain / flagged for reviewer attention
tasks.md§4.1).setup();maskAllImagesmasking text whenmaskAllTextsis off) are intentionally not addressed here — flagged for a separate change if confirmed cross-platform (tasks.md§4.2).Created with PostHog Code