Skip to content

Commit 3905fbc

Browse files
doquanghuyclaude
andcommitted
fix(workflows): also strip LF and C1 controls from gate show_file display
The control-char class skipped LF (so an embedded newline in a show_file path could break the boxed layout) and the C1 range (so \x9b CSI and other 8-bit controls survived). Widen the class to [\x00-\x08\x0a-\x1f\x7f-\x9f] (still keeping tab). Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
1 parent ead174c commit 3905fbc

2 files changed

Lines changed: 9 additions & 8 deletions

File tree

src/specify_cli/workflows/steps/gate/__init__.py

Lines changed: 5 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -10,11 +10,11 @@
1010
from specify_cli.workflows.base import StepBase, StepContext, StepResult, StepStatus
1111
from specify_cli.workflows.expressions import evaluate_expression
1212

13-
#: C0 control characters except tab. Stripped from anything derived from a
14-
#: ``show_file`` before it is printed — both the file's contents and the path
15-
#: itself — so a file (or a path) containing ANSI escapes (e.g. ``\x1b[2J``)
16-
#: cannot clear the screen or spoof the prompt/options at the terminal.
17-
_CONTROL_CHARS = re.compile(r"[\x00-\x08\x0b-\x1f\x7f]")
13+
#: Control characters except tab: C0 (incl. LF, so an embedded newline cannot
14+
#: break the boxed layout), DEL, and C1 (incl. ``\x9b`` CSI). Stripped from
15+
#: anything derived from a ``show_file`` before it is printed — the file's
16+
#: contents and the path itself — so neither can inject ANSI/terminal escapes.
17+
_CONTROL_CHARS = re.compile(r"[\x00-\x08\x0a-\x1f\x7f-\x9f]")
1818

1919

2020
class GateStep(StepBase):

tests/test_workflows.py

Lines changed: 4 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -995,9 +995,10 @@ def test_compose_prompt_sanitizes_show_file_path(self):
995995

996996
# The displayed path header (and the read-error notice it produces)
997997
# must not carry escapes even when the path string itself contains
998-
# control characters; the file is still opened with the raw value.
999-
out = GateStep._compose_prompt("Review.", "evil\x1b[2Jpath.md")
1000-
assert "\x1b" not in out
998+
# control characters — ESC, LF, and C1 CSI (\x9b); the file is still
999+
# opened with the raw value.
1000+
out = GateStep._compose_prompt("Review.", "ev\x1bil\x9b[2J\npath.md")
1001+
assert "\x1b" not in out and "\x9b" not in out
10011002
assert "evil[2Jpath.md:" in out
10021003

10031004
def test_interactive_non_string_message_renders(self, monkeypatch, capsys):

0 commit comments

Comments
 (0)