@@ -155,6 +155,9 @@ def setUp(self):
155155 self ._threads = test_support .threading_setup ()
156156 self .serv_evt = threading .Event ()
157157 self .client_evt = threading .Event ()
158+ # Capture SMTPChannel debug output
159+ self .old_DEBUGSTREAM = smtpd .DEBUGSTREAM
160+ smtpd .DEBUGSTREAM = StringIO .StringIO ()
158161 # Pick a random unused port by passing 0 for the port number
159162 self .serv = smtpd .DebuggingServer ((HOST , 0 ), ('nowhere' , - 1 ))
160163 # Keep a note of what port was assigned
@@ -176,6 +179,9 @@ def tearDown(self):
176179 test_support .threading_cleanup (* self ._threads )
177180 # restore sys.stdout
178181 sys .stdout = self .old_stdout
182+ # restore DEBUGSTREAM
183+ smtpd .DEBUGSTREAM .close ()
184+ smtpd .DEBUGSTREAM = self .old_DEBUGSTREAM
179185
180186 def testBasic (self ):
181187 # connect
@@ -201,6 +207,17 @@ def testNotImplemented(self):
201207 self .assertEqual (smtp .ehlo (), expected )
202208 smtp .quit ()
203209
210+
211+ def test_issue43124_putcmd_escapes_newline (self ):
212+ # see: https://bugs.python.org/issue43124
213+ smtp = smtplib .SMTP (HOST , self .port , local_hostname = 'localhost' ,
214+ timeout = 10 ) # support.LOOPBACK_TIMEOUT in newer Pythons
215+ self .addCleanup (smtp .close )
216+ with self .assertRaises (ValueError ) as exc :
217+ smtp .putcmd ('helo\n X-INJECTED' )
218+ self .assertIn ("prohibited newline characters" , str (exc .exception ))
219+ smtp .quit ()
220+
204221 def testVRFY (self ):
205222 # VRFY isn't implemented in DebuggingServer
206223 smtp = smtplib .SMTP (HOST , self .port , local_hostname = 'localhost' , timeout = 15 )
@@ -240,6 +257,54 @@ def testSend(self):
240257 mexpect = '%s%s\n %s' % (MSG_BEGIN , m , MSG_END )
241258 self .assertEqual (self .output .getvalue (), mexpect )
242259
260+ def test_issue43124_escape_localhostname (self ):
261+ # see: https://bugs.python.org/issue43124
262+ # connect and send mail
263+ m = 'wazzuuup\n linetwo'
264+ smtp = smtplib .SMTP (HOST , self .port , local_hostname = 'hi\n X-INJECTED' ,
265+ timeout = 10 ) # support.LOOPBACK_TIMEOUT in newer Pythons
266+ self .addCleanup (smtp .close )
267+ with self .assertRaises (ValueError ) as exc :
268+ smtp .sendmail ("hi@me.com" , "you@me.com" , m )
269+ self .assertIn (
270+ "prohibited newline characters: ehlo hi\\ nX-INJECTED" ,
271+ str (exc .exception ),
272+ )
273+ # XXX (see comment in testSend)
274+ time .sleep (0.01 )
275+ smtp .quit ()
276+
277+ debugout = smtpd .DEBUGSTREAM .getvalue ()
278+ self .assertNotIn ("X-INJECTED" , debugout )
279+
280+ def test_issue43124_escape_options (self ):
281+ # see: https://bugs.python.org/issue43124
282+ # connect and send mail
283+ m = 'wazzuuup\n linetwo'
284+ smtp = smtplib .SMTP (
285+ HOST , self .port , local_hostname = 'localhost' ,
286+ timeout = 10 ) # support.LOOPBACK_TIMEOUT in newer Pythons
287+
288+ self .addCleanup (smtp .close )
289+ # smtp.sendmail("hi@me.com", "you@me.com", m)
290+ # NB: gross hack but still cleaner than backporting whole ESMTP
291+ # support to DebuggingServer
292+ smtp .does_esmtp = 1
293+ with self .assertRaises (ValueError ) as exc :
294+ smtp .mail ("hi@me.com" , ["X-OPTION\n X-INJECTED-1" , "X-OPTION2\n X-INJECTED-2" ])
295+ msg = str (exc .exception )
296+ self .assertIn ("prohibited newline characters" , msg )
297+ self .assertIn ("X-OPTION\\ nX-INJECTED-1 X-OPTION2\\ nX-INJECTED-2" , msg )
298+ # XXX (see comment in testSend)
299+ time .sleep (0.01 )
300+ smtp .quit ()
301+
302+ debugout = smtpd .DEBUGSTREAM .getvalue ()
303+ self .assertNotIn ("X-OPTION" , debugout )
304+ self .assertNotIn ("X-OPTION2" , debugout )
305+ self .assertNotIn ("X-INJECTED-1" , debugout )
306+ self .assertNotIn ("X-INJECTED-2" , debugout )
307+
243308
244309class NonConnectingTests (unittest .TestCase ):
245310
0 commit comments