feat(mobile): protect native privacy and accessibility boundaries - #5683
Closed
iscekic wants to merge 3 commits into
Closed
feat(mobile): protect native privacy and accessibility boundaries#5683iscekic wants to merge 3 commits into
iscekic wants to merge 3 commits into
Conversation
This was referenced Aug 28, 2026
Contributor
Code Review SummaryStatus: No Issues Found | Recommendation: Merge Files Reviewed (47 files)
Reviewed by grok-4.6 · Input: 511.3K · Output: 32.4K · Cached: 1.1M Review guidance: REVIEW.md from base branch |
Contributor
Author
|
Retired. The owner cancelled this section and restarted the work with a tighter brief. The biometric gate scope grew into a full native-action admission audit; the replacement covers initial app unlock only. |
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.
Summary
File entries show the class, status (A = added, M = modified), and size as changed-line totals from the supplied statistics.
LocalAccessPrivacySnapshotreports native visibility;publishLocalAccessVisibilityrequires ready ownership, unlock state, capture protection, and a matching native generation before uncovering.Callers must await
armLocalAccessPrivacybefore mounting and usedisarmLocalAccessPrivacyafter unmounting; Android capture operations serialize with fresh keys.Access revocation covers immediately, and
assertNativeForegroundrejects inactive or unavailable native support; this level does not connect these controls to authenticated screens or tools.Files
apps/mobile/src/lib/local-access-privacy.ts— Source, A, 210 lines; adds access revocation, serialized capture ownership, stale-arm rejection, gate subscriptions, and guarded announcements.apps/mobile/src/lib/local-access-privacy.test.ts— Test, A, 324 lines; adds the privacy boundary suite.apps/mobile/src/lib/local-access-privacy-inventory.test.ts— Test, A, 590 lines; adds native privacy inventory checks.apps/mobile/modules/local-access-privacy/tests/native-test-helpers.ts— Test, A, 96 lines; adds shared native test helpers.PrivacyVisibilityStatestarts unarmed and invalidates older generations on foreground, protection, or failure changes; authentication and background timing remain outside native state.Once armed,
publishVisibilityaccepts only a matching foreground generation without failure, and duplicate publication leaves focus unchanged.Armed protection suppresses unpublished speech but permits foreground gate speech; unarmed announcements retain legacy admission except for stale-generation rejection.
Files
apps/mobile/modules/local-access-privacy/android/src/main/java/expo/modules/localaccessprivacy/PrivacyVisibilityState.java— Source, A, 60 lines; adds generation invalidation, fail-closed publication, and separate protected/gate speech admission.apps/mobile/modules/local-access-privacy/ios/PrivacyVisibilityState.swift— Source, A, 49 lines; adds matching native visibility rules without authentication or clock ownership.apps/mobile/modules/local-access-privacy/tests/PrivacyVisibilityStateTests.java— Test, A, 53 lines; adds the Java visibility reducer suite.apps/mobile/modules/local-access-privacy/tests/PrivacyVisibilityStateTests.swift— Test, A, 50 lines; adds the Swift visibility reducer suite.LocalAccessPrivacycompletes synchronous visibility operations on the main thread and rechecks asynchronous announcements immediately before native delivery.PrivacyModuleexposesonVisibilityChangesnapshots andonGateActionevents;PrivacyGateandPrivacyGateActioncarry text, identifiers, and disabled-by-default actions.JavaScript reloads cover retained windows instead of treating module destruction as an unlock, so a reload does not grant visibility.
Files
apps/mobile/modules/local-access-privacy/android/src/main/java/expo/modules/localaccessprivacy/LocalAccessPrivacyModule.kt— Source, A, 67 lines; adds Expo records, main-thread calls, events, queued speech checks, and cover-on-destroy handling.apps/mobile/modules/local-access-privacy/ios/LocalAccessPrivacyModule.swift— Source, A, 62 lines; adds the matching Expo contract, synchronous main-thread operations, queued speech checks, and cover-on-destroy handling.When armed,
LocalAccessPrivacycovers application windows synchronously on app, scene, or focus loss without changing authentication or background timing.Android tracks live
WindowIdownership through dialog removal; iOS includes separate scenes and legacy alert windows while excluding system prompts.Only explicit publication restores retained focus; Android observation failures deny access instead of granting a focus grace period.
Files
apps/mobile/modules/local-access-privacy/android/src/main/java/expo/modules/localaccessprivacy/LocalAccessPrivacy.kt— Source, A, 299 lines; coordinates activities, pre-show dialogs, modals, native focus, and fail-closed removal checkpoints.apps/mobile/modules/local-access-privacy/ios/LocalAccessPrivacy.swift— Source, A, 299 lines; covers scene and legacy windows, hides application accessibility and keyboards, and restores focus once.apps/mobile/modules/local-access-privacy/ios/LocalAccessPrivacyAppDelegateSubscriber.swift— Source, A, 25 lines; installs at launch and forwards inactive, background, and active callbacks synchronously.apps/mobile/modules/local-access-privacy/android/src/main/java/expo/modules/localaccessprivacy/LocalAccessPrivacyPackage.kt— Source, A, 16 lines; registers native lifecycle callbacks before Expo readiness forwarding.LocalAccessPrivacyGatemust contain only translated, non-sensitive text and action data; the authenticated shell retains responsibility for every action decision.Native covers isolate input and accessibility while showing a scrollable gate; actions require a matching generation, foreground coverage, and an enabled identifier.
Armed Android windows retain
FLAG_SECURE; iOS covers avoid system-owned key windows, and both platforms preserve drafts and restore application accessibility settings.Files
apps/mobile/modules/local-access-privacy/android/src/main/java/expo/modules/localaccessprivacy/ApplicationWindowCover.kt— Source, A, 243 lines; adds opaque covers, secure flags, keyboard isolation, title suppression, input confinement, and accessibility restoration.apps/mobile/modules/local-access-privacy/ios/PrivacySceneWindow.swift— Source, A, 107 lines; adds accessible, scalable gate controls, safe-area scrolling, and application-only key-window restoration.announceForA11ynow sends protected speech throughannounceLocalAccessPrivacy, including older-history and voice-listening producers that previously called React Native directly.The native delivery check drops obsolete generations and covered content without replay; empty messages and delivery failures do not interrupt completion flows.
Clients without
LocalAccessPrivacysilently drop these announcements; unarmed speech otherwise retains existing admission, and focus handling, toast presentation, and haptics remain unchanged.Files
apps/mobile/src/lib/a11y/announce.ts— Source, M, 15 lines; replaces direct speech delivery and retains trimming, best-effort failures, and focus helpers.apps/mobile/src/components/agents/session-message-list.tsx— Source, M, 5 lines; routes older-page arrival announcements through the guarded helper.apps/mobile/src/components/kilo-chat/message-list.tsx— Source, M, 4 lines; routes chat history arrival announcements through the guarded helper.apps/mobile/src/lib/voice-input/use-voice-input-actions.ts— Source, M, 5 lines; routes listening announcements through the helper while retaining haptics and voice actions.apps/mobile/src/lib/a11y/announce.test.ts— Test, M, 210 lines; updates the shared announcement suite.apps/mobile/src/lib/a11y/announcement-producer-inventory.test.ts— Test, A, 216 lines; adds announcement producer inventory coverage.apps/mobile/src/lib/a11y/announcing-toast.test.ts— Test, M, 188 lines; updates the toast announcement suite.apps/mobile/src/lib/a11y/status-announcement.test.ts— Test, M, 22 lines; updates status announcement coverage.apps/mobile/src/components/agents/session-message-list.mounted.test.tsx— Test, M, 201 lines; updates mounted session history coverage.apps/mobile/src/lib/voice-input/use-voice-input-feedback.test.ts— Test, M, 132 lines; updates voice feedback coverage.apps/mobile/src/lib/voice-input/use-voice-input.test.ts— Test, M, 32 lines; updates voice-input hook coverage.Expo now links
LocalAccessPrivacyinto Apple and Android builds without adding Android permissions.Its Apple build requires iOS 16.4, Swift 5.9, and
ExpoModulesCore; its Android build addsandroidx.fragment:fragment-ktx:1.8.9.Existing clients require native rebuilds, and Knip's
entryincludes the public privacy API while activation remains unconnected.Files
apps/mobile/knip.json— Source, M, 2 lines; adds the privacy API as an unused-code analysis entry.apps/mobile/modules/local-access-privacy/android/build.gradle— Source, A, 19 lines; configures the Expo Android library and AndroidX fragment dependency.apps/mobile/modules/local-access-privacy/android/src/main/AndroidManifest.xml— Source, A, 1 line; adds an empty library manifest without permissions.apps/mobile/modules/local-access-privacy/expo-module.config.json— Source, A, 10 lines; registers both native modules and the Apple lifecycle subscriber for autolinking.apps/mobile/modules/local-access-privacy/ios/LocalAccessPrivacy.podspec— Source, A, 26 lines; defines a static framework, the platform/toolchain requirements, and the repository license.apps/mobile/modules/local-access-privacy/package.json— Source, A, 7 lines; declares the private native package at version 1.0.0 with the repository license.The host coordinator runner compiles production
LocalAccessPrivacycoordinators against platform adapters and executes them without a device.It requires an existing external output location and Swift or Java/Kotlin tools; it does not establish native rendering or operating-system timing.
Files
apps/mobile/modules/local-access-privacy/tests/coordinator/run.sh— Source, A, 34 lines; adds iOS and Android host compilation and execution with explicit output and compiler arguments.apps/mobile/modules/local-access-privacy/tests/coordinator/android/Accessibility.kt— Test, A, 18 lines; adds an Android accessibility adapter.apps/mobile/modules/local-access-privacy/tests/coordinator/android/Activities.kt— Test, A, 26 lines; adds Android activity lifecycle adapters.apps/mobile/modules/local-access-privacy/tests/coordinator/android/ApplicationWindowCover.kt— Test, A, 38 lines; adds an Android window-cover test double.apps/mobile/modules/local-access-privacy/tests/coordinator/android/Bundle.kt— Test, A, 3 lines; adds an Android bundle adapter.apps/mobile/modules/local-access-privacy/tests/coordinator/android/Context.kt— Test, A, 12 lines; adds an Android context adapter.apps/mobile/modules/local-access-privacy/tests/coordinator/android/ExtraWindowEventListener.kt— Test, A, 8 lines; adds a React Native extra-window listener adapter.apps/mobile/modules/local-access-privacy/tests/coordinator/android/Fragments.kt— Test, A, 30 lines; adds Android fragment adapters.apps/mobile/modules/local-access-privacy/tests/coordinator/android/LocalAccessPrivacyCoordinatorTests.kt— Test, A, 225 lines; adds the Android coordinator suite.apps/mobile/modules/local-access-privacy/tests/coordinator/android/ReactContext.kt— Test, A, 11 lines; adds a React context adapter.apps/mobile/modules/local-access-privacy/tests/coordinator/android/ReactModalHostView.kt— Test, A, 6 lines; adds a modal host adapter.apps/mobile/modules/local-access-privacy/tests/coordinator/android/Views.kt— Test, A, 63 lines; adds Android view and window adapters.apps/mobile/modules/local-access-privacy/tests/coordinator/ios/LocalAccessPrivacyCoordinatorTests.swift— Test, A, 138 lines; adds the iOS coordinator suite.apps/mobile/modules/local-access-privacy/tests/coordinator/ios/UIKit.swift— Test, A, 93 lines; adds a UIKit adapter.Tests: 25 files changed (19 added, 6 modified): privacy helpers (3), visibility reducers (2), coordinators (13), and announcement producers (7).
Generated: 0 files changed.
Visual Changes
Visual Changes: N/A
Verification
No manual runtime tests have run; the final stack level owns the required device bundle.
Runtime verification remains bot-e2e, not human delegation.
Reviewer Notes
Human steps
LocalAccessPrivacybefore distributing this JavaScript.Scope and evidence
Kilo-Org/cloud./Users/igor/Projects/.worktrees/mobile-context-lock-758a.mobile-context-lock-758a-s4; review head:mobile-context-lock-758a-s5.Notes
This level adds native privacy boundaries and guarded announcements without activating biometric controls. Full iOS and Android verification runs on the final stack level.
Stacked PRs — merge bottom to top. Each level shows only its own diff.
Runtime verification (E2E, user advocacy, simplify) runs on the tip PR over every level.
Every level keeps its own checks, its own bot review, and its own threads; each one is answered on its own PR.
Each level is its own deliverable: it builds and passes its own checks alone.
A finding on a level is repaired on that level, then carried upward with stack.sh forward.
mobile-context-lock-758a— feat(mobile): add account-owned local access state #5642mobile-context-lock-758a-s2— fix(mobile): bind restored context and drafts to account scope #5651mobile-context-lock-758a-s3— fix(mobile): enforce local access at final transport dispatch #5658mobile-context-lock-758a-s4— fix(mobile): bind Kilo Chat effects to local access #5664mobile-context-lock-758a-s5— feat(mobile): protect native privacy and accessibility boundaries #5683 ← this PR (tip)