Skip to content
This repository was archived by the owner on Oct 2, 2026. It is now read-only.
Merged
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
14 changes: 12 additions & 2 deletions mesop/server/static_file_serving.py
Original file line number Diff line number Diff line change
Expand Up @@ -59,13 +59,23 @@ def noop():
re.VERBOSE,
)

# Matches control characters that are not part of an ESC-prefixed sequence but
# can still manipulate terminal output (e.g. CR/BS overwrite text, LF forges new
# log lines, 0x9B is an 8-bit CSI), plus Unicode bidi overrides which can
# visually reorder text.
_CONTROL_CHARS_RE = re.compile(
r"[\x00-\x1f\x7f-\x9f\u200e\u200f\u202a-\u202e\u2066-\u2069]"
)


def _sanitize_terminal(value: object) -> str:
# Prevent ANSI/VT100 terminal escape injection when logging
# Prevent terminal escape and control character injection when logging
# attacker-controlled data (e.g. CSP report fields).
if not isinstance(value, str):
value = str(value)
return _ANSI_ESCAPE_RE.sub("", value)
value = _ANSI_ESCAPE_RE.sub("", value)
# Strip any remaining control characters (including a stray ESC).
return _CONTROL_CHARS_RE.sub("", value)


def configure_static_file_serving(
Expand Down
64 changes: 64 additions & 0 deletions mesop/server/static_file_serving_test.py
Original file line number Diff line number Diff line change
Expand Up @@ -97,6 +97,41 @@ def test_sanitize_terminal_keeps_plain_text():
assert _sanitize_terminal(payload) == payload


def test_sanitize_terminal_removes_bare_control_characters():
# C0 controls (incl. CR/LF/BS/BEL), DEL, C1 controls (e.g. 8-bit CSI) and
# a stray ESC are not ESC-prefixed sequences but still affect the terminal.
for ch in [
"\x00",
"\x07",
"\x08",
"\t",
"\n",
"\x0b",
"\x0c",
"\r",
"\x0e",
"\x0f",
"\x1b",
"\x7f",
"\x85",
"\x9b",
]:
assert _sanitize_terminal(f"before{ch}after") == "beforeafter", repr(ch)


def test_sanitize_terminal_removes_bidi_overrides():
rlo, lri, pdi = chr(0x202E), chr(0x2066), chr(0x2069)
payload = f"abc{rlo}def{lri}g{pdi}"

assert _sanitize_terminal(payload) == "abcdefg"


def test_sanitize_terminal_keeps_non_ascii_text():
payload = "https://例え.jp/café?q=ü"

assert _sanitize_terminal(payload) == payload


def test_sanitize_terminal_coerces_non_str_input():
assert _sanitize_terminal(123) == "123"

Expand Down Expand Up @@ -132,6 +167,35 @@ def test_csp_report_sanitizes_ansi_escapes_in_output(capsys):
assert "evil.example" in output


def test_csp_report_strips_control_characters_in_output(capsys):
app = Flask(__name__)
configure_static_file_serving(
app,
static_file_runfiles_base="unused",
disable_gzip_cache=True,
)
client = app.test_client()

response = client.post(
"/__csp__",
json={
"csp-report": {
"document-uri": "https://example.com/page\nFAKE: forged line",
"blocked-uri": "https://evil.example/x\rINFO: server healthy\x08\x07",
"violated-directive": "connect-src",
}
},
)

assert response.status_code == 204
output = capsys.readouterr().out
for ch in ["\r", "\x08", "\x07"]:
assert ch not in output
assert "\nFAKE" not in output
assert "https://evil.example/xINFO: server healthy" in output
assert "/pageFAKE: forged line" in output


if __name__ == "__main__":
import pytest

Expand Down
Loading