Skip to content

bugfix: encoded query parameter (web) #18

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 12 commits into from
Jun 29, 2023
Merged

Conversation

pboos
Copy link
Contributor

@pboos pboos commented Jun 28, 2023

When a query parameter with a comma separated list is sent with , being encoded (e.g. 123%2C2%2C3 vs 1,2,3) then this would be interpreted as a string instead of a list.

This does a urldecode when passing the parameter to the validator.

@pboos pboos changed the title bugfix: encoded query parameter bugfix: encoded query parameter (web) Jun 29, 2023
@pboos pboos marked this pull request as ready for review June 29, 2023 05:46
@pboos pboos requested review from a team and dominik-riha June 29, 2023 05:46
@@ -1,4 +1,4 @@
openapi.validation.sample-rate=0.5
openapi.validation.sample-rate=1
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

ℹ️ Unrelated to fix. Increasing to 1 for better example.

@@ -79,14 +81,22 @@ public ValidationResult validateRequestObject(final RequestMetaData request, Str
private static SimpleRequest buildSimpleRequest(RequestMetaData request, String requestBody) {
var requestBuilder = new SimpleRequest.Builder(request.getMethod(), request.getUri().getPath());
URLEncodedUtils.parse(request.getUri(), StandardCharsets.UTF_8)
.forEach(p -> requestBuilder.withQueryParam(p.getName(), p.getValue()));
.forEach(p -> requestBuilder.withQueryParam(p.getName(), getQueryParameterValueWithOptionalDecode(p)));
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

ℹ️ This is the actual fix. All the rest is just other small improvements.

@@ -13,7 +12,7 @@ public class ServletMetaDataFactory {
public static final String HEADER_CONTENT_TYPE = "Content-Type";

public RequestMetaData buildRequestMetaData(HttpServletRequest request) {
var uri = ServletUriComponentsBuilder.fromRequest(request).build(Map.of());
var uri = ServletUriComponentsBuilder.fromRequest(request).build().toUri();
Copy link
Contributor Author

@pboos pboos Jun 29, 2023

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

ℹ️ Unrelated to fix. Not strictly needed, but this simplifies the code as internally the old call did this + additional unnecessary logic.

@@ -22,5 +22,6 @@ dependencies {

testImplementation 'org.springframework.boot:spring-boot-starter-test'
testImplementation 'org.springframework:spring-web'
testImplementation 'org.springframework:spring-webmvc'
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

ℹ️ Unrelated to fix. Not needed right now. But this dependency is useful for writing tests in the future.

Background:

I wrote some tests that were for a previous fix of the problem, but they got reverted as I found a nicer way to fix it. When I wrote those tests this dependency was needed for the tests to run. And it took a while to find this dependency to make the tests work. So better leave them for now so we save time in the future :)

@pboos pboos requested a review from huguenin June 29, 2023 07:33
) {
this.threadPoolExecutor = threadPoolExecutor;
this.validator = new OpenApiInteractionValidatorFactory().build(specificationFilePath, configuration);
this.validator = validator;
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

ℹ️ To mock the validator this needs to be injected. So I improved the architecture here (should have done this from the beginning).

if (requestBody != null) {
requestBuilder.withBody(requestBody);
}
request.getHeaders().forEach(requestBuilder::withHeader);
return requestBuilder.build();
}

private static String getQueryParameterValueWithOptionalDecode(NameValuePair p) {
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I wonder why we pass in the NameValuePair instead of directly the value. Why not pass in p.getValue() and rename the method to nullSafeURLDecode?


@Test
public void testWhenEncodedQueryParamIsPassedThenValidationShouldHappenWithQueryParamDecoded() {
var uri = URI.create("https://api.example.com?ids=1%2C2%2C3");
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Any other cases we should test, e.g. when & or = is contained encoded in the value, or when we have multiple name-value pairs being passed?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

So, added more tests for these cases you mentioned (and also for spaces encoded with +). Hope happy you are 😉 .

@pboos pboos requested a review from huguenin June 29, 2023 09:54
Copy link
Member

@huguenin huguenin left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Nice. Feedback for next time: It would make sense to split up into different test cases, so that a test failure will give you more insight into the specific case that is failing. If your tests fail now, the "minimum failing example" won't be evident. Good for this PR though.

@pboos pboos merged commit 2cccbb4 into main Jun 29, 2023
@pboos pboos deleted the bugfix/fix-encoded-query-parameter branch June 29, 2023 15:29
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

2 participants