Python 2.7.18 finishes the release line with a hostname validation fix
The final Python 2 release includes a repair for control characters in HTTP hostnames. Its contents explain why an available release and a supported runtime are different claims.
Python's final 2.7 release contains a security repair whose implementation arrived after the support deadline. That detail complicates a convenient description of the release as nothing more than a bundle of changes completed before January. Python 2.7.18 is available today, and it ends the Python 2 release line. Its contents still deserve a precise account.
The release notes in the tagged source include a March-dated change rejecting control characters in HTTP hostnames. Checking only the path had left another route for invalid input, as the corresponding issue explains. Remaining installations therefore receive a concrete fix while Python 2's unsupported status stays intact.
I have little affection for Python's dependency-management burden, but its libraries make it useful. That combination makes the final release relevant even to someone who wants the transition finished. The software surrounding an interpreter determines how easily it can be replaced, and the last artifact still needs to be understood accurately.
The release closes an unusually long maintenance branch
In his April 20 announcement, Benjamin Peterson calls 2.7.18 both the last Python 2.7 release and the last Python 2 release. He describes the work of maintaining the branch as Python 2 and Python 3 diverged. That history included exceptions to ordinary maintenance policy, including network-security feature backports under PEP 466 and hash randomization.
The announcement also credits the people producing Windows and macOS binaries. This is relevant to the release's practical meaning. Maintaining a language branch involves more than preserving its Python-level semantics. Users also need artifacts that integrate with supported installation environments and libraries, and someone has to produce those artifacts.
The release page supplies source archives and platform installers. Their availability establishes what can be downloaded. It does not establish a new period of upstream maintenance. The January support boundary and April release are separate events, and a release carrying a final set of changes cannot by itself promise another release when the next problem is found.
That distinction is particularly important for a security repair. Applying a known correction reduces a specific exposure. It does not provide an ongoing process for correcting unknown future defects. The release's finality makes those two statements relevant at the same time.
A URL has more than one component to validate
Issue 38576 describes the hostname problem associated with CVE-2019-18348. An earlier correction rejected control characters in a URL's path. The report demonstrated that the hostname could still contain them. The supplied diagnostic replaced the connection function to show that processing could proceed far enough to attempt a network connection when it should have rejected the input.
The report also explains an environmental complication. Some C-library hostname handling could truncate at a newline. An invalid hostname could consequently reach a network operation through one interpretation while retaining characters with significance to HTTP processing through another. The issue describes this as a remaining header-injection problem and identifies the hostname grammar as disallowing control characters.
The precise lesson is about the completeness of validation across components. Rejecting dangerous characters in a path says nothing about whether the host underwent the same check. A library that parses and uses both components has to preserve their separate constraints. A correction to one component can leave the overall operation exposed through another input field.
This is also why treating all URL validation as one generic operation is misleading. The report identifies the host as the missing location, the attempted connection as evidence of insufficient rejection, and control characters as the relevant input. Those details describe an actionable defect without requiring a claim that every malformed URL produces the same result on every platform.
The final implementation rejects the host during setup
In the released Python 2.7.18 httplib source, the connection constructor obtains self.host and self.port, then calls _validate_host(self.host). That validation searches for disallowed characters and raises InvalidURL if it finds a match. The check occurs during connection-object setup.
The implementation reuses the regular expression already used for disallowed URL-path characters. Its range includes ASCII control characters and space, along with the upper byte range specified in that source. The important change for this issue is the additional use of the validation on the hostname. The method's comment explicitly connects the check with CVE-2019-18348.
The following Python 2.7.18 example expresses the expected rejection without opening a socket. It constructs the connection object but never calls its connection or request methods.
import httplib
try:
httplib.HTTPConnection("example.com\r\ninvalid")
except httplib.InvalidURL:
pass
else:
raise AssertionError("control characters must be rejected")
A useful regression assertion checks the rejection boundary, not whether an external server happens to accept a malformed request. The constructor call is enough to exercise the validation visible in the released source. No network result is needed to decide whether this particular contract holds.
The chronology needs a narrower description
The issue discussion records a request to consider the fix for Python 2.7 before the final release, followed by the branch change implementing it. The maintenance note dates the change in March. That evidence does not support describing every 2.7.18 correction as completed before January's support deadline.
It also does not support calling the branch generally supported through April. An exceptional final correction and an ongoing commitment to accept changes are different things. The record of a specific fix tells us what the release contains. The final-release announcement tells us that the Python 2 release line ends here.
I prefer that explicit account to either dismissing the release or treating it as a support extension. Remaining Python 2 installations have an identifiable final artifact with concrete corrections. Their maintainers still need a separately supported runtime or a separately defined maintenance arrangement for work beyond it.
For this HTTP issue, the resulting behaviour is specific. The connection object validates its hostname after splitting host and port, and raises InvalidURL on a disallowed character. That closes the missing hostname check documented in the issue, including for callers reaching httplib through another library.