@@ -11,106 +11,190 @@ import (
1111)
1212
1313func TestCORS (t * testing.T ) {
14- e := echo .New ()
14+ var testCases = []struct {
15+ name string
16+ givenMW echo.MiddlewareFunc
17+ whenMethod string
18+ whenHeaders map [string ]string
19+ expectHeaders map [string ]string
20+ notExpectHeaders map [string ]string
21+ }{
22+ {
23+ name : "ok, wildcard origin" ,
24+ whenHeaders : map [string ]string {echo .HeaderOrigin : "localhost" },
25+ expectHeaders : map [string ]string {echo .HeaderAccessControlAllowOrigin : "*" },
26+ },
27+ {
28+ name : "ok, wildcard AllowedOrigin with no Origin header in request" ,
29+ notExpectHeaders : map [string ]string {echo .HeaderAccessControlAllowOrigin : "" },
30+ },
31+ {
32+ name : "ok, specific AllowOrigins and AllowCredentials" ,
33+ givenMW : CORSWithConfig (CORSConfig {
34+ AllowOrigins : []string {"localhost" },
35+ AllowCredentials : true ,
36+ MaxAge : 3600 ,
37+ }),
38+ whenHeaders : map [string ]string {echo .HeaderOrigin : "localhost" },
39+ expectHeaders : map [string ]string {
40+ echo .HeaderAccessControlAllowOrigin : "localhost" ,
41+ echo .HeaderAccessControlAllowCredentials : "true" ,
42+ },
43+ },
44+ {
45+ name : "ok, preflight request with matching origin for `AllowOrigins`" ,
46+ givenMW : CORSWithConfig (CORSConfig {
47+ AllowOrigins : []string {"localhost" },
48+ AllowCredentials : true ,
49+ MaxAge : 3600 ,
50+ }),
51+ whenMethod : http .MethodOptions ,
52+ whenHeaders : map [string ]string {
53+ echo .HeaderOrigin : "localhost" ,
54+ echo .HeaderContentType : echo .MIMEApplicationJSON ,
55+ },
56+ expectHeaders : map [string ]string {
57+ echo .HeaderAccessControlAllowOrigin : "localhost" ,
58+ echo .HeaderAccessControlAllowMethods : "GET,HEAD,PUT,PATCH,POST,DELETE" ,
59+ echo .HeaderAccessControlAllowCredentials : "true" ,
60+ echo .HeaderAccessControlMaxAge : "3600" ,
61+ },
62+ },
63+ {
64+ name : "ok, preflight request with wildcard `AllowOrigins` and `AllowCredentials` true" ,
65+ givenMW : CORSWithConfig (CORSConfig {
66+ AllowOrigins : []string {"*" },
67+ AllowCredentials : true ,
68+ MaxAge : 3600 ,
69+ }),
70+ whenMethod : http .MethodOptions ,
71+ whenHeaders : map [string ]string {
72+ echo .HeaderOrigin : "localhost" ,
73+ echo .HeaderContentType : echo .MIMEApplicationJSON ,
74+ },
75+ expectHeaders : map [string ]string {
76+ echo .HeaderAccessControlAllowOrigin : "*" , // Note: browsers will ignore and complain about responses having `*`
77+ echo .HeaderAccessControlAllowMethods : "GET,HEAD,PUT,PATCH,POST,DELETE" ,
78+ echo .HeaderAccessControlAllowCredentials : "true" ,
79+ echo .HeaderAccessControlMaxAge : "3600" ,
80+ },
81+ },
82+ {
83+ name : "ok, preflight request with wildcard `AllowOrigins` and `AllowCredentials` false" ,
84+ givenMW : CORSWithConfig (CORSConfig {
85+ AllowOrigins : []string {"*" },
86+ AllowCredentials : false , // important for this testcase
87+ MaxAge : 3600 ,
88+ }),
89+ whenMethod : http .MethodOptions ,
90+ whenHeaders : map [string ]string {
91+ echo .HeaderOrigin : "localhost" ,
92+ echo .HeaderContentType : echo .MIMEApplicationJSON ,
93+ },
94+ expectHeaders : map [string ]string {
95+ echo .HeaderAccessControlAllowOrigin : "*" ,
96+ echo .HeaderAccessControlAllowMethods : "GET,HEAD,PUT,PATCH,POST,DELETE" ,
97+ echo .HeaderAccessControlMaxAge : "3600" ,
98+ },
99+ notExpectHeaders : map [string ]string {
100+ echo .HeaderAccessControlAllowCredentials : "" ,
101+ },
102+ },
103+ {
104+ name : "ok, INSECURE preflight request with wildcard `AllowOrigins` and `AllowCredentials` true" ,
105+ givenMW : CORSWithConfig (CORSConfig {
106+ AllowOrigins : []string {"*" },
107+ AllowCredentials : true ,
108+ UnsafeWildcardOriginWithAllowCredentials : true , // important for this testcase
109+ MaxAge : 3600 ,
110+ }),
111+ whenMethod : http .MethodOptions ,
112+ whenHeaders : map [string ]string {
113+ echo .HeaderOrigin : "localhost" ,
114+ echo .HeaderContentType : echo .MIMEApplicationJSON ,
115+ },
116+ expectHeaders : map [string ]string {
117+ echo .HeaderAccessControlAllowOrigin : "localhost" , // This could end up as cross-origin attack
118+ echo .HeaderAccessControlAllowMethods : "GET,HEAD,PUT,PATCH,POST,DELETE" ,
119+ echo .HeaderAccessControlAllowCredentials : "true" ,
120+ echo .HeaderAccessControlMaxAge : "3600" ,
121+ },
122+ },
123+ {
124+ name : "ok, preflight request with Access-Control-Request-Headers" ,
125+ givenMW : CORSWithConfig (CORSConfig {
126+ AllowOrigins : []string {"*" },
127+ }),
128+ whenMethod : http .MethodOptions ,
129+ whenHeaders : map [string ]string {
130+ echo .HeaderOrigin : "localhost" ,
131+ echo .HeaderContentType : echo .MIMEApplicationJSON ,
132+ echo .HeaderAccessControlRequestHeaders : "Special-Request-Header" ,
133+ },
134+ expectHeaders : map [string ]string {
135+ echo .HeaderAccessControlAllowOrigin : "*" ,
136+ echo .HeaderAccessControlAllowHeaders : "Special-Request-Header" ,
137+ echo .HeaderAccessControlAllowMethods : "GET,HEAD,PUT,PATCH,POST,DELETE" ,
138+ },
139+ },
140+ {
141+ name : "ok, preflight request with `AllowOrigins` which allow all subdomains aaa with *" ,
142+ givenMW : CORSWithConfig (CORSConfig {
143+ AllowOrigins : []string {"http://*.example.com" },
144+ }),
145+ whenMethod : http .MethodOptions ,
146+ whenHeaders : map [string ]string {echo .HeaderOrigin : "http://aaa.example.com" },
147+ expectHeaders : map [string ]string {echo .HeaderAccessControlAllowOrigin : "http://aaa.example.com" },
148+ },
149+ {
150+ name : "ok, preflight request with `AllowOrigins` which allow all subdomains bbb with *" ,
151+ givenMW : CORSWithConfig (CORSConfig {
152+ AllowOrigins : []string {"http://*.example.com" },
153+ }),
154+ whenMethod : http .MethodOptions ,
155+ whenHeaders : map [string ]string {echo .HeaderOrigin : "http://bbb.example.com" },
156+ expectHeaders : map [string ]string {echo .HeaderAccessControlAllowOrigin : "http://bbb.example.com" },
157+ },
158+ }
159+ for _ , tc := range testCases {
160+ t .Run (tc .name , func (t * testing.T ) {
161+ e := echo .New ()
162+
163+ mw := CORS ()
164+ if tc .givenMW != nil {
165+ mw = tc .givenMW
166+ }
167+ h := mw (func (c echo.Context ) error {
168+ return nil
169+ })
15170
16- // Wildcard origin
17- req := httptest .NewRequest (http .MethodGet , "/" , nil )
18- rec := httptest .NewRecorder ()
19- c := e .NewContext (req , rec )
20- h := CORS ()(echo .NotFoundHandler )
21- req .Header .Set (echo .HeaderOrigin , "localhost" )
22- h (c )
23- assert .Equal (t , "*" , rec .Header ().Get (echo .HeaderAccessControlAllowOrigin ))
24-
25- // Wildcard AllowedOrigin with no Origin header in request
26- req = httptest .NewRequest (http .MethodGet , "/" , nil )
27- rec = httptest .NewRecorder ()
28- c = e .NewContext (req , rec )
29- h = CORS ()(echo .NotFoundHandler )
30- h (c )
31- assert .NotContains (t , rec .Header (), echo .HeaderAccessControlAllowOrigin )
32-
33- // Allow origins
34- req = httptest .NewRequest (http .MethodGet , "/" , nil )
35- rec = httptest .NewRecorder ()
36- c = e .NewContext (req , rec )
37- h = CORSWithConfig (CORSConfig {
38- AllowOrigins : []string {"localhost" },
39- AllowCredentials : true ,
40- MaxAge : 3600 ,
41- })(echo .NotFoundHandler )
42- req .Header .Set (echo .HeaderOrigin , "localhost" )
43- h (c )
44- assert .Equal (t , "localhost" , rec .Header ().Get (echo .HeaderAccessControlAllowOrigin ))
45- assert .Equal (t , "true" , rec .Header ().Get (echo .HeaderAccessControlAllowCredentials ))
46-
47- // Preflight request
48- req = httptest .NewRequest (http .MethodOptions , "/" , nil )
49- rec = httptest .NewRecorder ()
50- c = e .NewContext (req , rec )
51- req .Header .Set (echo .HeaderOrigin , "localhost" )
52- req .Header .Set (echo .HeaderContentType , echo .MIMEApplicationJSON )
53- cors := CORSWithConfig (CORSConfig {
54- AllowOrigins : []string {"localhost" },
55- AllowCredentials : true ,
56- MaxAge : 3600 ,
57- })
58- h = cors (echo .NotFoundHandler )
59- h (c )
60- assert .Equal (t , "localhost" , rec .Header ().Get (echo .HeaderAccessControlAllowOrigin ))
61- assert .NotEmpty (t , rec .Header ().Get (echo .HeaderAccessControlAllowMethods ))
62- assert .Equal (t , "true" , rec .Header ().Get (echo .HeaderAccessControlAllowCredentials ))
63- assert .Equal (t , "3600" , rec .Header ().Get (echo .HeaderAccessControlMaxAge ))
64-
65- // Preflight request with `AllowOrigins` *
66- req = httptest .NewRequest (http .MethodOptions , "/" , nil )
67- rec = httptest .NewRecorder ()
68- c = e .NewContext (req , rec )
69- req .Header .Set (echo .HeaderOrigin , "localhost" )
70- req .Header .Set (echo .HeaderContentType , echo .MIMEApplicationJSON )
71- cors = CORSWithConfig (CORSConfig {
72- AllowOrigins : []string {"*" },
73- AllowCredentials : true ,
74- MaxAge : 3600 ,
75- })
76- h = cors (echo .NotFoundHandler )
77- h (c )
78- assert .Equal (t , "localhost" , rec .Header ().Get (echo .HeaderAccessControlAllowOrigin ))
79- assert .NotEmpty (t , rec .Header ().Get (echo .HeaderAccessControlAllowMethods ))
80- assert .Equal (t , "true" , rec .Header ().Get (echo .HeaderAccessControlAllowCredentials ))
81- assert .Equal (t , "3600" , rec .Header ().Get (echo .HeaderAccessControlMaxAge ))
82-
83- // Preflight request with Access-Control-Request-Headers
84- req = httptest .NewRequest (http .MethodOptions , "/" , nil )
85- rec = httptest .NewRecorder ()
86- c = e .NewContext (req , rec )
87- req .Header .Set (echo .HeaderOrigin , "localhost" )
88- req .Header .Set (echo .HeaderContentType , echo .MIMEApplicationJSON )
89- req .Header .Set (echo .HeaderAccessControlRequestHeaders , "Special-Request-Header" )
90- cors = CORSWithConfig (CORSConfig {
91- AllowOrigins : []string {"*" },
92- })
93- h = cors (echo .NotFoundHandler )
94- h (c )
95- assert .Equal (t , "*" , rec .Header ().Get (echo .HeaderAccessControlAllowOrigin ))
96- assert .Equal (t , "Special-Request-Header" , rec .Header ().Get (echo .HeaderAccessControlAllowHeaders ))
97- assert .NotEmpty (t , rec .Header ().Get (echo .HeaderAccessControlAllowMethods ))
98-
99- // Preflight request with `AllowOrigins` which allow all subdomains with *
100- req = httptest .NewRequest (http .MethodOptions , "/" , nil )
101- rec = httptest .NewRecorder ()
102- c = e .NewContext (req , rec )
103- req .Header .Set (echo .HeaderOrigin , "http://aaa.example.com" )
104- cors = CORSWithConfig (CORSConfig {
105- AllowOrigins : []string {"http://*.example.com" },
106- })
107- h = cors (echo .NotFoundHandler )
108- h (c )
109- assert .Equal (t , "http://aaa.example.com" , rec .Header ().Get (echo .HeaderAccessControlAllowOrigin ))
110-
111- req .Header .Set (echo .HeaderOrigin , "http://bbb.example.com" )
112- h (c )
113- assert .Equal (t , "http://bbb.example.com" , rec .Header ().Get (echo .HeaderAccessControlAllowOrigin ))
171+ method := http .MethodGet
172+ if tc .whenMethod != "" {
173+ method = tc .whenMethod
174+ }
175+ req := httptest .NewRequest (method , "/" , nil )
176+ rec := httptest .NewRecorder ()
177+ c := e .NewContext (req , rec )
178+ for k , v := range tc .whenHeaders {
179+ req .Header .Set (k , v )
180+ }
181+
182+ err := h (c )
183+
184+ assert .NoError (t , err )
185+ header := rec .Header ()
186+ for k , v := range tc .expectHeaders {
187+ assert .Equal (t , v , header .Get (k ), "header: `%v` should be `%v`" , k , v )
188+ }
189+ for k , v := range tc .notExpectHeaders {
190+ if v == "" {
191+ assert .Len (t , header .Values (k ), 0 , "header: `%v` should not be set" , k )
192+ } else {
193+ assert .NotEqual (t , v , header .Get (k ), "header: `%v` should not be `%v`" , k , v )
194+ }
195+ }
196+ })
197+ }
114198}
115199
116200func Test_allowOriginScheme (t * testing.T ) {
0 commit comments