Skip to content

fix: mask maskView views and secure fields in bug report screenshots and replays - #55

Open
boehlerlukas wants to merge 2 commits into
masterfrom
fix/hc-audit-findings
Open

boehlerlukas wants to merge 2 commits into
masterfrom
fix/hc-audit-findings

Conversation

@boehlerlukas

Copy link
Copy Markdown
Contributor

What this fixes

1. Help-center audit finding 26 (high, privacy): Gleap.maskView did not mask bug report screenshots or app replays.

maskView, the automatic password / card / one-time-code masks and FLAG_SECURE were only applied by GleapWindowCapture, which handles capture requests. Bug report screenshots (ScreenshotTaker), silent bug report screenshots (SilentBugReportUtil) and app replay frames (ReplaysDetector) all go through ScreenshotUtil.takeScreenshot. That path copied the window with PixelCopy, or drew it, with no masks at all.

Fix: ScreenshotUtil now paints the same masks on every path:

  • the PixelCopy path: masks are taken when the copy starts and again when it finishes, so a view that scrolls in between stays covered
  • the below-API-26 draw path
  • the non-hardware-accelerated multi-window path
  • the app's GetBitmapCallback picture (Flutter), with the masks scaled onto it

It uses a new GleapWindowCapture.windowMasks / drawMasks, which reuses the capture code's scan (masked views plus isSensitive), its 8 dp padding and its FLAG_SECURE rule (the whole window goes black). If the masks cannot be worked out (for example a window with too many views), no screenshot is delivered (null), the same as a dropped capture frame.

2. Finding 24: setActivationMethods after the config loaded stopped app replays.

Gleap.setActivationMethods called clearAllDetectors(), which also unregisters the ReplaysDetector, and then rebuilt only shake and screenshot. Now, when the config has loaded and replays are on, it creates the replays detector again, the same way GleapInitializer.initGleap does.

3. Finding 24 (rest): THREE_FINGER_DOUBLE_TAB and FAB do nothing on Android.

GleapDetectorUtil.initDetectors only builds detectors for SHAKE and SCREENSHOT. This PR documents that on the enum: THREE_FINGER_DOUBLE_TAB is unsupported, FAB is replaced by the dashboard setting or showFeedbackButton(boolean), and NONE alone turns activation off. Behaviour is unchanged.

Verified

  • ./gradlew build (JDK: Android Studio JBR): BUILD SUCCESSFUL. That covers the unit tests, lint and assembling the app and the library.
  • ./gradlew :gleap:connectedDebugAndroidTest on the Medium_Phone_API_36.1 emulator: 7/7 passed (the 6 existing e2e tests plus the new one).
  • New e2e test ScreenshotMaskingE2ETest (privacy / sanitising, so it gets a test per the workspace rule). It runs the real ScreenshotUtil.takeScreenshot against the fake server and checks pixels: the Gleap.maskView view and the password field come out black, and the plain view stays red. I checked that it fails on master (expected:<-16777216> but was:<-65536>) and passes with the fix.
  • The replays change has no test (it is not core code); I verified it by reading the code.

Notes

Not fixed (needs decision / out of scope)

  • A real three-finger double-tap detector on Android (L). Removing or deprecating THREE_FINGER_DOUBLE_TAB / FAB is an API decision.
  • Calling setActivationMethods while the widget is open resumes the detectors (resumeAllDetectors sets widgetOpen=false). This existed before; I left it alone.
  • On Android, an empty array still falls back to the dashboard on the next config load. NONE means none. The Flutter and RN bridges now send NONE for an empty list.

🤖 Generated with Claude Code

boehlerlukas and others added 2 commits October 18, 2026 08:12
…and replays

Gleap.maskView (and the automatic password / card / one-time-code masks and
FLAG_SECURE) only applied to capture requests (GleapWindowCapture). Bug report
screenshots, silent bug report screenshots and app replay frames all come from
ScreenshotUtil, which copied or drew the window unmasked. ScreenshotUtil now
paints the same masks (GleapWindowCapture.windowMasks), taken when the copy
starts and when it finishes; a screenshot whose masks cannot be worked out is
dropped (null), as capture frames are.

setActivationMethods cleared every detector, the app replays included, and
only rebuilt shake / screenshot: replays stopped when it was called after the
config loaded. The replays detector is now recreated as after the config load.
GleapActivationMethod documents that THREE_FINGER_DOUBLE_TAB and FAB do nothing
on Android and that NONE alone turns the activation methods off.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
A frame still being taken (PixelCopy is async) when the old ReplaysDetector is
unregistered re-posted its loop, so the restart in setActivationMethods could
leave two loops running. Late frames no longer re-post after pause/unregister,
and resume never stacks a second loop.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant