diff options
author | Bjørn Erik Pedersen <bjorn.erik.pedersen@gmail.com> | 2021-12-16 15:12:13 +0100 |
---|---|---|
committer | Bjørn Erik Pedersen <bjorn.erik.pedersen@gmail.com> | 2021-12-17 09:50:28 +0100 |
commit | 44954497bcb2d6d589b9340a43323663061c7b42 (patch) | |
tree | 0d0d06b11e462ccff1a908c2b1c4dfd039b82787 /media/mediaType.go | |
parent | 22ef5da20d1685dfe6aff3bd9364c9b1f1d0d8f8 (diff) |
Always use content to resolve content type in resources.GetRemote
This is a security hardening measure; don't trust the URL extension or any `Content-Type`/`Content-Disposition` header on its own, always look at the file content using Go's `http.DetectContentType`.
This commit also adds ttf and otf media type definitions to Hugo.
Fixes #9302
Fixes #9301
Diffstat (limited to 'media/mediaType.go')
-rw-r--r-- | media/mediaType.go | 58 |
1 files changed, 58 insertions, 0 deletions
diff --git a/media/mediaType.go b/media/mediaType.go index eec7a27a8..0bdeb6db7 100644 --- a/media/mediaType.go +++ b/media/mediaType.go @@ -17,6 +17,7 @@ import ( "encoding/json" "errors" "fmt" + "net/http" "sort" "strings" @@ -60,6 +61,42 @@ type SuffixInfo struct { FullSuffix string `json:"fullSuffix"` } +// FromContent resolve the Type primarily using http.DetectContentType. +// If http.DetectContentType resolves to application/octet-stream, a zero Type is returned. +// If http.DetectContentType resolves to text/plain or application/xml, we try to get more specific using types and ext. +func FromContent(types Types, ext string, content []byte) Type { + ext = strings.TrimPrefix(ext, ".") + t := strings.Split(http.DetectContentType(content), ";")[0] + var m Type + if t == "application/octet-stream" { + return m + } + + var found bool + m, found = types.GetByType(t) + if !found { + if t == "text/xml" { + // This is how it's configured in Hugo by default. + m, found = types.GetByType("application/xml") + } + } + + if !found || ext == "" { + return m + } + + if m.Type() == "text/plain" || m.Type() == "application/xml" { + // http.DetectContentType isn't brilliant when it comes to common text formats, so we need to do better. + // For now we say that if it's detected to be a text format and the extension/content type in header reports + // it to be a text format, then we use that. + mm, _, found := types.GetFirstBySuffix(ext) + if found && mm.IsText() { + return mm + } + } + return m +} + // FromStringAndExt creates a Type from a MIME string and a given extension. func FromStringAndExt(t, ext string) (Type, error) { tp, err := fromString(t) @@ -122,6 +159,21 @@ func (m Type) Suffixes() []string { return strings.Split(m.suffixesCSV, ",") } +// IsText returns whether this Type is a text format. +// Note that this may currently return false negatives. +// TODO(bep) improve +func (m Type) IsText() bool { + if m.MainType == "text" { + return true + } + switch m.SubType { + case "javascript", "json", "rss", "xml", "svg", TOMLType.SubType, YAMLType.SubType: + return true + + } + return false +} + func (m *Type) init() { m.FirstSuffix.FullSuffix = "" m.FirstSuffix.Suffix = "" @@ -183,6 +235,10 @@ var ( BMPType = newMediaType("image", "bmp", []string{"bmp"}) WEBPType = newMediaType("image", "webp", []string{"webp"}) + // Common font types + TrueTypeFontType = newMediaType("font", "ttf", []string{"ttf"}) + OpenTypeFontType = newMediaType("font", "otf", []string{"otf"}) + // Common video types AVIType = newMediaType("video", "x-msvideo", []string{"avi"}) MPEGType = newMediaType("video", "mpeg", []string{"mpg", "mpeg"}) @@ -224,6 +280,8 @@ var DefaultTypes = Types{ OGGType, WEBMType, GPPType, + OpenTypeFontType, + TrueTypeFontType, } func init() { |