package cli import ( "crypto/sha256" "io" "os" "path/filepath" "runtime" "strings") func isGitcrawlShimPath(path string) bool { if path == "" { return false } resolved := path if eval, err := filepath.EvalSymlinks(path); err == nil { resolved = eval } for _, value := range []string{path, resolved} { base := strings.ToLower(filepath.Base(value)) if base == "gitcrawl" || base == "gitcrawl-gh" { return true } } return false} func usableRealGHPath(path string) bool { info, err := os.Stat(path) if err != nil || info.IsDir() || !ghBackendModeUsable(info.Mode(), runtime.GOOS) { return false } exe, err := os.Executable() if err != nil { return true } exeInfo, exeInfoErr := os.Stat(exe) if exeInfoErr == nil && os.SameFile(info, exeInfo) { return false } candidateReal, candidateErr := filepath.EvalSymlinks(path) exeReal, exeErr := filepath.EvalSymlinks(exe) if candidateErr == nil && exeErr == nil && candidateReal == exeReal { return false } return !sameExecutableContents(path, exe, info, exeInfo)} func ghBackendModeUsable(mode os.FileMode, goos string) bool { return goos == "windows" || mode&0111 != 0} func sameExecutableContents(candidate, exe string, candidateInfo, exeInfo os.FileInfo) bool { if candidateInfo == nil || exeInfo == nil || candidateInfo.Size() != exeInfo.Size() { return false } candidateHash, err := fileSHA256(candidate) if err != nil { return false } exeHash, err := fileSHA256(exe) if err != nil { return false } return candidateHash == exeHash} func fileSHA256(path string) ([32]byte, error) { file, err := os.Open(path) if err != nil { return [32]byte{}, err } defer file.Close() hash := sha256.New() if _, err := io.Copy(hash, file); err != nil { return [32]byte{}, err } var out [32]byte copy(out[:], hash.Sum(nil)) return out, nil}
package cli
import (
"crypto/sha256"
"io"
"os"
"path/filepath"
"runtime"
"strings"
)
func isGitcrawlShimPath(path string) bool {
if path == "" {
return false
}
resolved := path
if eval, err := filepath.EvalSymlinks(path); err == nil {
resolved = eval
}
for _, value := range []string{path, resolved} {
base := strings.ToLower(filepath.Base(value))
if base == "gitcrawl" || base == "gitcrawl-gh" {
return true
}
}
return false
}
func usableRealGHPath(path string) bool {
info, err := os.Stat(path)
if err != nil || info.IsDir() || !ghBackendModeUsable(info.Mode(), runtime.GOOS) {
return false
}
exe, err := os.Executable()
if err != nil {
return true
}
exeInfo, exeInfoErr := os.Stat(exe)
if exeInfoErr == nil && os.SameFile(info, exeInfo) {
return false
}
candidateReal, candidateErr := filepath.EvalSymlinks(path)
exeReal, exeErr := filepath.EvalSymlinks(exe)
if candidateErr == nil && exeErr == nil && candidateReal == exeReal {
return false
}
return !sameExecutableContents(path, exe, info, exeInfo)
}
func ghBackendModeUsable(mode os.FileMode, goos string) bool {
return goos == "windows" || mode&0111 != 0
}
func sameExecutableContents(candidate, exe string, candidateInfo, exeInfo os.FileInfo) bool {
if candidateInfo == nil || exeInfo == nil || candidateInfo.Size() != exeInfo.Size() {
return false
}
candidateHash, err := fileSHA256(candidate)
if err != nil {
return false
}
exeHash, err := fileSHA256(exe)
if err != nil {
return false
}
return candidateHash == exeHash
}
func fileSHA256(path string) ([32]byte, error) {
file, err := os.Open(path)
if err != nil {
return [32]byte{}, err
}
defer file.Close()
hash := sha256.New()
if _, err := io.Copy(hash, file); err != nil {
return [32]byte{}, err
}
var out [32]byte
copy(out[:], hash.Sum(nil))
return out, nil
}