Skip to content

Patch tornado for BDSA-2025-60810#68855

Open
twangboy wants to merge 1 commit intosaltstack:3006.xfrom
twangboy:issue/68853/3006.x
Open

Patch tornado for BDSA-2025-60810#68855
twangboy wants to merge 1 commit intosaltstack:3006.xfrom
twangboy:issue/68853/3006.x

Conversation

@twangboy
Copy link
Copy Markdown
Contributor

@twangboy twangboy commented Mar 27, 2026

What does this PR do?

Pulls in changes from tornadoweb/tornado@9c163ae

What issues does this PR fix or reference?

Fixes #68853

What was changed

1. _REASON_PHRASE_RE class attribute on RequestHandler

A compiled regex was added as a class-level constant, expressing the RFC 9112 ABNF grammar for a valid HTTP reason phrase:

reason-phrase = *( HTAB / SP / VCHAR / obs-text )

In regex terms: (?:[\t ]|[\x21-\x7E]|[\x80-\xFF])+ — one or more characters that are tab, space, printable ASCII (0x21–0x7E), or extended ASCII (0x80–0xFF). Critically, this excludes control characters including CR (0x0D) and LF (0x0A), which are the characters needed for header injection.

Design decision — why not add _ABNF to httputil.py?

The upstream 6.x patch references httputil._ABNF.reason_phrase, a class that centralises all RFC ABNF patterns. Adding the full _ABNF class to 4.5.3 would have been a larger, noisier change touching a file that has already had several patches applied, and the class is only needed in web.py for this specific fix. Inlining the single pattern as a class attribute keeps the change self-contained and minimises the diff.

2. Validation in set_status()

Before assigning self._reason, the code now checks whether the reason phrase is valid. If it contains < or fails the regex, reason is silently replaced with "Unknown".

The < check is a fast defence-in-depth against XSS even before the regex runs (the regex would catch it anyway, but the explicit check mirrors the upstream comment about defence-in-depth). The silent replacement rather than raising an exception also matches upstream's reasoning: set_status() is frequently called on error-handling paths, and raising an exception there could mask the original error or produce confusing double-faults.

3. escape.xhtml_escape() in write_error()

The reason phrase is now HTML-escaped when rendered into the default error page. This is a second layer of defence — even if something slips through set_status()'s validation, the output is still safe. escape.xhtml_escape was already imported in web.py, so no new import was needed.

4. if status_code != 304: guard in send_error()

HTTP 304 Not Modified responses must not carry a message body per the HTTP specification. The upstream commit includes this fix in the same changeset. Previously, write_error() would be called unconditionally, potentially writing a body for 304 responses. This fix is included because it was part of the same upstream commit addressing the same security hardening work.

Merge requirements satisfied?

[NOTICE] Bug fixes or features added to Salt require tests.

Commits signed with GPG?

Yes

@twangboy twangboy requested a review from a team as a code owner March 27, 2026 16:25
@twangboy twangboy force-pushed the issue/68853/3006.x branch from a6723c6 to 3ad626d Compare March 27, 2026 16:38
@twangboy twangboy self-assigned this Mar 27, 2026
@twangboy twangboy added the test:full Run the full test suite label Mar 27, 2026
@twangboy twangboy added this to the Sulpher v3006.24 milestone Mar 27, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

test:full Run the full test suite

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant