diff --git a/common/uri/src/main/java/io/helidon/common/uri/UriValidationException.java b/common/uri/src/main/java/io/helidon/common/uri/UriValidationException.java index 8393d91297d..bed5a48286a 100644 --- a/common/uri/src/main/java/io/helidon/common/uri/UriValidationException.java +++ b/common/uri/src/main/java/io/helidon/common/uri/UriValidationException.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2024 Oracle and/or its affiliates. + * Copyright (c) 2024, 2025 Oracle and/or its affiliates. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -38,6 +38,10 @@ public class UriValidationException extends IllegalArgumentException { * The value (containing illegal characters) that failed validation. */ private final char[] invalidValue; + /** + * The index of the invalid char. + */ + private final int index; /** * Create a new validation exception that uses a descriptive message and the failed chars. @@ -52,6 +56,7 @@ public UriValidationException(Segment segment, char[] invalidValue, String messa this.segment = segment; this.invalidValue = invalidValue; + this.index = -1; } /** @@ -67,6 +72,7 @@ public UriValidationException(Segment segment, char[] invalidValue, String messa this.segment = segment; this.invalidValue = invalidValue; + this.index = -1; } /** @@ -84,6 +90,7 @@ public UriValidationException(Segment segment, char[] invalidValue, String messa this.segment = segment; this.invalidValue = invalidValue; + this.index = index; } /** @@ -100,6 +107,7 @@ public UriValidationException(Segment segment, char[] invalidValue, String messa this.segment = segment; this.invalidValue = invalidValue; + this.index = index; } /** @@ -121,6 +129,16 @@ public Segment segment() { return segment; } + /** + * Returns a safe message that does not include any user input and can be returned + * as part of a response. + * + * @return a safe message + */ + public String safeMessage() { + return segment.text() + " contains invalid char" + (index != -1 ? " at index " + index : ""); + } + private static String toMessage(char[] value, String message) { Objects.requireNonNull(value); Objects.requireNonNull(message); diff --git a/webserver/tests/webserver/src/test/java/io/helidon/webserver/tests/BadPrologueTest.java b/webserver/tests/webserver/src/test/java/io/helidon/webserver/tests/BadPrologueTest.java index 80b83a712b0..063b6cdb187 100644 --- a/webserver/tests/webserver/src/test/java/io/helidon/webserver/tests/BadPrologueTest.java +++ b/webserver/tests/webserver/src/test/java/io/helidon/webserver/tests/BadPrologueTest.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2024 Oracle and/or its affiliates. + * Copyright (c) 2024, 2025 Oracle and/or its affiliates. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -76,9 +76,8 @@ void testBadQuery() { assertThat(response, containsString("400 Bad Request")); // beginning of message to the first double quote - assertThat(response, containsString("Query contains invalid char: ")); + assertThat(response, containsString("Query contains invalid char at index 2")); // end of message from double quote, index of bad char, and bad char - assertThat(response, containsString(", index: 2, char: 0x3c")); assertThat(response, not(containsString(">"))); } @@ -91,9 +90,8 @@ void testBadQueryCurly() { assertThat(response, containsString("400 Bad Request")); // beginning of message to the first double quote - assertThat(response, containsString("Query contains invalid char: ")); + assertThat(response, containsString("Query contains invalid char at index 10")); // end of message from double quote, index of bad char, and bad char - assertThat(response, containsString(", index: 10, char: 0x7b")); } @Test @@ -117,9 +115,8 @@ void testBadFragment() { assertThat(response, containsString("400 Bad Request")); // beginning of message to the first double quote - assertThat(response, containsString("Fragment contains invalid char: ")); + assertThat(response, containsString("Fragment contains invalid char at index 16")); // end of message from double quote, index of bad char, and bad char - assertThat(response, containsString(", index: 16, char: 0x3e")); assertThat(response, not(containsString(">"))); } } diff --git a/webserver/webserver/src/main/java/io/helidon/webserver/http1/Http1Connection.java b/webserver/webserver/src/main/java/io/helidon/webserver/http1/Http1Connection.java index acc07c4c963..33872b2f3ee 100644 --- a/webserver/webserver/src/main/java/io/helidon/webserver/http1/Http1Connection.java +++ b/webserver/webserver/src/main/java/io/helidon/webserver/http1/Http1Connection.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2022, 2024 Oracle and/or its affiliates. + * Copyright (c) 2022, 2025 Oracle and/or its affiliates. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -37,6 +37,7 @@ import io.helidon.common.mapper.MapperException; import io.helidon.common.task.InterruptableTask; import io.helidon.common.tls.TlsUtils; +import io.helidon.common.uri.UriValidationException; import io.helidon.common.uri.UriValidator; import io.helidon.http.BadRequestException; import io.helidon.http.DateTime; @@ -335,7 +336,7 @@ private void validatePrologue(HttpPrologue prologue) { .status(Status.BAD_REQUEST_400) .request(DirectTransportRequest.create(prologue, ServerRequestHeaders.create())) .setKeepAlive(false) - .message(e.getMessage()) + .message(e instanceof UriValidationException ve ? ve.safeMessage() : e.getMessage()) .safeMessage(true) .cause(e) .build();