diff --git a/src/prompt_toolkit/formatted_text/ansi.py b/src/prompt_toolkit/formatted_text/ansi.py
index 2f355c2b7..9904f872c 100644
--- a/src/prompt_toolkit/formatted_text/ansi.py
+++ b/src/prompt_toolkit/formatted_text/ansi.py
@@ -291,7 +291,16 @@ def ansi_escape(text: object) -> str:
"""
Replace characters with a special meaning.
"""
- return str(text).replace("\x1b", "?").replace("\b", "?")
+ # Besides ESC, the parser also treats \x9b as the start of a control
+ # sequence, and everything between \001 and \002 as raw output.
+ return (
+ str(text)
+ .replace("\x1b", "?")
+ .replace("\x9b", "?")
+ .replace("\x01", "?")
+ .replace("\x02", "?")
+ .replace("\b", "?")
+ )
class ANSIFormatter(Formatter):
diff --git a/src/prompt_toolkit/formatted_text/html.py b/src/prompt_toolkit/formatted_text/html.py
index a940ac8f8..94ea013a6 100644
--- a/src/prompt_toolkit/formatted_text/html.py
+++ b/src/prompt_toolkit/formatted_text/html.py
@@ -139,6 +139,7 @@ def html_escape(text: object) -> str:
.replace("<", "<")
.replace(">", ">")
.replace('"', """)
+ .replace("'", "'")
)
diff --git a/tests/test_formatted_text.py b/tests/test_formatted_text.py
index a111a7f20..484389713 100644
--- a/tests/test_formatted_text.py
+++ b/tests/test_formatted_text.py
@@ -209,6 +209,26 @@ def test_ansi_interpolation():
("ansigreen bg:ansimagenta", "2"),
]
+ # 8-bit CSI (\x9b) and the \001..\002 raw escape markers can't be
+ # injected through interpolated values either.
+ value = ANSI("{}").format("\x9b31mhi")
+ assert to_formatted_text(value) == [
+ ("", "?"),
+ ("", "3"),
+ ("", "1"),
+ ("", "m"),
+ ("", "h"),
+ ("", "i"),
+ ]
+
+ value = ANSI("%s") % "\x01x\x02y"
+ assert to_formatted_text(value) == [
+ ("", "?"),
+ ("", "x"),
+ ("", "?"),
+ ("", "y"),
+ ]
+
def test_interpolation():
value = Template(" {} ").format(HTML("hello"))
@@ -251,6 +271,13 @@ def test_html_interpolation():
value = HTML("{:02d}{:.3f}").format(3, 3.14159)
assert to_formatted_text(value) == [("class:b", "03"), ("class:u", "3.142")]
+ # Quotes inside attribute values.
+ value = HTML("").format("a'bg='b")
+ assert to_formatted_text(value) == [("fg:a'bg='b", "x")]
+
+ value = HTML('') % 'a"bg="b'
+ assert to_formatted_text(value) == [('fg:a"bg="b', "x")]
+
def test_merge_formatted_text():
html1 = HTML("hello")