diff --git a/src/lib-sieve/plugins/editheader/cmd-addheader.c b/src/lib-sieve/plugins/editheader/cmd-addheader.c index 6481edd94d54642b7ba7799ec7a5f74d52d07715..c76e0e239960dd728ab33a3f8fb12d72deb7fc47 100644 --- a/src/lib-sieve/plugins/editheader/cmd-addheader.c +++ b/src/lib-sieve/plugins/editheader/cmd-addheader.c @@ -83,6 +83,24 @@ const struct sieve_operation_def addheader_operation = { cmd_addheader_operation_execute }; +/* + * Utility + */ + +static bool _str_contains_nul(const string_t *str) +{ + const unsigned char *p, *pend; + + p = str_data(str); + pend = p + str_len(str); + while (p < pend) { + if (*p == '\0') + return TRUE; + p++; + } + return FALSE; +} + /* * Validation */ @@ -134,12 +152,18 @@ static bool cmd_addheader_validate if ( sieve_argument_is_string_literal(arg) ) { string_t *fvalue = sieve_ast_argument_str(arg); + if ( _str_contains_nul(fvalue) ) { + sieve_argument_validate_error(valdtr, arg, + "addheader command: specified value `%s' is invalid " + "(contains NUL character)", str_sanitize(str_c(fvalue), 80)); + return FALSE; + } + if ( !rfc2822_header_field_body_verify (str_c(fvalue), str_len(fvalue), TRUE, TRUE) ) { - sieve_argument_validate_error(valdtr, arg, + sieve_argument_validate_warning(valdtr, arg, "addheader command: specified value `%s' is invalid", str_sanitize(str_c(fvalue), 80)); - return FALSE; } if ( ext_editheader_header_too_large(cmd->ext, str_len(fvalue)) ) { @@ -281,10 +305,9 @@ static int cmd_addheader_operation_execute return SIEVE_EXEC_OK; } - if ( !rfc2822_header_field_body_verify - (str_c(value), str_len(value), TRUE, TRUE) ) { + if ( _str_contains_nul(value) ) { sieve_runtime_error(renv, NULL, "addheader action: " - "specified value `%s' is invalid", + "specified value `%s' is invalid (contains NUL character)", str_sanitize(str_c(value), 80)); return SIEVE_EXEC_FAILURE; } @@ -304,6 +327,8 @@ static int cmd_addheader_operation_execute str_sanitize(str_c(field_name), 80), str_sanitize(str_c(value), 80)); edmail = sieve_message_edit(renv->msgctx); - edit_mail_header_add(edmail, rfc2822_header_field_name_sanitize(str_c(field_name)), str_c(value), last); + edit_mail_header_add(edmail, + rfc2822_header_field_name_sanitize(str_c(field_name)), + str_c(value), last); return SIEVE_EXEC_OK; } diff --git a/tests/extensions/editheader/addheader.svtest b/tests/extensions/editheader/addheader.svtest index 1ce7d44471ba784eb96ffc8900f740ece1929f53..944d66fc044d2b7ef080d9b0d467333e68839eba 100644 --- a/tests/extensions/editheader/addheader.svtest +++ b/tests/extensions/editheader/addheader.svtest @@ -1,4 +1,5 @@ require "vnd.dovecot.testsuite"; +require "encoded-character"; require "variables"; require "fileinto"; require "mailbox"; @@ -524,4 +525,72 @@ test "Addheader - implicit keep" { } } +test_set "message" "${message}"; +test "Addheader - UTF 8" { + if size :over 76 { + test_fail "original message is longer than 76 bytes?!"; + } + + addheader "X-Some-Header" "Ðто теÑÑ‚!"; + fileinto :create "folder4"; + + if not test_result_execute { + test_fail "failed to execute result"; + } + + if not test_message :folder "folder4" 0 { + test_fail "message not stored"; + } + + if not exists "x-some-header" { + test_fail "header not added to stored message"; + } + + if not header :is "x-some-header" "Ðто теÑÑ‚!" { + if header :matches "x-some-header" "*" {} + test_fail "Bel character not retained: `${0}`"; + } + + if not body :matches "Frop!*" { + test_fail "body not retained in stored mail"; + } +} + +test_result_reset; + +test_set "message" "${message}"; +test "Addheader - devious characters" { + if size :over 76 { + test_fail "original message is longer than 76 bytes?!"; + } + + addheader "X-Some-Header" "Ring my ${hex:07}!"; + fileinto :create "folder5"; + + if not test_result_execute { + test_fail "failed to execute result"; + } + + if not test_message :folder "folder5" 0 { + test_fail "message not stored"; + } + + if not exists "x-some-header" { + test_fail "header not added to stored message"; + } + + if header :is "x-some-header" "Ring my !" { + if header :matches "x-some-header" "*" {} + test_fail "Bel character not retained: `${0}`"; + } + + if not header :is "x-some-header" "Ring my ${hex:07}!" { + if header :matches "x-some-header" "*" {} + test_fail "Incorrect header value: `${0}`"; + } + + if not body :matches "Frop!*" { + test_fail "body not retained in stored mail"; + } +} diff --git a/tests/extensions/editheader/errors.svtest b/tests/extensions/editheader/errors.svtest index b7d4136c54a7dea00e05b9f4cf50f125694d300a..1d1f24d57d81c649b5bd01a5d004fb7005785a02 100644 --- a/tests/extensions/editheader/errors.svtest +++ b/tests/extensions/editheader/errors.svtest @@ -1,6 +1,7 @@ require "vnd.dovecot.testsuite"; require "comparator-i;ascii-numeric"; require "relational"; +require "variables"; require "editheader"; @@ -53,16 +54,12 @@ test "Invalid field value" { test_fail "compile should have failed"; } - if not test_error :count "eq" :comparator "i;ascii-numeric" "3" { + if not test_error :count "eq" :comparator "i;ascii-numeric" "2" { test_fail "wrong number of errors reported"; } - if not test_error :index 1 :matches "*value*Yeah!?*invalid*" { - test_fail "wrong error reported (1)"; - } - - if not test_error :index 2 :matches "*value*Woah!*invalid*" { - test_fail "wrong error reported (2)"; + if not test_error :index 1 :matches "*value*Woah*invalid*" { + test_fail "wrong error reported (1): ${0}"; } } diff --git a/tests/extensions/editheader/errors/field-value.sieve b/tests/extensions/editheader/errors/field-value.sieve index 7dc3fd8ffbf6d253ad0ac2e8a59700a797bd28d7..c9f4eabd44a419032bb90f29956b44fe539b5edf 100644 --- a/tests/extensions/editheader/errors/field-value.sieve +++ b/tests/extensions/editheader/errors/field-value.sieve @@ -8,8 +8,8 @@ addheader "X-field" "Frop"; addheader "X-field" "Frop Frml"; -# Invalid 'BELL' -addheader "X-field" "Yeah!${hex:07}"; +# Invalid 'BELL'; but not an error +addheader "X-field" "Yeah${hex:07}!"; # Invalid 'NUL' -addheader "X-field" "Woah!${hex:00}"; +addheader "X-field" "Woah${hex:00}!";