fix: handle leaked ANSI escape sequences in ux input on PowerShell#7642
fix: handle leaked ANSI escape sequences in ux input on PowerShell#7642wbreza merged 1 commit intoAzure:mainfrom
Conversation
There was a problem hiding this comment.
Pull request overview
This PR addresses a Windows/PowerShell regression where arrow-key ANSI escape fragments ([A, [B, etc.) leak into filter text in pkg/ux prompts after switching to survey/v2/terminal rune reading. It does so by adding escape-sequence fragment detection at the shared Input layer so downstream components receive proper arrow-key constants.
Changes:
- Add helpers to map ANSI direction letters to
survey/v2/terminalarrow-key constants and to resolve leaked escape fragments. - Integrate escape-fragment resolution into the
Input.ReadInput()read loop using a small pending-rune buffer. - Add table-driven unit tests for direction mapping and escape resolution.
Reviewed changes
Copilot reviewed 2 out of 2 changed files in this pull request and generated 1 comment.
| File | Description |
|---|---|
cli/azd/pkg/ux/internal/input.go |
Adds ANSI escape fragment detection and integrates it into the input read loop. |
cli/azd/pkg/ux/internal/input_test.go |
Adds unit tests covering escape fragment resolution cases. |
d9a46f3 to
4ca8c0b
Compare
jongio
left a comment
There was a problem hiding this comment.
Fixes the PowerShell arrow-key leak at the right layer - the shared Input component all UX controls use. Tests are thorough.
Issues to address:
- input.go:93 - leaked-sequence branch also intercepts literal 'O' + uppercase A-D as arrow keys
- input.go:82 -
recognisedfails cspell; userecognized
4ca8c0b to
b43129d
Compare
Alternative Approach: Clear
|
| Dimension | Current PR (escape parsing) | Alt A (clear VTI flag) |
|---|---|---|
| Lines changed | +362 / -5 (75 logic + 264 tests) | ~15–25 lines |
| Root cause addressed? | ❌ Compensates downstream | ✅ Fixes at the source |
| Cross-platform side effects | [ then A–D in a filter on Linux/macOS is incorrectly consumed as an arrow key; ESC press blocks waiting for next char |
✅ None — Windows-only via build tag |
| Complexity | Moderate: pending-rune buffer, look-ahead state machine | Low: one bit-mask line |
| Maintenance | Ongoing (state machine, new escape sequences) | Near-zero (stable Win32 API since Windows 10 1511) |
| Regression risk | Moderate: changes input loop for all platforms | Low: only affects Windows terminal setup; RestoreTermMode restores original state |
| Ghostty fix preserved? | ✅ (POSIX, unaffected) | ✅ (POSIX, unaffected) |
Why this matters
The current fix introduces a real (if unlikely) false positive on non-Windows: if a user types a literal [ followed by A, B, C, or D in a filter field (e.g. searching for [Dev), the look-ahead in resolveEscapeSequence will block waiting for the next character and then swallow the pair as an arrow key. This only affects the leaked [ path — a pattern that exclusively occurs on Windows/PowerShell.
Additionally, on POSIX, the survey library's ReadRune() already correctly parses ANSI escape sequences via a buffered reader. The full ESC path in resolveEscapeSequence duplicates that logic and adds a blocking readRune() call after a standalone ESC press.
vhvb1989
left a comment
There was a problem hiding this comment.
There is a simpler alternative that addresses the root cause rather than compensating downstream: clearing ENABLE_VIRTUAL_TERMINAL_INPUT in SetTermMode() on Windows so the console delivers native virtual key codes instead of ANSI sequences.
See this comment for the full definition of the alternative and a detailed comparison with the current approach.
Key concerns with the current fix:
- Cross-platform false positive: On Linux/macOS, typing
[followed byA–Din a filter field is incorrectly consumed as an arrow key, since the leaked[+ direction pattern only occurs on Windows/PowerShell. - Blocking regression: The
resolveEscapeSequencelook-ahead does a blockingreadRune()call that can stall input processing on standalone[or ESC presses. - Code size: 362 lines (including a state machine with pending-rune buffer) vs ~20 lines for the root-cause fix.
The alternative (clearing ENABLE_VIRTUAL_TERMINAL_INPUT) is smaller, has zero cross-platform risk, and eliminates the problem at its source.
|
Draft PR illustrating Alternative A: #7646 3 files, +71 lines (mostly comments/copyright) vs the current 362-line approach. Builds and tests pass on both Linux and Windows cross-compilation. The fix is entirely Windows-scoped via build tags with zero impact on other platforms. |
…ng on Windows (Azure#7641) The survey library's Windows RuneReader dispatches arrow keys via wVirtualKeyCode when unicodeChar == 0. When ENABLE_VIRTUAL_TERMINAL_INPUT is set (as in Windows Terminal, PowerShell 7, VS Code, and Ghostty), the console delivers ANSI escape sequences instead, bypassing the VK code path and leaking literal characters into filter text. Clear the ENABLE_VIRTUAL_TERMINAL_INPUT flag after SetTermMode() so the console delivers native virtual-key codes. The original console mode is restored by RestoreTermMode() when input completes. Fixes Azure#7641 Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
jongio
left a comment
There was a problem hiding this comment.
I verified the survey library source directly (runereader_windows.go:72) - SetTermMode() clears ECHO|LINE|PROCESSED but never ENABLE_VIRTUAL_TERMINAL_INPUT. When PowerShell enables VTI, ReadConsoleInputW delivers 3 separate KEY_EVENTs (0x1b, [, A) instead of one event with VK_UP. The library's VK mapping (lines 118-137) already handles arrow keys correctly when VTI is off.
Two remaining concerns after the latest fixes:
-
[+direction false positive on POSIX: Typing
[B(searching for "[Build") or[A("[API") in a filter gets consumed as an arrow key on Linux/macOS. The[is common in search terms - bracket expressions, array notation, markdown links. This only happens in the leaked-sequence path that doesn't apply to those platforms. -
Per-keystroke allocation:
resolveEscapeSequencereturns[]rune{char}on every regular keypress (the common path), adding a heap allocation per keystroke that didn't exist before. Minor, but unnecessary in an input hot loop.
I agree with the alternative approach in #7646 - clearing VTI at the console level is smaller (~20 lines), addresses the root cause, has zero cross-platform risk via build tags, and RestoreTermMode() correctly re-enables VTI after input completes. I'd recommend pivoting to that approach.
b43129d to
c4dad45
Compare
|
Great suggestion, @vhvb1989 — you were absolutely right. Rewrote the fix to clear ENABLE_VIRTUAL_TERMINAL_INPUT after SetTermMode() instead of parsing leaked escape sequences. Much cleaner approach that fixes the root cause:
The original approach had the cross-platform false positive and blocking issues you flagged. This is ~67 lines total vs ~362 before. Thanks for pushing back on this — the result is significantly better. |
Azure Dev CLI Install InstructionsInstall scriptsMacOS/Linux
bash: pwsh: WindowsPowerShell install MSI install Standalone Binary
MSI
Documentationlearn.microsoft.com documentationtitle: Azure Developer CLI reference
|
jongio
left a comment
There was a problem hiding this comment.
Clean root-cause fix. Clearing VTI after SetTermMode() is the right approach - RestoreTermMode() correctly re-enables it from the saved state. Build tags keep it zero-risk on non-Windows. LGTM.

Summary
Fixes arrow keys printing escape sequence characters (
[A,[B,[C,[D) in the filter text of select and multi-select UX components when running azd in PowerShell.Problem
The survey library's
RuneReader.ReadRune()fails to parse ANSI escape sequences on Windows/PowerShell. The ESC byte (0x1b) is consumed but the remaining[and direction letter leak through as individual runes, passunicode.IsPrint(), and get appended to the filter text instead of being recognized as arrow key navigation.This was a regression introduced by PR #6739 (Ghostty terminal fix), which replaced
eiannone/keyboardwithsurvey/v2/terminal.Solution
Added escape sequence detection in
pkg/ux/internal/input.gothat intercepts leaked ANSI fragments and converts them to the proper key constants before they reach the handler logic. Handles both:ESC [ A/B/C/D) — standard arrow keysESC O A/B/C/D) — used by vim, tmux, screen[ A/B/C/DorO A/B/C/D) — where ESC was already consumedAll existing components (select, multi-select, confirm, prompt) benefit automatically since they share the
Inputlayer.Testing
Added 36 table-driven test cases covering:
Changes
pkg/ux/internal/input.goansiDirectionKey()andresolveEscapeSequence()helpers; integrated intoReadInput()loop with pending rune bufferpkg/ux/internal/input_test.goFixes #7641