Commit cf5a29b0 authored by Jacob Vosmaer's avatar Jacob Vosmaer

Prevent internal API responses from leaking

parent 69735eaf
package api
import (
"fmt"
"net/http"
"gitlab.com/gitlab-org/gitlab-workhorse/internal/helper"
)
func Block(h http.Handler) http.Handler {
return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
rw := &blocker{rw: w}
defer rw.Flush()
h.ServeHTTP(rw, r)
})
}
type blocker struct {
rw http.ResponseWriter
hijacked bool
status int
}
func (b *blocker) Header() http.Header {
return b.rw.Header()
}
func (b *blocker) Write(data []byte) (int, error) {
if b.status == 0 {
b.WriteHeader(http.StatusOK)
}
if b.hijacked {
return 0, nil
}
return b.rw.Write(data)
}
func (b *blocker) WriteHeader(status int) {
if b.status != 0 {
return
}
if b.Header().Get("Content-Type") == ResponseContentType {
b.status = 500
b.Header().Del("Content-Length")
b.hijacked = true
helper.Fail500(b.rw, fmt.Errorf("api.blocker: forbidden content-type: %q", ResponseContentType))
return
}
b.status = status
b.rw.WriteHeader(b.status)
}
func (b *blocker) Flush() {
b.WriteHeader(http.StatusOK)
}
...@@ -42,11 +42,13 @@ func (u *Upstream) configureRoutes() { ...@@ -42,11 +42,13 @@ func (u *Upstream) configureRoutes() {
) )
static := &staticpages.Static{u.DocumentRoot} static := &staticpages.Static{u.DocumentRoot}
proxy := senddata.SendData( proxy := senddata.SendData(
sendfile.SendFile(proxypkg.NewProxy( sendfile.SendFile(
u.Backend, apipkg.Block(
u.Version, proxypkg.NewProxy(
u.RoundTripper, u.Backend,
)), u.Version,
u.RoundTripper,
))),
git.SendArchive, git.SendArchive,
git.SendBlob, git.SendBlob,
git.SendDiff, git.SendDiff,
......
...@@ -330,8 +330,14 @@ func TestDownloadCacheCreate(t *testing.T) { ...@@ -330,8 +330,14 @@ func TestDownloadCacheCreate(t *testing.T) {
func TestRegularProjectsAPI(t *testing.T) { func TestRegularProjectsAPI(t *testing.T) {
apiResponse := "API RESPONSE" apiResponse := "API RESPONSE"
ts := testAuthServer(nil, 200, apiResponse)
ts := testhelper.TestServerWithHandler(regexp.MustCompile(`.`), func(w http.ResponseWriter, _ *http.Request) {
if _, err := w.Write([]byte(apiResponse)); err != nil {
t.Fatalf("write upstream response: %v", err)
}
})
defer ts.Close() defer ts.Close()
ws := startWorkhorseServer(ts.URL) ws := startWorkhorseServer(ts.URL)
defer ws.Close() defer ws.Close()
...@@ -737,6 +743,39 @@ func TestGetGitPatch(t *testing.T) { ...@@ -737,6 +743,39 @@ func TestGetGitPatch(t *testing.T) {
} }
} }
func TestApiContentTypeBlock(t *testing.T) {
wrongResponse := `{"hello":"world"}`
ts := testhelper.TestServerWithHandler(regexp.MustCompile(`.`), func(w http.ResponseWriter, _ *http.Request) {
w.Header().Set("Content-Type", api.ResponseContentType)
if _, err := w.Write([]byte(wrongResponse)); err != nil {
t.Fatalf("write upstream response: %v", err)
}
})
defer ts.Close()
ws := startWorkhorseServer(ts.URL)
defer ws.Close()
resourcePath := "/something"
resp, err := http.Get(ws.URL + resourcePath)
if err != nil {
t.Error(err)
}
defer resp.Body.Close()
if resp.StatusCode != 500 {
t.Errorf("GET %q: expected 500, got %d", resourcePath, resp.StatusCode)
}
body, err := ioutil.ReadAll(resp.Body)
if err != nil {
t.Fatal(err)
}
if strings.Contains(string(body), "world") {
t.Errorf("unexpected response body: %q", body)
}
}
func setupStaticFile(fpath, content string) error { func setupStaticFile(fpath, content string) error {
cwd, err := os.Getwd() cwd, err := os.Getwd()
if err != nil { if err != nil {
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment