Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
6 changes: 6 additions & 0 deletions cli.py
Original file line number Diff line number Diff line change
Expand Up @@ -1848,6 +1848,10 @@ def _strip_leaked_bracketed_paste_wrappers(text: str) -> str:
# these fragments are extremely unlikely to be intentional user input, and
# stripping them is better than sending corrupted prompts.
_SGR_MOUSE_BARE_RE = re.compile(r"<\d+;\d+;\d+[Mm]")
# Device Attributes (DA) response: terminal identity reply to ESC[c query.
# Format: ESC[?<params>c (primary DA), ESC[><params>c (secondary DA), or bare ESC[c.
_DA_ESC_RE = re.compile(r"\x1b\[\??>?[\d;]*c")
_DA_VISIBLE_RE = re.compile(r"\^\[\[\??>?[\d;]*c")
_TERMINAL_INPUT_MODE_RESET_SEQ = (
"\x1b[?1006l" # disable SGR mouse
"\x1b[?1003l" # disable any-motion tracking
Expand Down Expand Up @@ -1951,11 +1955,13 @@ def _strip_leaked_terminal_responses_with_meta(text: str) -> tuple[str, bool]:
text = _DSR_CPR_ESC_RE.sub("", text)
text, count = _SGR_MOUSE_ESC_RE.subn("", text)
had_mouse_reports = had_mouse_reports or count > 0
text = _DA_ESC_RE.sub("", text)

if has_visible:
text = _DSR_CPR_VISIBLE_RE.sub("", text)
text, count = _SGR_MOUSE_VISIBLE_RE.subn("", text)
had_mouse_reports = had_mouse_reports or count > 0
text = _DA_VISIBLE_RE.sub("", text)

if has_bare_mouse:
text, count = _SGR_MOUSE_BARE_RE.subn("", text)
Expand Down
30 changes: 30 additions & 0 deletions tests/cli/test_cli_terminal_response_sanitizer.py
Original file line number Diff line number Diff line change
Expand Up @@ -79,3 +79,33 @@ def test_strips_multiple_concatenated_sgr_mouse_reports(self):
def test_does_not_strip_regular_angle_bracket_text(self):
text = "render <div class='hero'> literal"
assert _strip_leaked_terminal_responses(text) == text

def test_strips_da_response_primary(self):
"""ESC[?1;2c — primary Device Attributes response (terminal identity)"""
text = "prefix\x1b[?1;2csuffix"
assert _strip_leaked_terminal_responses(text) == "prefixsuffix"

def test_strips_da_response_no_params(self):
"""ESC[c — bare DA response"""
text = "before\x1b[cafter"
assert _strip_leaked_terminal_responses(text) == "beforeafter"

def test_strips_da_response_secondary(self):
"""ESC[>0;0;0c — secondary DA response"""
text = "a\x1b[>0;0;0cb"
assert _strip_leaked_terminal_responses(text) == "ab"

def test_strips_da_response_visible_form(self):
"""^[[?1;2c — visible form after ESC stripped"""
text = "x^[[?1;2cy"
assert _strip_leaked_terminal_responses(text) == "xy"

def test_strips_combined_da_and_cpr(self):
"""Combined DA + CPR payload matching the reported '1c/2424' artifact"""
text = "\x1b[?1;2c\x1b[24;24R"
assert _strip_leaked_terminal_responses(text) == ""

def test_does_not_strip_cursor_forward(self):
"""ESC[2C (upper-case C) is Cursor Forward, NOT a DA response — must not be stripped"""
text = "abc\x1b[2Cdef"
assert _strip_leaked_terminal_responses(text) == "abc\x1b[2Cdef"