Skip to content

fix(cua-driver/windows): reject stale element-cache snapshots - #2075

Open
outdog-hwh wants to merge 2 commits into
trycua:mainfrom
outdog-hwh:fix/windows-stale-element-cache
Open

fix(cua-driver/windows): reject stale element-cache snapshots#2075
outdog-hwh wants to merge 2 commits into
trycua:mainfrom
outdog-hwh:fix/windows-stale-element-cache

Conversation

@outdog-hwh

@outdog-hwh outdog-hwh commented Jun 30, 2026

Copy link
Copy Markdown
Contributor

What changed

This adds a freshness guard to the Windows UIA/MSAA element cache so cached element centers and rects are no longer reused after the target window moves or resizes.

Instead of silently dispatching a click with stale screen coordinates, click-family tools now fail fast with a stale snapshot error and ask the caller to rerun get_window_state.

Related issue

Fixes #1984

Approach

  • Store the top-level window rect alongside each cached Windows element snapshot.
  • Treat cached centers/rects as stale when the current GetWindowRect no longer matches the captured rect.
  • Surface a specific stale-snapshot error from click, double_click, and right_click instead of the old generic cache-miss message.
  • Add regression tests for both center and rect lookups when the window rect changes.

Testing

  • cargo test -p platform-windows

Notes

This is the smaller fail-fast variant discussed in #1984. It does not try to auto-refresh the snapshot or hook window-move events yet.

Summary by CodeRabbit

  • New Features

    • Improved handling of window changes during click actions, with clearer guidance when a refreshed snapshot is needed.
    • Added better detection of outdated cached UI state so interactions can be retried more reliably.
  • Bug Fixes

    • Fixed inconsistent error messages when elements were missing from cache.
    • Reduced failures caused by stale window snapshots after a window moves or resizes.

@vercel

vercel Bot commented Jun 30, 2026

Copy link
Copy Markdown
Contributor

@outdog-hwh is attempting to deploy a commit to the Cua Team on Vercel.

A member of the Team first needs to authorize it.

@chatgpt-codex-connector

Copy link
Copy Markdown

You have reached your Codex usage limits for code reviews. You can see your limits in the Codex usage dashboard.

@coderabbitai

coderabbitai Bot commented Jun 30, 2026

Copy link
Copy Markdown
Contributor

Review Change Stack

Important

Review skipped

Auto incremental reviews are disabled on this repository.

Please check the settings in the CodeRabbit UI or the .coderabbit.yaml file in this repository. To trigger a single review, invoke the @coderabbitai review command.

⚙️ Run configuration

Configuration used: Organization UI

Review profile: CHILL

Plan: Pro

Run ID: 306d2966-637c-420a-a049-0c7d9049ab36

You can disable this status message by setting the reviews.review_status to false in the CodeRabbit configuration file.

Use the checkbox below for a quick retry:

  • 🔍 Trigger review
📝 Walkthrough

Walkthrough

ElementCache is extended to capture the top-level window rect at snapshot time and compare it against the live rect on each lookup, returning None when the window has moved or resized. A public is_snapshot_stale method exposes this check. Click tools (ClickTool, DoubleClickTool, RightClickTool) are updated to use two new shared error helpers that produce consistent error messages for stale and missing cache entries.

Changes

Windows element-cache staleness and error harmonization

Layer / File(s) Summary
CachedSnapshot window_rect field
libs/cua-driver/rust/crates/platform-windows/src/uia/cache.rs
Adds WindowRect tuple alias, GetWindowRect import, and optional window_rect field to CachedSnapshot.
Freshness-aware lookup and public staleness API
libs/cua-driver/rust/crates/platform-windows/src/uia/cache.rs
Implements live_window_rect, snapshot_is_stale, and with_fresh_snapshot; updates get_element_center/get_element_rect to use the freshness-aware path; adds public is_snapshot_stale(pid, hwnd) -> bool.
Staleness unit tests
libs/cua-driver/rust/crates/platform-windows/src/uia/cache.rs
Test-only helpers inject a current_window_rect and verify that center/rect lookups succeed on matching rect and return None when rect changes.
Shared error helpers and tool call sites
libs/cua-driver/rust/crates/platform-windows/src/tools/impl_.rs
Adds stale_snapshot_error and missing_cached_element_error helpers; updates ClickTool (MSAA expand, MSAA non-expand, UIA), DoubleClickTool, and RightClickTool to use them.

Estimated code review effort

🎯 3 (Moderate) | ⏱️ ~20 minutes

Possibly related issues

Possibly related PRs

  • trycua/cua#1995: Modifies the same DoubleClickTool/RightClickTool dispatch paths in impl_.rs, adding Chromium/Electron short-circuits.

Poem

🐇 Hop, hop — the window moved away,
But cache said "nope, it's here to stay!"
Now rects are checked before each click,
Stale snapshots caught — that old cache trick.
Fresh coords only, errors clear,
No more clicks that disappear! ✨

🚥 Pre-merge checks | ✅ 4 | ❌ 1

