fix: check Origin to match configured CORS Origin
This commit is contained in:
parent
d3ef0e366d
commit
af88b88ea5
|
@ -9,29 +9,10 @@ import (
|
||||||
)
|
)
|
||||||
|
|
||||||
func TestCORS(t *testing.T) {
|
func TestCORS(t *testing.T) {
|
||||||
SubTest(t, "Preflight", func(t *testing.T, store *MockFullDataStore, composer *StoreComposer) {
|
SubTest(t, "PreFlight - Conditional allow methods", func(t *testing.T, store *MockFullDataStore, composer *StoreComposer) {
|
||||||
handler, _ := NewHandler(Config{
|
|
||||||
StoreComposer: composer,
|
|
||||||
})
|
|
||||||
|
|
||||||
(&httpTest{
|
|
||||||
Method: "OPTIONS",
|
|
||||||
ReqHeader: map[string]string{
|
|
||||||
"Origin": "tus.io",
|
|
||||||
},
|
|
||||||
Code: http.StatusOK,
|
|
||||||
ResHeader: map[string]string{
|
|
||||||
"Access-Control-Allow-Headers": "Authorization, Origin, X-Requested-With, X-Request-ID, X-HTTP-Method-Override, Content-Type, Upload-Length, Upload-Offset, Tus-Resumable, Upload-Metadata, Upload-Defer-Length, Upload-Concat",
|
|
||||||
"Access-Control-Allow-Methods": "POST, HEAD, PATCH, OPTIONS, GET, DELETE",
|
|
||||||
"Access-Control-Max-Age": "86400",
|
|
||||||
"Access-Control-Allow-Origin": "tus.io",
|
|
||||||
},
|
|
||||||
}).Run(handler, t)
|
|
||||||
})
|
|
||||||
|
|
||||||
SubTest(t, "Conditional allow methods", func(t *testing.T, store *MockFullDataStore, composer *StoreComposer) {
|
|
||||||
handler, _ := NewHandler(Config{
|
handler, _ := NewHandler(Config{
|
||||||
StoreComposer: composer,
|
StoreComposer: composer,
|
||||||
|
CorsOrigin: "https://tus.io",
|
||||||
DisableTermination: true,
|
DisableTermination: true,
|
||||||
DisableDownload: true,
|
DisableDownload: true,
|
||||||
})
|
})
|
||||||
|
@ -39,37 +20,185 @@ func TestCORS(t *testing.T) {
|
||||||
(&httpTest{
|
(&httpTest{
|
||||||
Method: "OPTIONS",
|
Method: "OPTIONS",
|
||||||
ReqHeader: map[string]string{
|
ReqHeader: map[string]string{
|
||||||
"Origin": "tus.io",
|
"Origin": "https://tus.io",
|
||||||
},
|
},
|
||||||
Code: http.StatusOK,
|
Code: http.StatusOK,
|
||||||
ResHeader: map[string]string{
|
ResHeader: map[string]string{
|
||||||
"Access-Control-Allow-Headers": "Authorization, Origin, X-Requested-With, X-Request-ID, X-HTTP-Method-Override, Content-Type, Upload-Length, Upload-Offset, Tus-Resumable, Upload-Metadata, Upload-Defer-Length, Upload-Concat",
|
"Access-Control-Allow-Headers": "Authorization, Origin, X-Requested-With, X-Request-ID, X-HTTP-Method-Override, Content-Type, Upload-Length, Upload-Offset, Tus-Resumable, Upload-Metadata, Upload-Defer-Length, Upload-Concat",
|
||||||
"Access-Control-Allow-Methods": "POST, HEAD, PATCH, OPTIONS",
|
"Access-Control-Allow-Methods": "POST, HEAD, PATCH, OPTIONS",
|
||||||
"Access-Control-Max-Age": "86400",
|
"Access-Control-Max-Age": "86400",
|
||||||
"Access-Control-Allow-Origin": "tus.io",
|
"Access-Control-Allow-Origin": "https://tus.io",
|
||||||
},
|
},
|
||||||
}).Run(handler, t)
|
}).Run(handler, t)
|
||||||
})
|
})
|
||||||
|
SubTest(t, "PreFlight - No Origin configured", func(t *testing.T, store *MockFullDataStore, composer *StoreComposer) {
|
||||||
|
composer = NewStoreComposer()
|
||||||
|
composer.UseCore(store)
|
||||||
|
|
||||||
SubTest(t, "Request", func(t *testing.T, store *MockFullDataStore, composer *StoreComposer) {
|
|
||||||
handler, _ := NewHandler(Config{
|
handler, _ := NewHandler(Config{
|
||||||
StoreComposer: composer,
|
StoreComposer: composer,
|
||||||
|
CorsOrigin: "",
|
||||||
})
|
})
|
||||||
|
|
||||||
(&httpTest{
|
(&httpTest{
|
||||||
Name: "Actual request",
|
Method: "OPTIONS",
|
||||||
Method: "GET",
|
DisallowedResHeader: []string{
|
||||||
ReqHeader: map[string]string{
|
"Access-Control-Allow-Origin",
|
||||||
"Origin": "tus.io",
|
"Access-Control-Allow-Methods",
|
||||||
|
"Access-Control-Allow-Headers",
|
||||||
|
"Access-Control-Max-Age",
|
||||||
},
|
},
|
||||||
Code: http.StatusMethodNotAllowed,
|
Code: http.StatusOK,
|
||||||
ResHeader: map[string]string{
|
ReqHeader: map[string]string{
|
||||||
"Access-Control-Expose-Headers": "Upload-Offset, Location, Upload-Length, Tus-Version, Tus-Resumable, Tus-Max-Size, Tus-Extension, Upload-Metadata, Upload-Defer-Length, Upload-Concat",
|
"Origin": "https://tus.io",
|
||||||
"Access-Control-Allow-Origin": "tus.io",
|
|
||||||
},
|
},
|
||||||
}).Run(handler, t)
|
}).Run(handler, t)
|
||||||
})
|
})
|
||||||
|
SubTest(t, "PreFlight - Disabled CORS", func(t *testing.T, store *MockFullDataStore, composer *StoreComposer) {
|
||||||
|
composer = NewStoreComposer()
|
||||||
|
composer.UseCore(store)
|
||||||
|
|
||||||
|
handler, _ := NewHandler(Config{
|
||||||
|
StoreComposer: composer,
|
||||||
|
CorsOrigin: "",
|
||||||
|
DisableCors: true,
|
||||||
|
})
|
||||||
|
|
||||||
|
(&httpTest{
|
||||||
|
Method: "OPTIONS",
|
||||||
|
DisallowedResHeader: []string{
|
||||||
|
"Access-Control-Allow-Origin",
|
||||||
|
"Access-Control-Allow-Methods",
|
||||||
|
"Access-Control-Allow-Headers",
|
||||||
|
"Access-Control-Max-Age",
|
||||||
|
},
|
||||||
|
Code: http.StatusOK,
|
||||||
|
ReqHeader: map[string]string{
|
||||||
|
"Origin": "https://tus.io",
|
||||||
|
},
|
||||||
|
}).Run(handler, t)
|
||||||
|
})
|
||||||
|
SubTest(t, "PreFlight - Wildcard Origin", func(t *testing.T, store *MockFullDataStore, composer *StoreComposer) {
|
||||||
|
composer = NewStoreComposer()
|
||||||
|
composer.UseCore(store)
|
||||||
|
|
||||||
|
handler, _ := NewHandler(Config{
|
||||||
|
StoreComposer: composer,
|
||||||
|
CorsOrigin: "*",
|
||||||
|
})
|
||||||
|
|
||||||
|
(&httpTest{
|
||||||
|
Method: "OPTIONS",
|
||||||
|
ResHeader: map[string]string{
|
||||||
|
"Access-Control-Allow-Origin": "*",
|
||||||
|
"Access-Control-Allow-Methods": "POST, HEAD, PATCH, OPTIONS, GET, DELETE",
|
||||||
|
"Access-Control-Allow-Headers": "Authorization, Origin, X-Requested-With, X-Request-ID, X-HTTP-Method-Override, Content-Type, Upload-Length, Upload-Offset, Tus-Resumable, Upload-Metadata, Upload-Defer-Length, Upload-Concat",
|
||||||
|
"Access-Control-Max-Age": "86400",
|
||||||
|
},
|
||||||
|
Code: http.StatusOK,
|
||||||
|
ReqHeader: map[string]string{
|
||||||
|
"Origin": "https://tus.io",
|
||||||
|
},
|
||||||
|
}).Run(handler, t)
|
||||||
|
})
|
||||||
|
SubTest(t, "PreFlight - Matching Origin", func(t *testing.T, store *MockFullDataStore, composer *StoreComposer) {
|
||||||
|
composer = NewStoreComposer()
|
||||||
|
composer.UseCore(store)
|
||||||
|
|
||||||
|
handler, _ := NewHandler(Config{
|
||||||
|
StoreComposer: composer,
|
||||||
|
CorsOrigin: "https://tus.io",
|
||||||
|
})
|
||||||
|
|
||||||
|
(&httpTest{
|
||||||
|
Method: "OPTIONS",
|
||||||
|
ResHeader: map[string]string{
|
||||||
|
"Access-Control-Allow-Origin": "https://tus.io",
|
||||||
|
"Access-Control-Allow-Methods": "POST, HEAD, PATCH, OPTIONS, GET, DELETE",
|
||||||
|
"Access-Control-Allow-Headers": "Authorization, Origin, X-Requested-With, X-Request-ID, X-HTTP-Method-Override, Content-Type, Upload-Length, Upload-Offset, Tus-Resumable, Upload-Metadata, Upload-Defer-Length, Upload-Concat",
|
||||||
|
"Access-Control-Max-Age": "86400",
|
||||||
|
},
|
||||||
|
Code: http.StatusOK,
|
||||||
|
ReqHeader: map[string]string{
|
||||||
|
"Origin": "https://tus.io",
|
||||||
|
},
|
||||||
|
}).Run(handler, t)
|
||||||
|
})
|
||||||
|
SubTest(t, "PreFlight - Not Matching Origin", func(t *testing.T, store *MockFullDataStore, composer *StoreComposer) {
|
||||||
|
composer = NewStoreComposer()
|
||||||
|
composer.UseCore(store)
|
||||||
|
|
||||||
|
handler, _ := NewHandler(Config{
|
||||||
|
StoreComposer: composer,
|
||||||
|
CorsOrigin: "https://tus.net",
|
||||||
|
})
|
||||||
|
|
||||||
|
(&httpTest{
|
||||||
|
Method: "OPTIONS",
|
||||||
|
DisallowedResHeader: []string{
|
||||||
|
"Access-Control-Allow-Origin",
|
||||||
|
"Access-Control-Allow-Methods",
|
||||||
|
"Access-Control-Allow-Headers",
|
||||||
|
"Access-Control-Max-Age",
|
||||||
|
},
|
||||||
|
Code: http.StatusOK,
|
||||||
|
ReqHeader: map[string]string{
|
||||||
|
"Origin": "https://tus.io",
|
||||||
|
},
|
||||||
|
}).Run(handler, t)
|
||||||
|
})
|
||||||
|
SubTest(t, "Actual Request - Wildcard Origin", func(t *testing.T, store *MockFullDataStore, composer *StoreComposer) {
|
||||||
|
composer = NewStoreComposer()
|
||||||
|
composer.UseCore(store)
|
||||||
|
|
||||||
|
handler, _ := NewHandler(Config{
|
||||||
|
StoreComposer: composer,
|
||||||
|
CorsOrigin: "*",
|
||||||
|
})
|
||||||
|
|
||||||
|
(&httpTest{
|
||||||
|
Method: "POST",
|
||||||
|
ResHeader: map[string]string{
|
||||||
|
"Access-Control-Allow-Origin": "*",
|
||||||
|
"Access-Control-Expose-Headers": "Upload-Offset, Location, Upload-Length, Tus-Version, Tus-Resumable, Tus-Max-Size, Tus-Extension, Upload-Metadata, Upload-Defer-Length, Upload-Concat",
|
||||||
|
},
|
||||||
|
DisallowedResHeader: []string{
|
||||||
|
"Access-Control-Allow-Methods",
|
||||||
|
"Access-Control-Allow-Headers",
|
||||||
|
"Access-Control-Max-Age",
|
||||||
|
},
|
||||||
|
Code: http.StatusPreconditionFailed,
|
||||||
|
ReqHeader: map[string]string{
|
||||||
|
"Origin": "https://tus.io",
|
||||||
|
},
|
||||||
|
}).Run(handler, t)
|
||||||
|
})
|
||||||
|
SubTest(t, "Actual Request - Matching Origin", func(t *testing.T, store *MockFullDataStore, composer *StoreComposer) {
|
||||||
|
composer = NewStoreComposer()
|
||||||
|
composer.UseCore(store)
|
||||||
|
|
||||||
|
handler, _ := NewHandler(Config{
|
||||||
|
StoreComposer: composer,
|
||||||
|
CorsOrigin: "https://tus.io",
|
||||||
|
})
|
||||||
|
|
||||||
|
(&httpTest{
|
||||||
|
Method: "POST",
|
||||||
|
ResHeader: map[string]string{
|
||||||
|
"Access-Control-Allow-Origin": "https://tus.io",
|
||||||
|
"Access-Control-Expose-Headers": "Upload-Offset, Location, Upload-Length, Tus-Version, Tus-Resumable, Tus-Max-Size, Tus-Extension, Upload-Metadata, Upload-Defer-Length, Upload-Concat",
|
||||||
|
},
|
||||||
|
DisallowedResHeader: []string{
|
||||||
|
"Access-Control-Allow-Methods",
|
||||||
|
"Access-Control-Allow-Headers",
|
||||||
|
"Access-Control-Max-Age",
|
||||||
|
},
|
||||||
|
Code: http.StatusPreconditionFailed,
|
||||||
|
ReqHeader: map[string]string{
|
||||||
|
"Origin": "https://tus.io",
|
||||||
|
},
|
||||||
|
}).Run(handler, t)
|
||||||
|
})
|
||||||
SubTest(t, "AppendHeaders", func(t *testing.T, store *MockFullDataStore, composer *StoreComposer) {
|
SubTest(t, "AppendHeaders", func(t *testing.T, store *MockFullDataStore, composer *StoreComposer) {
|
||||||
handler, _ := NewHandler(Config{
|
handler, _ := NewHandler(Config{
|
||||||
StoreComposer: composer,
|
StoreComposer: composer,
|
||||||
|
@ -77,7 +206,7 @@ func TestCORS(t *testing.T) {
|
||||||
|
|
||||||
req, _ := http.NewRequest("OPTIONS", "", nil)
|
req, _ := http.NewRequest("OPTIONS", "", nil)
|
||||||
req.Header.Set("Tus-Resumable", "1.0.0")
|
req.Header.Set("Tus-Resumable", "1.0.0")
|
||||||
req.Header.Set("Origin", "tus.io")
|
req.Header.Set("Origin", "https://tus.io")
|
||||||
req.Host = "tus.io"
|
req.Host = "tus.io"
|
||||||
|
|
||||||
res := httptest.NewRecorder()
|
res := httptest.NewRecorder()
|
||||||
|
@ -96,20 +225,4 @@ func TestCORS(t *testing.T) {
|
||||||
t.Errorf("expected header to contain METHOD but got: %#v", methods)
|
t.Errorf("expected header to contain METHOD but got: %#v", methods)
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
|
|
||||||
SubTest(t, "Disable CORS", func(t *testing.T, store *MockFullDataStore, composer *StoreComposer) {
|
|
||||||
handler, _ := NewHandler(Config{
|
|
||||||
StoreComposer: composer,
|
|
||||||
DisableCors: true,
|
|
||||||
})
|
|
||||||
|
|
||||||
(&httpTest{
|
|
||||||
Method: "OPTIONS",
|
|
||||||
ReqHeader: map[string]string{
|
|
||||||
"Origin": "tus.io",
|
|
||||||
},
|
|
||||||
Code: http.StatusOK,
|
|
||||||
ResHeader: map[string]string{},
|
|
||||||
}).Run(handler, t)
|
|
||||||
})
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -225,9 +225,10 @@ func (handler *UnroutedHandler) Middleware(h http.Handler) http.Handler {
|
||||||
|
|
||||||
if origin := r.Header.Get("Origin"); !handler.config.DisableCors && origin != "" {
|
if origin := r.Header.Get("Origin"); !handler.config.DisableCors && origin != "" {
|
||||||
var configuredOrigin = handler.config.CorsOrigin
|
var configuredOrigin = handler.config.CorsOrigin
|
||||||
if configuredOrigin != "" {
|
if configuredOrigin == "*" {
|
||||||
origin = configuredOrigin
|
origin = "*"
|
||||||
}
|
}
|
||||||
|
if configuredOrigin == origin {
|
||||||
header.Set("Access-Control-Allow-Origin", origin)
|
header.Set("Access-Control-Allow-Origin", origin)
|
||||||
header.Set("Vary", "Origin")
|
header.Set("Vary", "Origin")
|
||||||
|
|
||||||
|
@ -251,6 +252,7 @@ func (handler *UnroutedHandler) Middleware(h http.Handler) http.Handler {
|
||||||
header.Add("Access-Control-Expose-Headers", "Upload-Offset, Location, Upload-Length, Tus-Version, Tus-Resumable, Tus-Max-Size, Tus-Extension, Upload-Metadata, Upload-Defer-Length, Upload-Concat")
|
header.Add("Access-Control-Expose-Headers", "Upload-Offset, Location, Upload-Length, Tus-Version, Tus-Resumable, Tus-Max-Size, Tus-Extension, Upload-Metadata, Upload-Defer-Length, Upload-Concat")
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
// Set current version used by the server
|
// Set current version used by the server
|
||||||
header.Set("Tus-Resumable", "1.0.0")
|
header.Set("Tus-Resumable", "1.0.0")
|
||||||
|
|
|
@ -55,6 +55,7 @@ type httpTest struct {
|
||||||
Code int
|
Code int
|
||||||
ResBody string
|
ResBody string
|
||||||
ResHeader map[string]string
|
ResHeader map[string]string
|
||||||
|
DisallowedResHeader []string
|
||||||
}
|
}
|
||||||
|
|
||||||
func (test *httpTest) Run(handler http.Handler, t *testing.T) *httptest.ResponseRecorder {
|
func (test *httpTest) Run(handler http.Handler, t *testing.T) *httptest.ResponseRecorder {
|
||||||
|
@ -82,6 +83,14 @@ func (test *httpTest) Run(handler http.Handler, t *testing.T) *httptest.Response
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
for _, key := range test.DisallowedResHeader {
|
||||||
|
header := w.Header().Get(key)
|
||||||
|
|
||||||
|
if header != "" {
|
||||||
|
t.Errorf("Not Expected '%s' (got '%s')", key, header)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
if test.ResBody != "" && w.Body.String() != test.ResBody {
|
if test.ResBody != "" && w.Body.String() != test.ResBody {
|
||||||
t.Errorf("Expected '%s' as body (got '%s'", test.ResBody, w.Body.String())
|
t.Errorf("Expected '%s' as body (got '%s'", test.ResBody, w.Body.String())
|
||||||
}
|
}
|
||||||
|
|
Loading…
Reference in New Issue