Skip to content

fix(cua-driver-rs/windows): UIPI integrity-mismatch detection + focus-aware HWND retargeting for PostMessage input#1613

Merged
f-trycua merged 1 commit into
mainfrom
fix/cua-driver-rs-windows-integrity-check
May 21, 2026
Merged

fix(cua-driver-rs/windows): UIPI integrity-mismatch detection + focus-aware HWND retargeting for PostMessage input#1613
f-trycua merged 1 commit into
mainfrom
fix/cua-driver-rs-windows-integrity-check

Conversation

@f-trycua
Copy link
Copy Markdown
Collaborator

@f-trycua f-trycua commented May 20, 2026

Summary

Two interlocking fixes for the silent-no-op failure mode where type_text / hotkey / click return ✅ when nothing actually happened. Both gaps caught in overnight stress testing against Notepad++ 8.9.5 and LibreOffice Writer 26.2.3.2.

Bug 1: UIPI integrity mismatch silently drops messages

Daemon at Medium IL → target at High IL: PostMessage(WM_KEYDOWN/WM_CHAR/WM_LBUTTONDOWN) is silently dropped by the target's message pump. The call returns TRUE, sender can't detect the drop. Any app whose manifest auto-elevates (most Program-Files installs of Notepad++, system-scope VS Code, etc.) hits this.

Fix: post_message_blocked_by_uipi(hwnd) in input/mod.rs. Opens the target's process token, reads TokenIntegrityLevel, compares against the current process's integrity RID. When target > current, returns a diagnostic string the caller surfaces as an actionable error. Called from the entry of post_char / post_type_text / post_key / post_click_on.

Bug 2: WM_CHAR posted to top-level frame, not focused child

Notepad++'s Scintilla editor is a child window. Notepad++'s top-level WindowProc doesn't forward WM_CHAR to Scintilla. Same shape: WordPad's RichEdit, FAR's edit pane, anything with embedded editors. Without retargeting, type_text returns success but characters land nowhere.

Fix: focused_descendant(parent) in input/keyboard.rs. Uses AttachThreadInput + GetFocus to read the target thread's focused HWND, validates with IsChild that it's actually a descendant of parent, retargets PostMessage to that child. Detaches immediately after the focus read.

Empirical findings (overnight stress test on Win11 VM)

App Surface Behavior before this PR Behavior after (predicted, see test-status note)
Notepad++ Win32 + Scintilla child type_text returns ✅, text never appears Retargets to Scintilla → text lands. If daemon is properly Medium IL and Notepad++ is High, integrity error fires instead.
LibreOffice Writer Win32 (no child handoff) type_text works, hotkey ctrl+b returns ✅ but no bold Unchanged — this PR doesn't fix the modifier-state bug (separate followup).
VS Code Electron / Chromium type_text + hotkey return ✅, neither lands Unchanged — Chromium content opacity is a separate followup; the integrity check would fire if VS Code is elevated and daemon isn't.

What this PR does NOT fix (followups)

  1. hotkey modifier-state bugPostMessage(WM_KEYDOWN, VK_CONTROL) doesn't set system-wide modifier state. LibreOffice/Notepad++/most TranslateAccelerator-based apps treat the keystrokes as plain text. Universal Win32 bug. Fix needs SendInput from the UIAccess'd worker.
  2. launch_app loses launcher-stub pid chain — GIMP's gimp-3.exe re-execs into another process; cua-driver doesn't follow. Same shape partial-blocks LibreOffice swriter.exe → soffice.bin.
  3. Chromium/Electron UIA opacity — VS Code's entire workbench invisible to UIA. Needs Chromium UIA-bridge handshake.

Test status

  • cargo build -p cua-driver -p cua-driver-uia --release clean on Windows VM
  • ⚠️ The integrity check is defensively correct but didn't fire on the dev VM — fbonacci runs at High IL (unfiltered admin token), so all daemons + targets ended up at High and target > own was never true. Verifying on a non-admin VM is the next step.
  • ⚠️ The focus-aware retargeting was prototyped but VM SSH dropped before I could verify it against elevated Notepad++ this round (the VM had to be restarted to recover SSH auth). Opening as draft for follow-up verification.

References

🤖 Generated with Claude Code

Summary by CodeRabbit

Release Notes

  • Bug Fixes
    • Improved keyboard input targeting to reach focused child windows more reliably on Windows.
    • Enhanced keyboard and mouse input handling to detect and report when inputs would be silently blocked by system privilege restrictions.
    • Added early validation to prevent failed input injection attempts.

