diff --git a/src/NzbDrone.Common.Test/Http/CookieUtilFixture.cs b/src/NzbDrone.Common.Test/Http/CookieUtilFixture.cs new file mode 100644 index 000000000..970633966 --- /dev/null +++ b/src/NzbDrone.Common.Test/Http/CookieUtilFixture.cs @@ -0,0 +1,101 @@ +using System; +using System.Collections.Generic; +using System.Diagnostics.CodeAnalysis; +using NUnit.Framework; +using NzbDrone.Common.Http; + +namespace NzbDrone.Common.Test.Http +{ + [TestFixture] + public class CookieUtilFixture + { + [Test] + public void CookieHeaderToDictionaryGood() + { + // valid cookies with non-alpha characters in the value + var cookieHeader = "__cfduid=d6237f041586694295; __cf_bm=TlOng/xyqckk-TMen38z+0RFYA7YA="; + var expectedCookieDictionary = new Dictionary + { + { "__cfduid", "d6237f041586694295" }, + { "__cf_bm", "TlOng/xyqckk-TMen38z+0RFYA7YA=" } + }; + CollectionAssert.AreEqual(expectedCookieDictionary, CookieUtil.CookieHeaderToDictionary(cookieHeader)); + } + + [Test] + public void CookieHeaderToDictionaryDuplicateKeys() + { + // cookie with duplicate keys and whitespace separator instead of ; + var cookieHeader = "__cfduid=d6237f041586694295; __cf_bm=TlOng/xyqckk-TMen38z+0RFYA7YA= __cf_bm=test"; + var expectedCookieDictionary = new Dictionary + { + { "__cfduid", "d6237f041586694295" }, + { "__cf_bm", "test" } // we always assume the latest value is the most recent + }; + CollectionAssert.AreEqual(expectedCookieDictionary, CookieUtil.CookieHeaderToDictionary(cookieHeader)); + } + + [Test] + public void CookieHeaderToDictionaryMalformed() + { + // malformed cookies + var cookieHeader = "__cfduidd6237f041586694295; __cf_;bm TlOng; good_cookie=value"; + var expectedCookieDictionary = new Dictionary { { "good_cookie", "value" }, }; + CollectionAssert.AreEqual(expectedCookieDictionary, CookieUtil.CookieHeaderToDictionary(cookieHeader)); + } + + [Test] + [SuppressMessage("ReSharper", "CollectionNeverUpdated.Local")] + public void CookieHeaderToDictionaryNull() + { + // null cookie header + var expectedCookieDictionary = new Dictionary(); + CollectionAssert.AreEqual(expectedCookieDictionary, CookieUtil.CookieHeaderToDictionary(null)); + } + + [Test] + public void CookieDictionaryToHeaderGood() + { + // valid cookies with non-alpha characters in the value + var cookieDictionary = new Dictionary + { + { "__cfduid", "d6237f041586694295" }, + { "__cf_bm", "TlOng/xyqckk-TMen38z+0RFYA7YA=" } + }; + var expectedCookieHeader = "__cfduid=d6237f041586694295; __cf_bm=TlOng/xyqckk-TMen38z+0RFYA7YA="; + CollectionAssert.AreEqual(expectedCookieHeader, CookieUtil.CookieDictionaryToHeader(cookieDictionary)); + } + + [Test] + public void CookieDictionaryToHeaderMalformed1() + { + // malformed key + var cookieDictionary = new Dictionary + { + { "__cf_=bm", "34234234" } + }; + var ex = Assert.Throws(() => CookieUtil.CookieDictionaryToHeader(cookieDictionary)); + Assert.AreEqual("The cookie '__cf_=bm=34234234' is malformed.", ex.Message); + } + + [Test] + public void CookieDictionaryToHeaderMalformed2() + { + // malformed value + var cookieDictionary = new Dictionary + { + { "__cf_bm", "34234 234" } + }; + var ex = Assert.Throws(() => CookieUtil.CookieDictionaryToHeader(cookieDictionary)); + Assert.AreEqual("The cookie '__cf_bm=34234 234' is malformed.", ex.Message); + } + + [Test] + public void CookieDictionaryToHeaderNull() + { + // null cookie dictionary + var expectedCookieHeader = ""; + CollectionAssert.AreEqual(expectedCookieHeader, CookieUtil.CookieDictionaryToHeader(null)); + } + } +} diff --git a/src/NzbDrone.Common/Http/CookieUtil.cs b/src/NzbDrone.Common/Http/CookieUtil.cs index 38c3c756b..ea408989a 100644 --- a/src/NzbDrone.Common/Http/CookieUtil.cs +++ b/src/NzbDrone.Common/Http/CookieUtil.cs @@ -9,7 +9,7 @@ namespace NzbDrone.Common.Http { // https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/Set-Cookie // NOTE: we are not checking non-ascii characters and we should - private static readonly Regex _CookieRegex = new Regex(@"([^\(\)<>@,;:\\""/\[\]\?=\{\}\s]+)=([^,;\\""\s]+)"); + private static readonly Regex CookieRegex = new (@"([^\(\)<>@,;:\\""/\[\]\?=\{\}\s]+)=([^,;\\""\s]+)"); private static readonly string[] FilterProps = { "COMMENT", "COMMENTURL", "DISCORD", "DOMAIN", "EXPIRES", "MAX-AGE", "PATH", "PORT", "SECURE", "VERSION", "HTTPONLY", "SAMESITE" }; private static readonly char[] InvalidKeyChars = { '(', ')', '<', '>', '@', ',', ';', ':', '\\', '"', '/', '[', ']', '?', '=', '{', '}', ' ', '\t', '\n' }; private static readonly char[] InvalidValueChars = { '"', ',', ';', '\\', ' ', '\t', '\n' }; @@ -22,7 +22,7 @@ namespace NzbDrone.Common.Http return cookieDictionary; } - var matches = _CookieRegex.Match(cookieHeader); + var matches = CookieRegex.Match(cookieHeader); while (matches.Success) { if (matches.Groups.Count > 2 && !FilterProps.Contains(matches.Groups[1].Value.ToUpperInvariant()))