Skip to content

Commit bc7adcc

Browse files
rikatzRoseWrightdev
authored andcommitted
Fix cors cel (kubernetes-sigs#4032)
* CORS: support a single wildcard entry as origin * CORS: Add test for CRD validations
1 parent 69d8ee9 commit bc7adcc

File tree

6 files changed

+181
-24
lines changed

6 files changed

+181
-24
lines changed

apis/v1/httproute_types.go

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1404,8 +1404,9 @@ type HTTPCORSFilter struct {
14041404
// Support: Extended
14051405
// +listType=set
14061406
// +kubebuilder:validation:MaxItems=64
1407+
// +kubebuilder:validation:XValidation:message="AllowOrigins cannot contain '*' alongside other origins",rule="!('*' in self && self.size() > 1)"
14071408
// +optional
1408-
AllowOrigins []AbsoluteURI `json:"allowOrigins,omitempty"`
1409+
AllowOrigins []CORSOrigin `json:"allowOrigins,omitempty"`
14091410

14101411
// AllowCredentials indicates whether the actual cross-origin request allows
14111412
// to include credentials.

apis/v1/shared_types.go

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -608,6 +608,17 @@ type PreciseHostname string
608608
// +kubebuilder:validation:Pattern=`^(([^:/?#]+):)(//([^/?#]*))([^?#]*)(\?([^#]*))?(#(.*))?`
609609
type AbsoluteURI string
610610

611+
// The CORSOrigin MUST NOT be a relative URI, and it MUST follow the URI syntax and
612+
// encoding rules specified in RFC3986. The CORSOrigin MUST include both a
613+
// scheme (e.g., "http" or "spiffe") and a scheme-specific-part, or it should be a single '*' character.
614+
// URIs that include an authority MUST include a fully qualified domain name or
615+
// IP address as the host.
616+
// <gateway:util:excludeFromCRD> The below regex was generated to simplify the assertion of scheme://host:<port> being port optional </gateway:util:excludeFromCRD>
617+
// +kubebuilder:validation:MinLength=1
618+
// +kubebuilder:validation:MaxLength=253
619+
// +kubebuilder:validation:Pattern=`(^\*$)|(^([a-zA-Z][a-zA-Z0-9+\-.]+):\/\/([^:/?#]+)(:([0-9]{1,5}))?$)`
620+
type CORSOrigin string
621+
611622
// Group refers to a Kubernetes Group. It must either be an empty string or a
612623
// RFC 1123 subdomain.
613624
//

apis/v1/zz_generated.deepcopy.go

Lines changed: 1 addition & 1 deletion
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

applyconfiguration/apis/v1/httpcorsfilter.go

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

config/crd/experimental/gateway.networking.k8s.io_httproutes.yaml

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

pkg/test/cel/httproute_experimental_test.go

Lines changed: 129 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -241,6 +241,135 @@ func toDuration(durationString string) *gatewayv1.Duration {
241241
return (*gatewayv1.Duration)(&durationString)
242242
}
243243

244+
func TestHTTPRouteCORS(t *testing.T) {
245+
tests := []struct {
246+
name string
247+
wantErrors []string
248+
corsfilter *gatewayv1.HTTPCORSFilter
249+
}{
250+
{
251+
name: "Valid cors should be accepted",
252+
wantErrors: nil,
253+
corsfilter: &gatewayv1.HTTPCORSFilter{
254+
AllowOrigins: []gatewayv1.CORSOrigin{
255+
"https://xpto.com",
256+
"http://*.abcd.com",
257+
"http://*.abcd.com:12345",
258+
},
259+
},
260+
},
261+
{
262+
name: "Using wildcard only is accepted",
263+
wantErrors: nil,
264+
corsfilter: &gatewayv1.HTTPCORSFilter{
265+
AllowOrigins: []gatewayv1.CORSOrigin{
266+
"*",
267+
},
268+
},
269+
},
270+
{
271+
name: "Wildcard and other hosts on the same origin list should be denied",
272+
wantErrors: []string{"AllowOrigins cannot contain '*' alongside other origins"},
273+
corsfilter: &gatewayv1.HTTPCORSFilter{
274+
AllowOrigins: []gatewayv1.CORSOrigin{
275+
"*",
276+
"https://xpto.com",
277+
},
278+
},
279+
},
280+
{
281+
name: "An origin without the format scheme://host should be denied",
282+
wantErrors: []string{"Invalid value: \"xpto.com\": spec.rules[0].filters[0].cors.allowOrigins[1] in body should match"},
283+
corsfilter: &gatewayv1.HTTPCORSFilter{
284+
AllowOrigins: []gatewayv1.CORSOrigin{
285+
"https://xpto.com",
286+
"xpto.com",
287+
},
288+
},
289+
},
290+
{
291+
name: "An origin as http://*.com should be accepted",
292+
wantErrors: nil,
293+
corsfilter: &gatewayv1.HTTPCORSFilter{
294+
AllowOrigins: []gatewayv1.CORSOrigin{
295+
"https://*.com",
296+
},
297+
},
298+
},
299+
{
300+
name: "An origin with an invalid port should be denied",
301+
wantErrors: []string{"Invalid value: \"https://xpto.com:notaport\": spec.rules[0].filters[0].cors.allowOrigins[0] in body should match"},
302+
corsfilter: &gatewayv1.HTTPCORSFilter{
303+
AllowOrigins: []gatewayv1.CORSOrigin{
304+
"https://xpto.com:notaport",
305+
},
306+
},
307+
},
308+
{
309+
name: "An origin with an value before the scheme definition should be denied",
310+
wantErrors: []string{"Invalid value: \"xpto/https://xpto.com\""},
311+
corsfilter: &gatewayv1.HTTPCORSFilter{
312+
AllowOrigins: []gatewayv1.CORSOrigin{
313+
"xpto/https://xpto.com",
314+
},
315+
},
316+
},
317+
{
318+
name: "Using an invalid HTTP method should be denied",
319+
wantErrors: []string{"Unsupported value: \"BAZINGA\""},
320+
corsfilter: &gatewayv1.HTTPCORSFilter{
321+
AllowMethods: []gatewayv1.HTTPMethodWithWildcard{
322+
"BAZINGA",
323+
},
324+
},
325+
},
326+
{
327+
name: "Using wildcard and a valid method should be denied",
328+
wantErrors: []string{"AllowMethods cannot contain '*' alongside other methods"},
329+
corsfilter: &gatewayv1.HTTPCORSFilter{
330+
AllowMethods: []gatewayv1.HTTPMethodWithWildcard{
331+
"GET",
332+
"*",
333+
"POST",
334+
},
335+
},
336+
},
337+
{
338+
name: "Using an array of valid methods should be accepted",
339+
wantErrors: nil,
340+
corsfilter: &gatewayv1.HTTPCORSFilter{
341+
AllowMethods: []gatewayv1.HTTPMethodWithWildcard{
342+
"GET",
343+
"OPTIONS",
344+
"POST",
345+
},
346+
},
347+
},
348+
}
349+
350+
for _, tc := range tests {
351+
t.Run(tc.name, func(t *testing.T) {
352+
route := &gatewayv1.HTTPRoute{
353+
ObjectMeta: metav1.ObjectMeta{
354+
Name: fmt.Sprintf("foo-%v", time.Now().UnixNano()),
355+
Namespace: metav1.NamespaceDefault,
356+
},
357+
Spec: gatewayv1.HTTPRouteSpec{Rules: []gatewayv1.HTTPRouteRule{
358+
{
359+
Filters: []gatewayv1.HTTPRouteFilter{
360+
{
361+
Type: gatewayv1.HTTPRouteFilterCORS,
362+
CORS: tc.corsfilter,
363+
},
364+
},
365+
},
366+
}},
367+
}
368+
validateHTTPRoute(t, route, tc.wantErrors)
369+
})
370+
}
371+
}
372+
244373
func TestHTTPRouteTimeouts(t *testing.T) {
245374
tests := []struct {
246375
name string

0 commit comments

Comments
 (0)