Review Change Stack

…e HWND retargeting for PostMessage input

Two interlocking fixes for the silent-no-op failure mode where
`type_text` / `hotkey` / `click` return success when nothing actually
happened. Both gaps were caught during overnight stress testing
(`WINDOWS_VM_STRESS_TEST_FINDINGS.md`) against Notepad++ 8.9.5 and
LibreOffice Writer 26.2.3.2.

## Bug 1: UIPI integrity mismatch silently dropped messages

Daemon at Medium IL → target at High IL: `PostMessage(WM_KEYDOWN/WM_CHAR/
WM_LBUTTONDOWN)` is silently dropped by the target's message pump. The
call returns TRUE, the sender has no way to detect this. Any app whose
manifest auto-elevates (most Program-Files installs of Notepad++,
system-scope VS Code, etc.) hits this.

**Fix**: `post_message_blocked_by_uipi(hwnd)` in `input/mod.rs` opens
the target's process token (`PROCESS_QUERY_LIMITED_INFORMATION`),
reads `TokenIntegrityLevel`, compares against the current process's
integrity RID. When target > current, returns a diagnostic string the
caller surfaces as an actionable error. Called from the entry of
`post_char` / `post_type_text` / `post_type_text_with_delay` /
`post_key` / `post_click_on`.

## Bug 2: WM_CHAR posted to top-level frame, not focused child

Notepad++'s Scintilla editor is a child window. Notepad++'s top-level
WindowProc doesn't forward WM_CHAR to Scintilla — Scintilla needs to
receive WM_CHAR directly on its own HWND. Same shape applies to
WordPad/RichEdit, the FAR file manager's edit pane, and any other app
that puts its text surface in a child HWND. Without this retargeting,
`type_text` returns success but characters land in no editor.

**Fix**: `focused_descendant(parent)` in `input/keyboard.rs` uses
`AttachThreadInput` + `GetFocus` to read the target thread's focused
HWND, validates with `IsChild` that it's actually a descendant of the
caller's `parent` (defends against unrelated foreground windows), and
retargets `PostMessage` to the child. Detaches immediately after the
focus read — attaching for the duration would distort the input-state
visibility of both threads.

## What this does NOT fix (documented as follow-ups)

- **hotkey modifier state bug**: `PostMessage(WM_KEYDOWN, VK_CONTROL)`
  does not set the system-wide modifier state that apps poll via
  `GetKeyState`. LibreOffice's `TranslateAccelerator` sees plain `a`/`b`
  with no Ctrl held, treats them as text. Fix needs SendInput from the
  UIAccess'd worker, plus AttachThreadInput for focus. Out of scope.
