feat(desktop): native OS input injection for remote control (LC-185, Windows first) #229
Loading…
Reference in a new issue
No description provided.
Delete branch "feat/lc-185-native-input-injection"
Deleting a branch is permanent. Although the deleted branch may continue to exist for a short time before it actually gets removed, it CANNOT be undone in most cases. Continue?
Controlled-side core of the TeamViewer-style remote-control story (LC-181): inject the input stream that arrives on the WebRTC data channel (LC-184) into the host OS. Windows first via
SendInput; other platforms are#[cfg(...)]no-ops until their slice lands. The controller side (LC-184, #228) and the server consent gate (LC-183, #227) already shipped.What lands
desktop/src/inject.rs). ManagedControlState= anactiveflag + held-keys/buttons sets.rc_session(active)arms/disarms on the LC-184 lifecycle; disarming force-releases every held key/button (stuck-key prevention on clean revoke, teardown, or channel drop).rc_input(frame)decodes the compact wire frame and synthesizes input, but only whileactive- the native re-assertion of the grant, so a peer flooding the channel with no live grant injects nothing.[0,1]over the virtual desktop viaMOUSEEVENTF_ABSOLUTE | MOUSEEVENTF_VIRTUALDESK(DPI/resolution-independent, noGetSystemMetrics). Keyboard: scan-code (KeyboardEvent.code -> PS/2 set-1,KEYEVENTF_EXTENDEDKEYfor nav/right-modifier keys). Modifier state is not consulted - the controller sends discrete down/up for the modifier keys themselves.main.rs). A Tauriinitialization_scriptforwards thelc:control-start/lc:control-end/lc:control-inputDOM events (LC-184) to the two commands viawindow.__TAURI_INTERNALS__.invoke, sowithGlobalTauristays off (no broad Tauri API exposed to the page).tauri://) origin unless a capability allows them; the server URL is user-configurable, so a runtimeCapabilityBuilderis added at setup scoped to exactly the configured origin (.local(false).remote("scheme://host[:port]/*")) granting onlyallow-rc-session/allow-rc-input.build.rsdeclares the app commands (AppManifest::commands) so tauri-build generates theallow-*permissions the runtime capability references. Declaring an app manifest also makes app commands ACL-checked for local pages, soset_server_urlis declared and grantedallow-set-server-urlincapabilities/default.json. Autogenerated permission files are gitignored (regenerated every build, likedesktop/gen/).Security posture
The bridge is reachable only from the configured server origin, and every event is gated on
active, which is flipped on only by the local user's explicit Grant click (lc:control-start). The data channel alone is never trusted. This is no weaker than the existingLETS_CHAT_SERVER_URLtrust (a compromised server page can already drive the IPC bridge); it is not a defense against a malicious server, which is documented.Documented limits (not defeated)
SendInputcannot drive a higher-integrity (elevated/admin) window; those events are silently dropped by Windows.[0,1]maps over the whole virtual desktop, matching a primary / full-screen share. Per-monitor surface selection is out of scope.Out of scope (later)
CGEvent+ Accessibility grant) injection backends.Verification
cargo check+clippy+fmtclean (exercises ACL generation,generate_context!acceptingallow-set-server-url, the runtime capability, and the non-Windows no-op module).cargo check --target x86_64-pc-windows-gnuclean in the mingw builder image (exercises the#[cfg(windows)]SendInputpath).just testsurface is untouched.Controlled-side core of the TeamViewer-style remote-control story (LC-181): inject the input stream that arrives on the WebRTC data channel (LC-184) into the host OS. Windows is the first target via SendInput; other platforms are #[cfg(...)] no-ops until their slice lands. The controller side (LC-184) and the server consent gate (LC-183) already shipped. inject.rs: a managed ControlState (active flag + held-keys/buttons sets) plus two Tauri commands. rc_session(active) arms/disarms on the LC-184 lifecycle events; disarming force-releases every held key/button (stuck-key prevention). rc_input(frame) decodes the compact wire frame and synthesizes input, but only while active - the native re-assertion of the grant, so a peer flooding the channel with no live grant injects nothing. Windows synthesis maps normalized [0,1] over the virtual desktop with MOUSEEVENTF_ABSOLUTE|VIRTUALDESK (DPI/resolution-independent) and uses scan-code keyboard (KeyboardEvent.code -> PS/2 set-1, extended flag for nav/right-modifier keys); modifier state is not consulted because the controller sends discrete down/up for the modifier keys themselves. main.rs: a Tauri initialization_script bridges the lc:control-start / lc:control-end / lc:control-input DOM events (LC-184) to the two commands via the internal IPC entrypoint, so withGlobalTauri stays off. App commands are unreachable from a remote (non-tauri://) origin unless a capability allows them, and the server URL is user-configurable, so a runtime CapabilityBuilder is added at setup scoped to exactly the configured origin (.local(false).remote("scheme://host[:port]/*")) and granting only allow-rc-session / allow-rc-input. This is no weaker than the existing LETS_CHAT_SERVER_URL trust posture; it is not a defense against a malicious server (documented). build.rs: declare the app commands via AppManifest::commands so tauri-build generates the allow-* permissions the runtime capability references. Declaring an app manifest also makes app commands ACL-checked for local pages, so set_server_url is declared and granted allow-set-server-url in capabilities/default.json. The autogenerated permission files are gitignored (regenerated every build, like desktop/gen/). Documented limits (not defeated): Windows UIPI blocks driving elevated/admin windows; single shared surface ([0,1] over the virtual desktop, matching a primary/full-screen share); approximate wheel granularity. Linux uinput/XTEST and macOS CGEvent are later subtasks. Verified: Linux desktop check + clippy + fmt, and a cargo check against x86_64-pc-windows-gnu for the SendInput path. Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>