Skip to content

Commit 3879ee4

Browse files
authored
Add validation for search parameter URL length (#5454)
* Add validation for search parameter URL length exceeding maximum limit
1 parent 28d581d commit 3879ee4

4 files changed

Lines changed: 79 additions & 3 deletions

File tree

src/Microsoft.Health.Fhir.Core/Resources.Designer.cs

Lines changed: 11 additions & 2 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

src/Microsoft.Health.Fhir.Core/Resources.resx

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -870,4 +870,7 @@
870870
<data name="ReindexingNoSearchParameterstoReindex" xml:space="preserve">
871871
<value>There are no search parameters to reindex for job Id: {0}.</value>
872872
</data>
873+
<data name="SearchParameterDefinitionInvalidUriExceedsMaxLength" xml:space="preserve">
874+
<value>Search Parameter URL {0} exceeds the maximum length limit of {1}</value>
875+
</data>
873876
</root>

src/Microsoft.Health.Fhir.Shared.Core/Features/Search/Parameters/SearchParameterValidator.cs

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -41,6 +41,7 @@ public class SearchParameterValidator : ISearchParameterValidator
4141
private readonly ISearchParameterOperations _searchParameterOperations;
4242
private readonly ISearchParameterComparer<SearchParameterInfo> _searchParameterComparer;
4343
private readonly ILogger _logger;
44+
private readonly int _maxUrlLength = 128;
4445

4546
private const string HttpPostName = "POST";
4647
private const string HttpPutName = "PUT";
@@ -103,6 +104,12 @@ public async Task ValidateSearchParameterInput(SearchParameter searchParam, stri
103104
validationFailures.Add(
104105
new ValidationFailure(nameof(Base.TypeName), Resources.SearchParameterDefinitionInvalidMissingUri));
105106
}
107+
else if (searchParam.Url.Length > _maxUrlLength)
108+
{
109+
_logger.LogInformation("Search parameter definition has a url that exceeds the maximum length. url: {Url}, length: {Length}", searchParam.Url, searchParam.Url.Length);
110+
validationFailures.Add(
111+
new ValidationFailure(nameof(searchParam.Url), string.Format(Resources.SearchParameterDefinitionInvalidUriExceedsMaxLength, searchParam.Url, _maxUrlLength)));
112+
}
106113
else
107114
{
108115
// Checks if the url is a valid url

test/Microsoft.Health.Fhir.Shared.Tests.E2E/Rest/Search/CustomSearchParamTests.cs

Lines changed: 58 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -9,7 +9,6 @@
99
using System.Net;
1010
using System.Threading.Tasks;
1111
using Hl7.Fhir.Model;
12-
using Hl7.Fhir.Serialization;
1312
using Microsoft.Health.Core.Extensions;
1413
using Microsoft.Health.Extensions.Xunit;
1514
using Microsoft.Health.Fhir.Client;
@@ -34,7 +33,9 @@ public class CustomSearchParamTests : SearchTestsBase<HttpIntegrationTestFixture
3433
{
3534
private readonly HttpIntegrationTestFixture _fixture;
3635
private readonly ITestOutputHelper _output;
36+
private const int MaxAllowedUrlLength = 128;
3737
private const int MaxRetryCount = 10;
38+
private const string UrlLengthValidationMessage = "exceeds the maximum length limit of 128";
3839

3940
public CustomSearchParamTests(HttpIntegrationTestFixture fixture, ITestOutputHelper output)
4041
: base(fixture)
@@ -69,5 +70,61 @@ public async Task GivenAnInvalidSearchParam_WhenCreatingParam_ThenMeaningfulErro
6970
Assert.Contains(ex.OperationOutcome.Issue, i => i.Diagnostics.Contains(errorMessage));
7071
}
7172
}
73+
74+
[Fact]
75+
public async Task GivenASearchParameterWithUrlLongerThan128_WhenCreating_ThenValidationErrorReturned()
76+
{
77+
SearchParameter searchParam = CreateCustomSearchParameter(MaxAllowedUrlLength + 1);
78+
79+
using FhirClientException exception = await Assert.ThrowsAsync<FhirClientException>(() => Client.CreateAsync(searchParam));
80+
81+
Assert.Contains(exception.OperationOutcome.Issue, issue => issue.Diagnostics.Contains(UrlLengthValidationMessage));
82+
}
83+
84+
[Fact]
85+
public async Task GivenAnExistingSearchParameter_WhenUpdatingWithUrlLongerThan128_ThenValidationErrorReturned()
86+
{
87+
SearchParameter searchParam = CreateCustomSearchParameter();
88+
89+
using FhirResponse<SearchParameter> createResponse = await Client.UpdateAsync(searchParam);
90+
Assert.Equal(HttpStatusCode.Created, createResponse.StatusCode);
91+
92+
searchParam.Url = CreateCustomSearchParameter(MaxAllowedUrlLength + 1).Url;
93+
94+
using FhirClientException exception = await Assert.ThrowsAsync<FhirClientException>(() => Client.UpdateAsync(searchParam));
95+
96+
Assert.Contains(exception.OperationOutcome.Issue, issue => issue.Diagnostics.Contains(UrlLengthValidationMessage));
97+
}
98+
99+
private static SearchParameter CreateCustomSearchParameter(int urlLength = MaxAllowedUrlLength)
100+
{
101+
string suffix = Guid.NewGuid().ToString("N");
102+
const string prefix = "http://example.org/fhir/SearchParameter/";
103+
104+
#if R5
105+
var baseResourceTypes = new List<VersionIndependentResourceTypesAll?>
106+
{
107+
VersionIndependentResourceTypesAll.Patient,
108+
};
109+
#else
110+
var baseResourceTypes = new List<ResourceType?>
111+
{
112+
ResourceType.Patient,
113+
};
114+
#endif
115+
116+
return new SearchParameter
117+
{
118+
Id = $"custom-search-param-{suffix[..8]}",
119+
Url = prefix + new string('a', urlLength - prefix.Length),
120+
Name = $"CustomSearchParam{suffix[..8]}",
121+
Status = PublicationStatus.Draft,
122+
Description = new Markdown("Custom search parameter used for E2E URL validation tests."),
123+
Code = $"customparam{suffix[..8]}",
124+
Base = baseResourceTypes,
125+
Type = SearchParamType.String,
126+
Expression = "Patient.name",
127+
};
128+
}
72129
}
73130
}

0 commit comments

Comments
 (0)