Skip to content

Commit 5fb8d7c

Browse files
committed
Adds tests on setting headers in sendmail suite
1 parent 70588b6 commit 5fb8d7c

File tree

3 files changed

+112
-20
lines changed

3 files changed

+112
-20
lines changed

rosetta-test-py/sendmail-python-emails.py

Lines changed: 10 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -120,13 +120,16 @@ def sendmail_connected(env, backend):
120120
# Send Message
121121
#
122122

123-
@sendmail_suite.placeholder("sendmail-send-message-with-options")
124-
def sendmail_send_message(env, sender:SMTPBackend, message, sender_address, recipient_addresses, message_options, recipients_options):
123+
@sendmail_suite.placeholder("sendmail-send-message-full")
124+
def sendmail_send_message(env, sender:SMTPBackend, message, sender_address, recipient_addresses, cc_addresses, bcc_addresses, custom_headers, message_options, recipients_options):
125125
try:
126126
message = emails.Message(text=message,
127127
subject="Test",
128128
mail_to=recipient_addresses,
129-
mail_from=sender_address)
129+
mail_from=sender_address,
130+
cc=cc_addresses,
131+
bcc=bcc_addresses,
132+
headers=custom_headers,)
130133
return [message.send(smtp=sender,smtp_mail_options=message_options, smtp_rcpt_options=recipients_options)]
131134
except Exception as e:
132135
return [e]
@@ -154,7 +157,8 @@ def sendmail_error(env, result: SMTPResponse):
154157
exclude_capabilities=(
155158
"root.connection.lazy-connection", # TODO: python-emails does not handle failed auth correctly
156159
"root.connection.eager-connection",
157-
"root.crlf-injection-detection.detection",
160+
"root.general-crlf-injection.detection",
161+
"root.headers.crlf-injection.mitigation",
158162
"root.unicode-messages.8bitmime.automatic-detection",
159163
"root.internationalized-email-addresses.smtputf8.explicit-options"),
160164
expected_failures=(
@@ -165,4 +169,5 @@ def sendmail_error(env, result: SMTPResponse):
165169
# The library should problably automatically detect whether smtputf8 is required
166170
"test_international_sender_mailbox_in_send-message_with_SMTPUTF8_support",
167171
"test_international_recipient_mailbox_in_send-message_with_SMTPUTF8_support",
168-
"test_Send_a_message_with_empty_recipient",))
172+
"test_Send_a_message_with_empty_recipient",
173+
"test_set_header_with_unicode_value")) # Encoding of unicode in header value seems wrong (underscore instead of space)

rosetta-test-py/sendmail-redmail.py

Lines changed: 8 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -130,11 +130,14 @@ def sendmail_connected(env, sender: EmailSender):
130130
# Send Message
131131
#
132132

133-
@sendmail_suite.placeholder("sendmail-send-message-with-options")
134-
def sendmail_send_message(env, sender: EmailSender, message, sender_address, recipient_addresses, message_options, recipients_options):
133+
@sendmail_suite.placeholder("sendmail-send-message-full")
134+
def sendmail_send_message(env, sender: EmailSender, message, sender_address, recipient_addresses, cc_addresses, bcc_addresses, custom_headers, message_options, recipients_options):
135135
try:
136136
sender.send(sender=sender_address,
137137
receivers=recipient_addresses,
138+
cc=cc_addresses,
139+
bcc=bcc_addresses,
140+
headers=custom_headers,
138141
subject="test",
139142
text=message)
140143
except Exception as e:
@@ -161,13 +164,14 @@ def sendmail_error(env, result):
161164

