Skip to content

Commit 8f35c3b

Browse files
committed
more serious CORS tests.
this commit introduces more serious CORS tests that check status response codes, and run real HTTP requests. License: MIT Signed-off-by: Juan Batiz-Benet <[email protected]>
1 parent d5f94be commit 8f35c3b

File tree

2 files changed

+342
-51
lines changed

2 files changed

+342
-51
lines changed

commands/http/handler_test.go

Lines changed: 309 additions & 50 deletions
Original file line numberDiff line numberDiff line change
@@ -3,79 +3,338 @@ package http
33
import (
44
"net/http"
55
"net/http/httptest"
6+
"net/url"
67
"testing"
78

89
cors "github.com/ipfs/go-ipfs/Godeps/_workspace/src/github.com/rs/cors"
910

10-
"github.com/ipfs/go-ipfs/commands"
11+
cmds "github.com/ipfs/go-ipfs/commands"
12+
ipfscmd "github.com/ipfs/go-ipfs/core/commands"
13+
coremock "github.com/ipfs/go-ipfs/core/mock"
1114
)
1215

1316
func assertHeaders(t *testing.T, resHeaders http.Header, reqHeaders map[string]string) {
1417
for name, value := range reqHeaders {
1518
if resHeaders.Get(name) != value {
16-
t.Errorf("Invalid header `%s', wanted `%s', got `%s'", name, value, resHeaders.Get(name))
19+
t.Errorf("Invalid header '%s', wanted '%s', got '%s'", name, value, resHeaders.Get(name))
1720
}
1821
}
1922
}
2023

