Skip to content

Commit 1967522

Browse files
authored
4.x: Validate that header has at least one value when list is used. (#8489)
* Validate that header has at least one value when list is used. * Fix usage where we may have called it with empty list.
1 parent 03579c4 commit 1967522

File tree

5 files changed

+89
-11
lines changed

5 files changed

+89
-11
lines changed

http/http/src/main/java/io/helidon/http/HeaderValues.java

+12-5
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
/*
2-
* Copyright (c) 2023 Oracle and/or its affiliates.
2+
* Copyright (c) 2023, 2024 Oracle and/or its affiliates.
33
*
44
* Licensed under the Apache License, Version 2.0 (the "License");
55
* you may not use this file except in compliance with the License.
@@ -290,9 +290,10 @@ public static Header create(String name, long value) {
290290
* Create a new header. This header is considered unchanging and not sensitive.
291291
*
292292
* @param name name of the header
293-
* @param values values of the header
293+
* @param values values of the header, must contain at least one value (which may be an empty String)
294294
* @return a new header
295295
* @see #create(io.helidon.http.HeaderName, boolean, boolean, String...)
296+
* @throws java.lang.IllegalArgumentException in case the collection is empty
296297
*/
297298
public static Header create(HeaderName name, String... values) {
298299
if (values.length == 0) {
@@ -305,9 +306,10 @@ public static Header create(HeaderName name, String... values) {
305306
* Create a new header. This header is considered unchanging and not sensitive.
306307
*
307308
* @param name name of the header
308-
* @param values values of the header
309+
* @param values values of the header, must contain at least one value (which may be an empty String)
309310
* @return a new header
310311
* @see #create(io.helidon.http.HeaderName, boolean, boolean, String...)
312+
* @throws java.lang.IllegalArgumentException in case the collection is empty
311313
*/
312314
public static Header create(String name, String... values) {
313315
return create(HeaderNames.create(name), values);
@@ -317,21 +319,26 @@ public static Header create(String name, String... values) {
317319
* Create a new header. This header is considered unchanging and not sensitive.
318320
*
319321
* @param name name of the header
320-
* @param values values of the header
322+
* @param values values of the header, must contain at least one value (which may be an empty String)
321323
* @return a new header
322324
* @see #create(io.helidon.http.HeaderName, boolean, boolean, String...)
325+
* @throws java.lang.IllegalArgumentException in case the collection is empty
323326
*/
324327
public static Header create(HeaderName name, Collection<String> values) {
328+
if (values.isEmpty()) {
329+
throw new IllegalArgumentException("Cannot create a header without a value. Header: " + name);
330+
}
325331
return new HeaderValueList(name, false, false, values);
326332
}
327333

328334
/**
329335
* Create a new header. This header is considered unchanging and not sensitive.
330336
*
331337
* @param name name of the header
332-
* @param values values of the header
338+
* @param values values of the header, must contain at least one value (which may be an empty String)
333339
* @return a new header
334340
* @see #create(io.helidon.http.HeaderName, boolean, boolean, String...)
341+
* @throws java.lang.IllegalArgumentException in case the collection is empty
335342
*/
336343
public static Header create(String name, Collection<String> values) {
337344
return create(HeaderNames.create(name), values);

http/http/src/main/java/io/helidon/http/ServerRequestHeaders.java

+1-1
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
/*
2-
* Copyright (c) 2022, 2023 Oracle and/or its affiliates.
2+
* Copyright (c) 2022, 2024 Oracle and/or its affiliates.
33
*
44
* Licensed under the Apache License, Version 2.0 (the "License");
55
* you may not use this file except in compliance with the License.
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,45 @@
1+
/*
2+
* Copyright (c) 2024 Oracle and/or its affiliates.
3+
*
4+
* Licensed under the Apache License, Version 2.0 (the "License");
5+
* you may not use this file except in compliance with the License.
6+
* You may obtain a copy of the License at
7+
*
8+
* http://www.apache.org/licenses/LICENSE-2.0
9+
*
10+
* Unless required by applicable law or agreed to in writing, software
11+
* distributed under the License is distributed on an "AS IS" BASIS,
12+
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13+
* See the License for the specific language governing permissions and
14+
* limitations under the License.
15+
*/
16+
17+
package io.helidon.http;
18+
19+
import java.util.List;
20+
import java.util.Locale;
21+
22+
import org.junit.jupiter.api.Test;
23+
24+
import static org.hamcrest.CoreMatchers.is;
25+
import static org.hamcrest.MatcherAssert.assertThat;
26+
import static org.junit.jupiter.api.Assertions.assertThrows;
27+
28+
class HeaderValuesTest {
29+
@Test
30+
void testEmptyValuesFail() {
31+
assertThrows(IllegalArgumentException.class, () -> HeaderValues.create("Name", List.of()));
32+
assertThrows(IllegalArgumentException.class, () -> HeaderValues.create("Name"));
33+
assertThrows(IllegalArgumentException.class, () -> HeaderValues.create(HeaderNames.CONTENT_DISPOSITION, List.of()));
34+
assertThrows(IllegalArgumentException.class, () -> HeaderValues.create(HeaderNames.CONTENT_DISPOSITION));
35+
}
36+
37+
@Test
38+
void testConstantValue() {
39+
// test a value to make sure we do not have a problem with initialization
40+
Header connectionClose = HeaderValues.CONNECTION_CLOSE;
41+
assertThat(connectionClose.name().toLowerCase(Locale.ROOT), is("connection"));
42+
assertThat(connectionClose.headerName(), is(HeaderNames.CONNECTION));
43+
assertThat(connectionClose.get(), is("close"));
44+
}
45+
}

security/providers/oidc/src/main/java/io/helidon/security/providers/oidc/TenantAuthenticationHandler.java

+10-4
Original file line numberDiff line numberDiff line change
@@ -718,11 +718,17 @@ private AuthenticationResponse processValidationResult(ProviderRequest providerR
718718
}
719719

720720
if (missingScopes.isEmpty()) {
721-
return AuthenticationResponse.builder()
721+
AuthenticationResponse.Builder response = AuthenticationResponse.builder()
722722
.status(SecurityResponse.SecurityStatus.SUCCESS)
723-
.user(subject)
724-
.responseHeader(HeaderNames.SET_COOKIE.defaultCase(), cookies)
725-
.build();
723+
.user(subject);
724+
725+
if (cookies.isEmpty()) {
726+
return response.build();
727+
} else {
728+
return response
729+
.responseHeader(HeaderNames.SET_COOKIE.defaultCase(), cookies)
730+
.build();
731+
}
726732
} else {
727733
return errorResponse(providerRequest,
728734
Status.FORBIDDEN_403,

security/security/src/main/java/io/helidon/security/SecurityResponse.java

+21-1
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
/*
2-
* Copyright (c) 2018, 2023 Oracle and/or its affiliates.
2+
* Copyright (c) 2018, 2024 Oracle and/or its affiliates.
33
*
44
* Licensed under the Apache License, Version 2.0 (the "License");
55
* you may not use this file except in compliance with the License.
@@ -232,6 +232,12 @@ public T throwable(Throwable exception) {
232232
* @return updated builder instance
233233
*/
234234
public T requestHeaders(Map<String, List<String>> headers) {
235+
headers.forEach((header, values) -> {
236+
if (values.isEmpty()) {
237+
throw new IllegalArgumentException("Header values cannot be empty, at least one value is required."
238+
+ "Request header name: " + header);
239+
}
240+
});
235241
this.requestHeaders.clear();
236242
this.requestHeaders.putAll(headers);
237243
return identity();
@@ -259,6 +265,10 @@ public T requestHeader(String header, String value) {
259265
* @return this instance
260266
*/
261267
public T requestHeader(String header, List<String> values) {
268+
if (values.isEmpty()) {
269+
throw new IllegalArgumentException("Header values cannot be empty, at least one value is required."
270+
+ "Request header name: " + header);
271+
}
262272
requestHeaders.put(header, values);
263273
return identity();
264274
}
@@ -270,6 +280,12 @@ public T requestHeader(String header, List<String> values) {
270280
* @return updated builder instance
271281
*/
272282
public T responseHeaders(Map<String, List<String>> headers) {
283+
headers.forEach((header, values) -> {
284+
if (values.isEmpty()) {
285+
throw new IllegalArgumentException("Header values cannot be empty, at least one value is required."
286+
+ "Response header name: " + header);
287+
}
288+
});
273289
this.responseHeaders.clear();
274290
this.responseHeaders.putAll(headers);
275291
return identity();
@@ -297,6 +313,10 @@ public T responseHeader(String header, String value) {
297313
* @return this instance
298314
*/
299315
public T responseHeader(String header, List<String> values) {
316+
if (values.isEmpty()) {
317+
throw new IllegalArgumentException("Header values cannot be empty, at least one value is required."
318+
+ "Response header name: " + header);
319+
}
300320
responseHeaders.put(header, values);
301321
return identity();
302322
}

0 commit comments

Comments
 (0)