Skip to content

hook: ui-input-guard blocks synthetic input at the user's live session - #291

Merged
EdbertChan merged 1 commit into
stack/EdbertChan/reflect/ui-input-guard-hook-freshness-20260908/hook-freshness-warns-checkout-behind-hooks-stale--e2558c7afrom
stack/EdbertChan/reflect/ui-input-guard-hook-freshness-20260908/ui-input-guard-blocks-synthetic-input-user-s-live--670196cd
Sep 9, 2026
Merged

hook: ui-input-guard blocks synthetic input at the user's live session#291
EdbertChan merged 1 commit into
stack/EdbertChan/reflect/ui-input-guard-hook-freshness-20260908/hook-freshness-warns-checkout-behind-hooks-stale--e2558c7afrom
stack/EdbertChan/reflect/ui-input-guard-hook-freshness-20260908/ui-input-guard-blocks-synthetic-input-user-s-live--670196cd

Conversation

@EdbertChan

@EdbertChan EdbertChan commented Sep 9, 2026

Copy link
Copy Markdown
Owner

Summary

Synthetic input acts on the session the user is sitting in. Typed into the wrong window it sends real messages and trips real shortcuts; a screen recording captures whatever they have open.

In one session an agent drove the user's Slack with AppleScript keystrokes while they were working in another app. Later keystrokes went into the macOS lock screen, and a stray message plus an accidental pin landed in their workspace. Across the transcript corpus, 3 sessions issued 17 such commands, 13 of them in that one.

This blocks those commands unless the user has granted a hands-off window, the screen is unlocked, and they have been idle a moment.

Backtested over every Bash command in the local transcript corpus: 41,152 commands, 22 flagged (0.053%) across 3 sessions. Every remaining hit was audited by hand and is a real input-driving or screen-capture call. Five false positives the backtest exposed are now fixtures: a git grep whose pattern lists the mechanisms, a which probe, a commit message naming them, and code holding them as literals in a heredoc or behind python3 -c.

The same backtest exposed two silent skips that reported unchecked commands as clean: a wrapper script reached through a shell variable was never resolved, and a script over 64 KB was skipped rather than read. Paths are now resolved, files are streamed in chunks, and a file past an 8 MB ceiling or unreadable is refused with its path and reason instead of assumed safe.

Review Claim

A Bash command that synthesizes input or records the screen is blocked unless a fresh hands-off marker exists, the screen is unlocked, and the user is idle.

Review Lane

behavior

Review Unit

tooling-policy

Safety Invariant

Only Bash commands carrying a named mechanism are considered: AppleScript System Events with keystroke, key code, or click at; cliclick; xdotool; screencapture -V; ffmpeg on an avfoundation screen device. The lock and idle probes fail open, so a probe error cannot block a clean command; a missing marker still blocks. Authoring a driver script through a write heredoc, and read-only search pipelines whose pattern merely contains the words, stay silent.

Slice Rationale

One mechanism, one commit, wiring included. The repo's install test requires every hook entrypoint to be wired in the commit that adds it, so a wiring-only follow-up slice left this one red on its own.

Non-goals

Does not follow a wrapper more than one level deep, and cannot see inside a compiled binary. Code handed to a non-shell interpreter counts as data, so input driven from inside a Python or Node program is a known gap. Does not police the Write or Edit tools: authoring a driver is allowed, running it is what needs a window. Lock and idle detection is macOS-only; elsewhere only the marker applies.

Test Plan

Test Plan
  • python3 -m unittest discover -s engine/hooks/ui-input-guard/tests — 21 tests, OK (the lock and idle probes take an injected platform, so the macOS branch is exercised on Linux CI too)
  • Corpus backtest over 41,152 real Bash commands — 22 flagged, hand-audited, no false positives
  • Live proof: installed into settings.json, then the harness refused a real PreToolUse Bash call with exit 2
  • python3 scripts/check_hook_test_coverage.py — OK (23 hooks)
  • python3 scripts/check_no_new_comments.py --base origin/main — ok

Revert Plan

Revert Plan
  • Safe to revert? Yes
  • Revert command: git revert <sha>
  • Post-revert steps: rerun ./install.sh, then rm -rf ~/.claude/hooks/ui-input-guard
  • Data migration? No

🤖 Generated with Claude Code

https://claude.ai/code/session_01KU2pPKob4MJ1NqjsfTNyYJ

@EdbertChan

EdbertChan commented Sep 9, 2026

Copy link
Copy Markdown
Owner Author

This pull request is part of a Mergify stack:

