github.com/henvic/wedeploycli@v1.7.6-0.20200319005353-3630f582f284/deployment/transport/git/pushhack.go (about)

     1  // Hack to make git push work fine on Windows
     2  // without a git-credential-helper issue / Invalid credentials error.
     3  // See https://github.com/wedeploy/cli/issues/323
     4  // This passes the token as part of the remote address
     5  // Security risk: prone to sniffing (on the same machine) on most operating systems.
     6  
     7  package git
     8  
     9  import (
    10  	"bytes"
    11  	"errors"
    12  	"fmt"
    13  	"os/exec"
    14  	"runtime"
    15  	"strings"
    16  
    17  	"github.com/henvic/wedeploycli/deployment/internal/groupuid"
    18  
    19  	version "github.com/hashicorp/go-version"
    20  	"github.com/henvic/wedeploycli/envs"
    21  	"github.com/henvic/wedeploycli/verbose"
    22  )
    23  
    24  // Tests were made on the following git versions on Windows:
    25  // 2.5.0, 2.5.1, 2.5.2, 2.5.2.2, 2.5.3, 2.6.0, 2.6.4, 2.7.4, 2.8.4,
    26  // 2.9.0, 2.9.2, 2.10.1, 2.12.1, 2.13.0, 2.13.3, 2.14.2.2, 2.14.2.3.
    27  // This issue doesn't appear to affect git on Linux (not even with 1.9.1).
    28  // Related: https://github.com/git-for-windows/git
    29  
    30  // Special treatment constraints:
    31  // 2.5.0 (Aug 18, 2015): weird git credential- error message, but still works
    32  // 2.5.1 (Aug 28, 2015): weird git credential- error message, but still works
    33  // 2.5.3 (Sep 18, 2015): starts breaking
    34  // 2.13.3 (Jul 13, 2017): working again
    35  const gitAffectedVersions = "> 2.5.1, < 2.13.3"
    36  
    37  func (t *Transport) pushHack() (groupUID string, err error) {
    38  	var params = []string{"push", t.getGitRemote(), "master", "--force", "--no-verify"}
    39  
    40  	if verbose.Enabled {
    41  		params = append(params, "--verbose")
    42  	}
    43  
    44  	verbose.Debug(fmt.Sprintf("Running git push %v master -force",
    45  		verbose.SafeEscape(t.getGitRemote())))
    46  
    47  	var wectx = t.settings.ConfigContext
    48  
    49  	var cmd = exec.CommandContext(t.ctx, "git", params...) // #nosec
    50  	cmd.Env = append(t.getConfigEnvs(),
    51  		"GIT_TERMINAL_PROMPT=0",
    52  		envs.GitCredentialRemoteToken+"="+wectx.Token(),
    53  	)
    54  	cmd.Dir = t.settings.WorkDir
    55  
    56  	var bufErr *bytes.Buffer
    57  
    58  	switch verbose.IsUnsafeMode() {
    59  	case true:
    60  		bufErr = copyErrStreamAndVerbose(cmd)
    61  	default:
    62  		bufErr = &bytes.Buffer{}
    63  		cmd.Stderr = bufErr
    64  	}
    65  
    66  	err = cmd.Run()
    67  
    68  	if err != nil {
    69  		bs := bufErr.String()
    70  		switch {
    71  		case strings.Contains(bs, "fatal: Authentication failed for"),
    72  			strings.Contains(bs, "could not read Username"):
    73  			return "", errors.New("invalid credentials: please update git and try again http://git-scm.com")
    74  		case strings.Contains(bs, "error: "):
    75  			return "", fmt.Errorf("git error: %v", verbose.SafeEscape(getGitErrors(bs).Error()))
    76  		default:
    77  			return "", err
    78  		}
    79  	}
    80  
    81  	return groupuid.Extract(bufErr.String())
    82  }
    83  
    84  func (t *Transport) addRemoteHack() error {
    85  	verbose.Debug("Adding remote with token")
    86  	var wectx = t.settings.ConfigContext
    87  
    88  	var gitServer = fmt.Sprintf("https://%v:@git.%v/%v.git",
    89  		wectx.Token(),
    90  		wectx.InfrastructureDomain(),
    91  		t.settings.ProjectID)
    92  
    93  	var params = []string{"remote", "add", t.getGitRemote(), gitServer}
    94  
    95  	verbose.Debug(fmt.Sprintf("Running git remote add %v %v",
    96  		t.getGitRemote(),
    97  		verbose.SafeEscape(gitServer)))
    98  
    99  	var cmd = exec.CommandContext(t.ctx, "git", params...) // #nosec
   100  	cmd.Env = t.getConfigEnvs()
   101  	cmd.Dir = t.settings.WorkDir
   102  
   103  	if verbose.IsUnsafeMode() {
   104  		cmd.Stderr = errStream
   105  	}
   106  
   107  	return cmd.Run()
   108  }
   109  
   110  func (t *Transport) useCredentialHack() bool {
   111  	if runtime.GOOS != "windows" {
   112  		return false
   113  	}
   114  
   115  	v, err := version.NewVersion(t.gitVersion)
   116  
   117  	if err != nil {
   118  		return false
   119  	}
   120  
   121  	constraints, err := version.NewConstraint(gitAffectedVersions)
   122  
   123  	if err != nil {
   124  		verbose.Debug(err)
   125  		return false
   126  	}
   127  
   128  	p := constraints.Check(v)
   129  
   130  	if p {
   131  		verbose.Debug("git version " + t.gitVersion + " is not compatible with credential-helper due to a bug")
   132  		verbose.Debug("fall back to passing token on remote")
   133  		verbose.Debug("limited debug messages for security reasons")
   134  		verbose.Debug("updating git is highly recommended")
   135  	}
   136  
   137  	return p
   138  }