162165
sendmail_suite.run(
163166
exclude=(
164-
"test_CRLF_detection_in_send-message_recipient",
167+
"test_CRLF_detection_in_send-message_recipient",
165168
"test_CRLF_mitigation_in_send-message_sender",
166169
"test_Connect_with_invalid_credentials"), # TODO redmail leaks sockets when credentials are invalid
167170
exclude_capabilities=(
168171
"root.unicode-messages.8bitmime.automatic-detection",
169172
"root.unicode-messages.8bitmime.mandatory-options",
170-
"root.internationalized-email-addresses.smtputf8.explicit-options",),
173+
"root.internationalized-email-addresses.smtputf8.explicit-options",
174+
"root.headers.crlf-injection.mitigation",), # redmail does detects CRLF injections in header values
171175
expected_failures=(
172176
"test_Handle_421_during_data_command",
173177
"test_Handle_421_at_start_of_data_command",

rosetta-test-suites/sendmail.ros

Lines changed: 94 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -28,9 +28,14 @@
2828
(placeholder '(sendmail-connected? connection) "Should return true if the connection is still open")
2929

3030
; Send Mail
31-
(placeholder '(sendmail-send-message-with-options connection message-content from to-list message-options to-list-options) "Send a message to the server. The message-options and the to-list-options are optional. The to-list-options is a list of option tuple lists, one for each receiver. The function should return a list corresponding to the responses from the server for each recipient in the to-list. If there is only a single response, a list with a single response should be returned.")
31+
(placeholder '(sendmail-send-message-full connection message-content from to-list cc-list bcc-list headers message-options to-list-options) "Send a message to the server. The cc-list and bcc-list are optional. The headers hash is optional. The message-options and the to-list-options are optional. The to-list-options is a list of option tuple lists, one for each receiver. The function should return a list corresponding to the responses from the server for each recipient in the to-list. If there is only a single response, a list with a single response should be returned.")
32+
33+
(define (sendmail-send-message-with-options connection message-content from to-list message-options to-list-options)
34+
(sendmail-send-message-full connection message-content from to-list '() '() (make-hash-table) message-options to-list-options))
3235
(define (sendmail-send-message connection content from to-list)
3336
(sendmail-send-message-with-options connection content from to-list '() '()))
37+
(define (sendmail-send-message-with-headers connection content from to-list headers-hash-map)
38+
(sendmail-send-message-full connection content from to-list '() '() headers-hash-map '() '()))
3439

3540
; Response accessors
3641
(placeholder '(send-success? response) "Return whether the sending was successful.")
@@ -59,6 +64,8 @@
5964
(define (compile-options-string options)
6065
(string-join (compile-options-strings options) " "))
6166

67+
(define (server-message-contains? content)
68+
(string-contains? (server-message-data server) content))
6269

6370
(setup (lambda ()
6471
(set! server '())))
@@ -142,8 +149,7 @@
142149

143150
(setup (lambda ()
144151
(set! server (start-mock-server))
145-
(connect-smtp-server)
146-
))
152+
(connect-smtp-server)))
147153

148154
(tearDown (lambda ()
149155
(sendmail-disconnect smtp-connection)
@@ -170,6 +176,8 @@
170176
(server-requests-with-command server "MAIL"))
171177
(string-append "Expected client to send empty sender: " (server-requests-with-command server "MAIL"))))))
172178

179+
; TODO: CC and BCC
180+
173181
; TODO
174182
;(test "Send message with a valid and an invalid recipient" (lambda ()
175183
; (let
@@ -229,7 +237,85 @@
229237

230238
))
231239

232-
(capability 'crlf-injection-detection (list
240+
(capability 'headers (list
241+
242+
(define smtp-connection '())
243+
(define (connect-smtp-server)
244+
(set! smtp-connection (sendmail-connect "localhost" (server-port server))))
245+
246+
(setup (lambda ()
247+
(set! server (start-mock-server))
248+
(connect-smtp-server)))
249+
250+
(tearDown (lambda ()
251+
(sendmail-disconnect smtp-connection)
252+
(set! smtp-connection '())))
253+
254+
(test "set basic header" (lambda ()
255+
(sendmail-send-message-with-headers smtp-connection "message content" "sender@sender.to" '("user@recipient.to") (alist->hash-table '(("x-my-header" "some value"))))
256+
(assert (server-message-contains? "x-my-header: some value") (string-append "Expected x-my-header to be present, but instead got " (server-message-data server)))))
257+
258+
(test "set header with unicode value" (lambda ()
259+
(sendmail-send-message-with-headers smtp-connection "message content" "sender@sender.to" '("user@recipient.to") (alist->hash-table '(("x-my-header" "¡some value"))))
260+
(assert
261+
(or
262+
(server-message-contains? "x-my-header: =?utf-8?q?=C2=A1some?= value")
263+
(server-message-contains? "x-my-header: =?utf-8?b?wqFzb21lIHZhbHVl?=")
264+
(server-message-contains? "x-my-header: =?UTF-8?Q?=C2=A1some?= value")
265+
(server-message-contains? "x-my-header: =?UTF-8?B?wqFzb21lIHZhbHVl?="))
266+
(string-append "Expected x-my-header to contain encoded ¡, but instead got " (server-message-data server)))
267+
(assert
268+
(server-message-contains? "message content")
269+
(string-append "Expected message content to be unaltered by unicode header value, but instead got" (server-message-data server)))))
270+
271+
(test "set override standard header" (lambda ()
272+
(sendmail-send-message-with-headers smtp-connection "message content" "sender@sender.to" '("user@recipient.to") (alist->hash-table '(("To" "another-user@recipient.to"))))
273+
(assert (server-message-contains? "To: another-user@recipient.to") (string-append "Expected To header field to include another-user..., but instead got " (server-message-data server)))))
274+
275+
(capability 'crlf-injection (list
276+
277+
(capability 'detection (list
278+
279+
(data-test "CRLF detection in basic header value"
280+
'(("1some\rvalue") ("2some\nvalue") ("3some\r\nvalue"))
281+
(lambda (header-value)
282+
(let
283+
((responses (sendmail-send-message-with-headers smtp-connection "message content" "sender@sender.to" '("user@recipient.to") (alist->hash-table (list (list "x-my-header" header-value))))))
284+
(assert (all? send-error? responses) (string-append "The header value: " header-value " should have resulted in error but did not:" responses)))))
285+
286+
(data-test "CRLF detection in unicode header value"
287+
'(("1¡some\rvalue") ("2¡some\nvalue") ("3¡some\r\nvalue"))
288+
(lambda (header-value)
289+
(let
290+
((responses (sendmail-send-message-with-headers smtp-connection "message content" "sender@sender.to" '("user@recipient.to") (alist->hash-table (list (list "x-my-header" header-value))))))
291+
(assert (all? send-error? responses) (string-append "The header value: " header-value " should have resulted in error but did not:" responses)))))
292+
293+
))
294+
295+
(capability 'mitigation (list
296+
297+
(data-test "CRLF mitigation in basic header value"
298+
'(("1some\rvalue") ("2some\nvalue") ("3some\r\nvalue"))
299+
(lambda (header-value)
300+
(let
301+
((responses (sendmail-send-message-with-headers smtp-connection "message content" "sender@sender.to" '("user@recipient.to") (alist->hash-table (list (list "x-my-header" header-value))))))
302+
(assert (all? send-success? responses) (string-append "Message was not sent successfully, instead got: " responses))
303+
(assert (not (server-message-contains? header-value)) (string-append "The header value: " header-value " should have been stripped of CRLF but did not:" (server-message-data server))))))
304+
305+
(data-test "CRLF mitigation in unicode header value"
306+
'(("1¡some \rvalue" "\rvalue") ("2¡some \nvalue" "\nvalue") ("3¡some \r\nvalue" "\r\nvalue"))
307+
(lambda (header-value fragment)
308+
(let
309+
((responses (sendmail-send-message-with-headers smtp-connection "message content" "sender@sender.to" '("user@recipient.to") (alist->hash-table (list (list "x-my-header" header-value))))))
310+
(assert (all? send-success? responses) (string-append "Message was not sent successfully, instead got: " responses))
311+
(assert (not (server-message-contains? fragment)) (string-append "The header value: " header-value " should have been stripped of CRLF but did not: " (server-message-data server))))))
312+
313+
))
314+
))
315+
316+
))
317+
318+
(capability 'general-crlf-injection (list
233319

234320
(define smtp-connection '())
235321
(define (connect-smtp-server)
@@ -320,9 +406,6 @@
320406
(sendmail-disconnect smtp-connection)
321407
(set! smtp-connection '())))
322408

323-
(define (server-message-data-contains content)
324-
(string-contains? content (server-message-data server)))
325-
326409
(capability '8bitmime (list
327410

328411
(define (activate-8bitmime server)
@@ -346,7 +429,7 @@
346429
smtp-connection "¡a test message containing unicode!" "sender@sender.com" '("user@recipient.com") '() '())))
347430
(assert (all? send-success? send-message-responses))
348431
(assert
349-
(not (server-message-data-contains "¡a test message containing unicode!")))
432+
(not (server-message-contains? "¡a test message containing unicode!")))
350433
(string-append
351434
"Expected server to receive message with unicode content encoded, but received: " (server-message-data server)))))
352435

@@ -358,7 +441,7 @@
358441
(assert (all? send-success? send-message-responses))
359442
(assert-any-request server "MAIL FROM:<sender@sender.com> BODY=8BITMIME")
360443
(assert
361-
(server-message-contains "¡a test message containing unicode!")
444+
(server-message-contains? "¡a test message containing unicode!")
362445
(string-append
363446
"Expected server to receive message with unicode content directly, but received: " (server-message-data server))))))
364447

@@ -373,7 +456,7 @@
373456
smtp-connection "¡a test message containing unicode!" "sender@sender.com" '("user@recipient.com") '() '())))
374457
(assert-any-request server "MAIL FROM:<sender@sender.com>")
375458
(assert
376-
(not (server-message-contains "¡a test message containing unicode!"))
459+
(not (server-message-contains? "¡a test message containing unicode!"))
377460
(string-append
378461
"Expected server to receive message with unicode content directly, but received: " (server-message-data server))))))
379462

@@ -385,7 +468,7 @@
385468
(assert (all? send-success? send-message-responses))
386469
(assert-any-request server "MAIL FROM:<sender@sender.com> BODY=8BITMIME")
387470
(assert
388-
(server-message-contains "¡a test message containing unicode!")
471+
(server-message-contains? "¡a test message containing unicode!")
389472
(string-append
390473
"Expected server to receive message with unicode content directly, but received: " (server-message-data server))))))
391474

0 commit comments

Comments
 (0)