Python 3.14.3 tightens email header serialization
Python 3.14.3 fixes header injection through email comment folding and extends generated-header checks. The changes make serialization failures part of the application contract.
Python 3.14.3 arrived on February 3 as the third maintenance release of the 3.14 branch. The announcement counts about 299 fixes and other changes since 3.14.2. For applications that generate email, the useful detail is in the versioned changelog. It describes a header injection bug caused by folding a comment and a separate change that makes the byte generator reject unsafe generated headers.
I value Python's libraries because they often provide the difficult implementation work before I start an application. That is also why a maintenance release deserves examination. Depending on a standard library means depending on its handling of protocol details, including cases that ordinary application tests may never exercise. My interest here is whether the bytes sent to another system retain the structure the application intended.
Folding can change the parsed message
The changelog describes a bug in modern email policies when a comment contains a very long sequence of characters that cannot be folded. A forced line wrap omitted the leading space required on the continuation line. The remaining comment could then be interpreted as another header field. Carefully constructed input could use that behavior for header injection.
The failure concerns the relationship between text and message structure. Before serialization, the application has a header containing a comment. After serialization, the receiving parser may see an additional header. The serializer has changed the grammatical role of input that the application treated as part of an existing field. That is why testing only the value assigned to an in-memory message object would miss the important result.
I would review email-producing code by asking where its header values originate and where the serialized output goes. An application may have a correct idea of its intended recipient or subject while still delegating the final wire representation to a formatter. The relevant test needs to inspect that representation or parse it again and check the resulting fields. This is a testing recommendation derived from the failure mechanism, not a claim that every email application is exploitable.
The release note identifies the affected behavior without claiming that all long comments are malicious. That distinction matters operationally. Real data can also reach unusual formatting paths. A fix should preserve valid messages while preventing output whose structure differs from the intended message.
The generator checks its own output
The second security entry says BytesGenerator now refuses headers that are unsafely folded or delimited. The relevant policy option is verify_generated_headers. Its documented default is true. When the generator detects a header that could parse as multiple headers or join adjacent data, it raises HeaderWriteError instead of writing it.
The documentation explicitly accounts for custom header classes as well as bugs in the email package. Output validation therefore has a purpose even when the application already validated its input. A custom implementation may produce invalid formatting, and the standard implementation can contain defects. The check operates where the generated representation is available to inspect.
The policy option itself predates this patch. The versioned documentation marks it as added in Python 3.13. Python 3.14.3 extends the refusal behavior described in its byte-generator security entry. Reading those sources together avoids treating an existing policy setting as an entirely new interface in this maintenance release.
For compatibility, the option also defaults to true under Compat32. The documentation permits an explicit false value for the older behavior but describes that behavior as unsafe. I would not treat disabling the option as the normal response to a newly failing message. The failure supplies a reason to inspect the generated header and its implementation.
A patch can make failure visible
There is an application-level consequence to rejecting output. A message-generation operation that previously completed may now raise an exception. That is a desirable correction when completion meant writing an ambiguous or unsafe header, but the application still has to handle it. I would check the code that records send failures and decides whether to retry.
A useful regression test would establish the fields the application intends to create, serialize a message containing difficult comment content, and verify that parsing the result preserves those fields. Where generation refuses the input, the test should verify that the application records a failed operation without reporting successful delivery. This follows the documented alternatives of producing acceptable output or raising HeaderWriteError.
I would keep ordinary message cases in the same test set. A change prompted by hostile input can also expose assumptions in a custom header class. The versioned policy documentation specifically identifies such classes as a possible source of unsafe output. Testing the actual application formatter is more informative than merely confirming that the interpreter reports the new patch version.
The release includes related hardening elsewhere. Its security section records rejection of control characters in cookie fields and values, WSGI header fields and parameters, and data URL media types. Those entries concern different interfaces, so they do not establish one universal validation rule. They do give maintainers concrete places to inspect if their applications depend on those standard-library components.
For email generation, I would prioritize the serialized-message regression and error path during the patch upgrade. The acceptance condition is specific: a generated header must retain its intended structure, and a rejected message must remain a failed application operation. That gives the maintenance update a testable consequence beyond installing a newer interpreter.