Merge pull request from GHSA-6rw3-3whw-jvjj
Report errors when finding executables and revise PATH search tests
This commit is contained in:
commit
46801d3b4e
@ -118,8 +118,11 @@ func postCloneSubmodules(args []string) error {
|
||||
// Use `git submodule foreach --recursive` to cascade into nested submodules
|
||||
// Also good to call a new instance of git-lfs rather than do things
|
||||
// inside this instance, since that way we get a clean env in that subrepo
|
||||
cmd := subprocess.ExecCommand("git", "submodule", "foreach", "--recursive",
|
||||
cmd, err := subprocess.ExecCommand("git", "submodule", "foreach", "--recursive",
|
||||
"git lfs pull")
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
cmd.Stderr = os.Stderr
|
||||
cmd.Stdin = os.Stdin
|
||||
cmd.Stdout = os.Stdout
|
||||
|
@ -4,6 +4,7 @@ import (
|
||||
"os"
|
||||
"strings"
|
||||
|
||||
"github.com/git-lfs/git-lfs/v3/errors"
|
||||
"github.com/git-lfs/git-lfs/v3/git"
|
||||
"github.com/git-lfs/git-lfs/v3/lfs"
|
||||
"github.com/git-lfs/git-lfs/v3/tools/humanize"
|
||||
@ -50,7 +51,11 @@ func lsFilesCommand(cmd *cobra.Command, args []string) {
|
||||
} else {
|
||||
fullref, err := git.CurrentRef()
|
||||
if err != nil {
|
||||
ref = git.EmptyTree()
|
||||
ref, err = git.EmptyTree()
|
||||
if err != nil {
|
||||
ExitWithError(errors.Wrap(
|
||||
err, tr.Tr.Get("Could not read empty Git tree object")))
|
||||
}
|
||||
} else {
|
||||
ref = fullref.Sha
|
||||
}
|
||||
|
@ -28,8 +28,12 @@ func statusCommand(cmd *cobra.Command, args []string) {
|
||||
ref, _ := git.CurrentRef()
|
||||
|
||||
scanIndexAt := "HEAD"
|
||||
var err error
|
||||
if ref == nil {
|
||||
scanIndexAt = git.EmptyTree()
|
||||
scanIndexAt, err = git.EmptyTree()
|
||||
if err != nil {
|
||||
ExitWithError(err)
|
||||
}
|
||||
}
|
||||
|
||||
scanner, err := lfs.NewPointerScanner(cfg.GitEnv(), cfg.OSEnv())
|
||||
|
@ -48,7 +48,10 @@ func cleanRootPath(pattern string) string {
|
||||
|
||||
if len(winBashPrefix) < 1 {
|
||||
// cmd.Path is something like C:\Program Files\Git\usr\bin\pwd.exe
|
||||
cmd := subprocess.ExecCommand("pwd")
|
||||
cmd, err := subprocess.ExecCommand("pwd")
|
||||
if err != nil {
|
||||
return pattern
|
||||
}
|
||||
winBashPrefix = strings.Replace(filepath.Dir(filepath.Dir(filepath.Dir(cmd.Path))), `\`, "/", -1) + "/"
|
||||
}
|
||||
|
||||
|
@ -147,7 +147,10 @@ func (i *gitIndexer) Add(path string) error {
|
||||
|
||||
if i.cmd == nil {
|
||||
// Fire up the update-index command
|
||||
cmd := git.UpdateIndexFromStdin()
|
||||
cmd, err := git.UpdateIndexFromStdin()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
cmd.Stdout = &i.output
|
||||
cmd.Stderr = &i.output
|
||||
stdin, err := cmd.StdinPipe()
|
||||
|
@ -241,7 +241,11 @@ func (a *AskPassCredentialHelper) getFromProgram(valueType credValueType, u *url
|
||||
|
||||
// 'cmd' will run the GIT_ASKPASS (or core.askpass) command prompting
|
||||
// for the desired valueType (`Username` or `Password`)
|
||||
cmd := subprocess.ExecCommand(a.Program, a.args(fmt.Sprintf("%s for %q", valueString, u))...)
|
||||
cmd, errVal := subprocess.ExecCommand(a.Program, a.args(fmt.Sprintf("%s for %q", valueString, u))...)
|
||||
if errVal != nil {
|
||||
tracerx.Printf("creds: failed to find GIT_ASKPASS command: %s", a.Program)
|
||||
return "", errVal
|
||||
}
|
||||
cmd.Stderr = &err
|
||||
cmd.Stdout = &value
|
||||
|
||||
@ -301,7 +305,10 @@ func (h *commandCredentialHelper) Approve(creds Creds) error {
|
||||
|
||||
func (h *commandCredentialHelper) exec(subcommand string, input Creds) (Creds, error) {
|
||||
output := new(bytes.Buffer)
|
||||
cmd := subprocess.ExecCommand("git", "credential", subcommand)
|
||||
cmd, err := subprocess.ExecCommand("git", "credential", subcommand)
|
||||
if err != nil {
|
||||
return nil, errors.New(tr.Tr.Get("failed to find `git credential %s`: %v", subcommand, err))
|
||||
}
|
||||
cmd.Stdin = bufferCreds(input)
|
||||
cmd.Stdout = output
|
||||
/*
|
||||
@ -316,7 +323,7 @@ func (h *commandCredentialHelper) exec(subcommand string, input Creds) (Creds, e
|
||||
*/
|
||||
cmd.Stderr = os.Stderr
|
||||
|
||||
err := cmd.Start()
|
||||
err = cmd.Start()
|
||||
if err == nil {
|
||||
err = cmd.Wait()
|
||||
}
|
||||
|
@ -199,7 +199,10 @@ func (c *Configuration) Source() (*ConfigurationSource, error) {
|
||||
|
||||
func (c *Configuration) gitConfig(args ...string) (string, error) {
|
||||
args = append([]string{"config", "--includes"}, args...)
|
||||
cmd := subprocess.ExecCommand("git", args...)
|
||||
cmd, err := subprocess.ExecCommand("git", args...)
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
if len(c.GitDir) > 0 {
|
||||
cmd.Dir = c.GitDir
|
||||
}
|
||||
|
109
git/git.go
109
git/git.go
@ -148,17 +148,20 @@ func IsZeroObjectID(s string) bool {
|
||||
return false
|
||||
}
|
||||
|
||||
func EmptyTree() string {
|
||||
func EmptyTree() (string, error) {
|
||||
emptyTreeMutex.Lock()
|
||||
defer emptyTreeMutex.Unlock()
|
||||
|
||||
if len(emptyTree) == 0 {
|
||||
cmd := gitNoLFS("hash-object", "-t", "tree", "/dev/null")
|
||||
cmd, err := gitNoLFS("hash-object", "-t", "tree", "/dev/null")
|
||||
if err != nil {
|
||||
return "", errors.New(tr.Tr.Get("failed to find `git hash-object`: %v", err))
|
||||
}
|
||||
cmd.Stdin = nil
|
||||
out, _ := cmd.Output()
|
||||
emptyTree = strings.TrimSpace(string(out))
|
||||
}
|
||||
return emptyTree
|
||||
return emptyTree, nil
|
||||
}
|
||||
|
||||
// Some top level information about a commit (only first line of message)
|
||||
@ -198,7 +201,7 @@ func gitConfigNoLFS(args ...string) []string {
|
||||
}
|
||||
|
||||
// Invoke Git with disabled LFS filters
|
||||
func gitNoLFS(args ...string) *subprocess.Cmd {
|
||||
func gitNoLFS(args ...string) (*subprocess.Cmd, error) {
|
||||
return subprocess.ExecCommand("git", gitConfigNoLFS(args...)...)
|
||||
}
|
||||
|
||||
@ -211,7 +214,7 @@ func gitNoLFSBuffered(args ...string) (*subprocess.BufferedCmd, error) {
|
||||
}
|
||||
|
||||
// Invoke Git with enabled LFS filters
|
||||
func git(args ...string) *subprocess.Cmd {
|
||||
func git(args ...string) (*subprocess.Cmd, error) {
|
||||
return subprocess.ExecCommand("git", args...)
|
||||
}
|
||||
|
||||
@ -253,7 +256,10 @@ func DiffIndex(ref string, cached bool, refresh bool) (*bufio.Scanner, error) {
|
||||
}
|
||||
|
||||
func HashObject(r io.Reader) (string, error) {
|
||||
cmd := gitNoLFS("hash-object", "--stdin")
|
||||
cmd, err := gitNoLFS("hash-object", "--stdin")
|
||||
if err != nil {
|
||||
return "", errors.New(tr.Tr.Get("failed to find `git hash-object`: %v", err))
|
||||
}
|
||||
cmd.Stdin = r
|
||||
out, err := cmd.Output()
|
||||
if err != nil {
|
||||
@ -381,7 +387,10 @@ func (c *Configuration) RemoteBranchForLocalBranch(localBranch string) string {
|
||||
}
|
||||
|
||||
func RemoteList() ([]string, error) {
|
||||
cmd := gitNoLFS("remote")
|
||||
cmd, err := gitNoLFS("remote")
|
||||
if err != nil {
|
||||
return nil, errors.New(tr.Tr.Get("failed to find `git remote`: %v", err))
|
||||
}
|
||||
|
||||
outp, err := cmd.StdoutPipe()
|
||||
if err != nil {
|
||||
@ -401,7 +410,10 @@ func RemoteList() ([]string, error) {
|
||||
}
|
||||
|
||||
func RemoteURLs(push bool) (map[string][]string, error) {
|
||||
cmd := gitNoLFS("remote", "-v")
|
||||
cmd, err := gitNoLFS("remote", "-v")
|
||||
if err != nil {
|
||||
return nil, errors.New(tr.Tr.Get("failed to find `git remote -v`: %v", err))
|
||||
}
|
||||
|
||||
outp, err := cmd.StdoutPipe()
|
||||
if err != nil {
|
||||
@ -451,7 +463,10 @@ func MapRemoteURL(url string, push bool) (string, bool) {
|
||||
// Refs returns all of the local and remote branches and tags for the current
|
||||
// repository. Other refs (HEAD, refs/stash, git notes) are ignored.
|
||||
func LocalRefs() ([]*Ref, error) {
|
||||
cmd := gitNoLFS("show-ref")
|
||||
cmd, err := gitNoLFS("show-ref")
|
||||
if err != nil {
|
||||
return nil, errors.New(tr.Tr.Get("failed to find `git show-ref`: %v", err))
|
||||
}
|
||||
|
||||
outp, err := cmd.StdoutPipe()
|
||||
if err != nil {
|
||||
@ -500,7 +515,10 @@ func UpdateRefIn(wd string, ref *Ref, to []byte, reason string) error {
|
||||
args = append(args, "-m", reason)
|
||||
}
|
||||
|
||||
cmd := gitNoLFS(args...)
|
||||
cmd, err := gitNoLFS(args...)
|
||||
if err != nil {
|
||||
return errors.New(tr.Tr.Get("failed to find `git update-ref`: %v", err))
|
||||
}
|
||||
cmd.Dir = wd
|
||||
|
||||
return cmd.Run()
|
||||
@ -578,7 +596,7 @@ func RewriteLocalPathAsURL(path string) string {
|
||||
return fmt.Sprintf("file://%s%s", slash, filepath.ToSlash(path))
|
||||
}
|
||||
|
||||
func UpdateIndexFromStdin() *subprocess.Cmd {
|
||||
func UpdateIndexFromStdin() (*subprocess.Cmd, error) {
|
||||
return git("update-index", "-q", "--refresh", "--stdin")
|
||||
}
|
||||
|
||||
@ -588,10 +606,13 @@ func UpdateIndexFromStdin() *subprocess.Cmd {
|
||||
// includeRemoteBranches: true to include refs on remote branches
|
||||
// onlyRemote: set to non-blank to only include remote branches on a single remote
|
||||
func RecentBranches(since time.Time, includeRemoteBranches bool, onlyRemote string) ([]*Ref, error) {
|
||||
cmd := gitNoLFS("for-each-ref",
|
||||
cmd, err := gitNoLFS("for-each-ref",
|
||||
`--sort=-committerdate`,
|
||||
`--format=%(refname) %(objectname) %(committerdate:iso)`,
|
||||
"refs")
|
||||
if err != nil {
|
||||
return nil, errors.New(tr.Tr.Get("failed to find `git for-each-ref`: %v", err))
|
||||
}
|
||||
outp, err := cmd.StdoutPipe()
|
||||
if err != nil {
|
||||
return nil, errors.New(tr.Tr.Get("failed to call `git for-each-ref`: %v", err))
|
||||
@ -687,8 +708,11 @@ func FormatGitDate(tm time.Time) string {
|
||||
|
||||
// Get summary information about a commit
|
||||
func GetCommitSummary(commit string) (*CommitSummary, error) {
|
||||
cmd := gitNoLFS("show", "-s",
|
||||
cmd, err := gitNoLFS("show", "-s",
|
||||
`--format=%H|%h|%P|%ai|%ci|%ae|%an|%ce|%cn|%s`, commit)
|
||||
if err != nil {
|
||||
return nil, errors.New(tr.Tr.Get("failed to find `git show`: %v", err))
|
||||
}
|
||||
|
||||
out, err := cmd.CombinedOutput()
|
||||
if err != nil {
|
||||
@ -722,7 +746,10 @@ func GetCommitSummary(commit string) (*CommitSummary, error) {
|
||||
}
|
||||
|
||||
func GitAndRootDirs() (string, string, error) {
|
||||
cmd := gitNoLFS("rev-parse", "--git-dir", "--show-toplevel")
|
||||
cmd, err := gitNoLFS("rev-parse", "--git-dir", "--show-toplevel")
|
||||
if err != nil {
|
||||
return "", "", errors.New(tr.Tr.Get("failed to find `git rev-parse --git-dir --show-toplevel`: %v", err))
|
||||
}
|
||||
buf := &bytes.Buffer{}
|
||||
cmd.Stderr = buf
|
||||
|
||||
@ -760,7 +787,10 @@ func GitAndRootDirs() (string, string, error) {
|
||||
}
|
||||
|
||||
func RootDir() (string, error) {
|
||||
cmd := gitNoLFS("rev-parse", "--show-toplevel")
|
||||
cmd, err := gitNoLFS("rev-parse", "--show-toplevel")
|
||||
if err != nil {
|
||||
return "", errors.New(tr.Tr.Get("failed to find `git rev-parse --show-toplevel`: %v", err))
|
||||
}
|
||||
out, err := cmd.Output()
|
||||
if err != nil {
|
||||
return "", errors.New(tr.Tr.Get("failed to call `git rev-parse --show-toplevel`: %v %v", err, string(out)))
|
||||
@ -775,7 +805,12 @@ func RootDir() (string, error) {
|
||||
}
|
||||
|
||||
func GitDir() (string, error) {
|
||||
cmd := gitNoLFS("rev-parse", "--git-dir")
|
||||
cmd, err := gitNoLFS("rev-parse", "--git-dir")
|
||||
if err != nil {
|
||||
// The %w format specifier is unique to fmt.Errorf(), so we
|
||||
// do not pass it to tr.Tr.Get().
|
||||
return "", fmt.Errorf("%s: %w", tr.Tr.Get("failed to find `git rev-parse --git-dir`"), err)
|
||||
}
|
||||
buf := &bytes.Buffer{}
|
||||
cmd.Stderr = buf
|
||||
out, err := cmd.Output()
|
||||
@ -797,7 +832,10 @@ func GitCommonDir() (string, error) {
|
||||
return GitDir()
|
||||
}
|
||||
|
||||
cmd := gitNoLFS("rev-parse", "--git-common-dir")
|
||||
cmd, err := gitNoLFS("rev-parse", "--git-common-dir")
|
||||
if err != nil {
|
||||
return "", errors.New(tr.Tr.Get("failed to find `git rev-parse --git-common-dir`: %v", err))
|
||||
}
|
||||
out, err := cmd.Output()
|
||||
buf := &bytes.Buffer{}
|
||||
cmd.Stderr = buf
|
||||
@ -1055,14 +1093,17 @@ func CloneWithoutFilters(flags CloneFlags, args []string) error {
|
||||
|
||||
// Now args
|
||||
cmdargs = append(cmdargs, args...)
|
||||
cmd := gitNoLFS(cmdargs...)
|
||||
cmd, err := gitNoLFS(cmdargs...)
|
||||
if err != nil {
|
||||
return errors.New(tr.Tr.Get("failed to find `git clone`: %v", err))
|
||||
}
|
||||
|
||||
// Assign all streams direct
|
||||
cmd.Stdout = os.Stdout
|
||||
cmd.Stderr = os.Stderr
|
||||
cmd.Stdin = os.Stdin
|
||||
|
||||
err := cmd.Start()
|
||||
err = cmd.Start()
|
||||
if err != nil {
|
||||
return errors.New(tr.Tr.Get("failed to start `git clone`: %v", err))
|
||||
}
|
||||
@ -1102,7 +1143,10 @@ func Checkout(treeish string, paths []string, force bool) error {
|
||||
// currently cached locally. No remote request is made to verify them.
|
||||
func CachedRemoteRefs(remoteName string) ([]*Ref, error) {
|
||||
var ret []*Ref
|
||||
cmd := gitNoLFS("show-ref")
|
||||
cmd, err := gitNoLFS("show-ref")
|
||||
if err != nil {
|
||||
return nil, errors.New(tr.Tr.Get("failed to find `git show-ref`: %v", err))
|
||||
}
|
||||
|
||||
outp, err := cmd.StdoutPipe()
|
||||
if err != nil {
|
||||
@ -1157,7 +1201,10 @@ func Fetch(remotes ...string) error {
|
||||
// accessing the remote via git ls-remote.
|
||||
func RemoteRefs(remoteName string) ([]*Ref, error) {
|
||||
var ret []*Ref
|
||||
cmd := gitNoLFS("ls-remote", "--heads", "--tags", "-q", remoteName)
|
||||
cmd, err := gitNoLFS("ls-remote", "--heads", "--tags", "-q", remoteName)
|
||||
if err != nil {
|
||||
return nil, errors.New(tr.Tr.Get("failed to find `git ls-remote`: %v", err))
|
||||
}
|
||||
|
||||
outp, err := cmd.StdoutPipe()
|
||||
if err != nil {
|
||||
@ -1216,8 +1263,11 @@ func AllRefs() ([]*Ref, error) {
|
||||
// the given working directory "wd", or an error if those references could not
|
||||
// be loaded.
|
||||
func AllRefsIn(wd string) ([]*Ref, error) {
|
||||
cmd := gitNoLFS(
|
||||
cmd, err := gitNoLFS(
|
||||
"for-each-ref", "--format=%(objectname)%00%(refname)")
|
||||
if err != nil {
|
||||
return nil, lfserrors.Wrap(err, tr.Tr.Get("failed to find `git for-each-ref`: %v", err))
|
||||
}
|
||||
cmd.Dir = wd
|
||||
|
||||
outp, err := cmd.StdoutPipe()
|
||||
@ -1262,12 +1312,15 @@ func GetTrackedFiles(pattern string) ([]string, error) {
|
||||
rootWildcard := len(safePattern) < len(pattern) && strings.ContainsRune(safePattern, '*')
|
||||
|
||||
var ret []string
|
||||
cmd := gitNoLFS(
|
||||
cmd, err := gitNoLFS(
|
||||
"-c", "core.quotepath=false", // handle special chars in filenames
|
||||
"ls-files",
|
||||
"--cached", // include things which are staged but not committed right now
|
||||
"--", // no ambiguous patterns
|
||||
safePattern)
|
||||
if err != nil {
|
||||
return nil, errors.New(tr.Tr.Get("failed to find `git ls-files`: %v", err))
|
||||
}
|
||||
|
||||
outp, err := cmd.StdoutPipe()
|
||||
if err != nil {
|
||||
@ -1321,7 +1374,10 @@ func GetFilesChanged(from, to string) ([]string, error) {
|
||||
}
|
||||
args = append(args, "--") // no ambiguous patterns
|
||||
|
||||
cmd := gitNoLFS(args...)
|
||||
cmd, err := gitNoLFS(args...)
|
||||
if err != nil {
|
||||
return nil, errors.New(tr.Tr.Get("failed to find `git diff-tree`: %v", err))
|
||||
}
|
||||
outp, err := cmd.StdoutPipe()
|
||||
if err != nil {
|
||||
return nil, errors.New(tr.Tr.Get("failed to call `git diff-tree`: %v", err))
|
||||
@ -1352,7 +1408,10 @@ func IsFileModified(filepath string) (bool, error) {
|
||||
"--", // separator in case filename ambiguous
|
||||
filepath,
|
||||
}
|
||||
cmd := git(args...)
|
||||
cmd, err := git(args...)
|
||||
if err != nil {
|
||||
return false, lfserrors.Wrap(err, tr.Tr.Get("failed to find `git status`"))
|
||||
}
|
||||
outp, err := cmd.StdoutPipe()
|
||||
if err != nil {
|
||||
return false, lfserrors.Wrap(err, tr.Tr.Get("Failed to call `git status`"))
|
||||
|
@ -35,7 +35,10 @@ func NewLsFiles(workingDir string, standardExclude bool, untracked bool) (*LsFil
|
||||
if untracked {
|
||||
args = append(args, "--others")
|
||||
}
|
||||
cmd := gitNoLFS(args...)
|
||||
cmd, err := gitNoLFS(args...)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
cmd.Dir = workingDir
|
||||
|
||||
tracerx.Printf("NewLsFiles: running in %s git %s",
|
||||
|
@ -169,7 +169,10 @@ func NewRevListScanner(include, excluded []string, opt *ScanRefsOptions) (*RevLi
|
||||
return nil, err
|
||||
}
|
||||
|
||||
cmd := gitNoLFS(args...).Cmd
|
||||
cmd, err := gitNoLFS(args...)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if len(opt.WorkingDir) > 0 {
|
||||
cmd.Dir = opt.WorkingDir
|
||||
}
|
||||
|
@ -76,7 +76,11 @@ func pipeExtensions(cfg *config.Configuration, request *pipeRequest) (response p
|
||||
arg := strings.Replace(value, "%f", request.fileName, -1)
|
||||
args = append(args, arg)
|
||||
}
|
||||
cmd := subprocess.ExecCommand(name, args...)
|
||||
var cmd *subprocess.Cmd
|
||||
cmd, err = subprocess.ExecCommand(name, args...)
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
ec := &extCommand{cmd: cmd, result: &pipeExtResult{name: e.Name}}
|
||||
extcmds = append(extcmds, ec)
|
||||
}
|
||||
|
@ -19,7 +19,11 @@ func appendRootCAsForHostFromPlatform(pool *x509.CertPool, host string) *x509.Ce
|
||||
// either, for consistency.
|
||||
|
||||
// find system.keychain for user-added certs (don't assume location)
|
||||
cmd := subprocess.ExecCommand("/usr/bin/security", "list-keychains")
|
||||
cmd, err := subprocess.ExecCommand("/usr/bin/security", "list-keychains")
|
||||
if err != nil {
|
||||
tracerx.Printf("Error getting command to list keychains: %v", err)
|
||||
return nil
|
||||
}
|
||||
kcout, err := cmd.Output()
|
||||
if err != nil {
|
||||
tracerx.Printf("Error listing keychains: %v", err)
|
||||
@ -54,7 +58,11 @@ func appendRootCAsForHostFromPlatform(pool *x509.CertPool, host string) *x509.Ce
|
||||
}
|
||||
|
||||
func appendRootCAsFromKeychain(pool *x509.CertPool, name, keychain string) *x509.CertPool {
|
||||
cmd := subprocess.ExecCommand("/usr/bin/security", "find-certificate", "-a", "-p", "-c", name, keychain)
|
||||
cmd, err := subprocess.ExecCommand("/usr/bin/security", "find-certificate", "-a", "-p", "-c", name, keychain)
|
||||
if err != nil {
|
||||
tracerx.Printf("Error getting command to read keychain %q: %v", keychain, err)
|
||||
return pool
|
||||
}
|
||||
data, err := cmd.Output()
|
||||
if err != nil {
|
||||
tracerx.Printf("Error reading keychain %q: %v", keychain, err)
|
||||
|
@ -80,7 +80,10 @@ func (c *sshAuthClient) Resolve(e Endpoint, method string) (sshAuthResponse, err
|
||||
}
|
||||
|
||||
exe, args := ssh.GetLFSExeAndArgs(c.os, c.git, &e.SSHMetadata, "git-lfs-authenticate", endpointOperation(e, method), false)
|
||||
cmd := subprocess.ExecCommand(exe, args...)
|
||||
cmd, err := subprocess.ExecCommand(exe, args...)
|
||||
if err != nil {
|
||||
return res, err
|
||||
}
|
||||
|
||||
// Save stdout and stderr in separate buffers
|
||||
var outbuf, errbuf bytes.Buffer
|
||||
@ -90,7 +93,7 @@ func (c *sshAuthClient) Resolve(e Endpoint, method string) (sshAuthResponse, err
|
||||
now := time.Now()
|
||||
|
||||
// Execute command
|
||||
err := cmd.Start()
|
||||
err = cmd.Start()
|
||||
if err == nil {
|
||||
err = cmd.Wait()
|
||||
}
|
||||
|
@ -119,7 +119,10 @@ func gitDirAtPath(path string) (string, error) {
|
||||
return "", err
|
||||
}
|
||||
|
||||
cmd := subprocess.ExecCommand("git", "rev-parse", "--git-dir")
|
||||
cmd, err := subprocess.ExecCommand("git", "rev-parse", "--git-dir")
|
||||
if err != nil {
|
||||
return "", errors.Wrap(err, tr.Tr.Get("failed to find `git rev-parse --git-dir`"))
|
||||
}
|
||||
cmd.Cmd.Env = env
|
||||
out, err := cmd.Output()
|
||||
if err != nil {
|
||||
|
@ -34,7 +34,10 @@ func NewSSHTransfer(osEnv config.Environment, gitEnv config.Environment, meta *S
|
||||
|
||||
func startConnection(id int, osEnv config.Environment, gitEnv config.Environment, meta *SSHMetadata, operation string) (*PktlineConnection, error) {
|
||||
exe, args := GetLFSExeAndArgs(osEnv, gitEnv, meta, "git-lfs-transfer", operation, true)
|
||||
cmd := subprocess.ExecCommand(exe, args...)
|
||||
cmd, err := subprocess.ExecCommand(exe, args...)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
r, err := cmd.StdoutPipe()
|
||||
if err != nil {
|
||||
return nil, err
|
||||
|
@ -20,7 +20,10 @@ import (
|
||||
// stdout & stderr pipes, wrapped in a BufferedCmd. The stdout buffer will be
|
||||
// of stdoutBufSize bytes.
|
||||
func BufferedExec(name string, args ...string) (*BufferedCmd, error) {
|
||||
cmd := ExecCommand(name, args...)
|
||||
cmd, err := ExecCommand(name, args...)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
stdout, err := cmd.StdoutPipe()
|
||||
if err != nil {
|
||||
return nil, err
|
||||
@ -49,7 +52,11 @@ func BufferedExec(name string, args ...string) (*BufferedCmd, error) {
|
||||
|
||||
// SimpleExec is a small wrapper around os/exec.Command.
|
||||
func SimpleExec(name string, args ...string) (string, error) {
|
||||
return Output(ExecCommand(name, args...))
|
||||
cmd, err := ExecCommand(name, args...)
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
return Output(cmd)
|
||||
}
|
||||
|
||||
func Output(cmd *Cmd) (string, error) {
|
||||
|
@ -8,9 +8,13 @@ import (
|
||||
)
|
||||
|
||||
// ExecCommand is a small platform specific wrapper around os/exec.Command
|
||||
func ExecCommand(name string, arg ...string) *Cmd {
|
||||
func ExecCommand(name string, arg ...string) (*Cmd, error) {
|
||||
cmd := exec.Command(name, arg...)
|
||||
cmd.Path, _ = LookPath(name)
|
||||
cmd.Env = fetchEnvironment()
|
||||
return newCmd(cmd)
|
||||
var err error
|
||||
cmd.Path, err = LookPath(name)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
cmd.Env = fetchEnvironment()
|
||||
return newCmd(cmd), nil
|
||||
}
|
||||
|
@ -9,10 +9,14 @@ import (
|
||||
)
|
||||
|
||||
// ExecCommand is a small platform specific wrapper around os/exec.Command
|
||||
func ExecCommand(name string, arg ...string) *Cmd {
|
||||
func ExecCommand(name string, arg ...string) (*Cmd, error) {
|
||||
cmd := exec.Command(name, arg...)
|
||||
cmd.Path, _ = LookPath(name)
|
||||
var err error
|
||||
cmd.Path, err = LookPath(name)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
cmd.SysProcAttr = &syscall.SysProcAttr{HideWindow: true}
|
||||
cmd.Env = fetchEnvironment()
|
||||
return newCmd(cmd)
|
||||
return newCmd(cmd), nil
|
||||
}
|
||||
|
@ -13,6 +13,7 @@ TEST_CMDS += ../bin/git-credential-lfstest$X
|
||||
TEST_CMDS += ../bin/lfs-askpass$X
|
||||
TEST_CMDS += ../bin/lfs-ssh-echo$X
|
||||
TEST_CMDS += ../bin/lfs-ssh-proxy-test$X
|
||||
TEST_CMDS += ../bin/lfstest-badpathcheck$X
|
||||
TEST_CMDS += ../bin/lfstest-count-tests$X
|
||||
TEST_CMDS += ../bin/lfstest-customadapter$X
|
||||
TEST_CMDS += ../bin/lfstest-gitserver$X
|
||||
|
19
t/cmd/lfstest-badpathcheck.go
Normal file
19
t/cmd/lfstest-badpathcheck.go
Normal file
@ -0,0 +1,19 @@
|
||||
//go:build testtools
|
||||
// +build testtools
|
||||
|
||||
package main
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"os"
|
||||
)
|
||||
|
||||
func main() {
|
||||
fmt.Println("exploit")
|
||||
fmt.Fprintln(os.Stderr, "exploit")
|
||||
|
||||
f, err := os.Create("exploit")
|
||||
if err != nil {
|
||||
f.Close()
|
||||
}
|
||||
}
|
@ -45,8 +45,7 @@ begin_test "askpass: push with bad askpass"
|
||||
|
||||
git config "credential.helper" ""
|
||||
GIT_TERMINAL_PROMPT=0 GIT_ASKPASS="lfs-askpass-2" SSH_ASKPASS="dont-call-me" GIT_TRACE=1 git push origin main 2>&1 | tee push.log
|
||||
grep "filling with GIT_ASKPASS" push.log # attempt askpass
|
||||
grep 'credential fill error: exec: "lfs-askpass-2"' push.log # askpass fails
|
||||
grep "failed to find GIT_ASKPASS command" push.log # attempt askpass
|
||||
grep "creds: git credential fill" push.log # attempt git credential
|
||||
)
|
||||
end_test
|
||||
|
134
t/t-path.sh
134
t/t-path.sh
@ -9,16 +9,18 @@ begin_test "does not look in current directory for git"
|
||||
reponame="$(basename "$0" ".sh")"
|
||||
git init "$reponame"
|
||||
cd "$reponame"
|
||||
export PATH="$(echo "$PATH" | sed -e "s/:.:/:/g" -e "s/::/:/g")"
|
||||
|
||||
printf "#!/bin/sh\necho exploit >&2\n" > git
|
||||
chmod +x git || true
|
||||
printf "echo exploit 1>&2\n" > git.bat
|
||||
cp "$BINPATH/lfstest-badpathcheck$X" "git$X"
|
||||
|
||||
# This needs to succeed. If it fails, that could be because our malicious
|
||||
# "git" is broken but got invoked anyway.
|
||||
git lfs env > output.log 2>&1
|
||||
! grep -q 'exploit' output.log
|
||||
# This should always succeed, even if git-lfs is incorrectly searching for
|
||||
# executables in the current directory first, because the "git-lfs env"
|
||||
# command ignores all errors when it runs "git config". So we should always
|
||||
# pass this step and then, if our malicious Git was executed, detect
|
||||
# its output below. If this command does fail, something else is wrong.
|
||||
PATH="$BINPATH" PATHEXT="$X" "git-lfs$X" env >output.log 2>&1
|
||||
|
||||
grep "exploit" output.log && false
|
||||
[ ! -f exploit ]
|
||||
)
|
||||
end_test
|
||||
|
||||
@ -30,32 +32,122 @@ begin_test "does not look in current directory for git with credential helper"
|
||||
setup_remote_repo "$reponame"
|
||||
|
||||
clone_repo "$reponame" credentials-1
|
||||
export PATH="$(echo "$PATH" | sed -e "s/:.:/:/g" -e "s/::/:/g")"
|
||||
|
||||
printf "#!/bin/sh\necho exploit >&2\ntouch exploit\n" > git
|
||||
chmod +x git || true
|
||||
printf "echo exploit 1>&2\r\necho >exploit" > git.bat
|
||||
|
||||
git lfs track "*.dat"
|
||||
printf abc > z.dat
|
||||
git add z.dat
|
||||
git add .gitattributes
|
||||
git add git git.bat
|
||||
git commit -m "Add files"
|
||||
|
||||
GITPATH="$(dirname "$(command -v git)")"
|
||||
|
||||
# We add our malicious Git to the index and then remove it from the
|
||||
# work tree so it is not found early, before we perform our key test.
|
||||
# Specifically, our "git push" below will run git-lfs, which then runs
|
||||
# "git credential", so if we are looking for Git in the current directory
|
||||
# first when running a credential helper, we will fail at that point
|
||||
# because our malicious Git will be found first.
|
||||
#
|
||||
# We prefer to check for this behavior during our "git-lfs pull" further
|
||||
# below when we are populating LFS objects into a clone of this repo
|
||||
# (which contains the malicious Git), so for now we remove the malicious
|
||||
# Git as soon as possible.
|
||||
cp "$BINPATH/lfstest-badpathcheck$X" "git$X"
|
||||
PATH="$BINPATH:$GITPATH" "$GITPATH/git$X" add "git$X"
|
||||
rm "git$X"
|
||||
|
||||
git commit -m "Add files"
|
||||
git push origin HEAD
|
||||
cd ..
|
||||
|
||||
unset GIT_ASKPASS SSH_ASKPASS
|
||||
|
||||
# This needs to succeed. If it fails, that could be because our malicious
|
||||
# "git" is broken but got invoked anyway.
|
||||
GIT_LFS_SKIP_SMUDGE=1 clone_repo "$reponame" credentials-2
|
||||
# When we call "git clone" below, it will run git-lfs as a smudge filter
|
||||
# during the post-clone checkout phase, and specifically will run git-lfs
|
||||
# in the newly cloned repository directory which contains a copy of our
|
||||
# malicious Git. So, if we are looking for Git in the current directory
|
||||
# first in most cases (and not just when running a credential helper),
|
||||
# then when git-lfs runs "git config" we will fail at that point because
|
||||
# our malicious Git will be found first. This occurs even if we specify
|
||||
# GIT_LFS_SKIP_SMUDGE=1 because git-lfs will still run "git config".
|
||||
#
|
||||
# We could ignore errors from clone_repo() and then search for the output
|
||||
# of our malicious Git in the t-path-credentials-2 directory; however,
|
||||
# this may be somewhat fragile as clone_repo() performs other steps such
|
||||
# as changing the current working directory to the new repo clone and
|
||||
# attempting to run "git config" there.
|
||||
#
|
||||
# Instead, since our key check of "git-lfs pull" below will also detect
|
||||
# the general failure case where we are looking for Git in the current
|
||||
# directory first when running most commands, we temporarily uninstall
|
||||
# Git LFS so no smudge filter will execute when "git clone" checks out the
|
||||
# repository.
|
||||
#
|
||||
# We also remove any "exploit" file potentially created by our malicious
|
||||
# Git in case it was run anywhere in clone_repo(), which may happen if
|
||||
# PATH contains the "." directory already. Note that we reset PATH
|
||||
# to contain only the necessary directories in our key "git-lfs pull"
|
||||
# check below.
|
||||
git lfs uninstall
|
||||
clone_repo "$reponame" t-path-credentials-2
|
||||
rm -f exploit
|
||||
pushd ..
|
||||
git lfs install
|
||||
popd
|
||||
|
||||
git lfs pull | tee output.log
|
||||
# As noted, if we are looking for Git in the current directory first
|
||||
# only when running a credential helper, then when this runs
|
||||
# "git credential", it will find our malicious Git in the current directory
|
||||
# and execute it.
|
||||
#
|
||||
# If we are looking for Git in the current directory first when running
|
||||
# most commands (and not just when running a credential helper), then this
|
||||
# will also find our malicious Git. However, in this case it will find it
|
||||
# earlier when we try to run "git config" rather than later when we try
|
||||
# to run "git credential".
|
||||
#
|
||||
# We use a pipeline with "tee" here so as to avoid an early failure in the
|
||||
# case that our "git-lfs pull" command executes our malicious Git.
|
||||
# Unlike "git-lfs env" in the other tests, "git-lfs pull" will halt when
|
||||
# it does not receive the normal output from Git. This in turn halts
|
||||
# our test due to our use of the "set -e" option, unless we terminate a
|
||||
# pipeline with successful command like "tee".
|
||||
PATH="$BINPATH:$GITPATH" PATHEXT="$X" "git-lfs$X" pull 2>&1 | tee output.log
|
||||
|
||||
! grep -q 'exploit' output.log
|
||||
[ ! -f ../exploit ]
|
||||
grep "exploit" output.log && false
|
||||
[ ! -f exploit ]
|
||||
)
|
||||
end_test
|
||||
|
||||
begin_test "does not look in current directory for wrong binary using PATHEXT"
|
||||
(
|
||||
set -e
|
||||
|
||||
# Windows is the only platform where Go searches for executable files
|
||||
# by appending file extensions from PATHEXT.
|
||||
[ "$IS_WINDOWS" -eq 0 ] && exit 0
|
||||
|
||||
reponame="$(basename "$0" ".sh")-notfound"
|
||||
git init "$reponame"
|
||||
cd "$reponame"
|
||||
|
||||
# Go on Windows always looks in the current directory first when creating
|
||||
# a command handler, so we need a dummy git.exe for it to find there since
|
||||
# we will restrict PATH to exclude the real Git when we run "git-lfs env"
|
||||
# below. If our git-lfs incorrectly proceeds to run the command handler
|
||||
# despite not finding Git in PATH either, Go may then search for a file
|
||||
# named "." with any path extension from PATHEXT and execute that file
|
||||
# instead, so we create a malicious file named "..exe" to check this case.
|
||||
touch "git$X"
|
||||
cp "$BINPATH/lfstest-badpathcheck$X" ".$X"
|
||||
|
||||
# This should always succeed, even if git-lfs is incorrectly searching for
|
||||
# executables in the current directory first, because the "git-lfs env"
|
||||
# command ignores all errors when it runs "git config". So we should always
|
||||
# pass this step and then, if our malicious program was executed, detect
|
||||
# its output below. If this command does fail, something else is wrong.
|
||||
PATH="$BINPATH" PATHEXT="$X" "git-lfs$X" env >output.log 2>&1
|
||||
|
||||
grep "exploit" output.log && false
|
||||
[ ! -f exploit ]
|
||||
)
|
||||
end_test
|
||||
|
@ -6,12 +6,14 @@ set -e
|
||||
UNAME=$(uname -s)
|
||||
IS_WINDOWS=0
|
||||
IS_MAC=0
|
||||
X=""
|
||||
SHASUM="shasum -a 256"
|
||||
PATH_SEPARATOR="/"
|
||||
|
||||
if [[ $UNAME == MINGW* || $UNAME == MSYS* || $UNAME == CYGWIN* ]]
|
||||
then
|
||||
IS_WINDOWS=1
|
||||
X=".exe"
|
||||
|
||||
# Windows might be MSYS2 which does not have the shasum Perl wrapper
|
||||
# script by default, so use sha256sum directly. MacOS on the other hand
|
||||
|
@ -38,7 +38,10 @@ func isCygwin() bool {
|
||||
return cygwinState.Enabled()
|
||||
}
|
||||
|
||||
cmd := subprocess.ExecCommand("uname")
|
||||
cmd, err := subprocess.ExecCommand("uname")
|
||||
if err != nil {
|
||||
return false
|
||||
}
|
||||
out, err := cmd.Output()
|
||||
if err != nil {
|
||||
return false
|
||||
|
@ -28,7 +28,12 @@ func Getwd() (dir string, err error) {
|
||||
}
|
||||
|
||||
func translateCygwinPath(path string) (string, error) {
|
||||
cmd := subprocess.ExecCommand("cygpath", "-w", path)
|
||||
cmd, err := subprocess.ExecCommand("cygpath", "-w", path)
|
||||
if err != nil {
|
||||
// If cygpath doesn't exist, that's okay: just return the paths
|
||||
// as we got it.
|
||||
return path, nil
|
||||
}
|
||||
// cygpath uses ISO-8850-1 as the default encoding if the locale is not
|
||||
// set, resulting in breakage, since we want a UTF-8 path.
|
||||
env := make([]string, 0, len(cmd.Env)+1)
|
||||
|
@ -125,7 +125,10 @@ func (a *customAdapter) WorkerStarting(workerNum int) (interface{}, error) {
|
||||
// If concurrent = false we have already dialled back workers to 1
|
||||
a.Trace("xfer: starting up custom transfer process %q for worker %d", a.name, workerNum)
|
||||
cmdName, cmdArgs := subprocess.FormatForShell(subprocess.ShellQuoteSingle(a.path), a.args)
|
||||
cmd := subprocess.ExecCommand(cmdName, cmdArgs...)
|
||||
cmd, err := subprocess.ExecCommand(cmdName, cmdArgs...)
|
||||
if err != nil {
|
||||
return nil, errors.New(tr.Tr.Get("failed to find custom transfer command %q remote: %v", a.path, err))
|
||||
}
|
||||
outp, err := cmd.StdoutPipe()
|
||||
if err != nil {
|
||||
return nil, errors.New(tr.Tr.Get("failed to get stdout for custom transfer command %q remote: %v", a.path, err))
|
||||
|
Loading…
Reference in New Issue
Block a user