mirror of
https://github.com/gohugoio/hugo.git
synced 2026-08-24 15:28:54 +00:00
Fix prevention of direct symlink reads in resources.Get
* Note for themes, this is only an issue for themes stored locally, e.g. below `themes/...`. Themes mounted as modules from GitHub gets symlinks stripped away. * Thas was also not an issue for file reading walking one or more directories. * This is an regression introduced in `v0.123.0`.
This commit is contained in:
@@ -106,6 +106,22 @@ func (fs *baseFileDecoratorFs) Stat(name string) (os.FileInfo, error) {
|
|||||||
return fim.(os.FileInfo), nil
|
return fim.(os.FileInfo), nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (fs *baseFileDecoratorFs) LstatIfPossible(name string) (os.FileInfo, bool, error) {
|
||||||
|
if lstater, ok := fs.Fs.(afero.Lstater); ok {
|
||||||
|
fi, ok, err := lstater.LstatIfPossible(name)
|
||||||
|
if err != nil {
|
||||||
|
return nil, false, err
|
||||||
|
}
|
||||||
|
fim, err := fs.decorate(fi, name)
|
||||||
|
if err != nil {
|
||||||
|
return nil, false, err
|
||||||
|
}
|
||||||
|
return fim.(os.FileInfo), ok, nil
|
||||||
|
}
|
||||||
|
fi, err := fs.Stat(name)
|
||||||
|
return fi, false, err
|
||||||
|
}
|
||||||
|
|
||||||
func (fs *baseFileDecoratorFs) Open(name string) (afero.File, error) {
|
func (fs *baseFileDecoratorFs) Open(name string) (afero.File, error) {
|
||||||
return fs.open(name)
|
return fs.open(name)
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -267,3 +267,13 @@ func ReadDirWithContext(ctx context.Context, f DirOnlyOps, count int) ([]iofs.Di
|
|||||||
}
|
}
|
||||||
return v, ctx, nil
|
return v, ctx, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// LstatIfPossible tries to use LstatIfPossible if the filesystem supports it, otherwise it falls back to Stat.
|
||||||
|
func LstatIfPossible(fs afero.Fs, name string) (os.FileInfo, error) {
|
||||||
|
if lstater, ok := fs.(afero.Lstater); ok {
|
||||||
|
fi, _, err := lstater.LstatIfPossible(name)
|
||||||
|
return fi, err
|
||||||
|
}
|
||||||
|
fi, err := fs.Stat(name)
|
||||||
|
return fi, err
|
||||||
|
}
|
||||||
|
|||||||
@@ -724,11 +724,16 @@ func (fs *RootMappingFs) statRoot(root *RootMapping, filename string) (FileMetaI
|
|||||||
}
|
}
|
||||||
|
|
||||||
filename = root.filename(filename)
|
filename = root.filename(filename)
|
||||||
fi, err := fs.Fs.Stat(filename)
|
fi, err := LstatIfPossible(fs.Fs, filename)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Don't allow symlinks to escape the mount.
|
||||||
|
if fi.Mode()&os.ModeSymlink != 0 {
|
||||||
|
return nil, os.ErrNotExist
|
||||||
|
}
|
||||||
|
|
||||||
var opener func() (afero.File, error)
|
var opener func() (afero.File, error)
|
||||||
if !fi.IsDir() {
|
if !fi.IsDir() {
|
||||||
// Open the file directly.
|
// Open the file directly.
|
||||||
|
|||||||
@@ -237,6 +237,21 @@ var commonTestScriptsParam = testscript.Params{
|
|||||||
ts.Fatalf("failed to write file: %v", err)
|
ts.Fatalf("failed to write file: %v", err)
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
// ln creates a symlink, but throws an error on Windows.
|
||||||
|
"ln": func(ts *testscript.TestScript, neg bool, args []string) {
|
||||||
|
if runtime.GOOS == "windows" {
|
||||||
|
ts.Fatalf("ln is not supported on Windows")
|
||||||
|
}
|
||||||
|
if len(args) != 2 {
|
||||||
|
ts.Fatalf("usage: ln TARGET LINKNAME")
|
||||||
|
}
|
||||||
|
target := ts.MkAbs(args[0])
|
||||||
|
linkname := ts.MkAbs(args[1])
|
||||||
|
err := os.Symlink(target, linkname)
|
||||||
|
if err != nil {
|
||||||
|
ts.Fatalf("failed to create symlink: %v", err)
|
||||||
|
}
|
||||||
|
},
|
||||||
|
|
||||||
// httpget checks that a HTTP resource's body matches (if it compiles as a regexp) or contains all of the strings given as arguments.
|
// httpget checks that a HTTP resource's body matches (if it compiles as a regexp) or contains all of the strings given as arguments.
|
||||||
"httpget": func(ts *testscript.TestScript, neg bool, args []string) {
|
"httpget": func(ts *testscript.TestScript, neg bool, args []string) {
|
||||||
@@ -314,6 +329,10 @@ var commonTestScriptsParam = testscript.Params{
|
|||||||
if !ok {
|
if !ok {
|
||||||
ts.Fatalf("stat %s: %v", filename, err)
|
ts.Fatalf("stat %s: %v", filename, err)
|
||||||
}
|
}
|
||||||
|
if ok && neg {
|
||||||
|
// OK.
|
||||||
|
continue
|
||||||
|
}
|
||||||
if fi.Size() == 0 {
|
if fi.Size() == 0 {
|
||||||
ts.Fatalf("%s is empty", filename)
|
ts.Fatalf("%s is empty", filename)
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -0,0 +1,39 @@
|
|||||||
|
[windows] skip
|
||||||
|
|
||||||
|
ln ./rootfile.txt ./themes/mytheme/assets/modassetsymlink.txt
|
||||||
|
ln ./rootfile.txt ./themes/mytheme/static/modstaticsymlink.txt
|
||||||
|
ln ./README.md ./content/pagesymlink.md
|
||||||
|
|
||||||
|
hugo
|
||||||
|
|
||||||
|
grep 'OK' public/index.html
|
||||||
|
! grep 'FAIL' public/index.html
|
||||||
|
|
||||||
|
tree public
|
||||||
|
|
||||||
|
stdout modassetok
|
||||||
|
! stdout modassetsymlink
|
||||||
|
stdout 'modstatictok'
|
||||||
|
! stdout 'modstaticsymlink'
|
||||||
|
stdout pageok
|
||||||
|
! stdout pagesymlink
|
||||||
|
|
||||||
|
-- hugo.toml --
|
||||||
|
disableKinds = ["taxonomy", "term", "rss"]
|
||||||
|
[[module.imports]]
|
||||||
|
path = 'mytheme'
|
||||||
|
-- README.md --
|
||||||
|
Read me.
|
||||||
|
-- layouts/all.html --
|
||||||
|
{{ with resources.Get "modassetok.txt"}}OK {{ .Publish }}{{ else }}FAIL{{ end }}
|
||||||
|
{{ with resources.Get "modassetsymlink.txt"}}FAIL {{ .Publish }}{{ else }}OK{{ end }}
|
||||||
|
Page: {{ .RelPermalink }}|{{ .Content }}|
|
||||||
|
-- content/pageok.md --
|
||||||
|
-- themes/mytheme/assets/modassetok.txt --
|
||||||
|
Content.
|
||||||
|
-- themes/mytheme/static/modstatictok.txt --
|
||||||
|
Content.
|
||||||
|
-- rootfile.txt --
|
||||||
|
Roo Content.
|
||||||
|
|
||||||
|
|
||||||
Reference in New Issue
Block a user