@@ -11,106 +11,190 @@ import (
11
11
)
12
12
13
13
func 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
+ })
15
170
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
+ }
114
198
}
115
199
116
200
func Test_allowOriginScheme (t * testing.T ) {
0 commit comments