Repository navigation
feat(desktop): bind Windows keys to the approved recipient and refuse elevated apps - #167
Conversation
… elevated apps post_typed checks that the windows in front still belong to the front and focused apps the shared guard approved. keyboard_refusal compares the target's token integrity level with ours, since Windows silently drops input sent to an app run as administrator, and refuses an app whose level cannot be read. Review notes from Gentleman-Programming#164 and Gentleman-Programming#165: one shared administrator message, the before/while typing wording picked by what was sent, an elevation test for the key recheck, and title() reuses title_checked().
|
@MarsSall the binding is solid. One change before merge:
Worth checking on the PC before merge rather than in CC8:
Fine for part 3:
|
…ady sent Gentleman-Programming#167: post_input chose "before"/"while" from its own call, but the helper posts one chunk per call and appended an uncertain range, so a change at the second chunk said nothing was sent after 20 characters. post_typed now returns a TypedError: RecipientChanged and FrontUnknown mean nothing of that post went out, and the helper reports them after the characters already sent. A token that refuses to be opened is taken as an elevated app.
|
Thanks @egdev6, both points are addressed in 73bd200. 1. Contradictory message after the first chunk: fixed.
The helper words the first two itself ( The New fake-backed tests:
2. Which message an elevated app gets: checked on the PC. From a non-elevated PowerShell in the desktop session (its own token reads Part 3 notes, recorded:
Linux |
|
@MarsSall 73bd200 resolves it. Two small things, fine for part 3:
|
The element under a point and the focused element with its window's default button come from UI Automation, with a 0.5 s timeout; anything it does not report is unreadable and asks for a confirmation. focused_app now also checks UI Automation's focused process. WinDesktop implements the helper's Desktop with this backend. COM is entered in one place. Review notes from #167: a token that cannot be read is no longer taken as elevated, a backend refusal of a key is worded like the key recheck, and a window in front with no keyboard focus says so.
What
CC6c, the last slice of CC6. It builds on #164 (the shared
keyboard_refusalhook) and #165 (Windows app identity). The code is still unwired until part 3'simpl Desktop, so no behavior changes on any platform.win::post_typed(event, recipient)binds a typed chunk, a Return, or a key to the recipient the shared guard approved, exactly as feat(desktop): bind computer-control keys to the app the agent saw and approved #159 does on macOS:target_ofthe pinned foreground root;A mismatch sends nothing ("The app receiving the keys changed before typing, so nothing was sent.").
win::keyboard_refusal(app)is the Windows side of feat(desktop): refuse keyboard input to apps the platform says cannot take it #164's hook. It compares the target process token's integrity level with ours, because Windows silently drops input sent to a higher-integrity process:Review notes folded in
From #164:
runs_as_administrator(name)lives inmod.rs, and bothwin.rsand the fake use it. Themcptests still assert the full literal, so a wording change shows up in them.target.is_none()is gone: the start of a call now pinsOption<(root, focus)>, or returns "Cannot tell which window is in front, so nothing was sent."done == 0) says "changed before typing, so nothing was sent"; after that, "changed while typing, so the rest was not sent".elevated_front_refuses_key_at_the_recheck: an app that becomes elevated whilekeyreads the focus is refused at the recheck, and nothing is posted. It passed as soon as it was written, because feat(desktop): refuse keyboard input to apps the platform says cannot take it #164's hook already covers that path, so it pins existing behavior rather than going RED.From #165:
title()is nowtitle_checked(hwnd).unwrap_or_default().Tests
New in
win.rs:this_process_takes_its_own_keys(CI): our own token's level is accepted.a_process_whose_level_cannot_be_read_gets_no_keys(CI): a nonexistent pid gets the "cannot tell" refusal.keys_for_another_recipient_are_not_posted(CI): a recipient the windows in front do not belong to gets an error. On a runner with nothing in front, that error is "Cannot tell which window is in front"; this test checks the returned error, not a send count.an_elevated_console_gets_no_keys(#[ignore], interactive): start a console with "Run as administrator" and pass its pid inGENTLE_DOT_ELEVATED_PID. I'll run it on the PC with CC8's end-to-end pass.Evidence:
cargo test --lib: 509 passed, 0 failed, no warnings.post_inputwith the source line by line. It found no bypass of the recipient check, and reviewed the token code: the two-callGetTokenInformation, an alignedu64buffer, the SID validated before its sub-authorities are read, and every handle closed. No blockers.Known limits (inherited, not new)
SendInputcannot be made atomic.@egdev6 this is CC6c. Part 3 (UI Automation and
impl Desktop, removing thedead_codeallow) is next.