Skip to content

Commit e3772fe

Browse files
Use case-insensitive sets for cors headers
This updates cors header lists to use case-insensitive sets so that duplicate entries aren't accidentally used.
1 parent 306fa1a commit e3772fe

File tree

11 files changed

+49
-25
lines changed

11 files changed

+49
-25
lines changed

smithy-aws-apigateway-openapi/src/main/java/software/amazon/smithy/aws/apigateway/openapi/AddCorsPreflightIntegration.java

+4-3
Original file line numberDiff line numberDiff line change
@@ -114,11 +114,12 @@ private static <T extends Trait> Map<CorsHeader, String> deduceCorsHeaders(
114114
// Access-Control-Allow-Headers header list. Note that any further modifications that
115115
// add headers during the Smithy to OpenAPI conversion process will need to update this
116116
// list of headers accordingly.
117-
Set<String> headerNames = new TreeSet<>(corsTrait.getAdditionalAllowedHeaders());
117+
Set<String> headerNames = new TreeSet<>(String.CASE_INSENSITIVE_ORDER);
118+
headerNames.addAll(corsTrait.getAdditionalAllowedHeaders());
118119

119120
// Sets additional allowed headers from the API Gateway config.
120-
List<String> additionalAllowedHeaders = context.getConfig().getExtensions(ApiGatewayConfig.class)
121-
.getAdditionalAllowedCorsHeaders();
121+
Set<String> additionalAllowedHeaders = context.getConfig().getExtensions(ApiGatewayConfig.class)
122+
.getAdditionalAllowedCorsHeadersSet();
122123
headerNames.addAll(additionalAllowedHeaders);
123124
headerNames.addAll(findAllHeaders(path, pathItem));
124125

smithy-aws-apigateway-openapi/src/main/java/software/amazon/smithy/aws/apigateway/openapi/AddCorsToGatewayResponses.java

+2-1
Original file line numberDiff line numberDiff line change
@@ -129,7 +129,8 @@ private ObjectNode updateGatewayResponse(
129129

130130
// Add the modeled additional headers. These could potentially be added by an
131131
// apigateway feature, so they need to be present.
132-
Set<String> exposedHeaders = new TreeSet<>(trait.getAdditionalExposedHeaders());
132+
Set<String> exposedHeaders = new TreeSet<>(String.CASE_INSENSITIVE_ORDER);
133+
exposedHeaders.addAll(trait.getAdditionalExposedHeaders());
133134

134135
// Find all headers exposed already in the response. These need to be added to the
135136
// Access-Control-Expose-Headers header if any are found.

smithy-aws-apigateway-openapi/src/main/java/software/amazon/smithy/aws/apigateway/openapi/AddCorsToRestIntegrations.java

+2-1
Original file line numberDiff line numberDiff line change
@@ -138,7 +138,8 @@ private ObjectNode updateIntegrationResponse(
138138
ObjectNode responseParams = response.getObjectMember(RESPONSE_PARAMETERS_KEY).orElseGet(Node::objectNode);
139139

140140
// Created a sorted set of all headers exposed in the integration.
141-
Set<String> headersToExpose = new TreeSet<>(deduced);
141+
Set<String> headersToExpose = new TreeSet<>(String.CASE_INSENSITIVE_ORDER);
142+
headersToExpose.addAll(deduced);
142143
responseParams.getStringMap().keySet().stream()
143144
.filter(parameterName -> parameterName.startsWith(HEADER_PREFIX))
144145
.map(parameterName -> parameterName.substring(HEADER_PREFIX.length()))

smithy-aws-apigateway-openapi/src/main/java/software/amazon/smithy/aws/apigateway/openapi/ApiGatewayConfig.java

+17-8
Original file line numberDiff line numberDiff line change
@@ -16,8 +16,12 @@
1616
package software.amazon.smithy.aws.apigateway.openapi;
1717

1818
import java.util.ArrayList;
19+
import java.util.Collection;
20+
import java.util.Collections;
1921
import java.util.List;
2022
import java.util.Objects;
23+
import java.util.Set;
24+
import software.amazon.smithy.utils.SetUtils;
2125

2226
/**
2327
* API Gateway OpenAPI configuration.
@@ -55,7 +59,7 @@ public enum ApiType {
5559

5660
private ApiType apiGatewayType = ApiType.REST;
5761
private boolean disableCloudFormationSubstitution;
58-
private List<String> additionalAllowedCorsHeaders = new ArrayList<>();
62+
private Set<String> additionalAllowedCorsHeaders = Collections.emptySet();
5963

6064
/**
6165
* @return Returns true if CloudFormation substitutions are disabled.
@@ -94,21 +98,26 @@ public void setApiGatewayType(ApiType apiGatewayType) {
9498
}
9599

96100
/**
97-
* @return the list of additional allowed CORS headers.
101+
* @deprecated Use {@link ApiGatewayConfig#getAdditionalAllowedCorsHeadersSet}
98102
*/
103+
@Deprecated
99104
public List<String> getAdditionalAllowedCorsHeaders() {
105+
return new ArrayList<>(additionalAllowedCorsHeaders);
106+
}
107+
108+
/**
109+
* @return the set of additional allowed CORS headers.
110+
*/
111+
public Set<String> getAdditionalAllowedCorsHeadersSet() {
100112
return additionalAllowedCorsHeaders;
101113
}
102114

103115
/**
104-
* Sets the list of additional allowed CORS headers.
105-
*
106-
* <p>If not set, this value defaults to setting "amz-sdk-invocation-id" and
107-
* "amz-sdk-request" as the additional allowed CORS headers.</p>
116+
* Sets the additional allowed CORS headers.
108117
*
109118
* @param additionalAllowedCorsHeaders additional cors headers to be allowed.
110119
*/
111-
public void setAdditionalAllowedCorsHeaders(List<String> additionalAllowedCorsHeaders) {
112-
this.additionalAllowedCorsHeaders = Objects.requireNonNull(additionalAllowedCorsHeaders);
120+
public void setAdditionalAllowedCorsHeaders(Collection<String> additionalAllowedCorsHeaders) {
121+
this.additionalAllowedCorsHeaders = SetUtils.caseInsensitiveCopyOf(additionalAllowedCorsHeaders);
113122
}
114123
}

smithy-aws-apigateway-openapi/src/main/java/software/amazon/smithy/aws/apigateway/openapi/CorsHeader.java

+2-1
Original file line numberDiff line numberDiff line change
@@ -53,7 +53,8 @@ static <T extends Trait> Set<String> deduceOperationResponseHeaders(
5353
// The deduced response headers of an operation consist of any headers
5454
// returned by security schemes, any headers returned by the protocol,
5555
// and any headers explicitly modeled on the operation.
56-
Set<String> result = new TreeSet<>(cors.getAdditionalExposedHeaders());
56+
Set<String> result = new TreeSet<>(String.CASE_INSENSITIVE_ORDER);
57+
result.addAll(cors.getAdditionalExposedHeaders());
5758
result.addAll(context.getOpenApiProtocol().getProtocolResponseHeaders(context, shape));
5859
result.addAll(context.getAllSecuritySchemeResponseHeaders());
5960

smithy-aws-apigateway-openapi/src/test/java/software/amazon/smithy/aws/apigateway/openapi/CorsTest.java

+1-1
Original file line numberDiff line numberDiff line change
@@ -56,7 +56,7 @@ public void setsConfiguredAdditionalAllowedHeaders() {
5656
OpenApiConfig config = new OpenApiConfig();
5757
config.setService(ShapeId.from("example.smithy#MyService"));
5858
ApiGatewayConfig apiGatewayConfig = new ApiGatewayConfig();
59-
apiGatewayConfig.setAdditionalAllowedCorsHeaders(ListUtils.of("foo","bar"));
59+
apiGatewayConfig.setAdditionalAllowedCorsHeaders(ListUtils.of("foo", "bar", "content-length"));
6060
config.putExtensions(apiGatewayConfig);
6161
ObjectNode result = OpenApiConverter.create().config(config).convertToNode(model);
6262
Node expectedNode = Node.parse(IoUtils.toUtf8String(

smithy-aws-apigateway-openapi/src/test/resources/software/amazon/smithy/aws/apigateway/openapi/cors-with-additional-headers.openapi.json

+2-2
Original file line numberDiff line numberDiff line change
@@ -94,7 +94,7 @@
9494
"statusCode": "200",
9595
"responseParameters": {
9696
"method.response.header.Access-Control-Max-Age": "'86400'",
97-
"method.response.header.Access-Control-Allow-Headers": "'Amz-Sdk-Invocation-Id,Amz-Sdk-Request,Authorization,Date,Host,X-Amz-Content-Sha256,X-Amz-Date,X-Amz-Security-Token,X-Amz-Target,X-Amz-User-Agent,X-Amzn-Trace-Id,X-Service-Input-Metadata,bar,foo'",
97+
"method.response.header.Access-Control-Allow-Headers": "'Amz-Sdk-Invocation-Id,Amz-Sdk-Request,Authorization,bar,content-length,Date,foo,Host,X-Amz-Content-Sha256,X-Amz-Date,X-Amz-Security-Token,X-Amz-Target,X-Amz-User-Agent,X-Amzn-Trace-Id,X-Service-Input-Metadata'",
9898
"method.response.header.Access-Control-Allow-Origin": "'https://www.example.com'",
9999
"method.response.header.Access-Control-Allow-Methods": "'GET'"
100100
}
@@ -265,7 +265,7 @@
265265
"statusCode": "200",
266266
"responseParameters": {
267267
"method.response.header.Access-Control-Max-Age": "'86400'",
268-
"method.response.header.Access-Control-Allow-Headers": "'Amz-Sdk-Invocation-Id,Amz-Sdk-Request,Authorization,Content-Length,Content-Type,Date,Host,X-Amz-Content-Sha256,X-Amz-Date,X-Amz-Security-Token,X-Amz-Target,X-Amz-User-Agent,X-Amzn-Trace-Id,X-EnumString,X-Foo-Header,X-Service-Input-Metadata,bar,foo'",
268+
"method.response.header.Access-Control-Allow-Headers": "'Amz-Sdk-Invocation-Id,Amz-Sdk-Request,Authorization,bar,content-length,Content-Type,Date,foo,Host,X-Amz-Content-Sha256,X-Amz-Date,X-Amz-Security-Token,X-Amz-Target,X-Amz-User-Agent,X-Amzn-Trace-Id,X-EnumString,X-Foo-Header,X-Service-Input-Metadata'",
269269
"method.response.header.Access-Control-Allow-Origin": "'https://www.example.com'",
270270
"method.response.header.Access-Control-Allow-Methods": "'DELETE,GET,PUT'"
271271
}

smithy-model/src/main/java/software/amazon/smithy/model/traits/CorsTrait.java

+2-3
Original file line numberDiff line numberDiff line change
@@ -15,7 +15,6 @@
1515

1616
package software.amazon.smithy.model.traits;
1717

18-
import java.util.LinkedHashSet;
1918
import java.util.Objects;
2019
import java.util.Optional;
2120
import java.util.Set;
@@ -121,12 +120,12 @@ public Builder maxAge(int maxAge) {
121120
}
122121

123122
public Builder additionalAllowedHeaders(Set<String> additionalAllowedHeaders) {
124-
this.additionalAllowedHeaders = new LinkedHashSet<>(Objects.requireNonNull(additionalAllowedHeaders));
123+
this.additionalAllowedHeaders = SetUtils.caseInsensitiveCopyOf(additionalAllowedHeaders);
125124
return this;
126125
}
127126

128127
public Builder additionalExposedHeaders(Set<String> additionalExposedHeaders) {
129-
this.additionalExposedHeaders = new LinkedHashSet<>(Objects.requireNonNull(additionalExposedHeaders));
128+
this.additionalExposedHeaders = SetUtils.caseInsensitiveCopyOf(additionalExposedHeaders);
130129
return this;
131130
}
132131

smithy-openapi/src/main/java/software/amazon/smithy/openapi/fromsmithy/protocols/AbstractRestProtocol.java

+5-3
Original file line numberDiff line numberDiff line change
@@ -116,7 +116,8 @@ abstract Schema createDocumentSchema(
116116

117117
@Override
118118
public Set<String> getProtocolRequestHeaders(Context<T> context, OperationShape operationShape) {
119-
Set<String> headers = new TreeSet<>(OpenApiProtocol.super.getProtocolRequestHeaders(context, operationShape));
119+
Set<String> headers = new TreeSet<>(String.CASE_INSENSITIVE_ORDER);
120+
headers.addAll(OpenApiProtocol.super.getProtocolRequestHeaders(context, operationShape));
120121

121122
HttpBindingIndex bindingIndex = HttpBindingIndex.of(context.getModel());
122123
String documentMediaType = getDocumentMediaType(context, operationShape, MessageType.REQUEST);
@@ -135,7 +136,8 @@ public Set<String> getProtocolRequestHeaders(Context<T> context, OperationShape
135136

136137
@Override
137138
public Set<String> getProtocolResponseHeaders(Context<T> context, OperationShape operationShape) {
138-
Set<String> headers = new TreeSet<>(OpenApiProtocol.super.getProtocolResponseHeaders(context, operationShape));
139+
Set<String> headers = new TreeSet<>(String.CASE_INSENSITIVE_ORDER);
140+
headers.addAll(OpenApiProtocol.super.getProtocolResponseHeaders(context, operationShape));
139141

140142
// If the operation has any defined output or errors, it can return content-type.
141143
if (operationShape.getOutput().isPresent() || !operationShape.getErrors().isEmpty()) {
@@ -150,7 +152,7 @@ public Set<String> getProtocolResponseHeaders(Context<T> context, OperationShape
150152
}
151153

152154
private Set<String> getChecksumHeaders(List<HttpChecksumProperty> httpChecksumProperties) {
153-
Set<String> headers = new TreeSet<>();
155+
Set<String> headers = new TreeSet<>(String.CASE_INSENSITIVE_ORDER);
154156
for (HttpChecksumProperty property : httpChecksumProperties) {
155157
if (property.getLocation().equals(Location.HEADER)) {
156158
headers.add(property.getName());

smithy-openapi/src/main/java/software/amazon/smithy/openapi/fromsmithy/protocols/AwsRestJson1Protocol.java

+4-2
Original file line numberDiff line numberDiff line change
@@ -62,7 +62,8 @@ public Class<RestJson1Trait> getProtocolType() {
6262
@Override
6363
public Set<String> getProtocolRequestHeaders(Context<RestJson1Trait> context, OperationShape operationShape) {
6464
// x-amz-api-version if it is an endpoint operation
65-
Set<String> headers = new TreeSet<>(super.getProtocolRequestHeaders(context, operationShape));
65+
Set<String> headers = new TreeSet<>(String.CASE_INSENSITIVE_ORDER);
66+
headers.addAll(super.getProtocolRequestHeaders(context, operationShape));
6667
headers.addAll(AWS_REQUEST_HEADERS);
6768
if (operationShape.hasTrait(ClientDiscoveredEndpointTrait.class)) {
6869
headers.add("X-Amz-Api-Version");
@@ -72,7 +73,8 @@ public Set<String> getProtocolRequestHeaders(Context<RestJson1Trait> context, Op
7273

7374
@Override
7475
public Set<String> getProtocolResponseHeaders(Context<RestJson1Trait> context, OperationShape operationShape) {
75-
Set<String> headers = new TreeSet<>(super.getProtocolResponseHeaders(context, operationShape));
76+
Set<String> headers = new TreeSet<>(String.CASE_INSENSITIVE_ORDER);
77+
headers.addAll(super.getProtocolResponseHeaders(context, operationShape));
7678
headers.addAll(AWS_RESPONSE_HEADERS);
7779
return headers;
7880
}

smithy-utils/src/main/java/software/amazon/smithy/utils/SetUtils.java

+8
Original file line numberDiff line numberDiff line change
@@ -19,7 +19,9 @@
1919
import java.util.Collections;
2020
import java.util.HashSet;
2121
import java.util.LinkedHashSet;
22+
import java.util.Objects;
2223
import java.util.Set;
24+
import java.util.TreeSet;
2325
import java.util.stream.Collector;
2426
import java.util.stream.Collectors;
2527

@@ -51,6 +53,12 @@ public static <T> Set<T> orderedCopyOf(Collection<? extends T> values) {
5153
return values.isEmpty() ? Collections.emptySet() : Collections.unmodifiableSet(new LinkedHashSet<>(values));
5254
}
5355

56+
public static Set<String> caseInsensitiveCopyOf(Collection<? extends String> values) {
57+
Set<String> caseInsensitiveSet = new TreeSet<>(String.CASE_INSENSITIVE_ORDER);
58+
caseInsensitiveSet.addAll(Objects.requireNonNull(values));
59+
return Collections.unmodifiableSet(caseInsensitiveSet);
60+
}
61+
5462
/**
5563
* Returns an unmodifiable set containing zero entries.
5664
*

0 commit comments

Comments
 (0)