1 // Copyright (c) 2009 The Go Authors. All rights reserved. 2 // 3 // Redistribution and use in source and binary forms, with or without 4 // modification, are permitted provided that the following conditions are 5 // met: 6 // 7 // * Redistributions of source code must retain the above copyright 8 // notice, this list of conditions and the following disclaimer. 9 // * Redistributions in binary form must reproduce the above 10 // copyright notice, this list of conditions and the following disclaimer 11 // in the documentation and/or other materials provided with the 12 // distribution. 13 // * Neither the name of Google Inc. nor the names of its 14 // contributors may be used to endorse or promote products derived from 15 // this software without specific prior written permission. 16 // 17 // THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS 18 // "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT 19 // LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR 20 // A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT 21 // OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, 22 // SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT 23 // LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, 24 // DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY 25 // THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT 26 // (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE 27 // OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 28 29 // Package safeexec provides alternatives for exec package functions to avoid 30 // accidentally executing binaries found in the current working directory on 31 // Windows. 32 package safeexec 33 34 import ( 35 "os" 36 "os/exec" 37 "path/filepath" 38 "strings" 39 ) 40 41 func chkStat(file string) error { 42 d, err := os.Stat(file) 43 if err != nil { 44 return err 45 } 46 if d.IsDir() { 47 return os.ErrPermission 48 } 49 return nil 50 } 51 52 func hasExt(file string) bool { 53 i := strings.LastIndex(file, ".") 54 if i < 0 { 55 return false 56 } 57 return strings.LastIndexAny(file, `:\/`) < i 58 } 59 60 func findExecutable(file string, exts []string) (string, error) { 61 if len(exts) == 0 { 62 return file, chkStat(file) 63 } 64 if hasExt(file) { 65 if chkStat(file) == nil { 66 return file, nil 67 } 68 } 69 for _, e := range exts { 70 if f := file + e; chkStat(f) == nil { 71 return f, nil 72 } 73 } 74 return "", os.ErrNotExist 75 } 76 77 // LookPath searches for an executable named file in the 78 // directories named by the PATH environment variable. 79 // If file contains a slash, it is tried directly and the PATH is not consulted. 80 // LookPath also uses PATHEXT environment variable to match 81 // a suitable candidate. 82 // The result may be an absolute path or a path relative to the current directory. 83 func LookPath(file string) (string, error) { 84 var exts []string 85 x := os.Getenv(`PATHEXT`) 86 if x != "" { 87 for _, e := range strings.Split(strings.ToLower(x), `;`) { 88 if e == "" { 89 continue 90 } 91 if e[0] != '.' { 92 e = "." + e 93 } 94 exts = append(exts, e) 95 } 96 } else { 97 exts = []string{".com", ".exe", ".bat", ".cmd"} 98 } 99 100 if strings.ContainsAny(file, `:\/`) { 101 if f, err := findExecutable(file, exts); err == nil { 102 return f, nil 103 } else { 104 return "", &exec.Error{file, err} 105 } 106 } 107 108 // https://github.com/golang/go/issues/38736 109 // if f, err := findExecutable(filepath.Join(".", file), exts); err == nil { 110 // return f, nil 111 // } 112 113 path := os.Getenv("path") 114 for _, dir := range filepath.SplitList(path) { 115 if f, err := findExecutable(filepath.Join(dir, file), exts); err == nil { 116 return f, nil 117 } 118 } 119 return "", &exec.Error{file, exec.ErrNotFound} 120 } 121