Skip to content

Add --allow-system-keys to ui send-keys#603

Merged
nmetulev merged 6 commits into
mainfrom
yeelam/ui-allow-system-keys
Jul 15, 2026
Merged

Add --allow-system-keys to ui send-keys#603
nmetulev merged 6 commits into
mainfrom
yeelam/ui-allow-system-keys

Conversation

@yeelam-gordon

Copy link
Copy Markdown
Contributor

Summary

  • add --allow-system-keys to winapp ui send-keys
  • keep the default safety posture (system-/shell-reserved combos are still blocked) and require an explicit opt-in for OS-wide hotkeys via --via send-input
  • preserve the existing no-target, foreground, and secure-desktop safeguards

Why

This enables intentional global hotkey automation (for example, PowerToys-style Win-key shortcuts) without weakening the default behavior.

By default, send-keys --via send-input refuses system-/shell-reserved combos
(win+<key>, alt+f4, alt+tab, ctrl+esc, ...) via SystemKeyGuard because they act
on the OS/shell beyond the target app when injected OS-wide. --allow-system-keys
is an explicit opt-in that bypasses that one guard so a caller can drive a
global hotkey (e.g. PowerToys' win+shift+v) - the low-level global hook watches
the OS-wide input stream, so the injected combo fires it.

- Default is unchanged (flag absent = still rejected); the bypass path logs an
  audit warning listing only the combo names (never the raw keys), consistent
  with the existing no-echo policy.
- No other boundary is relaxed: the no-target guard, foreground/locked-desktop
  guard (no_interactive_desktop), UIPI, and post-message path are untouched.
  Windows still blocks secure sequences like ctrl+alt+del (SAS) regardless.
- 4 new unit tests (flag sends, non-system combo unaffected, default still
  rejects, description discoverable); 55/55 send-keys + guard tests green.
- Docs + cli-schema + SKILL.md mirrors regenerated at 0.4.1.

Proven end-to-end: default rejects win+r; --allow-system-keys makes win+r open
the Run dialog (a Win-modified shell hotkey firing off the injected stream).

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Copilot AI review requested due to automatic review settings July 6, 2026 03:00

Copilot AI 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.

Pull request overview

This PR adds an opt-in --allow-system-keys flag to winapp ui send-keys. By default, --via send-input refuses to synthesize system-/shell-reserved combos (e.g. win+l, alt+f4, alt+tab) because those act OS-wide beyond the target app. The new flag lets a caller explicitly opt in to fire global hotkeys (e.g. PowerToys' win+shift+v) while preserving the default safety posture and all existing guards (no-target, foreground, secure-desktop). It fits into the ui automation surface of the CLI, extending the existing SystemKeyGuard gate rather than removing it.

Changes:

  • Adds AllowSystemKeysOption to UiSendKeysCommand; when a system combo is detected under send-input, the guard now blocks by default but falls through (with a warning) when the flag is set.
  • Adds four unit tests covering opt-in send, non-system no-op, default rejection, and option-description discoverability.
  • Updates all documentation surfaces (ui-automation.md, skill fragment, cli-schema.json, and both generated SKILL.md mirrors) to describe the flag.

Reviewed changes

Copilot reviewed 7 out of 7 changed files in this pull request and generated 1 comment.

Show a summary per file
File Description
src/winapp-CLI/WinApp.Cli/Commands/UiSendKeysCommand.cs Adds --allow-system-keys option and the conditional guard-bypass path with an audit warning
src/winapp-CLI/WinApp.Cli.Tests/UiCommandTests.SendKeys.cs Four new tests for the opt-in, unaffected non-system path, default rejection, and help-text content
docs/ui-automation.md Documents the flag and updates the system-reserved-combos guidance
docs/fragments/skills/winapp-cli/ui-automation.md Hand-written skill fragment updated with the opt-in example and behavior
docs/cli-schema.json Regenerated schema entry for the new boolean option
.github/plugin/skills/winapp-cli/ui-automation/SKILL.md Auto-generated skill mirror (example + options-table row)
.claude/skills/winapp-ui-automation/SKILL.md Auto-generated Claude skill mirror (example + options-table row)

💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.

Comment thread src/winapp-CLI/WinApp.Cli/Commands/UiSendKeysCommand.cs Outdated
@github-actions

github-actions Bot commented Jul 6, 2026

Copy link
Copy Markdown
Contributor

Build Metrics Report

Binary Sizes

Artifact Baseline Current Delta
CLI (ARM64) 31.90 MB 31.90 MB 📈 +7.5 KB (+0.02%)
CLI (x64) 32.22 MB 32.23 MB 📈 +8.0 KB (+0.02%)
MSIX (ARM64) 13.39 MB 13.39 MB 📈 +1.1 KB (+0.01%)
MSIX (x64) 14.23 MB 14.23 MB 📈 +2.0 KB (+0.01%)
NPM Package 27.93 MB 27.93 MB 📈 +1.8 KB (+0.01%)
NuGet Package 27.95 MB 27.96 MB 📈 +5.6 KB (+0.02%)

Test Results

1662 passed, 1 skipped out of 1663 tests in 497.7s (+25 tests, +100.6s vs. baseline)

Test Coverage

48.1% line coverage, 53.6% branch coverage · ✅ +0.1% vs. baseline

CLI Startup Time

42ms median (x64, winapp --version) · ✅ no change vs. baseline


Updated 2026-07-15 19:52:13 UTC · commit cfbba4d · workflow run

@nmetulev nmetulev left a comment

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

🤖 AI-generated review — produced by GitHub Copilot CLI at a maintainer's request. Every finding comes from an automated, multi-dimensional review plus a hands-on build-and-test of this branch in an isolated worktree. Treat file:line references as pointers to verify, not gospel. Nothing was pushed. Posting as a comment (no approve / request-changes).

Verdict: 🟡 SHIP-WITH-CHANGES — The double opt-in design is sound and the existing guards stay intact, but win+l should remain blocked and the silent no-op path needs a warning.

What I actually ran

Built (~33s, 0 warnings); 55/55 SendKeys + SystemKeyGuard tests pass. Against Notepad:

  • Normal text send-keys ✅.
  • win+l via --via send-input without the flag → BLOCKED, exit 1, with a clear, actionable error naming the combo and "Pass --allow-system-keys to opt in." ✅
  • win+up with --allow-system-keys --via send-input → fired OS-wide (Notepad maximized); win+down restored it — self-reversing ✅. (Injection works here when the target is foregrounded first.)
  • win+l with --allow-system-keys but no --via send-input → exit 0, via":"post-message", no warning, hotkey never fires (SEC-02).
  • No-target guard still fires with the flag ✅.

Must-fix

  • SEC-01 (High) — win+l is unblocked by the flag with no per-combo granularity (SystemKeyGuard.cs:56-59 collapses everything to a generic win+<key>; bypass at UiSendKeysCommand.cs:244). win+l is not a SAS — a user-mode SendInput of VK_LWIN+L reaches explorer's low-level hook and calls LockWorkStation(). A mistyped win+l vs win+left in automation locks the (remote) desktop. Consider keeping win+l (and alt+f4 / ctrl+shift+esc) blocked even with the opt-in; the cited use case is registered hotkeys like win+shift+v, not lock control.
  • SEC-02 (Medium) — silent no-op. --allow-system-keys without --via send-input is silently accepted (exit 0, no effect). Emit a LogWarning so this isn't a silent footgun.
  • COR-01 add a test for the --allow-system-keys + post-message path. DOCS-02 the docs name only ctrl+alt+del as "still blocked," implying other combos are safe — call out win+l / alt+f4 explicitly.

Fit & recommendation

Real, unmet use case (global hotkeys are unreachable via post-message), default posture unchanged, guards preserved, an actionable agent-readable error, and a LogWarning audit trail on every bypass. The tension is the security surface: a CLI/agent can now fire OS-wide hotkeys. The blanket on/off is acceptable to start if the docs and the win+l gap are addressed; a per-combo allowlist (e.g. --allow-system-keys win+shift+v) would be a cleaner long-term design and would keep the blast radius to what the caller actually needs.

@chiaramooney chiaramooney added this to the 0.5 milestone Jul 14, 2026
nmetulev and others added 4 commits July 14, 2026 13:34
…ests

Addresses AI review of PR #603: win+l is never bypassable even with --allow-system-keys (locks the session); LogWarning when --allow-system-keys is set without --via send-input; document win+l-stays-blocked; add tests for the post-message no-op path and the win+l refusal.

Co-authored-by: Copilot App <223556219+Copilot@users.noreply.github.com>

Copilot-Session: b62b906c-4985-4114-926c-2cf732a814d1
…hen tests (round 2)

M1: Add Warnings list to UiSendKeysResult so --json consumers see both the no-op
warning (--allow-system-keys ignored with post-message) and the audit trail warning
(system combo injected because flag was set) even when the global ILogger is suppressed.
Warnings accumulate in a List<string> passed from the command handler into the JSON
result; the logger calls are unchanged so the human path still works.

M2: Revert 1.0.0 version churn in all generated files back to 0.4.1. The Debug
build exe reports assembly version 1.0.0 but the authoritative version is 0.4.1 from
version.json; generated cli-schema.json, plugin.json, SKILL.md frontmatter, and
the .claude/ mirror are all restored to 0.4.1.

L: Strengthen SendKeys tests — assert warning text is present in the JSON warnings
array for both the post-message no-op case and the send-input audit case; add guard
permutations for cmd+l alias, win+shift+l extra-modifier combo, lone right-Win key
(vk=0x5c) both blocked and allowed, and assert that the refused-without-flag error
message contains --allow-system-keys to guide the caller.

Co-authored-by: Copilot App <223556219+Copilot@users.noreply.github.com>
Copilot-Session: b62b906c-4985-4114-926c-2cf732a814d1
)

Brings the branch up to current main (adds #winapp/bindings import and the
set-value LegacyIAccessible refactor). Resolved the agent-doc conflict by
taking both the send-keys --allow-system-keys addition and main's expanded
set-value description; regenerated all autogen from merged source and reverted
Debug-build version churn to 0.4.1.

Co-authored-by: Copilot App <223556219+Copilot@users.noreply.github.com>
Copilot-Session: b62b906c-4985-4114-926c-2cf732a814d1
@nmetulev
nmetulev merged commit 477292c into main Jul 15, 2026
22 checks passed
@nmetulev
nmetulev deleted the yeelam/ui-allow-system-keys branch July 15, 2026 20:05
azchohfi added a commit that referenced this pull request Jul 15, 2026
Reconcile PR7 (UI-command logic coverage) with two changes that landed on
main while it was in review:

- #629 (NuGet migration): auto-merged. HostBuilderExtensions.cs keeps both
  #629's NugetSourceProvider/NugetPackageDownloader registrations and my
  IOwnedWindowFinder/IPollDelay/ISystemUiQuery seam registrations.
  BaseCommandTests.cs: #629 and this branch independently added the same
  3-arg ParseAndInvokeWithCaptureAsync overload; collapsed to one (kept
  #629's 2-arg delegator + my doc-comment) to avoid a duplicate method.

- #603 (--allow-system-keys on ui send-keys): reconciled both product and
  tests so the feature AND my IPollDelay/ISystemUiQuery seam both survive.
  UiSendKeysCommand.cs: kept #603's AllowSystemKeysOption, warnings list,
  never-bypassable/soft-combo guard rewrite and JSON Warnings, plus my
  ISystemUiQuery systemQuery ctor param and IsXamlClassName seam read.
  UiCommandTests.SendKeys.cs: manual conflict resolution keeps the exact
  union of both sides' test methods (41 total, no duplicates).

Debug 0W/0E; Release both projects 0W/0E. UiCommandTests +
UiSessionServiceTests + SystemKeyGuard: 310 passed / 0 failed / 0 skipped.
All 32 UI-scope files remain >=95% (UiSendKeysCommand 232/232=100%).

Co-authored-by: Copilot App <223556219+Copilot@users.noreply.github.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.

4 participants