❌ Failed checks (1 warning)

Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 78.26% which is insufficient. The required threshold is 80.00%. Write docstrings for the functions missing them to satisfy the coverage threshold.
✅ Passed checks (4 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed The title is concise and accurately reflects the main change: rejecting stale Windows element-cache snapshots.
Linked Issues check ✅ Passed The PR implements the linked stale-snapshot guard for Windows cache lookups and click dispatch, matching issue #1984's candidate A.
Out of Scope Changes check ✅ Passed The error-helper refactor and MSAA guard support the stale-cache fix and do not introduce unrelated scope.
✨ Finishing Touches
🧪 Generate unit tests (beta)
  • Create PR with unit tests

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.

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 1

🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Inline comments:
In `@libs/cua-driver/rust/crates/platform-windows/src/uia/cache.rs`:
- Around line 96-100: In snapshot_is_stale, the fallback currently returns false
when current_window_rect is unavailable, which lets cached coordinates be
treated as fresh even after GetWindowRect fails. Update the staleness check to
fail closed: if CachedSnapshot.window_rect has a value but the live window rect
cannot be read, treat the snapshot as stale instead of fresh. Use
snapshot_is_stale and the current_window_rect handling as the focal points for
the fix.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: Organization UI

Review profile: CHILL

Plan: Pro

Run ID: cae4987f-2110-45a5-9db2-e14419a99854

📥 Commits

Reviewing files that changed from the base of the PR and between 7468487 and bfb8188.

📒 Files selected for processing (2)
  • libs/cua-driver/rust/crates/platform-windows/src/tools/impl_.rs
  • libs/cua-driver/rust/crates/platform-windows/src/uia/cache.rs

Comment on lines +96 to +100
fn snapshot_is_stale(snapshot: &CachedSnapshot, current_window_rect: Option<WindowRect>) -> bool {
match (snapshot.window_rect, current_window_rect) {
(Some(cached), Some(current)) => cached != current,
_ => false,
}

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🎯 Functional Correctness | 🟠 Major | ⚡ Quick win

Fail closed when the live window rect cannot be read.

When a snapshot has a cached rect but GetWindowRect now fails, this returns false and allows cached screen coordinates to be reused even though freshness could not be established.

Proposed fix
 fn snapshot_is_stale(snapshot: &CachedSnapshot, current_window_rect: Option<WindowRect>) -> bool {
     match (snapshot.window_rect, current_window_rect) {
         (Some(cached), Some(current)) => cached != current,
+        (Some(_), None) => true,
         _ => false,
     }
 }
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
fn snapshot_is_stale(snapshot: &CachedSnapshot, current_window_rect: Option<WindowRect>) -> bool {
match (snapshot.window_rect, current_window_rect) {
(Some(cached), Some(current)) => cached != current,
_ => false,
}
fn snapshot_is_stale(snapshot: &CachedSnapshot, current_window_rect: Option<WindowRect>) -> bool {
match (snapshot.window_rect, current_window_rect) {
(Some(cached), Some(current)) => cached != current,
(Some(_), None) => true,
_ => false,
}
}
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@libs/cua-driver/rust/crates/platform-windows/src/uia/cache.rs` around lines
96 - 100, In snapshot_is_stale, the fallback currently returns false when
current_window_rect is unavailable, which lets cached coordinates be treated as
fresh even after GetWindowRect fails. Update the staleness check to fail closed:
if CachedSnapshot.window_rect has a value but the live window rect cannot be
read, treat the snapshot as stale instead of fresh. Use snapshot_is_stale and
the current_window_rect handling as the focal points for the fix.

@f-trycua

f-trycua commented Jul 1, 2026

Copy link
Copy Markdown
Collaborator

This is a genuinely-missing fix — the pre-click freshness guard from #1984 (stale cached center after a window move) is not on main; 0.7.0's cache work was the UAF fix (RetainedElement), a different problem. The logic looks sound.

Two things before it can land: (1) it's conflicting against 0.7.0 — please rebase onto the RetainedElement refactor in cache.rs; (2) it's Windows-only and no CI compiled it, so it needs cargo test -p platform-windows plus a real-desktop run (ideally the multi-monitor repro from #1984). Rebase + a Windows validation and I'll merge. Keeping #1984 and #2015 open.

@outdog-hwh
outdog-hwh force-pushed the fix/windows-stale-element-cache branch from bfb8188 to 1d4d469 Compare July 2, 2026 02:00
@outdog-hwh

Copy link
Copy Markdown
Contributor Author

Thanks for the review. I rebased this onto current upstream/main / the 0.7.0 RetainedElement cache refactor and addressed the CodeRabbit stale-snapshot edge case.

What changed in this update:

  • Preserved the upstream retained-element UAF fix while keeping the stale window-rect guard.
  • Made stale detection fail closed when the cached snapshot has a window rect but the live GetWindowRect read is unavailable.
  • Added a regression test for that fail-closed path.
  • Updated the new cache_uaf_repro.rs fixture for the added window_rect field.

Validation run on Windows:

  • cargo test --offline -p platform-windows -> 91 passed, 2 ignored.
  • cargo build --offline -p cua-driver.
  • Real desktop smoke validation with a rebuilt daemon:
    • launched Notepad via cua-driver call launch_app {"name":"Notepad"}
    • restored the window to a visible rect
    • ran get_window_state to populate the shared element cache
    • moved the HWND by +60,+60 px
    • called click with the old element_index
    • got the expected stale-cache failure: Element [0] snapshot is stale for hwnd=200034. The window moved or resized after get_window_state. Re-run get_window_state to refresh the cache.
    • restored the window position and cleaned up the test processes.

The PR is now mergeable from GitHub's view. The remaining Vercel failure still appears to be the existing authorization/deploy permission issue, not a code check failure.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

Projects

None yet

Development

Successfully merging this pull request may close these issues.

cua-driver(windows): audit #1979 candidates — stale element-cache center + DoubleClickTool missing Chromium short-circuit

3 participants