summaryrefslogtreecommitdiff
path: root/workhorse/internal
diff options
context:
space:
mode:
Diffstat (limited to 'workhorse/internal')
-rw-r--r--workhorse/internal/headers/content_headers.go32
-rw-r--r--workhorse/internal/headers/content_headers_test.go56
-rw-r--r--workhorse/internal/senddata/contentprocessor/contentprocessor_test.go4
3 files changed, 85 insertions, 7 deletions
diff --git a/workhorse/internal/headers/content_headers.go b/workhorse/internal/headers/content_headers.go
index 854cc8abddd..54c7c1bdd95 100644
--- a/workhorse/internal/headers/content_headers.go
+++ b/workhorse/internal/headers/content_headers.go
@@ -1,6 +1,7 @@
package headers
import (
+ "mime"
"net/http"
"regexp"
@@ -13,8 +14,9 @@ var (
imageTypeRegex = regexp.MustCompile(`^image/*`)
svgMimeTypeRegex = regexp.MustCompile(`^image/svg\+xml$`)
- textTypeRegex = regexp.MustCompile(`^text/*`)
-
+ textTypeRegex = regexp.MustCompile(`^text/*`)
+ xmlTypeRegex = regexp.MustCompile(`^text/xml`)
+ xhtmlTypeRegex = regexp.MustCompile(`^text/html`)
videoTypeRegex = regexp.MustCompile(`^video/*`)
pdfTypeRegex = regexp.MustCompile(`application\/pdf`)
@@ -26,6 +28,8 @@ var (
// Mime types that can't be inlined. Usually subtypes of main types
var forbiddenInlineTypes = []*regexp.Regexp{svgMimeTypeRegex}
+var htmlRenderingTypes = []*regexp.Regexp{xmlTypeRegex, xhtmlTypeRegex}
+
// Mime types that can be inlined. We can add global types like "image/" or
// specific types like "text/plain". If there is a specific type inside a global
// allowed type that can't be inlined we must add it to the forbiddenInlineTypes var.
@@ -38,12 +42,28 @@ const (
textPlainContentType = "text/plain; charset=utf-8"
attachmentDispositionText = "attachment"
inlineDispositionText = "inline"
+ dummyFilename = "blob"
)
func SafeContentHeaders(data []byte, contentDisposition string) (string, string) {
- contentType := safeContentType(data)
+ detectedContentType := detectContentType(data)
+
+ contentType := safeContentType(detectedContentType)
contentDisposition = safeContentDisposition(contentType, contentDisposition)
+ // Some browsers will render XML inline unless a filename directive is provided with a non-xml file extension
+ // This overrides the filename directive in the case of XML data
+ for _, element := range htmlRenderingTypes {
+ if isType(detectedContentType, element) {
+ disposition, directives, err := mime.ParseMediaType(contentDisposition)
+ if err == nil {
+ directives["filename"] = dummyFilename
+ contentDisposition = mime.FormatMediaType(disposition, directives)
+ break
+ }
+ }
+ }
+
// Set attachments to application/octet-stream since browsers can do
// a better job distinguishing certain types (for example: ZIP files
// vs. Microsoft .docx files). However, browsers may safely render SVGs even
@@ -56,15 +76,17 @@ func SafeContentHeaders(data []byte, contentDisposition string) (string, string)
return contentType, contentDisposition
}
-func safeContentType(data []byte) string {
+func detectContentType(data []byte) string {
// Special case for svg because DetectContentType detects it as text
if svg.Is(data) {
return svgContentType
}
// Override any existing Content-Type header from other ResponseWriters
- contentType := http.DetectContentType(data)
+ return http.DetectContentType(data)
+}
+func safeContentType(contentType string) string {
// http.DetectContentType does not support JavaScript and would only
// return text/plain. But for cautionary measures, just in case they start supporting
// it down the road and start returning application/javascript, we want to handle it now
diff --git a/workhorse/internal/headers/content_headers_test.go b/workhorse/internal/headers/content_headers_test.go
new file mode 100644
index 00000000000..7cfce335d88
--- /dev/null
+++ b/workhorse/internal/headers/content_headers_test.go
@@ -0,0 +1,56 @@
+package headers
+
+import (
+ "os"
+ "testing"
+
+ "github.com/stretchr/testify/require"
+)
+
+func fileContents(fileName string) []byte {
+ fileContents, _ := os.ReadFile(fileName)
+ return fileContents
+}
+
+func TestHeaders(t *testing.T) {
+ tests := []struct {
+ desc string
+ fileContents []byte
+ expectedContentType string
+ expectedContentDisposition string
+ }{
+ {
+ desc: "XML file",
+ fileContents: fileContents("../../testdata/test.xml"),
+ expectedContentType: "text/plain; charset=utf-8",
+ expectedContentDisposition: "inline; filename=blob",
+ },
+ {
+ desc: "XHTML file",
+ fileContents: fileContents("../../testdata/index.xhtml"),
+ expectedContentType: "text/plain; charset=utf-8",
+ expectedContentDisposition: "inline; filename=blob",
+ },
+ {
+ desc: "svg+xml file",
+ fileContents: fileContents("../../testdata/xml.svg"),
+ expectedContentType: "image/svg+xml",
+ expectedContentDisposition: "attachment",
+ },
+ {
+ desc: "text file",
+ fileContents: []byte(`a text file`),
+ expectedContentType: "text/plain; charset=utf-8",
+ expectedContentDisposition: "inline",
+ },
+ }
+
+ for _, test := range tests {
+ t.Run(test.desc, func(t *testing.T) {
+ contentType, newContentDisposition := SafeContentHeaders(test.fileContents, "")
+
+ require.Equal(t, test.expectedContentType, contentType)
+ require.Equal(t, test.expectedContentDisposition, newContentDisposition)
+ })
+ }
+}
diff --git a/workhorse/internal/senddata/contentprocessor/contentprocessor_test.go b/workhorse/internal/senddata/contentprocessor/contentprocessor_test.go
index b04263de6b9..e863935be6f 100644
--- a/workhorse/internal/senddata/contentprocessor/contentprocessor_test.go
+++ b/workhorse/internal/senddata/contentprocessor/contentprocessor_test.go
@@ -51,13 +51,13 @@ func TestSetProperContentTypeAndDisposition(t *testing.T) {
{
desc: "HTML type",
contentType: "text/plain; charset=utf-8",
- contentDisposition: "inline",
+ contentDisposition: "inline; filename=blob",
body: "<html><body>Hello world!</body></html>",
},
{
desc: "Javascript within HTML type",
contentType: "text/plain; charset=utf-8",
- contentDisposition: "inline",
+ contentDisposition: "inline; filename=blob",
body: "<script>alert(\"foo\")</script>",
},
{