Repository: rsteube/carapace
Pull request #491 (2022-04-10 11:06:16)

embed execabs


Patch:
diff --git a/defaultActions.go b/defaultActions.go
index 9ae746c..c993d74 100644
--- a/defaultActions.go
+++ b/defaultActions.go
@@ -10,12 +10,11 @@ import (
 	"regexp"
 	"strings"
 
-	exec "golang.org/x/sys/execabs"
-
 	"github.com/rsteube/carapace/internal/common"
 	"github.com/rsteube/carapace/internal/config"
 	"github.com/rsteube/carapace/internal/shell/export"
 	"github.com/rsteube/carapace/pkg/style"
+	exec "github.com/rsteube/carapace/third_party/golang.org/x/sys/execabs"
 	"github.com/spf13/cobra"
 	"github.com/spf13/pflag"
 )
diff --git a/example/cmd/action/net/net.go b/example/cmd/action/net/net.go
index 27f9081..5d4f385 100644
--- a/example/cmd/action/net/net.go
+++ b/example/cmd/action/net/net.go
@@ -1,7 +1,7 @@
 package net
 
 import (
-	exec "golang.org/x/sys/execabs"
+	"os/exec"
 	"regexp"
 	"strings"
 
diff --git a/example/cmd/action/os/os.go b/example/cmd/action/os/os.go
index a16d4a8..56dabba 100644
--- a/example/cmd/action/os/os.go
+++ b/example/cmd/action/os/os.go
@@ -1,9 +1,9 @@
 package os
 
 import (
-	exec "golang.org/x/sys/execabs"
 	"io/ioutil"
 	"os"
+	"os/exec"
 	"strings"
 
 	"github.com/rsteube/carapace"
diff --git a/go.mod b/go.mod
index 5355eae..5a95a19 100644
--- a/go.mod
+++ b/go.mod
@@ -5,5 +5,4 @@ go 1.14
 require (
 	github.com/spf13/cobra v1.4.0
 	github.com/spf13/pflag v1.0.5
-	golang.org/x/sys v0.0.0-20211205182925-97ca703d548d
 )
diff --git a/go.sum b/go.sum
index ec06252..0dd8697 100644
--- a/go.sum
+++ b/go.sum
@@ -6,7 +6,5 @@ github.com/spf13/cobra v1.4.0 h1:y+wJpx64xcgO1V+RcnwW0LEHxTKRi2ZDPSBjWnrg88Q=
 github.com/spf13/cobra v1.4.0/go.mod h1:Wo4iy3BUC+X2Fybo0PDqwJIv3dNRiZLHQymsfxlB84g=
 github.com/spf13/pflag v1.0.5 h1:iy+VFUOCP1a+8yFto/drg2CJ5u0yRoB7fZw3DKv/JXA=
 github.com/spf13/pflag v1.0.5/go.mod h1:McXfInJRrz4CZXVZOBLb0bTZqETkiAhM9Iw0y3An2Bg=
-golang.org/x/sys v0.0.0-20211205182925-97ca703d548d h1:FjkYO/PPp4Wi0EAUOVLxePm7qVW4r4ctbWpURyuOD0E=
-golang.org/x/sys v0.0.0-20211205182925-97ca703d548d/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
 gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
 gopkg.in/yaml.v2 v2.4.0/go.mod h1:RDklbk79AGWmwhnvt/jBztapEOGDOx6ZbXqjP6csGnQ=
diff --git a/internal/assert/assert.go b/internal/assert/assert.go
index 65c6afa..7dfb468 100644
--- a/internal/assert/assert.go
+++ b/internal/assert/assert.go
@@ -4,11 +4,10 @@ package assert
 import (
 	"io/ioutil"
 	"os"
+	"os/exec"
 	"path/filepath"
 	"runtime"
 	"testing"
-
-	exec "golang.org/x/sys/execabs"
 )
 
 // Equal calls t.Error if given strings are not equal
diff --git a/third_party/golang.org/x/sys/LICENSE b/third_party/golang.org/x/sys/LICENSE
new file mode 100644
index 0000000..6a66aea
--- /dev/null
+++ b/third_party/golang.org/x/sys/LICENSE
@@ -0,0 +1,27 @@
+Copyright (c) 2009 The Go Authors. All rights reserved.
+
+Redistribution and use in source and binary forms, with or without
+modification, are permitted provided that the following conditions are
+met:
+
+   * Redistributions of source code must retain the above copyright
+notice, this list of conditions and the following disclaimer.
+   * Redistributions in binary form must reproduce the above
+copyright notice, this list of conditions and the following disclaimer
+in the documentation and/or other materials provided with the
+distribution.
+   * Neither the name of Google Inc. nor the names of its
+contributors may be used to endorse or promote products derived from
+this software without specific prior written permission.
+
+THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
diff --git a/third_party/golang.org/x/sys/PATENTS b/third_party/golang.org/x/sys/PATENTS
new file mode 100644
index 0000000..7330990
--- /dev/null
+++ b/third_party/golang.org/x/sys/PATENTS
@@ -0,0 +1,22 @@
+Additional IP Rights Grant (Patents)
+
+"This implementation" means the copyrightable works distributed by
+Google as part of the Go project.
+
+Google hereby grants to You a perpetual, worldwide, non-exclusive,
+no-charge, royalty-free, irrevocable (except as stated in this section)
+patent license to make, have made, use, offer to sell, sell, import,
+transfer and otherwise run, modify and propagate the contents of this
+implementation of Go, where such license applies only to those patent
+claims, both currently owned or controlled by Google and acquired in
+the future, licensable by Google that are necessarily infringed by this
+implementation of Go.  This grant does not include claims that would be
+infringed only as a consequence of further modification of this
+implementation.  If you or your agent or exclusive licensee institute or
+order or agree to the institution of patent litigation against any
+entity (including a cross-claim or counterclaim in a lawsuit) alleging
+that this implementation of Go or any code incorporated within this
+implementation of Go constitutes direct or contributory patent
+infringement, or inducement of patent infringement, then any patent
+rights granted to you under this License for this implementation of Go
+shall terminate as of the date such litigation is filed.
diff --git a/third_party/golang.org/x/sys/execabs/execabs.go b/third_party/golang.org/x/sys/execabs/execabs.go
new file mode 100644
index 0000000..7819249
--- /dev/null
+++ b/third_party/golang.org/x/sys/execabs/execabs.go
@@ -0,0 +1,102 @@
+// Copyright 2020 The Go Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style
+// license that can be found in the LICENSE file.
+
+// Package execabs is a drop-in replacement for os/exec
+// that requires PATH lookups to find absolute paths.
+// That is, execabs.Command("cmd") runs the same PATH lookup
+// as exec.Command("cmd"), but if the result is a path
+// which is relative, the Run and Start methods will report
+// an error instead of running the executable.
+//
+// See https://blog.golang.org/path-security for more information
+// about when it may be necessary or appropriate to use this package.
+package execabs
+
+import (
+	"context"
+	"fmt"
+	"os/exec"
+	"path/filepath"
+	"reflect"
+	"unsafe"
+)
+
+// ErrNotFound is the error resulting if a path search failed to find an executable file.
+// It is an alias for exec.ErrNotFound.
+var ErrNotFound = exec.ErrNotFound
+
+// Cmd represents an external command being prepared or run.
+// It is an alias for exec.Cmd.
+type Cmd = exec.Cmd
+
+// Error is returned by LookPath when it fails to classify a file as an executable.
+// It is an alias for exec.Error.
+type Error = exec.Error
+
+// An ExitError reports an unsuccessful exit by a command.
+// It is an alias for exec.ExitError.
+type ExitError = exec.ExitError
+
+func relError(file, path string) error {
+	return fmt.Errorf("%s resolves to executable in current directory (.%c%s)", file, filepath.Separator, path)
+}
+
+// LookPath searches for an executable named file in the directories
+// named by the PATH environment variable. If file contains a slash,
+// it is tried directly and the PATH is not consulted. The result will be
+// an absolute path.
+//
+// LookPath differs from exec.LookPath in its handling of PATH lookups,
+// which are used for file names without slashes. If exec.LookPath's
+// PATH lookup would have returned an executable from the current directory,
+// LookPath instead returns an error.
+func LookPath(file string) (string, error) {
+	path, err := exec.LookPath(file)
+	if err != nil {
+		return "", err
+	}
+	if filepath.Base(file) == file && !filepath.IsAbs(path) {
+		return "", relError(file, path)
+	}
+	return path, nil
+}
+
+func fixCmd(name string, cmd *exec.Cmd) {
+	if filepath.Base(name) == name && !filepath.IsAbs(cmd.Path) {
+		// exec.Command was called with a bare binary name and
+		// exec.LookPath returned a path which is not absolute.
+		// Set cmd.lookPathErr and clear cmd.Path so that it
+		// cannot be run.
+		lookPathErr := (*error)(unsafe.Pointer(reflect.ValueOf(cmd).Elem().FieldByName("lookPathErr").Addr().Pointer()))
+		if *lookPathErr == nil {
+			*lookPathErr = relError(name, cmd.Path)
+		}
+		cmd.Path = ""
+	}
+}
+
+// CommandContext is like Command but includes a context.
+//
+// The provided context is used to kill the process (by calling os.Process.Kill)
+// if the context becomes done before the command completes on its own.
+func CommandContext(ctx context.Context, name string, arg ...string) *exec.Cmd {
+	cmd := exec.CommandContext(ctx, name, arg...)
+	fixCmd(name, cmd)
+	return cmd
+
+}
+
+// Command returns the Cmd struct to execute the named program with the given arguments.
+// See exec.Command for most details.
+//
+// Command differs from exec.Command in its handling of PATH lookups,
+// which are used when the program name contains no slashes.
+// If exec.Command would have returned an exec.Cmd configured to run an
+// executable from the current directory, Command instead
+// returns an exec.Cmd that will return an error from Start or Run.
+func Command(name string, arg ...string) *exec.Cmd {
+	cmd := exec.Command(name, arg...)
+	fixCmd(name, cmd)
+	return cmd
+}


Test patch:
diff --git a/example/main_test.go b/example/main_test.go
index b120a1e..15ce186 100644
--- a/example/main_test.go
+++ b/example/main_test.go
@@ -2,8 +2,8 @@ package main
 
 import (
 	"bytes"
-	exec "golang.org/x/sys/execabs"
 	"os"
+	"os/exec"
 	"strings"
 	"testing"
 
diff --git a/third_party/golang.org/x/sys/execabs/execabs_test.go b/third_party/golang.org/x/sys/execabs/execabs_test.go
new file mode 100644
index 0000000..1e8cf78
--- /dev/null
+++ b/third_party/golang.org/x/sys/execabs/execabs_test.go
@@ -0,0 +1,132 @@
+// Copyright 2020 The Go Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style
+// license that can be found in the LICENSE file.
+
+package execabs
+
+import (
+	"context"
+	"fmt"
+	"io/ioutil"
+	"os"
+	"os/exec"
+	"path/filepath"
+	"runtime"
+	"testing"
+)
+
+// hasExec reports whether the current system can start new processes
+// using os.StartProcess or (more commonly) exec.Command.
+// Copied from internal/testenv.HasExec
+func hasExec() bool {
+	switch runtime.GOOS {
+	case "js", "ios":
+		return false
+	}
+	return true
+}
+
+// mustHaveExec checks that the current system can start new processes
+// using os.StartProcess or (more commonly) exec.Command.
+// If not, mustHaveExec calls t.Skip with an explanation.
+// Copied from internal/testenv.MustHaveExec
+func mustHaveExec(t testing.TB) {
+	if !hasExec() {
+		t.Skipf("skipping test: cannot exec subprocess on %s/%s", runtime.GOOS, runtime.GOARCH)
+	}
+}
+
+func TestFixCmd(t *testing.T) {
+	cmd := &exec.Cmd{Path: "hello"}
+	fixCmd("hello", cmd)
+	if cmd.Path != "" {
+		t.Errorf("fixCmd didn't clear cmd.Path")
+	}
+	expectedErr := fmt.Sprintf("hello resolves to executable in current directory (.%chello)", filepath.Separator)
+	if err := cmd.Run(); err == nil {
+		t.Fatal("Command.Run didn't fail")
+	} else if err.Error() != expectedErr {
+		t.Fatalf("Command.Run returned unexpected error: want %q, got %q", expectedErr, err.Error())
+	}
+}
+
+func TestCommand(t *testing.T) {
+	mustHaveExec(t)
+
+	for _, cmd := range []func(string) *Cmd{
+		func(s string) *Cmd { return Command(s) },
+		func(s string) *Cmd { return CommandContext(context.Background(), s) },
+	} {
+		tmpDir, err := ioutil.TempDir("", "execabs-test")
+		if err != nil {
+			t.Fatalf("ioutil.TempDir failed: %s", err)
+		}
+		defer os.RemoveAll(tmpDir)
+		executable := "execabs-test"
+		if runtime.GOOS == "windows" {
+			executable += ".exe"
+		}
+		if err = ioutil.WriteFile(filepath.Join(tmpDir, executable), []byte{1, 2, 3}, 0111); err != nil {
+			t.Fatalf("ioutil.WriteFile failed: %s", err)
+		}
+		cwd, err := os.Getwd()
+		if err != nil {
+			t.Fatalf("os.Getwd failed: %s", err)
+		}
+		defer os.Chdir(cwd)
+		if err = os.Chdir(tmpDir); err != nil {
+			t.Fatalf("os.Chdir failed: %s", err)
+		}
+		if runtime.GOOS != "windows" {
+			// add "." to PATH so that exec.LookPath looks in the current directory on
+			// non-windows platforms as well
+			origPath := os.Getenv("PATH")
+			defer os.Setenv("PATH", origPath)
+			os.Setenv("PATH", fmt.Sprintf(".:%s", origPath))
+		}
+		expectedErr := fmt.Sprintf("execabs-test resolves to executable in current directory (.%c%s)", filepath.Separator, executable)
+		if err = cmd("execabs-test").Run(); err == nil {
+			t.Fatalf("Command.Run didn't fail when exec.LookPath returned a relative path")
+		} else if err.Error() != expectedErr {
+			t.Errorf("Command.Run returned unexpected error: want %q, got %q", expectedErr, err.Error())
+		}
+	}
+}
+
+func TestLookPath(t *testing.T) {
+	mustHaveExec(t)
+
+	tmpDir, err := ioutil.TempDir("", "execabs-test")
+	if err != nil {
+		t.Fatalf("ioutil.TempDir failed: %s", err)
+	}
+	defer os.RemoveAll(tmpDir)
+	executable := "execabs-test"
+	if runtime.GOOS == "windows" {
+		executable += ".exe"
+	}
+	if err = ioutil.WriteFile(filepath.Join(tmpDir, executable), []byte{1, 2, 3}, 0111); err != nil {
+		t.Fatalf("ioutil.WriteFile failed: %s", err)
+	}
+	cwd, err := os.Getwd()
+	if err != nil {
+		t.Fatalf("os.Getwd failed: %s", err)
+	}
+	defer os.Chdir(cwd)
+	if err = os.Chdir(tmpDir); err != nil {
+		t.Fatalf("os.Chdir failed: %s", err)
+	}
+	if runtime.GOOS != "windows" {
+		// add "." to PATH so that exec.LookPath looks in the current directory on
+		// non-windows platforms as well
+		origPath := os.Getenv("PATH")
+		defer os.Setenv("PATH", origPath)
+		os.Setenv("PATH", fmt.Sprintf(".:%s", origPath))
+	}
+	expectedErr := fmt.Sprintf("execabs-test resolves to executable in current directory (.%c%s)", filepath.Separator, executable)
+	if _, err := LookPath("execabs-test"); err == nil {
+		t.Fatalf("LookPath didn't fail when finding a non-relative path")
+	} else if err.Error() != expectedErr {
+		t.Errorf("LookPath returned unexpected error: want %q, got %q", expectedErr, err.Error())
+	}
+}