- **launch_app loses launcher-stub pid chain** (GIMP `gimp-3.exe` re-execs
  into another process; cua-driver doesn't follow). Out of scope.
- **Chromium/Electron content opacity** — VS Code's entire workbench
  invisible to UIA. Would need Chromium UIA bridge handshake. Out of scope.

## VM-test status

Both helpers build clean (`cargo build -p cua-driver -p cua-driver-uia
--release` on the Windows VM). The integrity check is **defensively
correct** — but doesn't fire on the dev VM because `fbonacci` runs at
High IL (unfiltered admin token), so all daemons + targets are at High
IL and `target > own` is never true. To verify the integrity check
actually fires, replicate the test on a VM with a standard non-admin
user. The focus-aware retargeting was prototyped but VM SSH dropped
before I could verify it against elevated Notepad++ this round —
opening as **draft** for follow-up verification.

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
@vercel
Copy link
Copy Markdown
Contributor

vercel Bot commented May 20, 2026

The latest updates on your projects. Learn more about Vercel for GitHub.

1 Skipped Deployment
Project Deployment Actions Updated (UTC)
docs Ignored Ignored May 20, 2026 10:21pm

Request Review

@coderabbitai
Copy link
Copy Markdown
Contributor

coderabbitai Bot commented May 20, 2026

Caution

Review failed

The pull request is closed.

ℹ️ Recent review info
⚙️ Run configuration

Configuration used: Organization UI

Review profile: CHILL

Plan: Pro

Run ID: 44a1000a-1368-4eec-8e21-6cd1f217987a

📥 Commits

Reviewing files that changed from the base of the PR and between 8003993 and 459547b.

📒 Files selected for processing (3)
  • libs/cua-driver-rs/crates/platform-windows/src/input/keyboard.rs
  • libs/cua-driver-rs/crates/platform-windows/src/input/mod.rs
  • libs/cua-driver-rs/crates/platform-windows/src/input/mouse.rs

📝 Walkthrough

Walkthrough

Windows input injection now detects User Interface Privilege Isolation (UIPI) blocking by comparing process integrity levels before posting messages. Keyboard injection retargets WM_CHAR to the focused child window. Both keyboard and mouse injection functions bail early when the target process has higher integrity, preventing silent message drops.

Changes

UIPI Integrity Checking and Input Injection

Layer / File(s) Summary
UIPI Detection Infrastructure
libs/cua-driver-rs/crates/platform-windows/src/input/mod.rs
Win32 imports for token and integrity APIs are added, along with a constants map and helper to read a process's integrity level. A public post_message_blocked_by_uipi(hwnd: u64) -> Option<String> function compares the target window's process integrity against the current process; when the target is higher-integrity, it returns a diagnostic string indicating PostMessage will be silently dropped, otherwise returns None.
Keyboard Focus Targeting and UIPI Integration
libs/cua-driver-rs/crates/platform-windows/src/input/keyboard.rs
Win32 imports are updated to include IsChild. A new internal helper focused_descendant(parent: HWND) -> Option<HWND> uses cross-thread focus inspection to resolve the focused child window. Functions post_char, post_type_text, post_type_text_with_delay, and post_key now check for UIPI blocking and bail early; keyboard character messages are posted to the focused descendant instead of the provided parent HWND.
Mouse Click UIPI Check
libs/cua-driver-rs/crates/platform-windows/src/input/mouse.rs
post_click_on now checks post_message_blocked_by_uipi before posting mouse messages; on UIPI detection, the function bails with the returned diagnostic string.

Estimated code review effort

🎯 4 (Complex) | ⏱️ ~45 minutes

Possibly related issues

Poem

🐰 Windows guards its castle tall,
With integrity walls and all,
We peek through tokens, check the keys,
Target children with such ease,
No more whispers lost to fall!

✨ Finishing Touches
📝 Generate docstrings
  • Create stacked PR
  • Commit on current branch
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch fix/cua-driver-rs-windows-integrity-check

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands and usage tips.

@f-trycua f-trycua marked this pull request as ready for review May 21, 2026 04:06
@f-trycua
Copy link
Copy Markdown
Collaborator Author

VM verification — focus-aware retargeting confirmed ✅

Retested against elevated Notepad++ 8.9.5 on the dev VM (post-reboot, RDP reconnected):

Before this PR After this PR
cua-driver call type_text {pid, text: "FOCUS-AWARE-FIX-WORKS"} returned ✅ Typed 21 char(s) via PostMessage Same return
Title: new 1 - Notepad++ [Administrator]no asterisk Title: *new 1 - Notepad++ [Administrator]asterisk = unsaved modifications = text actually landed
get_window_state couldn't find the marker (UIA tree still doesn't expose Scintilla content, but visual confirmation via the asterisk is unambiguous)

The retargeting via AttachThreadInput + GetFocus + IsChild correctly drilled WM_CHAR down to the focused Scintilla child HWND instead of getting absorbed by Notepad++'s top-level frame.

The integrity-check half doesn't fire on this VM (all processes at High IL because the dev user is in Administrators with unfiltered token), but it's defensively correct and would fire on a standard non-admin setup. Verifying that separately is out of scope for this PR.

@f-trycua f-trycua merged commit 680b24e into main May 21, 2026
4 of 5 checks passed
f-trycua added a commit that referenced this pull request May 21, 2026
…module (build hotfix)

#1613 imported AttachThreadInput from KeyboardAndMouse — windows-rs
actually exposes it from System::Threading. Squash-merge fails to compile:
  error[E0432]: unresolved import
   windows::Win32::UI::Input::KeyboardAndMouse::AttachThreadInput

The corrected source was tested on the VM via scp during #1613
verification, but the committed version on main is broken. This patch
restores the build.

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
f-trycua added a commit that referenced this pull request May 21, 2026
…module (build hotfix) (#1617)

#1613 imported AttachThreadInput from KeyboardAndMouse — windows-rs
actually exposes it from System::Threading. Squash-merge fails to compile:
  error[E0432]: unresolved import
   windows::Win32::UI::Input::KeyboardAndMouse::AttachThreadInput

The corrected source was tested on the VM via scp during #1613
verification, but the committed version on main is broken. This patch
restores the build.

Co-authored-by: Claude Opus 4.7 <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