Commit c487ba45 authored by Stan Hu's avatar Stan Hu

Use application/octet-stream as Content-Type for files in CI artifacts

This eliminates the need for Workhorse to set `Content-Type` by guessing
based on file extension.

If `Content-Disposition` is `attachment`, the browser will prompt the
user for an action. In Chrome, the user is always prompted to save the
file, and the `filename` extension in the `Content-Disposition` header
determines the saved file's default extension. Chrome appears to ignore
`Content-Type` if `Content-Disposition` is an attachment.

In Firefox, the user is prompted with two options: save the file, or
open it with a program. Again, the `filename` extension in the
`Content-Disposition` header determines the saved file's default
extension. In addition, this extension is used to guess which default
program should open the file. If the extension isn't present, Firefox
falls back to `Content-Type`.

Safari combines both approaches. Like Chrome, it only prompts the user
to save the file. Like Firefox, it falls back to the extension provided
in `Content-Type` if the `Content-Disposition` filename is not present.

Hence, Workhorse's setting of `Content-Type` based on extension provides
no additional information and may even be a security risk if the
`Content-Disposition` is subverted.

Changelog: changed
parent e247eac4
......@@ -5,7 +5,6 @@ import (
"context"
"fmt"
"io"
"mime"
"net/http"
"os"
"os/exec"
......@@ -53,14 +52,6 @@ func (e *entry) Inject(w http.ResponseWriter, r *http.Request, sendData string)
}
}
func detectFileContentType(fileName string) string {
contentType := mime.TypeByExtension(filepath.Ext(fileName))
if contentType == "" {
contentType = "application/octet-stream"
}
return contentType
}
func unpackFileFromZip(ctx context.Context, archivePath, encodedFilename string, headers http.Header, output io.Writer) error {
fileName, err := zipartifacts.DecodeFileEntry(encodedFilename)
if err != nil {
......@@ -97,7 +88,15 @@ func unpackFileFromZip(ctx context.Context, archivePath, encodedFilename string,
// Write http headers about the file
headers.Set("Content-Length", contentLength)
headers.Set("Content-Type", detectFileContentType(fileName))
// Using application/octet-stream tells the client that we don't
// really know what Content-Type is. Since this file is being sent
// as attachment, browsers don't need to know to save the
// file. Chrome doesn't appear to pay attention to Content-Type when
// Content-Disposition is an attachment, and Firefox only uses it if there
// is no extension in the filename. Thus, there's no need for
// Workhorse to guess Content-Type based on the filename.
headers.Set("Content-Type", "application/octet-stream")
headers.Set("Content-Disposition", "attachment; filename=\""+escapeQuotes(basename)+"\"")
// Copy file body to client
if _, err := io.Copy(output, reader); err != nil {
......
......@@ -54,7 +54,7 @@ func TestDownloadingFromValidArchive(t *testing.T) {
testhelper.RequireResponseHeader(t, response,
"Content-Type",
"text/plain; charset=utf-8")
"application/octet-stream")
testhelper.RequireResponseHeader(t, response,
"Content-Disposition",
"attachment; filename=\"test.txt\"")
......@@ -88,7 +88,7 @@ func TestDownloadingFromValidHTTPArchive(t *testing.T) {
testhelper.RequireResponseHeader(t, response,
"Content-Type",
"text/plain; charset=utf-8")
"application/octet-stream")
testhelper.RequireResponseHeader(t, response,
"Content-Disposition",
"attachment; filename=\"test.txt\"")
......
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