diff --git a/Cloudflare.Tests/Extensions/StringExtensionsTest.cs b/Cloudflare.Tests/Extensions/StringExtensionsTest.cs index 3f6428d..b40aac9 100644 --- a/Cloudflare.Tests/Extensions/StringExtensionsTest.cs +++ b/Cloudflare.Tests/Extensions/StringExtensionsTest.cs @@ -46,6 +46,21 @@ namespace Cloudflare.Tests.Extensions // Assert - ArgumentOutOfRangeException } + [DataTestMethod] + [DataRow("023e105f4ecef8ad9ca31a8372d0c35")] + [DataRow("023e105f4ecef8ad9ca31a8372d0C353")] + [DataRow("023e105f4ecef8ad9ca31a8372d0y353")] + [ExpectedException(typeof(ArgumentException))] + public void ShouldThrowArgumentExceptionForValidateId(string id) + { + // Arrange + + // Act + id.ValidateCloudflareId(); + + // Assert - ArgumentException + } + [TestMethod] public void ShouldValidateName() { diff --git a/Cloudflare/CloudflareClient.cs b/Cloudflare/CloudflareClient.cs index 5d553d3..b14c9c3 100644 --- a/Cloudflare/CloudflareClient.cs +++ b/Cloudflare/CloudflareClient.cs @@ -198,9 +198,12 @@ namespace AMWD.Net.Api.Cloudflare }; } + // Ensure a clean base URL + string baseUrl = _clientOptions.BaseUrl.Trim().TrimEnd('/'); + var client = new HttpClient(handler, true) { - BaseAddress = new Uri(_clientOptions.BaseUrl), + BaseAddress = new Uri(baseUrl + '/'), Timeout = _clientOptions.Timeout, }; @@ -259,6 +262,8 @@ namespace AMWD.Net.Api.Cloudflare private string BuildRequestUrl(string requestPath, IQueryParameterFilter? queryFilter = null) { + // Ensure a clean request path + string reqPath = requestPath.Trim().TrimStart('/'); var dict = new Dictionary(); if (_clientOptions.DefaultQueryParams.Count > 0) @@ -275,12 +280,12 @@ namespace AMWD.Net.Api.Cloudflare } if (dict.Count == 0) - return requestPath; + return reqPath; string[] param = dict.Select(kvp => $"{kvp.Key}={WebUtility.UrlEncode(kvp.Value)}").ToArray(); string query = string.Join("&", param); - return $"{requestPath}?{query}"; + return $"{reqPath}?{query}"; } private static HttpContent? ConvertRequest(T request) diff --git a/Cloudflare/Extensions/StringExtensions.cs b/Cloudflare/Extensions/StringExtensions.cs index 4117bc6..7ace8c7 100644 --- a/Cloudflare/Extensions/StringExtensions.cs +++ b/Cloudflare/Extensions/StringExtensions.cs @@ -8,6 +8,7 @@ namespace AMWD.Net.Api.Cloudflare /// public static class StringExtensions { + private static readonly Regex _idCheckRegex = new(@"^[0-9a-f]{32}$", RegexOptions.Compiled); private static readonly Regex _emailCheckRegex = new(@"^[a-zA-Z0-9!#$%&'*+/=?^_`{|}~-]+(?:\.[a-zA-Z0-9!#$%&'*+/=?^_`{|}~-]+)*@(?:[a-zA-Z0-9](?:[a-zA-Z0-9-]*[a-zA-Z0-9])?\.)+[a-zA-Z0-9](?:[a-zA-Z0-9-]*[a-zA-Z0-9])?$", RegexOptions.Compiled); /// @@ -26,6 +27,13 @@ namespace AMWD.Net.Api.Cloudflare if (id.Length > 32) throw new ArgumentOutOfRangeException(nameof(id)); + + if (!_idCheckRegex.IsMatch(id)) + throw new ArgumentException("Invalid Cloudflare ID", nameof(id)); + + // TODO: It seems like Cloudflare IDs are GUIDs - should be verified. + //if (!Guid.TryParse(id, out _)) + // throw new ArgumentException("Invalid Cloudflare ID", nameof(id)); } ///