hugo

Unnamed repository; edit this file 'description' to name the repository.

git clone git://git.shimmy1996.com/hugo.git
commit 10ae7c3210cd1add14d3750aa9512a87df0e1146
parent ae2d1bd52df0099190ef9195666d0788708b0385
Author: Bjørn Erik Pedersen <bjorn.erik.pedersen@gmail.com>
Date:   Fri, 18 Dec 2020 18:20:12 +0100

Improve LookPath

Diffstat:
Acommon/hexec/safeCommand.go | 45+++++++++++++++++++++++++++++++++++++++++++++
Mcreate/content.go | 7+++++--
Mgo.mod | 1+
Mgo.sum | 2++
Mhugolib/js_test.go | 10+++++++---
Mhugolib/resource_chain_babel_test.go | 6++++--
Mhugolib/resource_chain_test.go | 7+++++--
Mmarkup/asciidocext/convert.go | 5+++--
Mmarkup/internal/external.go | 16+++++++++++-----
Mmarkup/pandoc/convert.go | 5++---
Mmarkup/rst/convert.go | 7++++---
Mmodules/client.go | 7++++++-
Mreleaser/git.go | 5+++--
Mreleaser/releaser.go | 5+++--
Mresources/resource_transformers/babel/babel.go | 12++++++++----
Mresources/resource_transformers/postcss/postcss.go | 14++++++++++----
Mscripts/fork_go_templates/main.go | 9+++++----
Mtpl/internal/go_templates/testenv/testenv.go | 8+++++---
18 files changed, 129 insertions(+), 42 deletions(-)
diff --git a/common/hexec/safeCommand.go b/common/hexec/safeCommand.go
@@ -0,0 +1,45 @@
+// Copyright 2020 The Hugo Authors. All rights reserved.
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+package hexec
+
+import (
+	"context"
+
+	"os/exec"
+
+	"github.com/cli/safeexec"
+)
+
+// SafeCommand is a wrapper around os/exec Command which uses a LookPath
+// implementation that does not search in current directory before looking in PATH.
+// See https://github.com/cli/safeexec and the linked issues.
+func SafeCommand(name string, arg ...string) (*exec.Cmd, error) {
+	bin, err := safeexec.LookPath(name)
+	if err != nil {
+		return nil, err
+	}
+
+	return exec.Command(bin, arg...), nil
+}
+
+// SafeCommandContext wraps CommandContext
+// See SafeCommand for more context.
+func SafeCommandContext(ctx context.Context, name string, arg ...string) (*exec.Cmd, error) {
+	bin, err := safeexec.LookPath(name)
+	if err != nil {
+		return nil, err
+	}
+
+	return exec.CommandContext(ctx, bin, arg...), nil
+}
diff --git a/create/content.go b/create/content.go
@@ -18,12 +18,12 @@ import (
 	"bytes"
 	"io"
 	"os"
-	"os/exec"
 	"path/filepath"
 	"strings"
 
 	"github.com/pkg/errors"
 
+	"github.com/gohugoio/hugo/common/hexec"
 	"github.com/gohugoio/hugo/hugofs/files"
 
 	"github.com/gohugoio/hugo/hugofs"
@@ -105,7 +105,10 @@ func NewContent(
 		jww.FEEDBACK.Printf("Editing %s with %q ...\n", targetPath, editor)
 
 		editorCmd := append(strings.Fields(editor), contentPath)
-		cmd := exec.Command(editorCmd[0], editorCmd[1:]...)
+		cmd, err := hexec.SafeCommand(editorCmd[0], editorCmd[1:]...)
+		if err != nil {
+			return err
+		}
 		cmd.Stdin = os.Stdin
 		cmd.Stdout = os.Stdout
 		cmd.Stderr = os.Stderr
diff --git a/go.mod b/go.mod
@@ -13,6 +13,7 @@ require (
 	github.com/bep/gitmap v1.1.2
 	github.com/bep/golibsass v0.7.0
 	github.com/bep/tmc v0.5.1
+	github.com/cli/safeexec v1.0.0
 	github.com/disintegration/gift v1.2.1
 	github.com/dlclark/regexp2 v1.4.0 // indirect
 	github.com/dustin/go-humanize v1.0.0
diff --git a/go.sum b/go.sum
@@ -148,6 +148,8 @@ github.com/cheekybits/is v0.0.0-20150225183255-68e9c0620927/go.mod h1:h/aW8ynjgk
 github.com/chzyer/logex v1.1.10/go.mod h1:+Ywpsq7O8HXn0nuIou7OrIPyXbp3wmkHB+jjWRnGsAI=
 github.com/chzyer/readline v0.0.0-20180603132655-2972be24d48e/go.mod h1:nSuG5e5PlCu98SY8svDHJxuZscDgtXS6KTTbou5AhLI=
 github.com/chzyer/test v0.0.0-20180213035817-a1ea475d72b1/go.mod h1:Q3SI9o4m/ZMnBNeIyt5eFwwo7qiLfzFZmjNmxjkiQlU=
+github.com/cli/safeexec v1.0.0 h1:0VngyaIyqACHdcMNWfo6+KdUYnqEr2Sg+bSP1pdF+dI=
+github.com/cli/safeexec v1.0.0/go.mod h1:Z/D4tTN8Vs5gXYHDCbaM1S/anmEDnJb1iW0+EJ5zx3Q=
 github.com/client9/misspell v0.3.4/go.mod h1:qj6jICC3Q7zFZvVWo7KLAzC3yx5G7kyvSDkc90ppPyw=
 github.com/cncf/udpa/go v0.0.0-20191209042840-269d4d468f6f/go.mod h1:M8M6+tZqaGXZJjfX53e64911xZQV5JYwmTeXPW+k8Sc=
 github.com/coreos/bbolt v1.3.2 h1:wZwiHHUieZCquLkDL0B8UhzreNWsPHooDAG3q34zk0s=
diff --git a/hugolib/js_test.go b/hugolib/js_test.go
@@ -16,11 +16,12 @@ package hugolib
 import (
 	"fmt"
 	"os"
-	"os/exec"
 	"path/filepath"
 	"runtime"
 	"testing"
 
+	"github.com/gohugoio/hugo/common/hexec"
+
 	"github.com/gohugoio/hugo/htesting"
 
 	"github.com/spf13/viper"
@@ -125,7 +126,9 @@ TS: {{ template "print" $ts }}
 
 	b.WithSourceFile("assets/js/included.js", includedJS)
 
-	out, err := exec.Command("npm", "install").CombinedOutput()
+	cmd, err := hexec.SafeCommand("npm", "install")
+	b.Assert(err, qt.IsNil)
+	out, err := cmd.CombinedOutput()
 	b.Assert(err, qt.IsNil, qt.Commentf(string(out)))
 
 	b.Build(BuildCfg{})
@@ -193,7 +196,8 @@ require github.com/gohugoio/hugoTestProjectJSModImports v0.5.0 // indirect
 }`)
 
 	b.Assert(os.Chdir(workDir), qt.IsNil)
-	_, err = exec.Command("npm", "install").CombinedOutput()
+	cmd, _ := hexec.SafeCommand("npm", "install")
+	_, err = cmd.CombinedOutput()
 	b.Assert(err, qt.IsNil)
 
 	b.Build(BuildCfg{})
diff --git a/hugolib/resource_chain_babel_test.go b/hugolib/resource_chain_babel_test.go
@@ -16,11 +16,12 @@ package hugolib
 import (
 	"bytes"
 	"os"
-	"os/exec"
 	"path/filepath"
 	"runtime"
 	"testing"
 
+	"github.com/gohugoio/hugo/common/hexec"
+
 	jww "github.com/spf13/jwalterweatherman"
 
 	"github.com/gohugoio/hugo/htesting"
@@ -111,7 +112,8 @@ Transpiled: {{ $transpiled.Content | safeJS }}
 	b.WithSourceFile("babel.config.js", babelConfig)
 
 	b.Assert(os.Chdir(workDir), qt.IsNil)
-	_, err = exec.Command("npm", "install").CombinedOutput()
+	cmd, _ := hexec.SafeCommand("npm", "install")
+	_, err = cmd.CombinedOutput()
 	b.Assert(err, qt.IsNil)
 
 	b.Build(BuildCfg{})
diff --git a/hugolib/resource_chain_test.go b/hugolib/resource_chain_test.go
@@ -19,13 +19,15 @@ import (
 	"io"
 	"math/rand"
 	"os"
-	"os/exec"
+
 	"path/filepath"
 	"runtime"
 	"strings"
 	"testing"
 	"time"
 
+	"github.com/gohugoio/hugo/common/hexec"
+
 	jww "github.com/spf13/jwalterweatherman"
 
 	"github.com/gohugoio/hugo/common/herrors"
@@ -930,7 +932,8 @@ class-in-b {
 	b.WithSourceFile("postcss.config.js", postcssConfig)
 
 	b.Assert(os.Chdir(workDir), qt.IsNil)
-	_, err = exec.Command("npm", "install").CombinedOutput()
+	cmd, err := hexec.SafeCommand("npm", "install")
+	_, err = cmd.CombinedOutput()
 	b.Assert(err, qt.IsNil)
 	b.Build(BuildCfg{})
 
diff --git a/markup/asciidocext/convert.go b/markup/asciidocext/convert.go
@@ -18,9 +18,10 @@ package asciidocext
 
 import (
 	"bytes"
-	"os/exec"
 	"path/filepath"
 
+	"github.com/cli/safeexec"
+
 	"github.com/gohugoio/hugo/identity"
 	"github.com/gohugoio/hugo/markup/asciidocext/asciidocext_config"
 	"github.com/gohugoio/hugo/markup/converter"
@@ -193,7 +194,7 @@ func (a *asciidocConverter) appendArg(args []string, option, value, defaultValue
 }
 
 func getAsciidoctorExecPath() string {
-	path, err := exec.LookPath("asciidoctor")
+	path, err := safeexec.LookPath("asciidoctor")
 	if err != nil {
 		return ""
 	}
diff --git a/markup/internal/external.go b/markup/internal/external.go
@@ -2,9 +2,11 @@ package internal
 
 import (
 	"bytes"
-	"os/exec"
 	"strings"
 
+	"github.com/cli/safeexec"
+	"github.com/gohugoio/hugo/common/hexec"
+
 	"github.com/gohugoio/hugo/markup/converter"
 )
 
@@ -13,12 +15,16 @@ func ExternallyRenderContent(
 	ctx converter.DocumentContext,
 	content []byte, path string, args []string) []byte {
 	logger := cfg.Logger
-	cmd := exec.Command(path, args...)
+	cmd, err := hexec.SafeCommand(path, args...)
+	if err != nil {
+		logger.Errorf("%s rendering %s: %v", path, ctx.DocumentName, err)
+		return nil
+	}
 	cmd.Stdin = bytes.NewReader(content)
 	var out, cmderr bytes.Buffer
 	cmd.Stdout = &out
 	cmd.Stderr = &cmderr
-	err := cmd.Run()
+	err = cmd.Run()
 	// Most external helpers exit w/ non-zero exit code only if severe, i.e.
 	// halting errors occurred. -> log stderr output regardless of state of err
 	for _, item := range strings.Split(cmderr.String(), "\n") {
@@ -40,9 +46,9 @@ func normalizeExternalHelperLineFeeds(content []byte) []byte {
 }
 
 func GetPythonExecPath() string {
-	path, err := exec.LookPath("python")
+	path, err := safeexec.LookPath("python")
 	if err != nil {
-		path, err = exec.LookPath("python.exe")
+		path, err = safeexec.LookPath("python.exe")
 		if err != nil {
 			return ""
 		}
diff --git a/markup/pandoc/convert.go b/markup/pandoc/convert.go
@@ -15,8 +15,7 @@
 package pandoc
 
 import (
-	"os/exec"
-
+	"github.com/cli/safeexec"
 	"github.com/gohugoio/hugo/identity"
 	"github.com/gohugoio/hugo/markup/internal"
 
@@ -65,7 +64,7 @@ func (c *pandocConverter) getPandocContent(src []byte, ctx converter.DocumentCon
 }
 
 func getPandocExecPath() string {
-	path, err := exec.LookPath("pandoc")
+	path, err := safeexec.LookPath("pandoc")
 	if err != nil {
 		return ""
 	}
diff --git a/markup/rst/convert.go b/markup/rst/convert.go
@@ -16,9 +16,10 @@ package rst
 
 import (
 	"bytes"
-	"os/exec"
 	"runtime"
 
+	"github.com/cli/safeexec"
+
 	"github.com/gohugoio/hugo/identity"
 	"github.com/gohugoio/hugo/markup/internal"
 
@@ -96,9 +97,9 @@ func (c *rstConverter) getRstContent(src []byte, ctx converter.DocumentContext) 
 }
 
 func getRstExecPath() string {
-	path, err := exec.LookPath("rst2html")
+	path, err := safeexec.LookPath("rst2html")
 	if err != nil {
-		path, err = exec.LookPath("rst2html.py")
+		path, err = safeexec.LookPath("rst2html.py")
 		if err != nil {
 			return ""
 		}
diff --git a/modules/client.go b/modules/client.go
@@ -28,6 +28,8 @@ import (
 	"strings"
 	"time"
 
+	"github.com/gohugoio/hugo/common/hexec"
+
 	hglob "github.com/gohugoio/hugo/hugofs/glob"
 
 	"github.com/gobwas/glob"
@@ -537,7 +539,10 @@ func (c *Client) runGo(
 	}
 
 	stderr := new(bytes.Buffer)
-	cmd := exec.CommandContext(ctx, "go", args...)
+	cmd, err := hexec.SafeCommandContext(ctx, "go", args...)
+	if err != nil {
+		return err
+	}
 
 	cmd.Env = c.environ
 	cmd.Dir = c.ccfg.WorkingDir
diff --git a/releaser/git.go b/releaser/git.go
@@ -15,11 +15,12 @@ package releaser
 
 import (
 	"fmt"
-	"os/exec"
 	"regexp"
 	"sort"
 	"strconv"
 	"strings"
+
+	"github.com/gohugoio/hugo/common/hexec"
 )
 
 var issueRe = regexp.MustCompile(`(?i)[Updates?|Closes?|Fix.*|See] #(\d+)`)
@@ -148,7 +149,7 @@ func extractIssues(body string) []int {
 type gitInfos []gitInfo
 
 func git(args ...string) (string, error) {
-	cmd := exec.Command("git", args...)
+	cmd, _ := hexec.SafeCommand("git", args...)
 	out, err := cmd.CombinedOutput()
 	if err != nil {
 		return "", fmt.Errorf("git failed: %q: %q (%q)", err, out, args)
diff --git a/releaser/releaser.go b/releaser/releaser.go
@@ -20,11 +20,12 @@ import (
 	"io/ioutil"
 	"log"
 	"os"
-	"os/exec"
 	"path/filepath"
 	"regexp"
 	"strings"
 
+	"github.com/gohugoio/hugo/common/hexec"
+
 	"github.com/gohugoio/hugo/common/hugo"
 	"github.com/pkg/errors"
 )
@@ -266,7 +267,7 @@ func (r *ReleaseHandler) release(releaseNotesFile string) error {
 		args = append(args, "--skip-publish")
 	}
 
-	cmd := exec.Command("goreleaser", args...)
+	cmd, _ := hexec.SafeCommand("goreleaser", args...)
 	cmd.Stdout = os.Stdout
 	cmd.Stderr = os.Stderr
 	err := cmd.Run()
diff --git a/resources/resource_transformers/babel/babel.go b/resources/resource_transformers/babel/babel.go
@@ -16,10 +16,11 @@ package babel
 import (
 	"bytes"
 	"io"
-	"os/exec"
 	"path/filepath"
 	"strconv"
 
+	"github.com/cli/safeexec"
+	"github.com/gohugoio/hugo/common/hexec"
 	"github.com/gohugoio/hugo/common/loggers"
 
 	"github.com/gohugoio/hugo/common/hugo"
@@ -108,10 +109,10 @@ func (t *babelTransformation) Transform(ctx *resources.ResourceTransformationCtx
 
 	binary := csiBinPath
 
-	if _, err := exec.LookPath(binary); err != nil {
+	if _, err := safeexec.LookPath(binary); err != nil {
 		// Try PATH
 		binary = binaryName
-		if _, err := exec.LookPath(binary); err != nil {
+		if _, err := safeexec.LookPath(binary); err != nil {
 			// This may be on a CI server etc. Will fall back to pre-built assets.
 			return herrors.ErrFeatureNotAvailable
 		}
@@ -152,7 +153,10 @@ func (t *babelTransformation) Transform(ctx *resources.ResourceTransformationCtx
 	}
 	cmdArgs = append(cmdArgs, "--filename="+ctx.SourcePath)
 
-	cmd := exec.Command(binary, cmdArgs...)
+	cmd, err := hexec.SafeCommand(binary, cmdArgs...)
+	if err != nil {
+		return err
+	}
 
 	cmd.Stdout = ctx.To
 	cmd.Stderr = io.MultiWriter(infoW, &errBuf)
diff --git a/resources/resource_transformers/postcss/postcss.go b/resources/resource_transformers/postcss/postcss.go
@@ -19,13 +19,16 @@ import (
 	"encoding/hex"
 	"io"
 	"io/ioutil"
-	"os/exec"
 	"path"
 	"path/filepath"
 	"regexp"
 	"strconv"
 	"strings"
 
+	"github.com/cli/safeexec"
+
+	"github.com/gohugoio/hugo/common/hexec"
+
 	"github.com/gohugoio/hugo/common/hugo"
 
 	"github.com/gohugoio/hugo/common/loggers"
@@ -146,10 +149,10 @@ func (t *postcssTransformation) Transform(ctx *resources.ResourceTransformationC
 
 	binary := csiBinPath
 
-	if _, err := exec.LookPath(binary); err != nil {
+	if _, err := safeexec.LookPath(binary); err != nil {
 		// Try PATH
 		binary = binaryName
-		if _, err := exec.LookPath(binary); err != nil {
+		if _, err := safeexec.LookPath(binary); err != nil {
 			// This may be on a CI server etc. Will fall back to pre-built assets.
 			return herrors.ErrFeatureNotAvailable
 		}
@@ -186,7 +189,10 @@ func (t *postcssTransformation) Transform(ctx *resources.ResourceTransformationC
 		cmdArgs = append(cmdArgs, optArgs...)
 	}
 
-	cmd := exec.Command(binary, cmdArgs...)
+	cmd, err := hexec.SafeCommand(binary, cmdArgs...)
+	if err != nil {
+		return err
+	}
 
 	var errBuf bytes.Buffer
 	infoW := loggers.LoggerToWriterWithPrefix(logger.Info(), "postcss")
diff --git a/scripts/fork_go_templates/main.go b/scripts/fork_go_templates/main.go
@@ -5,11 +5,12 @@ import (
 	"io/ioutil"
 	"log"
 	"os"
-	"os/exec"
 	"path/filepath"
 	"regexp"
 	"strings"
 
+	"github.com/gohugoio/hugo/common/hexec"
+
 	"github.com/gohugoio/hugo/common/hugio"
 
 	"github.com/spf13/afero"
@@ -203,7 +204,7 @@ func removeAll(expression, content string) string {
 }
 
 func rewrite(filename, rule string) {
-	cmf := exec.Command("gofmt", "-w", "-r", rule, filename)
+	cmf, _ := hexec.SafeCommand("gofmt", "-w", "-r", rule, filename)
 	out, err := cmf.CombinedOutput()
 	if err != nil {
 		log.Fatal("gofmt failed:", string(out))
@@ -211,7 +212,7 @@ func rewrite(filename, rule string) {
 }
 
 func goimports(dir string) {
-	cmf := exec.Command("goimports", "-w", dir)
+	cmf, _ := hexec.SafeCommand("goimports", "-w", dir)
 	out, err := cmf.CombinedOutput()
 	if err != nil {
 		log.Fatal("goimports failed:", string(out))
@@ -219,7 +220,7 @@ func goimports(dir string) {
 }
 
 func gofmt(dir string) {
-	cmf := exec.Command("gofmt", "-w", dir)
+	cmf, _ := hexec.SafeCommand("gofmt", "-w", dir)
 	out, err := cmf.CombinedOutput()
 	if err != nil {
 		log.Fatal("gofmt failed:", string(out))
diff --git a/tpl/internal/go_templates/testenv/testenv.go b/tpl/internal/go_templates/testenv/testenv.go
@@ -13,7 +13,6 @@ package testenv
 import (
 	"errors"
 	"flag"
-	"github.com/gohugoio/hugo/tpl/internal/go_templates/cfg"
 	"os"
 	"os/exec"
 	"path/filepath"
@@ -22,6 +21,9 @@ import (
 	"strings"
 	"sync"
 	"testing"
+
+	"github.com/cli/safeexec"
+	"github.com/gohugoio/hugo/tpl/internal/go_templates/cfg"
 )
 
 // Builder reports the name of the builder running this test
@@ -111,7 +113,7 @@ func GoTool() (string, error) {
 	if _, err := os.Stat(path); err == nil {
 		return path, nil
 	}
-	goBin, err := exec.LookPath("go" + exeSuffix)
+	goBin, err := safeexec.LookPath("go" + exeSuffix)
 	if err != nil {
 		return "", errors.New("cannot find go tool: " + err.Error())
 	}
@@ -162,7 +164,7 @@ func MustHaveExecPath(t testing.TB, path string) {
 
 	err, found := execPaths.Load(path)
 	if !found {
-		_, err = exec.LookPath(path)
+		_, err = safeexec.LookPath(path)
 		err, _ = execPaths.LoadOrStore(path, err)
 	}
 	if err != nil {