--- /dev/null
+/***************************************************************************
+ * _ _ ____ _
+ * Project ___| | | | _ \| |
+ * / __| | | | |_) | |
+ * | (__| |_| | _ <| |___
+ * \___|\___/|_| \_\_____|
+ *
+ * Copyright (C) Daniel Stenberg, <daniel@haxx.se>, et al.
+ *
+ * This software is licensed as described in the file COPYING, which
+ * you should have received as part of this distribution. The terms
+ * are also available at https://curl.se/docs/copyright.html.
+ *
+ * You may opt to use, copy, modify, merge, publish, distribute and/or sell
+ * copies of the Software, and permit persons to whom the Software is
+ * furnished to do so, under the terms of the COPYING file.
+ *
+ * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY
+ * KIND, either express or implied.
+ *
+ * SPDX-License-Identifier: curl
+ *
+ ***************************************************************************/
+#include "unitcheck.h"
+#include "urldata.h"
+#include "mime.h"
+
+#if !defined(CURL_DISABLE_MIME) && (!defined(CURL_DISABLE_HTTP) || \
+ !defined(CURL_DISABLE_SMTP) || !defined(CURL_DISABLE_IMAP))
+
+/* does any generated part header carry a raw CR or LF, i.e. a split line? */
+static bool headers_have_ctrl(const curl_mimepart *part)
+{
+ const struct curl_slist *h;
+ for(h = part->curlheaders; h; h = h->next)
+ if(h->data[strcspn(h->data, "\r\n")])
+ return TRUE;
+ return FALSE;
+}
+
+static CURLcode t3227_setup(void)
+{
+ CURLcode result = CURLE_OK;
+ global_init(CURL_GLOBAL_ALL);
+ return result;
+}
+
+static void t3227_stop(CURL *easy, curl_mime *mime)
+{
+ curl_mime_free(mime);
+ if(easy)
+ curl_easy_cleanup(easy);
+ curl_global_cleanup();
+}
+
+static CURLcode test_unit3227(const char *arg)
+{
+ CURL *easy = NULL;
+ curl_mime *mime = NULL;
+ curl_mimepart *part;
+
+ UNITTEST_BEGIN(t3227_setup())
+
+ easy = curl_easy_init();
+ abort_unless(easy, "curl_easy_init()");
+ mime = curl_mime_init(easy);
+ abort_unless(mime, "curl_mime_init()");
+
+ /* A CR or LF in the mail-part name or filename would split the generated
+ Content-Disposition header. The mail strategy must reject it. */
+
+ part = curl_mime_addpart(mime);
+ abort_unless(part, "curl_mime_addpart()");
+ curl_mime_data(part, "x", 1);
+ curl_mime_filename(part, "a\r\nX-Injected: 1");
+ fail_unless(Curl_mime_prepare_headers(easy, part, NULL, NULL,
+ MIMESTRATEGY_MAIL) ==
+ CURLE_BAD_FUNCTION_ARGUMENT,
+ "CRLF filename accepted for mail");
+
+ part = curl_mime_addpart(mime);
+ abort_unless(part, "curl_mime_addpart()");
+ curl_mime_data(part, "x", 1);
+ curl_mime_name(part, "a\nb");
+ fail_unless(Curl_mime_prepare_headers(easy, part, NULL, NULL,
+ MIMESTRATEGY_MAIL) ==
+ CURLE_BAD_FUNCTION_ARGUMENT,
+ "LF name accepted for mail");
+
+ part = curl_mime_addpart(mime);
+ abort_unless(part, "curl_mime_addpart()");
+ curl_mime_data(part, "x", 1);
+ curl_mime_filename(part, "a\rb");
+ fail_unless(Curl_mime_prepare_headers(easy, part, NULL, NULL,
+ MIMESTRATEGY_MAIL) ==
+ CURLE_BAD_FUNCTION_ARGUMENT,
+ "CR filename accepted for mail");
+
+ /* A regular name and filename are accepted and produce a clean header. */
+ part = curl_mime_addpart(mime);
+ abort_unless(part, "curl_mime_addpart()");
+ curl_mime_data(part, "x", 1);
+ curl_mime_name(part, "field");
+ curl_mime_filename(part, "readme.txt");
+ fail_unless(Curl_mime_prepare_headers(easy, part, NULL, NULL,
+ MIMESTRATEGY_MAIL) == CURLE_OK,
+ "plain mail part rejected");
+ fail_if(headers_have_ctrl(part), "clean mail header split");
+
+ /* The HTTP form strategy percent-encodes CR/LF, so it stays accepted and
+ produces no split line. */
+ part = curl_mime_addpart(mime);
+ abort_unless(part, "curl_mime_addpart()");
+ curl_mime_data(part, "x", 1);
+ curl_mime_filename(part, "a\r\nX-Injected: 1");
+ fail_unless(Curl_mime_prepare_headers(easy, part, NULL, NULL,
+ MIMESTRATEGY_FORM) == CURLE_OK,
+ "CRLF filename rejected for form");
+ fail_if(headers_have_ctrl(part), "form header split despite encoding");
+
+ UNITTEST_END(t3227_stop(easy, mime))
+}
+
+#else /* mime disabled */
+
+static CURLcode test_unit3227(const char *arg)
+{
+ UNITTEST_BEGIN_SIMPLE
+ puts("nothing to do when mime is disabled");
+ UNITTEST_END_SIMPLE
+}
+
+#endif