diff --git a/get_test.go b/get_test.go index c8047bc..7739fb1 100644 --- a/get_test.go +++ b/get_test.go @@ -56,7 +56,8 @@ func TestGet(t *testing.T) { URL: "yes", ResHeader: map[string]string{ "Content-Length": "5", - "Content-Disposition": `inline;filename="file.jpg\"evil"`, + "Content-Type": "application/octet-stream", + "Content-Disposition": `attachment;filename="file.jpg\"evil"`, }, Code: http.StatusOK, ResBody: "hello", @@ -70,9 +71,6 @@ func TestGet(t *testing.T) { SubTest(t, "EmptyDownload", func(t *testing.T, store *MockFullDataStore) { store.EXPECT().GetInfo("yes").Return(FileInfo{ Offset: 0, - MetaData: map[string]string{ - "filename": "file.jpg\"evil", - }, }, nil) handler, _ := NewHandler(Config{ @@ -84,7 +82,7 @@ func TestGet(t *testing.T) { URL: "yes", ResHeader: map[string]string{ "Content-Length": "0", - "Content-Disposition": `inline;filename="file.jpg\"evil"`, + "Content-Disposition": `attachment`, }, Code: http.StatusNoContent, ResBody: "", diff --git a/unrouted_handler.go b/unrouted_handler.go index 2285e58..8c6e286 100644 --- a/unrouted_handler.go +++ b/unrouted_handler.go @@ -542,17 +542,26 @@ func (handler *UnroutedHandler) GetFile(w http.ResponseWriter, r *http.Request) // Set headers before sending responses w.Header().Set("Content-Length", strconv.FormatInt(info.Offset, 10)) + w.Header().Set("Content-Type", "application/octet-stream") + + // Force browsers to download the file instead of displaying it inline. + // Otherwise someone could upload malicious HTML which would then be + // executed if a victim visits this URL. + // See https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/Content-Disposition + // TODO: Consider lifting this limitation and allow e.g. images to be shown + // TODO: Consider pulling Content-Type from meta data if filename, ok := info.MetaData["filename"]; ok { - w.Header().Set("Content-Disposition", "inline;filename="+strconv.Quote(filename)) + w.Header().Set("Content-Disposition", "attachment;filename="+strconv.Quote(filename)) + } else { + w.Header().Set("Content-Disposition", "attachment") } - // Do not do anything if no data is stored yet. + // If no data has been uploaded yet, respond with an empty "204 No Content" status. if info.Offset == 0 { handler.sendResp(w, r, http.StatusNoContent) return } - // Get reader src, err := handler.composer.GetReader.GetReader(id) if err != nil { handler.sendError(w, r, err)