mirror of
https://github.com/gohugoio/hugo.git
synced 2024-11-21 20:46:30 -05:00
media: Also consider extension in FromContent
As used in `resources.GetRemote`. This will now reject image files with text and text files with images.
This commit is contained in:
parent
ce04011096
commit
6779117f72
7 changed files with 85 additions and 43 deletions
|
@ -22,16 +22,15 @@ import (
|
||||||
"net/http"
|
"net/http"
|
||||||
"net/http/httptest"
|
"net/http/httptest"
|
||||||
"os"
|
"os"
|
||||||
|
|
||||||
"github.com/gohugoio/hugo/config"
|
|
||||||
|
|
||||||
"github.com/gohugoio/hugo/resources/resource_transformers/tocss/dartsass"
|
|
||||||
|
|
||||||
"path/filepath"
|
"path/filepath"
|
||||||
"strings"
|
"strings"
|
||||||
"testing"
|
"testing"
|
||||||
"time"
|
"time"
|
||||||
|
|
||||||
|
"github.com/gohugoio/hugo/config"
|
||||||
|
|
||||||
|
"github.com/gohugoio/hugo/resources/resource_transformers/tocss/dartsass"
|
||||||
|
|
||||||
jww "github.com/spf13/jwalterweatherman"
|
jww "github.com/spf13/jwalterweatherman"
|
||||||
|
|
||||||
"github.com/gohugoio/hugo/common/herrors"
|
"github.com/gohugoio/hugo/common/herrors"
|
||||||
|
@ -57,7 +56,6 @@ func TestSCSSWithIncludePaths(t *testing.T) {
|
||||||
{"libsass", func() bool { return scss.Supports() }},
|
{"libsass", func() bool { return scss.Supports() }},
|
||||||
{"dartsass", func() bool { return dartsass.Supports() }},
|
{"dartsass", func() bool { return dartsass.Supports() }},
|
||||||
} {
|
} {
|
||||||
|
|
||||||
c.Run(test.name, func(c *qt.C) {
|
c.Run(test.name, func(c *qt.C) {
|
||||||
if !test.supports() {
|
if !test.supports() {
|
||||||
c.Skip(fmt.Sprintf("Skip %s", test.name))
|
c.Skip(fmt.Sprintf("Skip %s", test.name))
|
||||||
|
@ -107,9 +105,7 @@ T1: {{ $r.Content }}
|
||||||
|
|
||||||
b.AssertFileContent(filepath.Join(workDir, "public/index.html"), `T1: moo{color:#fff}`)
|
b.AssertFileContent(filepath.Join(workDir, "public/index.html"), `T1: moo{color:#fff}`)
|
||||||
})
|
})
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
func TestSCSSWithRegularCSSImport(t *testing.T) {
|
func TestSCSSWithRegularCSSImport(t *testing.T) {
|
||||||
|
@ -122,7 +118,6 @@ func TestSCSSWithRegularCSSImport(t *testing.T) {
|
||||||
{"libsass", func() bool { return scss.Supports() }},
|
{"libsass", func() bool { return scss.Supports() }},
|
||||||
{"dartsass", func() bool { return dartsass.Supports() }},
|
{"dartsass", func() bool { return dartsass.Supports() }},
|
||||||
} {
|
} {
|
||||||
|
|
||||||
c.Run(test.name, func(c *qt.C) {
|
c.Run(test.name, func(c *qt.C) {
|
||||||
if !test.supports() {
|
if !test.supports() {
|
||||||
c.Skip(fmt.Sprintf("Skip %s", test.name))
|
c.Skip(fmt.Sprintf("Skip %s", test.name))
|
||||||
|
@ -202,11 +197,9 @@ moo {
|
||||||
}
|
}
|
||||||
|
|
||||||
/* foo */`)
|
/* foo */`)
|
||||||
|
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
func TestSCSSWithThemeOverrides(t *testing.T) {
|
func TestSCSSWithThemeOverrides(t *testing.T) {
|
||||||
|
@ -219,7 +212,6 @@ func TestSCSSWithThemeOverrides(t *testing.T) {
|
||||||
{"libsass", func() bool { return scss.Supports() }},
|
{"libsass", func() bool { return scss.Supports() }},
|
||||||
{"dartsass", func() bool { return dartsass.Supports() }},
|
{"dartsass", func() bool { return dartsass.Supports() }},
|
||||||
} {
|
} {
|
||||||
|
|
||||||
c.Run(test.name, func(c *qt.C) {
|
c.Run(test.name, func(c *qt.C) {
|
||||||
if !test.supports() {
|
if !test.supports() {
|
||||||
c.Skip(fmt.Sprintf("Skip %s", test.name))
|
c.Skip(fmt.Sprintf("Skip %s", test.name))
|
||||||
|
@ -319,7 +311,6 @@ T1: {{ $r.Content }}
|
||||||
)
|
)
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// https://github.com/gohugoio/hugo/issues/6274
|
// https://github.com/gohugoio/hugo/issues/6274
|
||||||
|
@ -333,7 +324,6 @@ func TestSCSSWithIncludePathsSass(t *testing.T) {
|
||||||
{"libsass", func() bool { return scss.Supports() }},
|
{"libsass", func() bool { return scss.Supports() }},
|
||||||
{"dartsass", func() bool { return dartsass.Supports() }},
|
{"dartsass", func() bool { return dartsass.Supports() }},
|
||||||
} {
|
} {
|
||||||
|
|
||||||
c.Run(test.name, func(c *qt.C) {
|
c.Run(test.name, func(c *qt.C) {
|
||||||
if !test.supports() {
|
if !test.supports() {
|
||||||
c.Skip(fmt.Sprintf("Skip %s", test.name))
|
c.Skip(fmt.Sprintf("Skip %s", test.name))
|
||||||
|
@ -620,6 +610,7 @@ func TestResourceChains(t *testing.T) {
|
||||||
return
|
return
|
||||||
|
|
||||||
case "/authenticated/":
|
case "/authenticated/":
|
||||||
|
w.Header().Set("Content-Type", "text/plain")
|
||||||
if r.Header.Get("Authorization") == "Bearer abcd" {
|
if r.Header.Get("Authorization") == "Bearer abcd" {
|
||||||
w.Write([]byte(`Welcome`))
|
w.Write([]byte(`Welcome`))
|
||||||
return
|
return
|
||||||
|
@ -628,6 +619,7 @@ func TestResourceChains(t *testing.T) {
|
||||||
return
|
return
|
||||||
|
|
||||||
case "/post":
|
case "/post":
|
||||||
|
w.Header().Set("Content-Type", "text/plain")
|
||||||
if r.Method == http.MethodPost {
|
if r.Method == http.MethodPost {
|
||||||
body, err := ioutil.ReadAll(r.Body)
|
body, err := ioutil.ReadAll(r.Body)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
|
@ -1247,8 +1239,8 @@ class-in-b {
|
||||||
// TODO(bep) for some reason, we have starting to get
|
// TODO(bep) for some reason, we have starting to get
|
||||||
// execute of template failed: template: index.html:5:25
|
// execute of template failed: template: index.html:5:25
|
||||||
// on CI (GitHub action).
|
// on CI (GitHub action).
|
||||||
//b.Assert(fe.Position().LineNumber, qt.Equals, 5)
|
// b.Assert(fe.Position().LineNumber, qt.Equals, 5)
|
||||||
//b.Assert(fe.Error(), qt.Contains, filepath.Join(workDir, "assets/css/components/b.css:4:1"))
|
// b.Assert(fe.Error(), qt.Contains, filepath.Join(workDir, "assets/css/components/b.css:4:1"))
|
||||||
|
|
||||||
// Remove PostCSS
|
// Remove PostCSS
|
||||||
b.Assert(os.RemoveAll(filepath.Join(workDir, "node_modules")), qt.IsNil)
|
b.Assert(os.RemoveAll(filepath.Join(workDir, "node_modules")), qt.IsNil)
|
||||||
|
|
|
@ -28,6 +28,8 @@ import (
|
||||||
"github.com/mitchellh/mapstructure"
|
"github.com/mitchellh/mapstructure"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
var zero Type
|
||||||
|
|
||||||
const (
|
const (
|
||||||
defaultDelimiter = "."
|
defaultDelimiter = "."
|
||||||
)
|
)
|
||||||
|
@ -64,16 +66,14 @@ type SuffixInfo struct {
|
||||||
// FromContent resolve the Type primarily using http.DetectContentType.
|
// 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 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.
|
// 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 {
|
func FromContent(types Types, extensionHints []string, content []byte) Type {
|
||||||
ext = strings.TrimPrefix(ext, ".")
|
|
||||||
t := strings.Split(http.DetectContentType(content), ";")[0]
|
t := strings.Split(http.DetectContentType(content), ";")[0]
|
||||||
var m Type
|
|
||||||
if t == "application/octet-stream" {
|
if t == "application/octet-stream" {
|
||||||
return m
|
return zero
|
||||||
}
|
}
|
||||||
|
|
||||||
var found bool
|
var found bool
|
||||||
m, found = types.GetByType(t)
|
m, found := types.GetByType(t)
|
||||||
if !found {
|
if !found {
|
||||||
if t == "text/xml" {
|
if t == "text/xml" {
|
||||||
// This is how it's configured in Hugo by default.
|
// This is how it's configured in Hugo by default.
|
||||||
|
@ -81,19 +81,36 @@ func FromContent(types Types, ext string, content []byte) Type {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if !found || ext == "" {
|
if !found {
|
||||||
return m
|
return zero
|
||||||
}
|
}
|
||||||
|
|
||||||
if m.Type() == "text/plain" || m.Type() == "application/xml" {
|
var mm Type
|
||||||
// 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
|
for _, extension := range extensionHints {
|
||||||
// it to be a text format, then we use that.
|
extension = strings.TrimPrefix(extension, ".")
|
||||||
mm, _, found := types.GetFirstBySuffix(ext)
|
mm, _, found = types.GetFirstBySuffix(extension)
|
||||||
if found && mm.IsText() {
|
if found {
|
||||||
return mm
|
break
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if found {
|
||||||
|
if m == mm {
|
||||||
|
return m
|
||||||
|
}
|
||||||
|
|
||||||
|
if m.IsText() && mm.IsText() {
|
||||||
|
// 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.
|
||||||
|
return mm
|
||||||
|
}
|
||||||
|
|
||||||
|
// E.g. an image with a *.js extension.
|
||||||
|
return zero
|
||||||
|
}
|
||||||
|
|
||||||
return m
|
return m
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -15,7 +15,6 @@ package media
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"encoding/json"
|
"encoding/json"
|
||||||
"fmt"
|
|
||||||
"io/ioutil"
|
"io/ioutil"
|
||||||
"path/filepath"
|
"path/filepath"
|
||||||
"sort"
|
"sort"
|
||||||
|
@ -194,15 +193,39 @@ func TestFromContent(t *testing.T) {
|
||||||
content, err := ioutil.ReadFile(filename)
|
content, err := ioutil.ReadFile(filename)
|
||||||
c.Assert(err, qt.IsNil)
|
c.Assert(err, qt.IsNil)
|
||||||
ext := strings.TrimPrefix(paths.Ext(filename), ".")
|
ext := strings.TrimPrefix(paths.Ext(filename), ".")
|
||||||
fmt.Println("=>", ext)
|
var exts []string
|
||||||
|
if ext == "jpg" {
|
||||||
|
exts = append(exts, "foo", "bar", "jpg")
|
||||||
|
} else {
|
||||||
|
exts = []string{ext}
|
||||||
|
}
|
||||||
expected, _, found := mtypes.GetFirstBySuffix(ext)
|
expected, _, found := mtypes.GetFirstBySuffix(ext)
|
||||||
c.Assert(found, qt.IsTrue)
|
c.Assert(found, qt.IsTrue)
|
||||||
got := FromContent(mtypes, ext, content)
|
got := FromContent(mtypes, exts, content)
|
||||||
c.Assert(got, qt.Equals, expected)
|
c.Assert(got, qt.Equals, expected)
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func TestFromContentFakes(t *testing.T) {
|
||||||
|
c := qt.New(t)
|
||||||
|
|
||||||
|
files, err := filepath.Glob("./testdata/fake.*")
|
||||||
|
c.Assert(err, qt.IsNil)
|
||||||
|
mtypes := DefaultTypes
|
||||||
|
|
||||||
|
for _, filename := range files {
|
||||||
|
name := filepath.Base(filename)
|
||||||
|
c.Run(name, func(c *qt.C) {
|
||||||
|
content, err := ioutil.ReadFile(filename)
|
||||||
|
c.Assert(err, qt.IsNil)
|
||||||
|
ext := strings.TrimPrefix(paths.Ext(filename), ".")
|
||||||
|
got := FromContent(mtypes, []string{ext}, content)
|
||||||
|
c.Assert(got, qt.Equals, zero)
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
func TestDecodeTypes(t *testing.T) {
|
func TestDecodeTypes(t *testing.T) {
|
||||||
c := qt.New(t)
|
c := qt.New(t)
|
||||||
|
|
||||||
|
|
BIN
media/testdata/fake.js
vendored
Normal file
BIN
media/testdata/fake.js
vendored
Normal file
Binary file not shown.
After Width: | Height: | Size: 13 KiB |
3
media/testdata/fake.png
vendored
Normal file
3
media/testdata/fake.png
vendored
Normal file
|
@ -0,0 +1,3 @@
|
||||||
|
function foo() {
|
||||||
|
return "foo";
|
||||||
|
}
|
BIN
media/testdata/resource.jpe
vendored
Normal file
BIN
media/testdata/resource.jpe
vendored
Normal file
Binary file not shown.
After Width: | Height: | Size: 114 KiB |
|
@ -110,21 +110,30 @@ func (c *Client) FromRemote(uri string, optionsm map[string]interface{}) (resour
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
var extensionHint string
|
var extensionHints []string
|
||||||
|
|
||||||
if arr, _ := mime.ExtensionsByType(res.Header.Get("Content-Type")); len(arr) == 1 {
|
contentType := res.Header.Get("Content-Type")
|
||||||
extensionHint = arr[0]
|
|
||||||
|
// mime.ExtensionsByType gives a long list of extensions for text/plain,
|
||||||
|
// just use ".txt".
|
||||||
|
if strings.HasPrefix(contentType, "text/plain") {
|
||||||
|
extensionHints = []string{".txt"}
|
||||||
|
} else {
|
||||||
|
exts, _ := mime.ExtensionsByType(contentType)
|
||||||
|
if exts != nil {
|
||||||
|
extensionHints = exts
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Look for a file extention
|
// Look for a file extention. If it's .txt, look for a more specific.
|
||||||
if extensionHint == "" {
|
if extensionHints == nil || extensionHints[0] == ".txt" {
|
||||||
if ext := path.Ext(filename); ext != "" {
|
if ext := path.Ext(filename); ext != "" {
|
||||||
extensionHint = ext
|
extensionHints = []string{ext}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Now resolve the media type primarily using the content.
|
// Now resolve the media type primarily using the content.
|
||||||
mediaType := media.FromContent(c.rs.MediaTypes, extensionHint, body)
|
mediaType := media.FromContent(c.rs.MediaTypes, extensionHints, body)
|
||||||
if mediaType.IsZero() {
|
if mediaType.IsZero() {
|
||||||
return nil, errors.Errorf("failed to resolve media type for remote resource %q", uri)
|
return nil, errors.Errorf("failed to resolve media type for remote resource %q", uri)
|
||||||
}
|
}
|
||||||
|
@ -140,7 +149,6 @@ func (c *Client) FromRemote(uri string, optionsm map[string]interface{}) (resour
|
||||||
},
|
},
|
||||||
RelTargetFilename: filepath.Clean(resourceID),
|
RelTargetFilename: filepath.Clean(resourceID),
|
||||||
})
|
})
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
func (c *Client) validateFromRemoteArgs(uri string, options fromRemoteOptions) error {
|
func (c *Client) validateFromRemoteArgs(uri string, options fromRemoteOptions) error {
|
||||||
|
@ -213,7 +221,7 @@ func (o fromRemoteOptions) BodyReader() io.Reader {
|
||||||
}
|
}
|
||||||
|
|
||||||
func decodeRemoteOptions(optionsm map[string]interface{}) (fromRemoteOptions, error) {
|
func decodeRemoteOptions(optionsm map[string]interface{}) (fromRemoteOptions, error) {
|
||||||
var options = fromRemoteOptions{
|
options := fromRemoteOptions{
|
||||||
Method: "GET",
|
Method: "GET",
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -224,5 +232,4 @@ func decodeRemoteOptions(optionsm map[string]interface{}) (fromRemoteOptions, er
|
||||||
options.Method = strings.ToUpper(options.Method)
|
options.Method = strings.ToUpper(options.Method)
|
||||||
|
|
||||||
return options, nil
|
return options, nil
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
Loading…
Reference in a new issue