21-
func originCfg(origin string) *ServerConfig {
24+
func assertStatus(t *testing.T, actual, expected int) {
25+
if actual != expected {
26+
t.Errorf("Expected status: %d got: %d", expected, actual)
27+
}
28+
}
29+
30+
func originCfg(origins []string) *ServerConfig {
2231
return &ServerConfig{
2332
CORSOpts: &cors.Options{
24-
AllowedOrigins: []string{origin},
33+
AllowedOrigins: origins,
34+
},
35+
}
36+
}
37+
38+
type testCase struct {
39+
Method string
40+
Path string
41+
Code int
42+
Origin string
43+
Referer string
44+
AllowOrigins []string
45+
ReqHeaders map[string]string
46+
ResHeaders map[string]string
47+
}
48+
49+
func getTestServer(t *testing.T, origins []string) *httptest.Server {
50+
cmdsCtx, err := coremock.MockCmdsCtx()
51+
if err != nil {
52+
t.Error("failure to initialize mock cmds ctx", err)
53+
return nil
54+
}
55+
56+
cmdRoot := &cmds.Command{
57+
Subcommands: map[string]*cmds.Command{
58+
"version": ipfscmd.VersionCmd,
2559
},
2660
}
61+
62+
handler := NewHandler(cmdsCtx, cmdRoot, originCfg(origins))
63+
return httptest.NewServer(handler)
2764
}
2865

29-
func TestDisallowedOrigin(t *testing.T) {
30-
res := httptest.NewRecorder()
31-
req, _ := http.NewRequest("GET", "http://example.com/foo", nil)
32-
req.Header.Add("Origin", "http://barbaz.com")
33-
34-
handler := NewHandler(commands.Context{}, nil, originCfg(""))
35-
handler.ServeHTTP(res, req)
36-
37-
assertHeaders(t, res.Header(), map[string]string{
38-
"Access-Control-Allow-Origin": "",
39-
"Access-Control-Allow-Methods": "",
40-
"Access-Control-Allow-Credentials": "",
41-
"Access-Control-Max-Age": "",
42-
"Access-Control-Expose-Headers": "",
43-
})
66+
func (tc *testCase) test(t *testing.T) {
67+
// defaults
68+
method := tc.Method
69+
if method == "" {
70+
method = "GET"
71+
}
72+
73+
path := tc.Path
74+
if path == "" {
75+
path = "/api/v0/version"
76+
}
77+
78+
expectCode := tc.Code
79+
if expectCode == 0 {
80+
expectCode = 200
81+
}
82+
83+
// request
84+
req, err := http.NewRequest(method, path, nil)
85+
if err != nil {
86+
t.Error(err)
87+
return
88+
}
89+
90+
for k, v := range tc.ReqHeaders {
91+
req.Header.Add(k, v)
92+
}
93+
if tc.Origin != "" {
94+
req.Header.Add("Origin", tc.Origin)
95+
}
96+
if tc.Referer != "" {
97+
req.Header.Add("Referer", tc.Referer)
98+
}
99+
100+
// server
101+
server := getTestServer(t, tc.AllowOrigins)
102+
if server == nil {
103+
return
104+
}
105+
defer server.Close()
106+
107+
req.URL, err = url.Parse(server.URL + path)
108+
if err != nil {
109+
t.Error(err)
110+
return
111+
}
112+
113+
res, err := http.DefaultClient.Do(req)
114+
if err != nil {
115+
t.Error(err)
116+
return
117+
}
118+
119+
// checks
120+
t.Log("GET", server.URL+path, req.Header, res.Header)
121+
assertHeaders(t, res.Header, tc.ResHeaders)
122+
assertStatus(t, res.StatusCode, expectCode)
123+
}
124+
125+
func TestDisallowedOrigins(t *testing.T) {
126+
gtc := func(origin string, allowedOrigins []string) testCase {
127+
return testCase{
128+
Origin: origin,
129+
AllowOrigins: allowedOrigins,
130+
ResHeaders: map[string]string{
131+
ACAOrigin: "",
132+
ACAMethods: "",
133+
ACACredentials: "",
134+
"Access-Control-Max-Age": "",
135+
"Access-Control-Expose-Headers": "",
136+
},
137+
Code: http.StatusForbidden,
138+
}
139+
}
140+
141+
tcs := []testCase{
142+
gtc("http://barbaz.com", nil),
143+
gtc("http://barbaz.com", []string{"http://localhost"}),
144+
gtc("http://127.0.0.1", []string{"http://localhost"}),
145+
gtc("http://localhost", []string{"http://127.0.0.1"}),
146+
gtc("http://127.0.0.1:1234", nil),
147+
gtc("http://localhost:1234", nil),
148+
}
149+
150+
for _, tc := range tcs {
151+
tc.test(t)
152+
}
153+
}
154+
155+
func TestAllowedOrigins(t *testing.T) {
156+
gtc := func(origin string, allowedOrigins []string) testCase {
157+
return testCase{
158+
Origin: origin,
159+
AllowOrigins: allowedOrigins,
160+
ResHeaders: map[string]string{
161+
ACAOrigin: origin,
162+
ACAMethods: "",
163+
ACACredentials: "",
164+
"Access-Control-Max-Age": "",
165+
"Access-Control-Expose-Headers": "",
166+
},
167+
Code: http.StatusOK,
168+
}
169+
}
170+
171+
tcs := []testCase{
172+
gtc("http://barbaz.com", []string{"http://barbaz.com", "http://localhost"}),
173+
gtc("http://localhost", []string{"http://barbaz.com", "http://localhost"}),
174+
gtc("http://localhost", nil),
175+
gtc("http://127.0.0.1", nil),
176+
}
177+
178+
for _, tc := range tcs {
179+
tc.test(t)
180+
}
44181
}
45182

46183
func TestWildcardOrigin(t *testing.T) {
47-
res := httptest.NewRecorder()
48-
req, _ := http.NewRequest("GET", "http://example.com/foo", nil)
49-
req.Header.Add("Origin", "http://foobar.com")
50-
51-
handler := NewHandler(commands.Context{}, nil, originCfg("*"))
52-
handler.ServeHTTP(res, req)
53-
54-
assertHeaders(t, res.Header(), map[string]string{
55-
"Access-Control-Allow-Origin": "http://foobar.com",
56-
"Access-Control-Allow-Methods": "",
57-
"Access-Control-Allow-Headers": "",
58-
"Access-Control-Allow-Credentials": "",
59-
"Access-Control-Max-Age": "",
60-
"Access-Control-Expose-Headers": "",
61-
})
184+
gtc := func(origin string, allowedOrigins []string) testCase {
185+
return testCase{
186+
Origin: origin,
187+
AllowOrigins: allowedOrigins,
188+
ResHeaders: map[string]string{
189+
ACAOrigin: origin,
190+
ACAMethods: "",
191+
ACACredentials: "",
192+
"Access-Control-Max-Age": "",
193+
"Access-Control-Expose-Headers": "",
194+
},
195+
Code: http.StatusOK,
196+
}
197+
}
198+
199+
tcs := []testCase{
200+
gtc("http://barbaz.com", []string{"*"}),
201+
gtc("http://barbaz.com", []string{"http://localhost", "*"}),
202+
gtc("http://127.0.0.1", []string{"http://localhost", "*"}),
203+
gtc("http://localhost", []string{"http://127.0.0.1", "*"}),
204+
gtc("http://127.0.0.1", []string{"*"}),
205+
gtc("http://localhost", []string{"*"}),
206+
gtc("http://127.0.0.1:1234", []string{"*"}),
207+
gtc("http://localhost:1234", []string{"*"}),
208+
}
209+
210+
for _, tc := range tcs {
211+
tc.test(t)
212+
}
213+
}
214+
215+
func TestDisallowedReferer(t *testing.T) {
216+
gtc := func(referer string, allowedOrigins []string) testCase {
217+
return testCase{
218+
Origin: "http://localhost",
219+
Referer: referer,
220+
AllowOrigins: allowedOrigins,
221+
ResHeaders: map[string]string{
222+
ACAOrigin: "http://localhost",
223+
ACAMethods: "",
224+
ACACredentials: "",
225+
"Access-Control-Max-Age": "",
226+
"Access-Control-Expose-Headers": "",
227+
},
228+
Code: http.StatusForbidden,
229+
}
230+
}
231+
232+
tcs := []testCase{
233+
gtc("http://foobar.com", nil),
234+
gtc("http://localhost:1234", nil),
235+
gtc("http://127.0.0.1:1234", nil),
236+
}
237+
238+
for _, tc := range tcs {
239+
tc.test(t)
240+
}
241+
}
242+
243+
func TestAllowedReferer(t *testing.T) {
244+
gtc := func(referer string, allowedOrigins []string) testCase {
245+
return testCase{
246+
Origin: "http://localhost",
247+
AllowOrigins: allowedOrigins,
248+
ResHeaders: map[string]string{
249+
ACAOrigin: "http://localhost",
250+
ACAMethods: "",
251+
ACACredentials: "",
252+
"Access-Control-Max-Age": "",
253+
"Access-Control-Expose-Headers": "",
254+
},
255+
Code: http.StatusOK,
256+
}
257+
}
258+
259+
tcs := []testCase{
260+
gtc("http://barbaz.com", []string{"http://barbaz.com", "http://localhost"}),
261+
gtc("http://localhost", []string{"http://barbaz.com", "http://localhost"}),
262+
gtc("http://localhost", nil),
263+
gtc("http://127.0.0.1", nil),
264+
}
265+
266+
for _, tc := range tcs {
267+
tc.test(t)
268+
}
269+
}
270+
271+
func TestWildcardReferer(t *testing.T) {
272+
gtc := func(origin string, allowedOrigins []string) testCase {
273+
return testCase{
274+
Origin: origin,
275+
AllowOrigins: allowedOrigins,
276+
ResHeaders: map[string]string{
277+
ACAOrigin: origin,
278+
ACAMethods: "",
279+
ACACredentials: "",
280+
"Access-Control-Max-Age": "",
281+
"Access-Control-Expose-Headers": "",
282+
},
283+
Code: http.StatusOK,
284+
}
285+
}
286+
287+
tcs := []testCase{
288+
gtc("http://barbaz.com", []string{"*"}),
289+
gtc("http://barbaz.com", []string{"http://localhost", "*"}),
290+
gtc("http://127.0.0.1", []string{"http://localhost", "*"}),
291+
gtc("http://localhost", []string{"http://127.0.0.1", "*"}),
292+
gtc("http://127.0.0.1", []string{"*"}),
293+
gtc("http://localhost", []string{"*"}),
294+
gtc("http://127.0.0.1:1234", []string{"*"}),
295+
gtc("http://localhost:1234", []string{"*"}),
296+
}
297+
298+
for _, tc := range tcs {
299+
tc.test(t)
300+
}
62301
}
63302

64303
func TestAllowedMethod(t *testing.T) {
65-
res := httptest.NewRecorder()
66-
req, _ := http.NewRequest("OPTIONS", "http://example.com/foo", nil)
67-
req.Header.Add("Origin", "http://www.foobar.com")
68-
req.Header.Add("Access-Control-Request-Method", "PUT")
69-
70-
handler := NewHandler(commands.Context{}, nil, originCfg("http://www.foobar.com"))
71-
handler.ServeHTTP(res, req)
72-
73-
assertHeaders(t, res.Header(), map[string]string{
74-
"Access-Control-Allow-Origin": "http://www.foobar.com",
75-
"Access-Control-Allow-Methods": "PUT",
76-
"Access-Control-Allow-Headers": "",
77-
"Access-Control-Allow-Credentials": "",
78-
"Access-Control-Max-Age": "",
79-
"Access-Control-Expose-Headers": "",
80-
})
304+
gtc := func(method string, ok bool) testCase {
305+
code := http.StatusOK
306+
hdrs := map[string]string{
307+
ACAOrigin: "http://localhost",
308+
ACAMethods: method,
309+
ACACredentials: "",
310+
"Access-Control-Max-Age": "",
311+
"Access-Control-Expose-Headers": "",
312+
}
313+
314+
if !ok {
315+
hdrs[ACAOrigin] = ""
316+
hdrs[ACAMethods] = ""
317+
}
318+
319+
return testCase{
320+
Method: "OPTIONS",
321+
Origin: "http://localhost",
322+
AllowOrigins: []string{"*"},
323+
ReqHeaders: map[string]string{
324+
"Access-Control-Request-Method": method,
325+
},
326+
ResHeaders: hdrs,
327+
Code: code,
328+
}
329+
}
330+
331+
tcs := []testCase{
332+
gtc("PUT", true),
333+
gtc("GET", true),
334+
gtc("FOOBAR", false),
335+
}
336+
337+
for _, tc := range tcs {
338+
tc.test(t)
339+
}
81340
}

0 commit comments

Comments
 (0)