Skip to content

Feat: Add iOS helpers that put secure rectangles in screen points for SDK 7 - #1

Draft
dsheikherev wants to merge 1 commit into
releasefrom
feat/ios-secure-rect-screen-points
Draft

dsheikherev wants to merge 1 commit into
releasefrom
feat/ios-secure-rect-screen-points

Conversation

@dsheikherev

Copy link
Copy Markdown

Draft until the plugin moves to the 7.x iOS SDK. Nothing calls these helpers yet; the plugin's behaviour does not change.

Why

The 7.x iOS SDK records every window of the app at its place on the screen (bugsee/bugsee-cocoa#170). Since bugsee/bugsee-cocoa#186 it keeps doing that while secure rectangles are registered. It pulls a wrapper's secure rectangles through -[BGSBugseeWrapper secureRectanglesForDisplay:] and expects [version, count, left, top, right, bottom, ...] as packed int32, in screen points (BGSContracts.h).

The Dart side (lib/src/ui.dart, _addElementBounds) sends five ints per secure widget: [id, x, y, width, height]. They are measured from the Flutter view's origin, in points; on iOS the device pixel ratio is forced to 1. The 6.x SDK reads that payload through +[Bugsee setInternalSecureRectangles:] with a 20-byte stride. That method does not exist in 7.x.

If the plugin forwarded the values unchanged to 7.x, a Flutter view that does not start at the screen's origin would be recorded in the clear, with the black off to the side of it. That happens to an iPad window in Stage Manager or Split View, and to the iPhone Duo with two windows side by side.

What

New files only, in ios/bugsee_flutter/Sources/bugsee_flutter/. They are private headers (not in include/) under both CocoaPods and SPM.

  • BGSFlutterSecureRectanglesBuffer(elements, intCount, origin) takes the Dart payload as it is.
    • Every rectangle is moved by the view's origin on the screen, edges are rounded outward, and the result is packed for the contract.
    • The version is always 0 (BGSSecureRectanglesVersionUnknown), so the SDK compares the rectangles itself. The result also changes when only the window moves, for example in a Stage Manager drag, and Dart publishes nothing then. A version derived from Dart's publishes would go stale, which the contract calls a privacy defect.
    • Malformed input (no data with a non-zero count) returns nil. The caller must not hand that to the SDK, because nil clears the rectangles.
  • BGSFlutterViewOriginOnScreen(view, &origin) returns where the Flutter view starts in the frame the SDK records. It uses the SDK's own conversion (+[BGSTrackerApplication screenRectForRect:inView:]): through the screen's fixed space. On Mac Catalyst, for iOS apps on a Mac and for a window on no screen, it falls back to the window's frame origin.

When the plugin moves to 7.x, call both from the wrapper's secureRectanglesForDisplay:, on the main thread. The React Native wrapper uses the same conversion (bugsee/bugsee-react-native#30).

Checks

The plugin has no iOS test target and Flutter was not installed on the machine, so the checks ran outside the repo.

  • Build: compiles with -Wall -Wextra -Werror and the availability warnings for an iOS 11 simulator and device, an iOS 15 device, and Mac Catalyst.
  • Buffer, 13/13:
    • a payload built the way ui.dart builds it (two obscured text fields) lands on the fields, both with the view at the screen origin and with the view at {359, 64};
    • the iPad case: {60, 260} in a window at {359, 64} becomes {419, 324};
    • fractional origins, degenerate and partial input, clamping, the nil case, and immutability of the result.
  • View origin: correct in portrait and landscape on an iPhone 17 Pro simulator. That run has one full-screen scene, so the offset-window branch is the SDK's formula and was not exercised here.

An adversarial review caught that an earlier version read four ints in physical pixels. Fed the real payload, it would have redacted nothing.

Known gap, for the move to 7.x

Dart rounds x, y, width, height to whole points with round(), so an edge can sit up to half a point inside the widget. Rounding outward on the Dart side (floor the left and top, ceil the right and bottom) would close that.

Base

The base is release because this branch starts at 9.0.5. Against main (9.0.2) the PR would also carry the 9.0.3–9.0.5 bump commits. Retarget as needed.

🤖 Generated with Claude Code

… SDK 7

The 7.x iOS SDK records every window of the app at its place on the
screen, and it pulls a wrapper's secure rectangles through
-[BGSBugseeWrapper secureRectanglesForDisplay:] as
[version, count, left, top, right, bottom, ...] in screen points
(BGSContracts.h). The Dart side (lib/src/ui.dart) sends five ints per
secure widget, [id, x, y, width, height], measured from the Flutter
view's origin in points (the device pixel ratio is forced to 1 on iOS);
the 6.x SDK reads that payload with a 20-byte stride. With SDK 7, a
Flutter view that does not start at the screen's origin would be recorded
in the clear: an iPad window in Stage Manager or Split View, or the
iPhone Duo with two windows side by side.

- BGSFlutterSecureRectanglesBuffer takes the Dart payload as it is,
  moves every rectangle by the view's origin on the screen, rounds the
  edges outward, and packs the result for the contract. The version is
  always 0, so the SDK compares the rectangles itself: the result also
  changes when only the window moves, and Dart publishes nothing then.
  A malformed input (no data with a non-zero count) returns nil, which
  the caller must not hand to the SDK, as nil clears the rectangles.
- BGSFlutterViewOriginOnScreen returns where the Flutter view starts in
  the frame the SDK records. It uses the SDK's own conversion: through
  the screen's fixed space, or the window's frame origin on Mac Catalyst,
  for iOS apps on a Mac and for a window on no screen.

Nothing calls these helpers yet: the plugin still uses the 6.x API
(setInternalSecureRectangles:). When the plugin moves to SDK 7, call them
from the wrapper's secureRectanglesForDisplay:. The React Native wrapper
uses the same conversion (bugsee-react-native#30).

Checked:
- Compiles without warnings for an iOS 11 simulator and device, an
  iOS 15 device and Mac Catalyst.
- 13 buffer checks pass, including a payload built the way ui.dart builds
  it (two obscured text fields) and the iPad case: {60, 260} in a window
  at {359, 64} becomes {419, 324}.
- The view origin is correct in portrait and landscape on an iPhone 17
  Pro simulator (one full-screen scene; the offset-window branch is the
  SDK's formula, not exercised here).
- An adversarial review found that an earlier draft read four ints in
  physical pixels; it would have redacted nothing.

The plugin has no iOS test target, so these checks ran outside the repo.

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