From 26d5972ab55e166c7038e101fcbf0cfd4ca3d7a2 Mon Sep 17 00:00:00 2001 From: Lenin Alevski Date: Wed, 2 Mar 2022 12:18:43 -0800 Subject: [PATCH] Whitelist for preview files from the backend (#1651) This PR adds a whitelist of safe files to download with `Content-Disposition: inline;` from the backend, all other files will be force download via `Content-Disposition: attachment;` existing svg files will still be rendered in a secure way via the html `image` tag. reference: https://digi.ninja/blog/svg_xss.php Signed-off-by: Lenin Alevski --- operatorapi/proxy.go | 2 +- restapi/user_objects.go | 6 +++--- restapi/utils.go | 43 +++++++++++++++++++++++++++++++++++++++++ 3 files changed, 47 insertions(+), 4 deletions(-) diff --git a/operatorapi/proxy.go b/operatorapi/proxy.go index 792041ebc..64e3f2e33 100644 --- a/operatorapi/proxy.go +++ b/operatorapi/proxy.go @@ -254,7 +254,7 @@ func serveProxy(responseWriter http.ResponseWriter, req *http.Request) { } // Allow iframes responseWriter.Header().Set("X-Frame-Options", "SAMEORIGIN") - responseWriter.Header().Set("X-XSS-Protection", "1") + responseWriter.Header().Set("X-XSS-Protection", "1; mode=block") io.Copy(responseWriter, resp.Body) diff --git a/restapi/user_objects.go b/restapi/user_objects.go index 9327b99fe..02c626ab9 100644 --- a/restapi/user_objects.go +++ b/restapi/user_objects.go @@ -415,18 +415,18 @@ func getDownloadObjectResponse(session *models.Principal, params user_api.Downlo LogError("Unable to parse range header input %s: %v", params.HTTPRequest.Header.Get("Range"), err) return } + contentType := stat.ContentType + rw.Header().Set("X-XSS-Protection", "1; mode=block") - if isPreview { + if isPreview && isSafeToPreview(contentType) { rw.Header().Set("Content-Disposition", fmt.Sprintf("inline; filename=\"%s\"", escapedName)) rw.Header().Set("X-Frame-Options", "SAMEORIGIN") - rw.Header().Set("X-XSS-Protection", "1") } else { rw.Header().Set("Content-Disposition", fmt.Sprintf("attachment; filename=\"%s\"", escapedName)) } rw.Header().Set("Last-Modified", stat.LastModified.UTC().Format(http.TimeFormat)) - contentType := stat.ContentType if isPreview { // In case content type was uploaded as octet-stream, we double verify content type if stat.ContentType == "application/octet-stream" { diff --git a/restapi/utils.go b/restapi/utils.go index 314df8400..dd624dd05 100644 --- a/restapi/utils.go +++ b/restapi/utils.go @@ -144,3 +144,46 @@ func ExpireSessionCookie() http.Cookie { func SanitizeEncodedPrefix(rawPrefix string) string { return strings.Replace(rawPrefix, " ", "+", -1) } + +var safeMimeTypes = []string{ + "image/jpeg", + "mage/apng", + "image/avif", + "image/webp", + "image/bmp", + "image/x-icon", + "image/gif", + "image/png", + "image/heic", + "image/heif", + "application/pdf", + "text/plain", + "application/json", + "audio/wav", + "audio/mpeg", + "audio/aiff", + "audio/dsd", + "video/mp4", + "video/x-msvideo", + "video/mpeg", + "audio/webm", + "video/webm", + "video/quicktime", + "video/x-flv", + "audio/x-matroska", + "video/x-matroska", + "video/x-ms-wmv", + "application/metastream", + "video/avchd-stream", + "audio/mp4", + "video/mp4", +} + +func isSafeToPreview(str string) bool { + for _, v := range safeMimeTypes { + if v == str { + return true + } + } + return false +}