From: Daniel Stenberg Date: Sun, 7 Dec 2025 15:09:13 +0000 (+0100) Subject: cookie: only keep and use the canonical cleaned up path X-Git-Tag: rc-8_18_0-2~105 X-Git-Url: http://git.ipfire.org/cgi-bin/gitweb.cgi?a=commitdiff_plain;h=a093c93994d2afed3beed94809844c961d3f4a64;p=thirdparty%2Fcurl.git cookie: only keep and use the canonical cleaned up path Instead of keeping both versions around. Closes #19864 --- diff --git a/lib/cookie.c b/lib/cookie.c index 743a7a8328..7efce5611f 100644 --- a/lib/cookie.c +++ b/lib/cookie.c @@ -67,7 +67,6 @@ static void freecookie(struct Cookie *co, bool maintoo) { curlx_free(co->domain); curlx_free(co->path); - curlx_free(co->spath); curlx_free(co->name); curlx_free(co->value); if(maintoo) @@ -227,20 +226,19 @@ static size_t cookiehash(const char * const domain) /* * cookie path sanitize */ -static char *sanitize_cookie_path(const char *cookie_path) +static char *sanitize_cookie_path(const char *cookie_path, size_t len) { - size_t len = strlen(cookie_path); - /* some sites send path attribute within '"'. */ - if(cookie_path[0] == '\"') { + if(len && (cookie_path[0] == '\"')) { cookie_path++; len--; + + if(len && (cookie_path[len - 1] == '\"')) + len--; } - if(len && (cookie_path[len - 1] == '\"')) - len--; /* RFC6265 5.2.4 The Path Attribute */ - if(cookie_path[0] != '/') + if(!len || (cookie_path[0] != '/')) /* Let cookie-path be the default-path. */ return curlx_strdup("/"); @@ -393,23 +391,23 @@ static CURLcode storecookie(struct Cookie *co, struct Curl_str *cp, result = strstore(&co->value, curlx_str(&cp[COOKIE_VALUE]), curlx_strlen(&cp[COOKIE_VALUE])); if(!result) { - if(curlx_strlen(&cp[COOKIE_PATH])) - result = strstore(&co->path, curlx_str(&cp[COOKIE_PATH]), - curlx_strlen(&cp[COOKIE_PATH])); + size_t plen = 0; + if(curlx_strlen(&cp[COOKIE_PATH])) { + path = curlx_str(&cp[COOKIE_PATH]); + plen = curlx_strlen(&cp[COOKIE_PATH]); + } else if(path) { /* No path was given in the header line, set the default */ const char *endslash = strrchr(path, '/'); - if(endslash) { - size_t pathlen = (endslash - path + 1); /* include end slash */ - co->path = Curl_memdup0(path, pathlen); - if(!co->path) - result = CURLE_OUT_OF_MEMORY; - } + if(endslash) + plen = (endslash - path + 1); /* include end slash */ + else + plen = strlen(path); } - if(!result && co->path) { - co->spath = sanitize_cookie_path(co->path); - if(!co->spath) + if(path) { + co->path = sanitize_cookie_path(path, plen); + if(!co->path) result = CURLE_OUT_OF_MEMORY; } } @@ -734,23 +732,17 @@ static CURLcode parse_netscape(struct Cookie *co, /* The file format allows the path field to remain not filled in */ if(strncmp("TRUE", ptr, len) && strncmp("FALSE", ptr, len)) { /* only if the path does not look like a boolean option! */ - co->path = Curl_memdup0(ptr, len); + co->path = sanitize_cookie_path(ptr, len); if(!co->path) return CURLE_OUT_OF_MEMORY; - else { - co->spath = sanitize_cookie_path(co->path); - if(!co->spath) - return CURLE_OUT_OF_MEMORY; - } break; } - /* this does not look like a path, make one up! */ - co->path = curlx_strdup("/"); - if(!co->path) - return CURLE_OUT_OF_MEMORY; - co->spath = curlx_strdup("/"); - if(!co->spath) - return CURLE_OUT_OF_MEMORY; + else { + /* this does not look like a path, make one up! */ + co->path = curlx_strdup("/"); + if(!co->path) + return CURLE_OUT_OF_MEMORY; + } fields++; /* add a field and fall down to secure */ FALLTHROUGH(); case 3: @@ -875,7 +867,7 @@ static bool replace_existing(struct Curl_easy *data, matching_domains = TRUE; if(matching_domains && /* the domains were identical */ - clist->spath && co->spath && /* both have paths */ + clist->path && co->path && /* both have paths */ clist->secure && !co->secure && !secure) { size_t cllen; const char *sep = NULL; @@ -887,15 +879,15 @@ static bool replace_existing(struct Curl_easy *data, * "/loginhelper" is ok. */ - DEBUGASSERT(clist->spath[0]); - if(clist->spath[0]) - sep = strchr(clist->spath + 1, '/'); + DEBUGASSERT(clist->path[0]); + if(clist->path[0]) + sep = strchr(clist->path + 1, '/'); if(sep) - cllen = sep - clist->spath; + cllen = sep - clist->path; else - cllen = strlen(clist->spath); + cllen = strlen(clist->path); - if(curl_strnequal(clist->spath, co->spath, cllen)) { + if(curl_strnequal(clist->path, co->path, cllen)) { infof(data, "cookie '%s' for domain '%s' dropped, would " "overlay an existing cookie", co->name, co->domain); return FALSE; @@ -918,10 +910,10 @@ static bool replace_existing(struct Curl_easy *data, if(replace_old) { /* the domains were identical */ - if(clist->spath && co->spath && - !curl_strequal(clist->spath, co->spath)) + if(clist->path && co->path && + !curl_strequal(clist->path, co->path)) replace_old = FALSE; - else if(!clist->spath != !co->spath) + else if(!clist->path != !co->path) replace_old = FALSE; } @@ -1007,7 +999,7 @@ Curl_cookie_add(struct Curl_easy *data, * The __Host- prefix requires the cookie to be secure, have a "/" path * and not have a domain set. */ - if(co->secure && co->path && strcmp(co->path, "/") == 0 && !co->tailmatch) + if(co->secure && co->path && !strcmp(co->path, "/") && !co->tailmatch) ; else goto fail; @@ -1324,7 +1316,7 @@ CURLcode Curl_cookie_getlist(struct Curl_easy *data, * now check the left part of the path with the cookies path * requirement */ - if(!co->spath || pathmatch(co->spath, path)) { + if(!co->path || pathmatch(co->path, path)) { /* * This is a match and we add it to the return-linked-list diff --git a/lib/cookie.h b/lib/cookie.h index c1fb23ee4c..03877e509f 100644 --- a/lib/cookie.h +++ b/lib/cookie.h @@ -34,8 +34,7 @@ struct Cookie { struct Curl_llist_node getnode; /* for getlist */ char *name; /* = value */ char *value; /* name = */ - char *path; /* path = which is in Set-Cookie: */ - char *spath; /* sanitized cookie path */ + char *path; /* canonical path */ char *domain; /* domain = */ curl_off_t expires; /* expires = */ unsigned int creationtime; /* time when the cookie was written */ diff --git a/tests/data/test1105 b/tests/data/test1105 index ea42efb6a9..e076681729 100644 --- a/tests/data/test1105 +++ b/tests/data/test1105 @@ -61,8 +61,8 @@ userid=myname&password=mypassword # https://curl.se/docs/http-cookies.html # This file was generated by libcurl! Edit at your own risk. -127.0.0.1 FALSE "/silly/" FALSE 0 mismatch this -127.0.0.1 FALSE /we/want/ FALSE 0 foobar name +127.0.0.1 FALSE /silly FALSE 0 mismatch this +127.0.0.1 FALSE /we/want FALSE 0 foobar name diff --git a/tests/data/test1561 b/tests/data/test1561 index df9477dd47..84b291b12e 100644 --- a/tests/data/test1561 +++ b/tests/data/test1561 @@ -98,7 +98,7 @@ Accept: */* www.example.com FALSE / TRUE 0 __Host-SID 12346 .example.com TRUE / TRUE 0 supersupersuper secret .example.com TRUE / TRUE 0 __SecURE-SID 12346 -.example.com TRUE /%TESTNUMBER/login/ TRUE 0 supersuper secret +.example.com TRUE /%TESTNUMBER/login TRUE 0 supersuper secret .example.com TRUE /1561 TRUE 0 super secret diff --git a/tests/data/test1903 b/tests/data/test1903 index 228b4d6ae4..9b90151c09 100644 --- a/tests/data/test1903 +++ b/tests/data/test1903 @@ -55,8 +55,8 @@ cookies # https://curl.se/docs/http-cookies.html # This file was generated by libcurl! Edit at your own risk. -%HOSTIP FALSE /we/want/ FALSE 0 foobar name -%HOSTIP FALSE /we/want/ FALSE 0 secondcookie present +%HOSTIP FALSE /we/want FALSE 0 foobar name +%HOSTIP FALSE /we/want FALSE 0 secondcookie present diff --git a/tests/data/test1905 b/tests/data/test1905 index 690e8553ec..e09f8dc60e 100644 --- a/tests/data/test1905 +++ b/tests/data/test1905 @@ -53,8 +53,8 @@ Accept: */* # https://curl.se/docs/http-cookies.html # This file was generated by libcurl! Edit at your own risk. -%HOSTIP FALSE /we/want/ FALSE 0 secondcookie present -%HOSTIP FALSE /we/want/ FALSE 0 foobar name +%HOSTIP FALSE /we/want FALSE 0 secondcookie present +%HOSTIP FALSE /we/want FALSE 0 foobar name diff --git a/tests/data/test31 b/tests/data/test31 index 8d99df6da8..8353b709fc 100644 --- a/tests/data/test31 +++ b/tests/data/test31 @@ -111,22 +111,22 @@ Accept: */* # https://curl.se/docs/http-cookies.html # This file was generated by libcurl! Edit at your own risk. -test31.curl FALSE /we/want/ FALSE 0 %hex[%c3%82%c2%b3%c3%83%5c%78%39%32%c3%83%5c%78%39%61%c3%83%5c%78%38%64%c3%83%5c%78%39%37]hex% %96%A6g%9Ay%B0%A5g%A7tm%7C%95%9A -test31.curl FALSE /we/want/ FALSE 0 prespace yes before -test31.curl FALSE /we/want/ FALSE 0 withspaces2 before equals -test31.curl FALSE /we/want/ FALSE 0 withspaces yes within and around -.test31.curl TRUE /we/want/ FALSE 0 blexp yesyes -#HttpOnly_test31.curl FALSE /silly/ FALSE 0 magic yessir -test31.curl FALSE /we/want/ FALSE %days[400] nodomain value +test31.curl FALSE /we/want FALSE 0 %hex[%c3%82%c2%b3%c3%83%5c%78%39%32%c3%83%5c%78%39%61%c3%83%5c%78%38%64%c3%83%5c%78%39%37]hex% %96%A6g%9Ay%B0%A5g%A7tm%7C%95%9A +test31.curl FALSE /we/want FALSE 0 prespace yes before +test31.curl FALSE /we/want FALSE 0 withspaces2 before equals +test31.curl FALSE /we/want FALSE 0 withspaces yes within and around +.test31.curl TRUE /we/want FALSE 0 blexp yesyes +#HttpOnly_test31.curl FALSE /silly FALSE 0 magic yessir +test31.curl FALSE /we/want FALSE %days[400] nodomain value .test31.curl TRUE / FALSE 0 partmatch present -#HttpOnly_.test31.curl TRUE /p4/ FALSE 0 httponly myvalue1 -#HttpOnly_.test31.curl TRUE /p4/ FALSE 0 httpo4 value4 -#HttpOnly_.test31.curl TRUE /p3/ FALSE 0 httpo3 value3 -#HttpOnly_.test31.curl TRUE /p2/ FALSE 0 httpo2 value2 -#HttpOnly_.test31.curl TRUE /p1/ FALSE 0 httpo1 value1 +#HttpOnly_.test31.curl TRUE /p4 FALSE 0 httponly myvalue1 +#HttpOnly_.test31.curl TRUE /p4 FALSE 0 httpo4 value4 +#HttpOnly_.test31.curl TRUE /p3 FALSE 0 httpo3 value3 +#HttpOnly_.test31.curl TRUE /p2 FALSE 0 httpo2 value2 +#HttpOnly_.test31.curl TRUE /p1 FALSE 0 httpo1 value1 .test31.curl TRUE /overwrite FALSE 0 overwrite this2 -.test31.curl TRUE /silly/ FALSE 0 ISMATCH this -.test31.curl TRUE /silly/ FALSE 0 ismatch this +.test31.curl TRUE /silly FALSE 0 ISMATCH this +.test31.curl TRUE /silly FALSE 0 ismatch this test31.curl FALSE / FALSE 0 blankdomain sure diff --git a/tests/data/test420 b/tests/data/test420 index c1d6173f4f..7b8bd08fde 100644 --- a/tests/data/test420 +++ b/tests/data/test420 @@ -44,9 +44,9 @@ http://%HOSTIP:%HTTPPORT/func_test/del_cookie -b %LOGDIR/cookie%TESTNUMBER -c %L #HttpOnly_%HOSTIP FALSE /func_test FALSE 21709598616 mycookie5 990 #HttpOnly_%HOSTIP FALSE /func_test FALSE 21709598616 mycookie4 950 #HttpOnly_%HOSTIP FALSE /func_test FALSE 21709598616 mycookie3 900 -#HttpOnly_%HOSTIP FALSE /func_test/ FALSE 21709598616 mycookie2 5900 +#HttpOnly_%HOSTIP FALSE /func_test FALSE 21709598616 mycookie2 5900 #HttpOnly_%HOSTIP FALSE / FALSE 21709598616 mycookie1 4900 -#HttpOnly_%HOSTIP FALSE /func_test/ FALSE 0 mycookie 1200 +#HttpOnly_%HOSTIP FALSE /func_test FALSE 0 mycookie 1200 cookies @@ -61,7 +61,7 @@ GET /func_test/del_cookie HTTP/1.1 Host: %HOSTIP:%HTTPPORT User-Agent: curl/%VERSION Accept: */* -Cookie: mycookie2=5900; mycookie=1200; mycookie3=900; mycookie4=950; mycookie5=990; mycookie6=991; mycookie1=4900 +Cookie: mycookie2=5900; mycookie3=900; mycookie4=950; mycookie5=990; mycookie6=991; mycookie=1200; mycookie1=4900 @@ -69,7 +69,7 @@ Cookie: mycookie2=5900; mycookie=1200; mycookie3=900; mycookie4=950; mycookie5=9 # https://curl.se/docs/http-cookies.html # This file was generated by libcurl! Edit at your own risk. -#HttpOnly_127.0.0.1 FALSE /func_test/ FALSE 21709598616 mycookie2 5900 +#HttpOnly_127.0.0.1 FALSE /func_test FALSE 21709598616 mycookie2 5900 diff --git a/tests/data/test444 b/tests/data/test444 index 4c092bef1b..0b050e1146 100644 --- a/tests/data/test444 +++ b/tests/data/test444 @@ -138,56 +138,56 @@ Accept: */* # https://curl.se/docs/http-cookies.html # This file was generated by libcurl! Edit at your own risk. -attack.invalid FALSE /a/b/ FALSE 0 cookie-50 yes -attack.invalid FALSE /a/b/ FALSE 0 cookie-49 yes -attack.invalid FALSE /a/b/ FALSE 0 cookie-48 yes -attack.invalid FALSE /a/b/ FALSE 0 cookie-47 yes -attack.invalid FALSE /a/b/ FALSE 0 cookie-46 yes -attack.invalid FALSE /a/b/ FALSE 0 cookie-45 yes -attack.invalid FALSE /a/b/ FALSE 0 cookie-44 yes -attack.invalid FALSE /a/b/ FALSE 0 cookie-43 yes -attack.invalid FALSE /a/b/ FALSE 0 cookie-42 yes -attack.invalid FALSE /a/b/ FALSE 0 cookie-41 yes -attack.invalid FALSE /a/b/ FALSE 0 cookie-40 yes -attack.invalid FALSE /a/b/ FALSE 0 cookie-39 yes -attack.invalid FALSE /a/b/ FALSE 0 cookie-38 yes -attack.invalid FALSE /a/b/ FALSE 0 cookie-37 yes -attack.invalid FALSE /a/b/ FALSE 0 cookie-36 yes -attack.invalid FALSE /a/b/ FALSE 0 cookie-35 yes -attack.invalid FALSE /a/b/ FALSE 0 cookie-34 yes -attack.invalid FALSE /a/b/ FALSE 0 cookie-33 yes -attack.invalid FALSE /a/b/ FALSE 0 cookie-32 yes -attack.invalid FALSE /a/b/ FALSE 0 cookie-31 yes -attack.invalid FALSE /a/b/ FALSE 0 cookie-30 yes -attack.invalid FALSE /a/b/ FALSE 0 cookie-29 yes -attack.invalid FALSE /a/b/ FALSE 0 cookie-28 yes -attack.invalid FALSE /a/b/ FALSE 0 cookie-27 yes -attack.invalid FALSE /a/b/ FALSE 0 cookie-26 yes -attack.invalid FALSE /a/b/ FALSE 0 cookie-25 yes -attack.invalid FALSE /a/b/ FALSE 0 cookie-24 yes -attack.invalid FALSE /a/b/ FALSE 0 cookie-23 yes -attack.invalid FALSE /a/b/ FALSE 0 cookie-22 yes -attack.invalid FALSE /a/b/ FALSE 0 cookie-21 yes -attack.invalid FALSE /a/b/ FALSE 0 cookie-20 yes -attack.invalid FALSE /a/b/ FALSE 0 cookie-19 yes -attack.invalid FALSE /a/b/ FALSE 0 cookie-18 yes -attack.invalid FALSE /a/b/ FALSE 0 cookie-17 yes -attack.invalid FALSE /a/b/ FALSE 0 cookie-16 yes -attack.invalid FALSE /a/b/ FALSE 0 cookie-15 yes -attack.invalid FALSE /a/b/ FALSE 0 cookie-14 yes -attack.invalid FALSE /a/b/ FALSE 0 cookie-13 yes -attack.invalid FALSE /a/b/ FALSE 0 cookie-12 yes -attack.invalid FALSE /a/b/ FALSE 0 cookie-11 yes -attack.invalid FALSE /a/b/ FALSE 0 cookie-10 yes -attack.invalid FALSE /a/b/ FALSE 0 cookie-9 yes -attack.invalid FALSE /a/b/ FALSE 0 cookie-8 yes -attack.invalid FALSE /a/b/ FALSE 0 cookie-7 yes -attack.invalid FALSE /a/b/ FALSE 0 cookie-6 yes -attack.invalid FALSE /a/b/ FALSE 0 cookie-5 yes -attack.invalid FALSE /a/b/ FALSE 0 cookie-4 yes -attack.invalid FALSE /a/b/ FALSE 0 cookie-3 yes -attack.invalid FALSE /a/b/ FALSE 0 cookie-2 yes -attack.invalid FALSE /a/b/ FALSE 0 cookie-1 yes +attack.invalid FALSE /a/b FALSE 0 cookie-50 yes +attack.invalid FALSE /a/b FALSE 0 cookie-49 yes +attack.invalid FALSE /a/b FALSE 0 cookie-48 yes +attack.invalid FALSE /a/b FALSE 0 cookie-47 yes +attack.invalid FALSE /a/b FALSE 0 cookie-46 yes +attack.invalid FALSE /a/b FALSE 0 cookie-45 yes +attack.invalid FALSE /a/b FALSE 0 cookie-44 yes +attack.invalid FALSE /a/b FALSE 0 cookie-43 yes +attack.invalid FALSE /a/b FALSE 0 cookie-42 yes +attack.invalid FALSE /a/b FALSE 0 cookie-41 yes +attack.invalid FALSE /a/b FALSE 0 cookie-40 yes +attack.invalid FALSE /a/b FALSE 0 cookie-39 yes +attack.invalid FALSE /a/b FALSE 0 cookie-38 yes +attack.invalid FALSE /a/b FALSE 0 cookie-37 yes +attack.invalid FALSE /a/b FALSE 0 cookie-36 yes +attack.invalid FALSE /a/b FALSE 0 cookie-35 yes +attack.invalid FALSE /a/b FALSE 0 cookie-34 yes +attack.invalid FALSE /a/b FALSE 0 cookie-33 yes +attack.invalid FALSE /a/b FALSE 0 cookie-32 yes +attack.invalid FALSE /a/b FALSE 0 cookie-31 yes +attack.invalid FALSE /a/b FALSE 0 cookie-30 yes +attack.invalid FALSE /a/b FALSE 0 cookie-29 yes +attack.invalid FALSE /a/b FALSE 0 cookie-28 yes +attack.invalid FALSE /a/b FALSE 0 cookie-27 yes +attack.invalid FALSE /a/b FALSE 0 cookie-26 yes +attack.invalid FALSE /a/b FALSE 0 cookie-25 yes +attack.invalid FALSE /a/b FALSE 0 cookie-24 yes +attack.invalid FALSE /a/b FALSE 0 cookie-23 yes +attack.invalid FALSE /a/b FALSE 0 cookie-22 yes +attack.invalid FALSE /a/b FALSE 0 cookie-21 yes +attack.invalid FALSE /a/b FALSE 0 cookie-20 yes +attack.invalid FALSE /a/b FALSE 0 cookie-19 yes +attack.invalid FALSE /a/b FALSE 0 cookie-18 yes +attack.invalid FALSE /a/b FALSE 0 cookie-17 yes +attack.invalid FALSE /a/b FALSE 0 cookie-16 yes +attack.invalid FALSE /a/b FALSE 0 cookie-15 yes +attack.invalid FALSE /a/b FALSE 0 cookie-14 yes +attack.invalid FALSE /a/b FALSE 0 cookie-13 yes +attack.invalid FALSE /a/b FALSE 0 cookie-12 yes +attack.invalid FALSE /a/b FALSE 0 cookie-11 yes +attack.invalid FALSE /a/b FALSE 0 cookie-10 yes +attack.invalid FALSE /a/b FALSE 0 cookie-9 yes +attack.invalid FALSE /a/b FALSE 0 cookie-8 yes +attack.invalid FALSE /a/b FALSE 0 cookie-7 yes +attack.invalid FALSE /a/b FALSE 0 cookie-6 yes +attack.invalid FALSE /a/b FALSE 0 cookie-5 yes +attack.invalid FALSE /a/b FALSE 0 cookie-4 yes +attack.invalid FALSE /a/b FALSE 0 cookie-3 yes +attack.invalid FALSE /a/b FALSE 0 cookie-2 yes +attack.invalid FALSE /a/b FALSE 0 cookie-1 yes diff --git a/tests/data/test46 b/tests/data/test46 index 715616e23c..9d872bab6d 100644 --- a/tests/data/test46 +++ b/tests/data/test46 @@ -87,7 +87,7 @@ Cookie: empty=; mooo2=indeed2; mooo=indeed # https://curl.se/docs/http-cookies.html # This file was generated by libcurl! Edit at your own risk. -domain..tld FALSE /want/ FALSE 0 simplyhuge %repeat[3998 x z]% +domain..tld FALSE /want FALSE 0 simplyhuge %repeat[3998 x z]% domain..tld FALSE / FALSE 0 justaname%TAB domain..tld FALSE / FALSE 0 ASPSESSIONIDQGGQQSJJ GKNBDIFAAOFDPDAIEAKDIBKE domain..tld FALSE / FALSE 0 ckySession temporary diff --git a/tests/data/test61 b/tests/data/test61 index cd38c00005..050d0b393c 100644 --- a/tests/data/test61 +++ b/tests/data/test61 @@ -71,8 +71,8 @@ Accept: */* # https://curl.se/docs/http-cookies.html # This file was generated by libcurl! Edit at your own risk. -.host.foo.com TRUE /we/want/ FALSE %days[400] test2 yes -#HttpOnly_.foo.com TRUE /we/want/ FALSE %days[400] test yes +.host.foo.com TRUE /we/want FALSE %days[400] test2 yes +#HttpOnly_.foo.com TRUE /we/want FALSE %days[400] test yes