Improve LookPath

This commit is contained in:
Bjørn Erik Pedersen 2020-12-18 18:20:12 +01:00
parent ae2d1bd52d
commit 10ae7c3210
No known key found for this signature in database
GPG key ID: 330E6E2BD4859D8F
18 changed files with 129 additions and 42 deletions

View file

@ -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
}

View file

@ -18,12 +18,12 @@ import (
"bytes" "bytes"
"io" "io"
"os" "os"
"os/exec"
"path/filepath" "path/filepath"
"strings" "strings"
"github.com/pkg/errors" "github.com/pkg/errors"
"github.com/gohugoio/hugo/common/hexec"
"github.com/gohugoio/hugo/hugofs/files" "github.com/gohugoio/hugo/hugofs/files"
"github.com/gohugoio/hugo/hugofs" "github.com/gohugoio/hugo/hugofs"
@ -105,7 +105,10 @@ func NewContent(
jww.FEEDBACK.Printf("Editing %s with %q ...\n", targetPath, editor) jww.FEEDBACK.Printf("Editing %s with %q ...\n", targetPath, editor)
editorCmd := append(strings.Fields(editor), contentPath) 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.Stdin = os.Stdin
cmd.Stdout = os.Stdout cmd.Stdout = os.Stdout
cmd.Stderr = os.Stderr cmd.Stderr = os.Stderr

1
go.mod
View file

@ -13,6 +13,7 @@ require (
github.com/bep/gitmap v1.1.2 github.com/bep/gitmap v1.1.2
github.com/bep/golibsass v0.7.0 github.com/bep/golibsass v0.7.0
github.com/bep/tmc v0.5.1 github.com/bep/tmc v0.5.1
github.com/cli/safeexec v1.0.0
github.com/disintegration/gift v1.2.1 github.com/disintegration/gift v1.2.1
github.com/dlclark/regexp2 v1.4.0 // indirect github.com/dlclark/regexp2 v1.4.0 // indirect
github.com/dustin/go-humanize v1.0.0 github.com/dustin/go-humanize v1.0.0

2
go.sum
View file

@ -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/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/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/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/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/cncf/udpa/go v0.0.0-20191209042840-269d4d468f6f/go.mod h1:M8M6+tZqaGXZJjfX53e64911xZQV5JYwmTeXPW+k8Sc=
github.com/coreos/bbolt v1.3.2 h1:wZwiHHUieZCquLkDL0B8UhzreNWsPHooDAG3q34zk0s= github.com/coreos/bbolt v1.3.2 h1:wZwiHHUieZCquLkDL0B8UhzreNWsPHooDAG3q34zk0s=

View file

@ -16,11 +16,12 @@ package hugolib
import ( import (
"fmt" "fmt"
"os" "os"
"os/exec"
"path/filepath" "path/filepath"
"runtime" "runtime"
"testing" "testing"
"github.com/gohugoio/hugo/common/hexec"
"github.com/gohugoio/hugo/htesting" "github.com/gohugoio/hugo/htesting"
"github.com/spf13/viper" "github.com/spf13/viper"
@ -125,7 +126,9 @@ TS: {{ template "print" $ts }}
b.WithSourceFile("assets/js/included.js", includedJS) 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.Assert(err, qt.IsNil, qt.Commentf(string(out)))
b.Build(BuildCfg{}) b.Build(BuildCfg{})
@ -193,7 +196,8 @@ require github.com/gohugoio/hugoTestProjectJSModImports v0.5.0 // indirect
}`) }`)
b.Assert(os.Chdir(workDir), qt.IsNil) 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.Assert(err, qt.IsNil)
b.Build(BuildCfg{}) b.Build(BuildCfg{})

View file

@ -16,11 +16,12 @@ package hugolib
import ( import (
"bytes" "bytes"
"os" "os"
"os/exec"
"path/filepath" "path/filepath"
"runtime" "runtime"
"testing" "testing"
"github.com/gohugoio/hugo/common/hexec"
jww "github.com/spf13/jwalterweatherman" jww "github.com/spf13/jwalterweatherman"
"github.com/gohugoio/hugo/htesting" "github.com/gohugoio/hugo/htesting"
@ -111,7 +112,8 @@ Transpiled: {{ $transpiled.Content | safeJS }}
b.WithSourceFile("babel.config.js", babelConfig) b.WithSourceFile("babel.config.js", babelConfig)
b.Assert(os.Chdir(workDir), qt.IsNil) 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.Assert(err, qt.IsNil)
b.Build(BuildCfg{}) b.Build(BuildCfg{})

View file

@ -19,13 +19,15 @@ import (
"io" "io"
"math/rand" "math/rand"
"os" "os"
"os/exec"
"path/filepath" "path/filepath"
"runtime" "runtime"
"strings" "strings"
"testing" "testing"
"time" "time"
"github.com/gohugoio/hugo/common/hexec"
jww "github.com/spf13/jwalterweatherman" jww "github.com/spf13/jwalterweatherman"
"github.com/gohugoio/hugo/common/herrors" "github.com/gohugoio/hugo/common/herrors"
@ -930,7 +932,8 @@ class-in-b {
b.WithSourceFile("postcss.config.js", postcssConfig) b.WithSourceFile("postcss.config.js", postcssConfig)
b.Assert(os.Chdir(workDir), qt.IsNil) 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.Assert(err, qt.IsNil)
b.Build(BuildCfg{}) b.Build(BuildCfg{})

View file

@ -18,9 +18,10 @@ package asciidocext
import ( import (
"bytes" "bytes"
"os/exec"
"path/filepath" "path/filepath"
"github.com/cli/safeexec"
"github.com/gohugoio/hugo/identity" "github.com/gohugoio/hugo/identity"
"github.com/gohugoio/hugo/markup/asciidocext/asciidocext_config" "github.com/gohugoio/hugo/markup/asciidocext/asciidocext_config"
"github.com/gohugoio/hugo/markup/converter" "github.com/gohugoio/hugo/markup/converter"
@ -193,7 +194,7 @@ func (a *asciidocConverter) appendArg(args []string, option, value, defaultValue
} }
func getAsciidoctorExecPath() string { func getAsciidoctorExecPath() string {
path, err := exec.LookPath("asciidoctor") path, err := safeexec.LookPath("asciidoctor")
if err != nil { if err != nil {
return "" return ""
} }

View file

@ -2,9 +2,11 @@ package internal
import ( import (
"bytes" "bytes"
"os/exec"
"strings" "strings"
"github.com/cli/safeexec"
"github.com/gohugoio/hugo/common/hexec"
"github.com/gohugoio/hugo/markup/converter" "github.com/gohugoio/hugo/markup/converter"
) )
@ -13,12 +15,16 @@ func ExternallyRenderContent(
ctx converter.DocumentContext, ctx converter.DocumentContext,
content []byte, path string, args []string) []byte { content []byte, path string, args []string) []byte {
logger := cfg.Logger 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) cmd.Stdin = bytes.NewReader(content)
var out, cmderr bytes.Buffer var out, cmderr bytes.Buffer
cmd.Stdout = &out cmd.Stdout = &out
cmd.Stderr = &cmderr cmd.Stderr = &cmderr
err := cmd.Run() err = cmd.Run()
// Most external helpers exit w/ non-zero exit code only if severe, i.e. // 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 // halting errors occurred. -> log stderr output regardless of state of err
for _, item := range strings.Split(cmderr.String(), "\n") { for _, item := range strings.Split(cmderr.String(), "\n") {
@ -40,9 +46,9 @@ func normalizeExternalHelperLineFeeds(content []byte) []byte {
} }
func GetPythonExecPath() string { func GetPythonExecPath() string {
path, err := exec.LookPath("python") path, err := safeexec.LookPath("python")
if err != nil { if err != nil {
path, err = exec.LookPath("python.exe") path, err = safeexec.LookPath("python.exe")
if err != nil { if err != nil {
return "" return ""
} }

View file

@ -15,8 +15,7 @@
package pandoc package pandoc
import ( import (
"os/exec" "github.com/cli/safeexec"
"github.com/gohugoio/hugo/identity" "github.com/gohugoio/hugo/identity"
"github.com/gohugoio/hugo/markup/internal" "github.com/gohugoio/hugo/markup/internal"
@ -65,7 +64,7 @@ func (c *pandocConverter) getPandocContent(src []byte, ctx converter.DocumentCon
} }
func getPandocExecPath() string { func getPandocExecPath() string {
path, err := exec.LookPath("pandoc") path, err := safeexec.LookPath("pandoc")
if err != nil { if err != nil {
return "" return ""
} }

View file

@ -16,9 +16,10 @@ package rst
import ( import (
"bytes" "bytes"
"os/exec"
"runtime" "runtime"
"github.com/cli/safeexec"
"github.com/gohugoio/hugo/identity" "github.com/gohugoio/hugo/identity"
"github.com/gohugoio/hugo/markup/internal" "github.com/gohugoio/hugo/markup/internal"
@ -96,9 +97,9 @@ func (c *rstConverter) getRstContent(src []byte, ctx converter.DocumentContext)
} }
func getRstExecPath() string { func getRstExecPath() string {
path, err := exec.LookPath("rst2html") path, err := safeexec.LookPath("rst2html")
if err != nil { if err != nil {
path, err = exec.LookPath("rst2html.py") path, err = safeexec.LookPath("rst2html.py")
if err != nil { if err != nil {
return "" return ""
} }

View file

@ -28,6 +28,8 @@ import (
"strings" "strings"
"time" "time"
"github.com/gohugoio/hugo/common/hexec"
hglob "github.com/gohugoio/hugo/hugofs/glob" hglob "github.com/gohugoio/hugo/hugofs/glob"
"github.com/gobwas/glob" "github.com/gobwas/glob"
@ -537,7 +539,10 @@ func (c *Client) runGo(
} }
stderr := new(bytes.Buffer) 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.Env = c.environ
cmd.Dir = c.ccfg.WorkingDir cmd.Dir = c.ccfg.WorkingDir

View file

@ -15,11 +15,12 @@ package releaser
import ( import (
"fmt" "fmt"
"os/exec"
"regexp" "regexp"
"sort" "sort"
"strconv" "strconv"
"strings" "strings"
"github.com/gohugoio/hugo/common/hexec"
) )
var issueRe = regexp.MustCompile(`(?i)[Updates?|Closes?|Fix.*|See] #(\d+)`) var issueRe = regexp.MustCompile(`(?i)[Updates?|Closes?|Fix.*|See] #(\d+)`)
@ -148,7 +149,7 @@ func extractIssues(body string) []int {
type gitInfos []gitInfo type gitInfos []gitInfo
func git(args ...string) (string, error) { func git(args ...string) (string, error) {
cmd := exec.Command("git", args...) cmd, _ := hexec.SafeCommand("git", args...)
out, err := cmd.CombinedOutput() out, err := cmd.CombinedOutput()
if err != nil { if err != nil {
return "", fmt.Errorf("git failed: %q: %q (%q)", err, out, args) return "", fmt.Errorf("git failed: %q: %q (%q)", err, out, args)

View file

@ -20,11 +20,12 @@ import (
"io/ioutil" "io/ioutil"
"log" "log"
"os" "os"
"os/exec"
"path/filepath" "path/filepath"
"regexp" "regexp"
"strings" "strings"
"github.com/gohugoio/hugo/common/hexec"
"github.com/gohugoio/hugo/common/hugo" "github.com/gohugoio/hugo/common/hugo"
"github.com/pkg/errors" "github.com/pkg/errors"
) )
@ -266,7 +267,7 @@ func (r *ReleaseHandler) release(releaseNotesFile string) error {
args = append(args, "--skip-publish") args = append(args, "--skip-publish")
} }
cmd := exec.Command("goreleaser", args...) cmd, _ := hexec.SafeCommand("goreleaser", args...)
cmd.Stdout = os.Stdout cmd.Stdout = os.Stdout
cmd.Stderr = os.Stderr cmd.Stderr = os.Stderr
err := cmd.Run() err := cmd.Run()

View file

@ -16,10 +16,11 @@ package babel
import ( import (
"bytes" "bytes"
"io" "io"
"os/exec"
"path/filepath" "path/filepath"
"strconv" "strconv"
"github.com/cli/safeexec"
"github.com/gohugoio/hugo/common/hexec"
"github.com/gohugoio/hugo/common/loggers" "github.com/gohugoio/hugo/common/loggers"
"github.com/gohugoio/hugo/common/hugo" "github.com/gohugoio/hugo/common/hugo"
@ -108,10 +109,10 @@ func (t *babelTransformation) Transform(ctx *resources.ResourceTransformationCtx
binary := csiBinPath binary := csiBinPath
if _, err := exec.LookPath(binary); err != nil { if _, err := safeexec.LookPath(binary); err != nil {
// Try PATH // Try PATH
binary = binaryName 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. // This may be on a CI server etc. Will fall back to pre-built assets.
return herrors.ErrFeatureNotAvailable return herrors.ErrFeatureNotAvailable
} }
@ -152,7 +153,10 @@ func (t *babelTransformation) Transform(ctx *resources.ResourceTransformationCtx
} }
cmdArgs = append(cmdArgs, "--filename="+ctx.SourcePath) 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.Stdout = ctx.To
cmd.Stderr = io.MultiWriter(infoW, &errBuf) cmd.Stderr = io.MultiWriter(infoW, &errBuf)

View file

@ -19,13 +19,16 @@ import (
"encoding/hex" "encoding/hex"
"io" "io"
"io/ioutil" "io/ioutil"
"os/exec"
"path" "path"
"path/filepath" "path/filepath"
"regexp" "regexp"
"strconv" "strconv"
"strings" "strings"
"github.com/cli/safeexec"
"github.com/gohugoio/hugo/common/hexec"
"github.com/gohugoio/hugo/common/hugo" "github.com/gohugoio/hugo/common/hugo"
"github.com/gohugoio/hugo/common/loggers" "github.com/gohugoio/hugo/common/loggers"
@ -146,10 +149,10 @@ func (t *postcssTransformation) Transform(ctx *resources.ResourceTransformationC
binary := csiBinPath binary := csiBinPath
if _, err := exec.LookPath(binary); err != nil { if _, err := safeexec.LookPath(binary); err != nil {
// Try PATH // Try PATH
binary = binaryName 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. // This may be on a CI server etc. Will fall back to pre-built assets.
return herrors.ErrFeatureNotAvailable return herrors.ErrFeatureNotAvailable
} }
@ -186,7 +189,10 @@ func (t *postcssTransformation) Transform(ctx *resources.ResourceTransformationC
cmdArgs = append(cmdArgs, optArgs...) cmdArgs = append(cmdArgs, optArgs...)
} }
cmd := exec.Command(binary, cmdArgs...) cmd, err := hexec.SafeCommand(binary, cmdArgs...)
if err != nil {
return err
}
var errBuf bytes.Buffer var errBuf bytes.Buffer
infoW := loggers.LoggerToWriterWithPrefix(logger.Info(), "postcss") infoW := loggers.LoggerToWriterWithPrefix(logger.Info(), "postcss")

View file

@ -5,11 +5,12 @@ import (
"io/ioutil" "io/ioutil"
"log" "log"
"os" "os"
"os/exec"
"path/filepath" "path/filepath"
"regexp" "regexp"
"strings" "strings"
"github.com/gohugoio/hugo/common/hexec"
"github.com/gohugoio/hugo/common/hugio" "github.com/gohugoio/hugo/common/hugio"
"github.com/spf13/afero" "github.com/spf13/afero"
@ -203,7 +204,7 @@ func removeAll(expression, content string) string {
} }
func rewrite(filename, rule 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() out, err := cmf.CombinedOutput()
if err != nil { if err != nil {
log.Fatal("gofmt failed:", string(out)) log.Fatal("gofmt failed:", string(out))
@ -211,7 +212,7 @@ func rewrite(filename, rule string) {
} }
func goimports(dir string) { func goimports(dir string) {
cmf := exec.Command("goimports", "-w", dir) cmf, _ := hexec.SafeCommand("goimports", "-w", dir)
out, err := cmf.CombinedOutput() out, err := cmf.CombinedOutput()
if err != nil { if err != nil {
log.Fatal("goimports failed:", string(out)) log.Fatal("goimports failed:", string(out))
@ -219,7 +220,7 @@ func goimports(dir string) {
} }
func gofmt(dir string) { func gofmt(dir string) {
cmf := exec.Command("gofmt", "-w", dir) cmf, _ := hexec.SafeCommand("gofmt", "-w", dir)
out, err := cmf.CombinedOutput() out, err := cmf.CombinedOutput()
if err != nil { if err != nil {
log.Fatal("gofmt failed:", string(out)) log.Fatal("gofmt failed:", string(out))

View file

@ -13,7 +13,6 @@ package testenv
import ( import (
"errors" "errors"
"flag" "flag"
"github.com/gohugoio/hugo/tpl/internal/go_templates/cfg"
"os" "os"
"os/exec" "os/exec"
"path/filepath" "path/filepath"
@ -22,6 +21,9 @@ import (
"strings" "strings"
"sync" "sync"
"testing" "testing"
"github.com/cli/safeexec"
"github.com/gohugoio/hugo/tpl/internal/go_templates/cfg"
) )
// Builder reports the name of the builder running this test // 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 { if _, err := os.Stat(path); err == nil {
return path, nil return path, nil
} }
goBin, err := exec.LookPath("go" + exeSuffix) goBin, err := safeexec.LookPath("go" + exeSuffix)
if err != nil { if err != nil {
return "", errors.New("cannot find go tool: " + err.Error()) 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) err, found := execPaths.Load(path)
if !found { if !found {
_, err = exec.LookPath(path) _, err = safeexec.LookPath(path)
err, _ = execPaths.LoadOrStore(path, err) err, _ = execPaths.LoadOrStore(path, err)
} }
if err != nil { if err != nil {