You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
{{ message }}
Repository navigation
Add privacy cover to prevent card data in app-switcher snapshots - #558
Add a lazily created opaque privacyCoverView in FramesPaymentViewController.
setupPrivacyCover() pins it full-screen over view in viewDidLoad; it starts hidden.
registerPrivacyCoverHandlers() / deregisterPrivacyCoverHandlers() subscribe to UIApplication.willResignActiveNotification / didBecomeActiveNotification (and their UIScene equivalents on iOS 13+) in viewWillAppear / viewWillDisappear.
addPrivacyCover() brings the cover to front and shows it; removePrivacyCover() hides it again.
Update testPaymentViewsHierarchy to reflect the new subview count (2).
Add four new unit tests covering cover visibility on resign/become- active and that observers are cleaned up on viewWillDisappear.
Issue
Issue ticket number and link.
Proposed changes
Describe the big picture of your changes here to communicate to the maintainers why we should accept this pull request. If it fixes a bug or resolves a feature request, be sure to link to that issue.
Test Steps
If there's any functionality change, please list a step by step guide of how to verify the changes, and/or upload a screen recording for any visible changes.
For instance:
Go to the main screen
Tap on primary button
Verify the image is shown on the screen
Checklist
Put an x in the boxes that apply. You can also fill these out after creating the PR. If you're unsure about any of them, don't hesitate to ask. We're here to help! This is simply a reminder of what we are going to look for before merging your code.
Reviewers assigned
I have performed a self-review of my code and manual testing
Lint and unit tests pass locally with my changes
I have added tests that prove my fix is effective or that my feature works
I have added necessary documentation (if applicable)
Further comments
If this is a relatively large or complex change, kick off the discussion by explaining why you choose the solution you did and what alternatives you considered, etc...
… snapshots
When the app resigns active, iOS captures the current window into a
snapshot used in the app-switcher, potentially exposing card number,
expiry, CVV and cardholder name in cleartext (CWE-524,
find_01GGqWLbh2boaS9unYDSQa8U).
Changes:
- Add a lazily created opaque privacyCoverView in
FramesPaymentViewController.
- setupPrivacyCover() pins it full-screen over view in viewDidLoad; it
starts hidden.
- registerPrivacyCoverHandlers() / deregisterPrivacyCoverHandlers()
subscribe to UIApplication.willResignActiveNotification /
didBecomeActiveNotification (and their UIScene equivalents on iOS 13+)
in viewWillAppear / viewWillDisappear.
- addPrivacyCover() brings the cover to front and shows it;
removePrivacyCover() hides it again.
- Update testPaymentViewsHierarchy to reflect the new subview count (2).
- Add four new unit tests covering cover visibility on resign/become-
active and that observers are cleaned up on viewWillDisappear.
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
The reason will be displayed to describe this comment to others. Learn more.
🟡 Changes recommended
Unfiltered scene notifications let unrelated windows change the payment cover’s visibility, while missed activation events can leave the opaque, touch-blocking cover stuck onscreen.
…stration
- registerPrivacyCoverHandlers now binds UIScene notifications to the
specific UIWindowScene that hosts the payment form's window (object:
owningScene). This prevents deactivation events from an unrelated
scene/window from toggling the privacy cover incorrectly.
- When no windowScene is available (pre-scene hosts, unit tests) the
implementation falls back to UIApplication-level notifications,
preserving backward compatibility.
- syncPrivacyCoverToCurrentState() is called after adding observers to
align cover visibility with the actual foreground/background state at
registration time. This eliminates a race where the cover could remain
opaque if the app became active while the VC was off-screen and the
activation notification was not received.
- Added regression test testPrivacyCoverSyncedOnReappearAfterMissedActivation
covering the stale-cover scenario described above.
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
viewWillAppear can run before the view has a window, so
view.window?.windowScene resolves to nil and registerPrivacyCoverHandlers
falls back to application-level notifications. If the payment scene then
deactivates independently (while another scene keeps the app active),
the app-level notification never fires and the cover stays hidden.
Add viewDidAppear which, once the view is guaranteed to be in the window
hierarchy, tears down the previously registered observers and re-registers
using the now-available UIWindowScene. The sync performed inside
registerPrivacyCoverHandlers then aligns cover visibility with the current
scene state. If view.window?.windowScene is still nil (non-scene hosts,
unit tests), the method is a no-op and app-level observers remain active.
Add regression test testPrivacyCoverObserversUpgradedAfterWindowAttachment
to confirm observers are live and functional after the viewDidAppear path.
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
The reason will be displayed to describe this comment to others. Learn more.
🟡 Changes recommended
Privacy observers are removed before the payment form is fully hidden, leaving card fields exposed during navigation transitions, and the scene-observer upgrade path lacks effective test coverage.
Removing the observers in viewWillDisappear leaves the payment form unprotected while it is still visible during an animated or interactive navigation transition. For example, backgrounding during an unfinished back swipe delivers the deactivation notification after deregistration, so the cover stays hidden over the visible card fields. Keep privacy observation active until viewDidDisappear, and update the observer-cleanup test to verify protection during the transition as well as cleanup afterward.
The reason will be displayed to describe this comment to others. Learn more.
🟡 Changes recommended
Privacy observers are removed while card data may still be visible during transitions, creating a capture risk, and the scene-observer handoff lacks effective regression coverage.
…ne provider
Add an internal windowSceneProvider closure to FramesPaymentViewController
so tests can supply a scene token without requiring a real scene-backed window.
In production the closure is nil and the implementation falls back to
view.window?.windowScene unchanged.
Extract resolvedWindowScene() private helper (iOS 13+) that consults the
provider first, then view.window?.windowScene. Both registerPrivacyCoverHandlers
and viewDidAppear now call this helper, keeping the resolution logic in one place.
Replace the previous testPrivacyCoverObserversUpgradedAfterWindowAttachment
with a test that actually exercises the upgrade branch:
- Phase 1: windowSceneProvider returns nil → app-level observers registered
- Phase 2: provider returns a scene token → viewDidAppear upgrades to scene-scoped
- Asserts app-level notifications no longer affect the cover after the upgrade
- Asserts owning-scene deactivation shows the cover
- Asserts an unrelated-scene notification does not affect the cover
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
…tions
Move deregisterPrivacyCoverHandlers() from viewWillDisappear to viewDidDisappear.
During an animated or interactive dismissal the payment view is still visible and
in the view hierarchy, so observers must remain active until the transition
completes and the view has fully left the screen.
Update testPrivacyCoverObserversRemovedOnDisappear to assert both sides of this
boundary: cover still responds between viewWillDisappear and viewDidDisappear,
and stops responding after viewDidDisappear deregisters the observers.
Update testPrivacyCoverSyncedOnReappearAfterMissedActivation to call
viewDidDisappear (the new deregistration point) instead of viewWillDisappear.
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
The reason will be displayed to describe this comment to others. Learn more.
🔵 Needs a closer look
Privacy observers are removed in viewWillDisappear while payment fields may still be visible, leaving sensitive card data unprotected if the scene deactivates before the transition completes.
The reason will be displayed to describe this comment to others. Learn more.
🔵 Needs a closer look
Security-sensitive redaction still needs device-level validation of real iOS app-switcher snapshots, since notification-driven tests cannot verify UIKit capture timing with keyboards visible, scene-based hosts, or navigation transitions.
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
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.
Changes:
Issue
Issue ticket number and link.
Proposed changes
Describe the big picture of your changes here to communicate to the maintainers why we should accept this pull request. If it fixes a bug or resolves a feature request, be sure to link to that issue.
Test Steps
If there's any functionality change, please list a step by step guide of how to verify the changes, and/or upload a screen recording for any visible changes.
Checklist
Put an
xin the boxes that apply. You can also fill these out after creating the PR. If you're unsure about any of them, don't hesitate to ask. We're here to help! This is simply a reminder of what we are going to look for before merging your code.Further comments
If this is a relatively large or complex change, kick off the discussion by explaining why you choose the solution you did and what alternatives you considered, etc...