# Pull Request Link
1 hook: hook-freshness warns when the checkout behind the hooks is stale #290
2 hook: ui-input-guard blocks synthetic input at the user's live session #291 👈
3 docs: prove UI work on a disposable surface, not the user's session #292
4 hook(wrong-check-reflect): catch a concession that the user found the miss #298
5 gate: an unchecked file is not a clean file #296

@cursor

cursor Bot commented Sep 9, 2026

Copy link
Copy Markdown

Bugbot couldn't run - usage limit reached

Bugbot is counted against Cursor usage for this user or team, and this run hit a usage or spend limit.

A user or team admin can review and increase usage limits in the Cursor dashboard.

(requestId: serverGenReqId_c477b065-d373-4be7-ba2a-80bb72cda3e8)

@EdbertChan
EdbertChan force-pushed the stack/EdbertChan/reflect/ui-input-guard-hook-freshness-20260908/ui-input-guard-blocks-synthetic-input-user-s-live--670196cd branch from bad10af to 2d22904 Compare September 9, 2026 05:09
@cursor

cursor Bot commented Sep 9, 2026

Copy link
Copy Markdown

Bugbot couldn't run - usage limit reached

Bugbot is counted against Cursor usage for this user or team, and this run hit a usage or spend limit.

A user or team admin can review and increase usage limits in the Cursor dashboard.

(requestId: serverGenReqId_3d29e9ae-85c3-40c3-858e-40c0c5c015df)

@EdbertChan

EdbertChan commented Sep 9, 2026

Copy link
Copy Markdown
Owner Author

Revision history

# Type Changes Reason Date
1 initial bad10af 2026-09-09 05:10 UTC
2 content bad10af → 2d22904 (raw) 2026-09-09 05:10 UTC
3 content 2d22904 → 445e5b0 (raw) 2026-09-09 05:39 UTC
4 content 445e5b0 → 3eea768 2026-09-09 06:22 UTC

@EdbertChan
EdbertChan force-pushed the stack/EdbertChan/reflect/ui-input-guard-hook-freshness-20260908/ui-input-guard-blocks-synthetic-input-user-s-live--670196cd branch from 2d22904 to 445e5b0 Compare September 9, 2026 05:39
@cursor

cursor Bot commented Sep 9, 2026

Copy link
Copy Markdown

Bugbot couldn't run - usage limit reached

Bugbot is counted against Cursor usage for this user or team, and this run hit a usage or spend limit.

A user or team admin can review and increase usage limits in the Cursor dashboard.

(requestId: serverGenReqId_a5a21c1a-58bc-4cb1-87c9-70a747c55385)

AppleScript System Events keystrokes and clicks, cliclick, xdotool,
screencapture video, and ffmpeg screen capture act on the session the user is
sitting in. They are blocked unless a hands-off window marker is open and
fresh, the screen is unlocked, and the user has been idle a moment.

A command that runs a local script is scanned through that script, with shell
variables in the path resolved first, and files are streamed rather than
skipped for being large: an unchecked file is not a clean file. Past the scan
ceiling the command is refused with the reason.

Ships with its own installer wiring, since the repo's install test requires
every hook entrypoint to be wired in the commit that adds it.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01KU2pPKob4MJ1NqjsfTNyYJ
Change-Id: I670196cdbe46ac0dad0cd9c097033d312f1cffb6
@EdbertChan
EdbertChan force-pushed the stack/EdbertChan/reflect/ui-input-guard-hook-freshness-20260908/ui-input-guard-blocks-synthetic-input-user-s-live--670196cd branch from 445e5b0 to 3eea768 Compare September 9, 2026 06:22
@EdbertChan
EdbertChan force-pushed the stack/EdbertChan/reflect/ui-input-guard-hook-freshness-20260908/hook-freshness-warns-checkout-behind-hooks-stale--e2558c7a branch from 817ee2a to 2d1bff8 Compare September 9, 2026 06:22
@cursor

cursor Bot commented Sep 9, 2026

Copy link
Copy Markdown

Bugbot couldn't run - usage limit reached

Bugbot is counted against Cursor usage for this user or team, and this run hit a usage or spend limit.

A user or team admin can review and increase usage limits in the Cursor dashboard.

(requestId: serverGenReqId_c041be79-a8ab-4f3e-a4ae-1c572be8cabb)

@EdbertChan
EdbertChan merged commit 314f044 into stack/EdbertChan/reflect/ui-input-guard-hook-freshness-20260908/hook-freshness-warns-checkout-behind-hooks-stale--e2558c7a Sep 9, 2026
8 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant