diff --git a/.github/workflows/git.yml b/.github/workflows/git.yml index fcec675eb..c945e72ff 100644 --- a/.github/workflows/git.yml +++ b/.github/workflows/git.yml @@ -1,5 +1,8 @@ on: [push, pull_request] name: Git Compatibility +permissions: + contents: read + jobs: test: strategy: @@ -14,12 +17,12 @@ jobs: steps: - name: Install Go - uses: actions/setup-go@v1 + uses: actions/setup-go@v3 with: go-version: 1.20.x - name: Checkout code - uses: actions/checkout@v2 + uses: actions/checkout@v3 - name: Install build dependencies run: sudo apt-get install gettext diff --git a/.github/workflows/test.yml b/.github/workflows/test.yml index b576d386e..ce5872d03 100644 --- a/.github/workflows/test.yml +++ b/.github/workflows/test.yml @@ -1,5 +1,8 @@ on: [push, pull_request] name: Test +permissions: + contents: read + jobs: version-matrix: strategy: @@ -11,12 +14,12 @@ jobs: runs-on: ${{ matrix.platform }} steps: - name: Install Go - uses: actions/setup-go@v1 + uses: actions/setup-go@v3 with: go-version: ${{ matrix.go-version }} - name: Checkout code - uses: actions/checkout@v2 + uses: actions/checkout@v3 - name: Configure known hosts if: matrix.platform != 'ubuntu-latest' diff --git a/Makefile b/Makefile index d10922fb1..2acb8bc45 100644 --- a/Makefile +++ b/Makefile @@ -27,7 +27,7 @@ build-git: test: @echo "running against `git version`"; \ - $(GOTEST) ./... + $(GOTEST) -race ./... test-coverage: @echo "running against `git version`"; \ diff --git a/_examples/README.md b/_examples/README.md index 3a4c539d0..1f150f99b 100644 --- a/_examples/README.md +++ b/_examples/README.md @@ -23,7 +23,6 @@ Here you can find a list of annotated _go-git_ examples: - [remotes](remotes/main.go) - Working with remotes: adding, removing, etc. - [progress](progress/main.go) - Printing the progress information from the sideband. - [revision](revision/main.go) - Solve a revision into a commit. -- [config](config/main.go) - Explains how to work with config files. - [submodule](submodule/main.go) - Submodule update remote. ### Advanced diff --git a/go.mod b/go.mod index c46d2446a..85fd7b132 100644 --- a/go.mod +++ b/go.mod @@ -1,13 +1,15 @@ module github.com/go-git/go-git/v5 +go 1.13 + require ( - github.com/ProtonMail/go-crypto v0.0.0-20221026131551-cf6655e29de4 - github.com/acomagu/bufpipe v1.0.3 + github.com/ProtonMail/go-crypto v0.0.0-20230217124315-7d5c6f04bbb8 + github.com/acomagu/bufpipe v1.0.4 github.com/armon/go-socks5 v0.0.0-20160902184237-e75332964ef5 github.com/emirpasic/gods v1.18.1 github.com/gliderlabs/ssh v0.3.5 github.com/go-git/gcfg v1.5.0 - github.com/go-git/go-billy/v5 v5.4.0 + github.com/go-git/go-billy/v5 v5.4.1 github.com/go-git/go-git-fixtures/v4 v4.3.1 github.com/google/go-cmp v0.5.9 github.com/imdario/mergo v0.3.13 @@ -19,12 +21,10 @@ require ( github.com/sergi/go-diff v1.1.0 github.com/skeema/knownhosts v1.1.0 github.com/xanzy/ssh-agent v0.3.3 - golang.org/x/crypto v0.3.0 - golang.org/x/net v0.2.0 - golang.org/x/sys v0.3.0 - golang.org/x/text v0.4.0 + golang.org/x/crypto v0.6.0 + golang.org/x/net v0.7.0 + golang.org/x/sys v0.5.0 + golang.org/x/text v0.7.0 gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c gopkg.in/warnings.v0 v0.1.2 // indirect ) - -go 1.13 diff --git a/go.sum b/go.sum index 536173547..a7548e292 100644 --- a/go.sum +++ b/go.sum @@ -1,9 +1,9 @@ github.com/Microsoft/go-winio v0.5.2 h1:a9IhgEQBCUEk6QCdml9CiJGhAws+YwffDHEMp1VMrpA= github.com/Microsoft/go-winio v0.5.2/go.mod h1:WpS1mjBmmwHBEWmogvA2mj8546UReBk4v8QkMxJ6pZY= -github.com/ProtonMail/go-crypto v0.0.0-20221026131551-cf6655e29de4 h1:ra2OtmuW0AE5csawV4YXMNGNQQXvLRps3z2Z59OPO+I= -github.com/ProtonMail/go-crypto v0.0.0-20221026131551-cf6655e29de4/go.mod h1:UBYPn8k0D56RtnR8RFQMjmh4KrZzWJ5o7Z9SYjossQ8= -github.com/acomagu/bufpipe v1.0.3 h1:fxAGrHZTgQ9w5QqVItgzwj235/uYZYgbXitB+dLupOk= -github.com/acomagu/bufpipe v1.0.3/go.mod h1:mxdxdup/WdsKVreO5GpW4+M/1CE2sMG4jeGJ2sYmHc4= +github.com/ProtonMail/go-crypto v0.0.0-20230217124315-7d5c6f04bbb8 h1:wPbRQzjjwFc0ih8puEVAOFGELsn1zoIIYdxvML7mDxA= +github.com/ProtonMail/go-crypto v0.0.0-20230217124315-7d5c6f04bbb8/go.mod h1:I0gYDMZ6Z5GRU7l58bNFSkPTFN6Yl12dsUlAZ8xy98g= +github.com/acomagu/bufpipe v1.0.4 h1:e3H4WUzM3npvo5uv95QuJM3cQspFNtFBzvJ2oNjKIDQ= +github.com/acomagu/bufpipe v1.0.4/go.mod h1:mxdxdup/WdsKVreO5GpW4+M/1CE2sMG4jeGJ2sYmHc4= github.com/anmitsu/go-shlex v0.0.0-20200514113438-38f4b401e2be h1:9AeTilPcZAjCFIImctFaOjnTIavg87rW78vTPkQqLI8= github.com/anmitsu/go-shlex v0.0.0-20200514113438-38f4b401e2be/go.mod h1:ySMOLuWl6zY27l47sB3qLNK6tF2fkHG55UZxx8oIVo4= github.com/armon/go-socks5 v0.0.0-20160902184237-e75332964ef5 h1:0CwZNZbxp69SHPdPJAN/hZIm0C4OItdklCFmMRWYpio= @@ -22,8 +22,8 @@ github.com/gliderlabs/ssh v0.3.5/go.mod h1:8XB4KraRrX39qHhT6yxPsHedjA08I/uBVwj4x github.com/go-git/gcfg v1.5.0 h1:Q5ViNfGF8zFgyJWPqYwA7qGFoMTEiBmdlkcfRmpIMa4= github.com/go-git/gcfg v1.5.0/go.mod h1:5m20vg6GwYabIxaOonVkTdrILxQMpEShl1xiMF4ua+E= github.com/go-git/go-billy/v5 v5.3.1/go.mod h1:pmpqyWchKfYfrkb/UVH4otLvyi/5gJlGI4Hb3ZqZ3W0= -github.com/go-git/go-billy/v5 v5.4.0 h1:Vaw7LaSTRJOUric7pe4vnzBSgyuf2KrLsu2Y4ZpQBDE= -github.com/go-git/go-billy/v5 v5.4.0/go.mod h1:vjbugF6Fz7JIflbVpl1hJsGjSHNltrSw45YK/ukIvQg= +github.com/go-git/go-billy/v5 v5.4.1 h1:Uwp5tDRkPr+l/TnbHOQzp+tmJfLceOlbVucgpTz8ix4= +github.com/go-git/go-billy/v5 v5.4.1/go.mod h1:vjbugF6Fz7JIflbVpl1hJsGjSHNltrSw45YK/ukIvQg= github.com/go-git/go-git-fixtures/v4 v4.3.1 h1:y5z6dd3qi8Hl+stezc8p3JxDkoTRqMAlKnXHuzrfjTQ= github.com/go-git/go-git-fixtures/v4 v4.3.1/go.mod h1:8LHG1a3SRW71ettAD/jW13h8c6AqjVSeL11RAdgaqpo= github.com/google/go-cmp v0.5.9 h1:O2Tfq5qg4qc4AmwVlvv0oLiVAGB7enBSJ2x2DqQFi38= @@ -73,8 +73,8 @@ golang.org/x/crypto v0.0.0-20220525230936-793ad666bf5e/go.mod h1:IxCIyHEi3zRg3s0 golang.org/x/crypto v0.0.0-20220622213112-05595931fe9d/go.mod h1:IxCIyHEi3zRg3s0A5j5BB6A9Jmi73HwBIUl50j+osU4= golang.org/x/crypto v0.0.0-20220826181053-bd7e27e6170d/go.mod h1:IxCIyHEi3zRg3s0A5j5BB6A9Jmi73HwBIUl50j+osU4= golang.org/x/crypto v0.1.0/go.mod h1:RecgLatLF4+eUMCP1PoPZQb+cVrJcOPbHkTkbkB9sbw= -golang.org/x/crypto v0.3.0 h1:a06MkbcxBrEFc0w0QIZWXrH/9cCX6KJyWbBOIwAn+7A= -golang.org/x/crypto v0.3.0/go.mod h1:hebNnKkNXi2UzZN1eVRvBB7co0a+JxK6XbPiWVs/3J4= +golang.org/x/crypto v0.6.0 h1:qfktjS5LUO+fFKeJXZ+ikTRijMmljikvG68fpMMruSc= +golang.org/x/crypto v0.6.0/go.mod h1:OFC/31mSvZgRz0V1QTNCzfAI1aIRzbiufJtkMIlEp58= golang.org/x/mod v0.6.0-dev.0.20220419223038-86c51ed26bb4/go.mod h1:jJ57K6gSWd91VN4djpZkiMVwK6gcyfeH4XE8wZrZaV4= golang.org/x/mod v0.6.0/go.mod h1:4mET923SAdbXp2ki8ey+zGs1SLqsuM2Y0uvdZR/fUNI= golang.org/x/net v0.0.0-20190620200207-3b0461eec859/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= @@ -83,8 +83,9 @@ golang.org/x/net v0.0.0-20211112202133-69e39bad7dc2/go.mod h1:9nx3DQGgdP8bBQD5qx golang.org/x/net v0.0.0-20220722155237-a158d28d115b/go.mod h1:XRhObCWvk6IyKnWLug+ECip1KBveYUHfp+8e9klMJ9c= golang.org/x/net v0.0.0-20220826154423-83b083e8dc8b/go.mod h1:YDH+HFinaLZZlnHAfSS6ZXJJ9M9t4Dl22yv3iI2vPwk= golang.org/x/net v0.1.0/go.mod h1:Cx3nUiGt4eDBEyega/BKRp+/AlGL8hYe7U9odMt2Cco= -golang.org/x/net v0.2.0 h1:sZfSu1wtKLGlWI4ZZayP0ck9Y73K1ynO6gqzTdBVdPU= -golang.org/x/net v0.2.0/go.mod h1:KqCZLdyyvdV855qA2rE3GC2aiw5xGR5TEjj8smXukLY= +golang.org/x/net v0.6.0/go.mod h1:2Tu9+aMcznHK/AK1HMvgo6xiTLG5rD5rZLDS+rp2Bjs= +golang.org/x/net v0.7.0 h1:rJrUqqhjsgNp7KqAIc25s9pZnjU7TUcSY7HcVZjdn1g= +golang.org/x/net v0.7.0/go.mod h1:2Tu9+aMcznHK/AK1HMvgo6xiTLG5rD5rZLDS+rp2Bjs= golang.org/x/sync v0.0.0-20190423024810-112230192c58/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sync v0.0.0-20220722155255-886fb9371eb4/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= @@ -102,21 +103,22 @@ golang.org/x/sys v0.0.0-20220722155257-8c9f86f7a55f/go.mod h1:oPkhp1MJrh7nUepCBc golang.org/x/sys v0.0.0-20220728004956-3c1f35247d10/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20220825204002-c680a09ffe64/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.1.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= -golang.org/x/sys v0.2.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= -golang.org/x/sys v0.3.0 h1:w8ZOecv6NaNa/zC8944JTU3vz4u6Lagfk4RPQxv92NQ= golang.org/x/sys v0.3.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.5.0 h1:MUK/U/4lj1t1oPg0HfuXDN/Z1wv31ZJ/YcPiGccS4DU= +golang.org/x/sys v0.5.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo= golang.org/x/term v0.0.0-20210927222741-03fcf44c2211/go.mod h1:jbD1KX2456YbFQfuXm/mYQcufACuNUgVhRMnK/tPxf8= golang.org/x/term v0.0.0-20220722155259-a9ba230a4035/go.mod h1:jbD1KX2456YbFQfuXm/mYQcufACuNUgVhRMnK/tPxf8= golang.org/x/term v0.1.0/go.mod h1:jbD1KX2456YbFQfuXm/mYQcufACuNUgVhRMnK/tPxf8= -golang.org/x/term v0.2.0 h1:z85xZCsEl7bi/KwbNADeBYoOP0++7W1ipu+aGnpwzRM= -golang.org/x/term v0.2.0/go.mod h1:TVmDHMZPmdnySmBfhjOoOdhjzdE1h4u1VwSiw2l1Nuc= +golang.org/x/term v0.5.0 h1:n2a8QNdAb0sZNpU9R1ALUXBbY+w51fCQDN+7EdxNBsY= +golang.org/x/term v0.5.0/go.mod h1:jMB1sMXY+tzblOD4FWmEbocvup2/aLOaQEp7JmGp78k= golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= golang.org/x/text v0.3.3/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ= golang.org/x/text v0.3.6/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ= golang.org/x/text v0.3.7/go.mod h1:u+2+/6zg+i71rQMx5EYifcz6MCKuco9NR6JIITiCfzQ= -golang.org/x/text v0.4.0 h1:BrVqGRd7+k1DiOgtnFvAkoQEWQvBc25ouMJM6429SFg= golang.org/x/text v0.4.0/go.mod h1:mrYo+phRRbMaCq/xk9113O4dZlRixOauAjOtrjsXDZ8= +golang.org/x/text v0.7.0 h1:4BRB4x83lYWy72KwLD/qYDuTu7q9PjSagHvijDw7cLo= +golang.org/x/text v0.7.0/go.mod h1:mrYo+phRRbMaCq/xk9113O4dZlRixOauAjOtrjsXDZ8= golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= golang.org/x/tools v0.0.0-20191119224855-298f0cb1881e/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= golang.org/x/tools v0.1.12/go.mod h1:hNGJHUnrk76NpqgfD5Aqm5Crs+Hm0VOH/i9J2+nxYbc= diff --git a/plumbing/transport/internal/common/common.go b/plumbing/transport/internal/common/common.go index d0e9a2974..b2c2fee38 100644 --- a/plumbing/transport/internal/common/common.go +++ b/plumbing/transport/internal/common/common.go @@ -374,7 +374,7 @@ func (s *session) checkNotFoundError() error { case <-t.C: return ErrTimeoutExceeded case line, ok := <-s.firstErrLine: - if !ok { + if !ok || len(line) == 0 { return nil } diff --git a/plumbing/transport/internal/common/common_test.go b/plumbing/transport/internal/common/common_test.go index c60ef3b05..affa78706 100644 --- a/plumbing/transport/internal/common/common_test.go +++ b/plumbing/transport/internal/common/common_test.go @@ -76,3 +76,17 @@ func (s *CommonSuite) TestIsRepoNotFoundErrorForGogsAccessDenied(c *C) { c.Assert(isRepoNotFound, Equals, true) } + +func (s *CommonSuite) TestCheckNotFoundError(c *C) { + firstErrLine := make(chan string, 1) + + session := session{ + firstErrLine: firstErrLine, + } + + firstErrLine <- "" + + err := session.checkNotFoundError() + + c.Assert(err, IsNil) +} diff --git a/plumbing/transport/ssh/auth_method.go b/plumbing/transport/ssh/auth_method.go index 9d3bcd359..e89ce4ba3 100644 --- a/plumbing/transport/ssh/auth_method.go +++ b/plumbing/transport/ssh/auth_method.go @@ -43,6 +43,7 @@ const ( type KeyboardInteractive struct { User string Challenge ssh.KeyboardInteractiveChallenge + HostKeyCallbackHelper } func (a *KeyboardInteractive) Name() string { @@ -54,18 +55,19 @@ func (a *KeyboardInteractive) String() string { } func (a *KeyboardInteractive) ClientConfig() (*ssh.ClientConfig, error) { - return &ssh.ClientConfig{ + return a.SetHostKeyCallback(&ssh.ClientConfig{ User: a.User, Auth: []ssh.AuthMethod{ a.Challenge, }, - }, nil + }) } // Password implements AuthMethod by using the given password. type Password struct { User string Password string + HostKeyCallbackHelper } func (a *Password) Name() string { @@ -77,10 +79,10 @@ func (a *Password) String() string { } func (a *Password) ClientConfig() (*ssh.ClientConfig, error) { - return &ssh.ClientConfig{ + return a.SetHostKeyCallback(&ssh.ClientConfig{ User: a.User, Auth: []ssh.AuthMethod{ssh.Password(a.Password)}, - }, nil + }) } // PasswordCallback implements AuthMethod by using a callback @@ -88,6 +90,7 @@ func (a *Password) ClientConfig() (*ssh.ClientConfig, error) { type PasswordCallback struct { User string Callback func() (pass string, err error) + HostKeyCallbackHelper } func (a *PasswordCallback) Name() string { @@ -99,16 +102,17 @@ func (a *PasswordCallback) String() string { } func (a *PasswordCallback) ClientConfig() (*ssh.ClientConfig, error) { - return &ssh.ClientConfig{ + return a.SetHostKeyCallback(&ssh.ClientConfig{ User: a.User, Auth: []ssh.AuthMethod{ssh.PasswordCallback(a.Callback)}, - }, nil + }) } // PublicKeys implements AuthMethod by using the given key pairs. type PublicKeys struct { User string Signer ssh.Signer + HostKeyCallbackHelper } // NewPublicKeys returns a PublicKeys from a PEM encoded private key. An @@ -147,10 +151,10 @@ func (a *PublicKeys) String() string { } func (a *PublicKeys) ClientConfig() (*ssh.ClientConfig, error) { - return &ssh.ClientConfig{ + return a.SetHostKeyCallback(&ssh.ClientConfig{ User: a.User, Auth: []ssh.AuthMethod{ssh.PublicKeys(a.Signer)}, - }, nil + }) } func username() (string, error) { @@ -173,6 +177,7 @@ func username() (string, error) { type PublicKeysCallback struct { User string Callback func() (signers []ssh.Signer, err error) + HostKeyCallbackHelper } // NewSSHAgentAuth returns a PublicKeysCallback based on a SSH agent, it opens @@ -207,10 +212,10 @@ func (a *PublicKeysCallback) String() string { } func (a *PublicKeysCallback) ClientConfig() (*ssh.ClientConfig, error) { - return &ssh.ClientConfig{ + return a.SetHostKeyCallback(&ssh.ClientConfig{ User: a.User, Auth: []ssh.AuthMethod{ssh.PublicKeysCallback(a.Callback)}, - }, nil + }) } // NewKnownHostsCallback returns ssh.HostKeyCallback based on a file based on a @@ -286,9 +291,6 @@ func filterKnownHostsFiles(files ...string) ([]string, error) { // HostKeyCallbackHelper is a helper that provides common functionality to // configure HostKeyCallback into a ssh.ClientConfig. -// Deprecated in favor of SetConfigHostKeyFields (see common.go) which provides -// a mechanism for also setting ClientConfig.HostKeyAlgorithms for a specific -// host. type HostKeyCallbackHelper struct { // HostKeyCallback is the function type used for verifying server keys. // If nil default callback will be create using NewKnownHostsCallback diff --git a/plumbing/transport/ssh/common.go b/plumbing/transport/ssh/common.go index 4b9ac0797..e06958a3b 100644 --- a/plumbing/transport/ssh/common.go +++ b/plumbing/transport/ssh/common.go @@ -10,6 +10,7 @@ import ( "github.com/go-git/go-git/v5/plumbing/transport" "github.com/go-git/go-git/v5/plumbing/transport/internal/common" + "github.com/skeema/knownhosts" "github.com/kevinburke/ssh_config" "golang.org/x/crypto/ssh" @@ -122,9 +123,18 @@ func (c *command) connect() error { return err } hostWithPort := c.getHostWithPort() - config, err = SetConfigHostKeyFields(config, hostWithPort) - if err != nil { - return err + if config.HostKeyCallback == nil { + kh, err := newKnownHosts() + if err != nil { + return err + } + config.HostKeyCallback = kh.HostKeyCallback() + config.HostKeyAlgorithms = kh.HostKeyAlgorithms(hostWithPort) + } else if len(config.HostKeyAlgorithms) == 0 { + // Set the HostKeyAlgorithms based on HostKeyCallback. + // For background see https://github.com/go-git/go-git/issues/411 as well as + // https://github.com/golang/go/issues/29286 for root cause. + config.HostKeyAlgorithms = knownhosts.HostKeyAlgorithms(config.HostKeyCallback, hostWithPort) } overrideConfig(c.config, config) @@ -167,23 +177,6 @@ func dial(network, addr string, config *ssh.ClientConfig) (*ssh.Client, error) { return ssh.NewClient(c, chans, reqs), nil } -// SetConfigHostKeyFields sets cfg.HostKeyCallback and cfg.HostKeyAlgorithms -// based on OpenSSH known_hosts. cfg is modified in-place. hostWithPort must be -// supplied, since the algorithms will be set based on the known host keys for -// that specific host. Otherwise, golang.org/x/crypto/ssh can return an error -// upon connecting to a host whose *first* key is not known, even though other -// keys (of different types) are known and match properly. -// For background see https://github.com/go-git/go-git/issues/411 as well as -// https://github.com/golang/go/issues/29286 for root cause. -func SetConfigHostKeyFields(cfg *ssh.ClientConfig, hostWithPort string) (*ssh.ClientConfig, error) { - kh, err := newKnownHosts() - if err == nil { - cfg.HostKeyCallback = kh.HostKeyCallback() - cfg.HostKeyAlgorithms = kh.HostKeyAlgorithms(hostWithPort) - } - return cfg, err -} - func (c *command) getHostWithPort() string { if addr, found := c.doGetHostWithPortFromSSHConfig(); found { return addr diff --git a/plumbing/transport/ssh/common_test.go b/plumbing/transport/ssh/common_test.go index 6d634d532..496e82d17 100644 --- a/plumbing/transport/ssh/common_test.go +++ b/plumbing/transport/ssh/common_test.go @@ -5,23 +5,25 @@ import ( "github.com/go-git/go-git/v5/plumbing/transport" + "github.com/gliderlabs/ssh" "github.com/kevinburke/ssh_config" - "golang.org/x/crypto/ssh" + stdssh "golang.org/x/crypto/ssh" + "golang.org/x/crypto/ssh/testdata" . "gopkg.in/check.v1" ) func Test(t *testing.T) { TestingT(t) } func (s *SuiteCommon) TestOverrideConfig(c *C) { - config := &ssh.ClientConfig{ + config := &stdssh.ClientConfig{ User: "foo", - Auth: []ssh.AuthMethod{ - ssh.Password("yourpassword"), + Auth: []stdssh.AuthMethod{ + stdssh.Password("yourpassword"), }, - HostKeyCallback: ssh.FixedHostKey(nil), + HostKeyCallback: stdssh.FixedHostKey(nil), } - target := &ssh.ClientConfig{} + target := &stdssh.ClientConfig{} overrideConfig(config, target) c.Assert(target.User, Equals, "foo") @@ -30,11 +32,11 @@ func (s *SuiteCommon) TestOverrideConfig(c *C) { } func (s *SuiteCommon) TestOverrideConfigKeep(c *C) { - config := &ssh.ClientConfig{ + config := &stdssh.ClientConfig{ User: "foo", } - target := &ssh.ClientConfig{ + target := &stdssh.ClientConfig{ User: "bar", } @@ -93,12 +95,69 @@ func (s *SuiteCommon) TestDefaultSSHConfigWildcard(c *C) { c.Assert(cmd.getHostWithPort(), Equals, "github.com:22") } +func (s *SuiteCommon) TestIgnoreHostKeyCallback(c *C) { + uploadPack := &UploadPackSuite{ + opts: []ssh.Option{ + ssh.HostKeyPEM(testdata.PEMBytes["ed25519"]), + }, + } + uploadPack.SetUpSuite(c) + // Use the default client, which does not have a host key callback + uploadPack.Client = DefaultClient + auth, err := NewPublicKeys("foo", testdata.PEMBytes["rsa"], "") + c.Assert(err, IsNil) + c.Assert(auth, NotNil) + auth.HostKeyCallback = stdssh.InsecureIgnoreHostKey() + ep := uploadPack.newEndpoint(c, "bar.git") + ps, err := uploadPack.Client.NewUploadPackSession(ep, auth) + c.Assert(err, IsNil) + c.Assert(ps, NotNil) +} + +func (s *SuiteCommon) TestFixedHostKeyCallback(c *C) { + hostKey, err := stdssh.ParsePrivateKey(testdata.PEMBytes["ed25519"]) + c.Assert(err, IsNil) + uploadPack := &UploadPackSuite{ + opts: []ssh.Option{ + ssh.HostKeyPEM(testdata.PEMBytes["ed25519"]), + }, + } + uploadPack.SetUpSuite(c) + // Use the default client, which does not have a host key callback + uploadPack.Client = DefaultClient + auth, err := NewPublicKeys("foo", testdata.PEMBytes["rsa"], "") + c.Assert(err, IsNil) + c.Assert(auth, NotNil) + auth.HostKeyCallback = stdssh.FixedHostKey(hostKey.PublicKey()) + ep := uploadPack.newEndpoint(c, "bar.git") + ps, err := uploadPack.Client.NewUploadPackSession(ep, auth) + c.Assert(err, IsNil) + c.Assert(ps, NotNil) +} + +func (s *SuiteCommon) TestFailHostKeyCallback(c *C) { + uploadPack := &UploadPackSuite{ + opts: []ssh.Option{ + ssh.HostKeyPEM(testdata.PEMBytes["ed25519"]), + }, + } + uploadPack.SetUpSuite(c) + // Use the default client, which does not have a host key callback + uploadPack.Client = DefaultClient + auth, err := NewPublicKeys("foo", testdata.PEMBytes["rsa"], "") + c.Assert(err, IsNil) + c.Assert(auth, NotNil) + ep := uploadPack.newEndpoint(c, "bar.git") + _, err = uploadPack.Client.NewUploadPackSession(ep, auth) + c.Assert(err, NotNil) +} + func (s *SuiteCommon) TestIssue70(c *C) { uploadPack := &UploadPackSuite{} uploadPack.SetUpSuite(c) - config := &ssh.ClientConfig{ - HostKeyCallback: ssh.InsecureIgnoreHostKey(), + config := &stdssh.ClientConfig{ + HostKeyCallback: stdssh.InsecureIgnoreHostKey(), } r := &runner{ config: config, diff --git a/plumbing/transport/ssh/upload_pack_test.go b/plumbing/transport/ssh/upload_pack_test.go index e65e04a7a..f172feeda 100644 --- a/plumbing/transport/ssh/upload_pack_test.go +++ b/plumbing/transport/ssh/upload_pack_test.go @@ -25,6 +25,7 @@ import ( type UploadPackSuite struct { test.UploadPackSuite fixtures.Suite + opts []ssh.Option port int base string @@ -57,6 +58,9 @@ func (s *UploadPackSuite) SetUpSuite(c *C) { s.UploadPackSuite.NonExistentEndpoint = s.newEndpoint(c, "non-existent.git") server := &ssh.Server{Handler: handlerSSH} + for _, opt := range s.opts { + opt(server) + } go func() { log.Fatal(server.Serve(l)) }() diff --git a/repository.go b/repository.go index 7292df627..2a06f8be3 100644 --- a/repository.go +++ b/repository.go @@ -750,21 +750,20 @@ func (r *Repository) buildTagSignature(tag *object.Tag, signKey *openpgp.Entity) // If you want to check to see if the tag is an annotated tag, you can call // TagObject on the hash of the reference in ForEach: // -// ref, err := r.Tag("v0.1.0") -// if err != nil { -// // Handle error -// } -// -// obj, err := r.TagObject(ref.Hash()) -// switch err { -// case nil: -// // Tag object present -// case plumbing.ErrObjectNotFound: -// // Not a tag object -// default: -// // Some other error -// } +// ref, err := r.Tag("v0.1.0") +// if err != nil { +// // Handle error +// } // +// obj, err := r.TagObject(ref.Hash()) +// switch err { +// case nil: +// // Tag object present +// case plumbing.ErrObjectNotFound: +// // Not a tag object +// default: +// // Some other error +// } func (r *Repository) Tag(name string) (*plumbing.Reference, error) { ref, err := r.Reference(plumbing.ReferenceName(path.Join("refs", "tags", name)), false) if err != nil { @@ -1241,26 +1240,25 @@ func commitIterFunc(order LogOrder) func(c *object.Commit) object.CommitIter { // If you want to check to see if the tag is an annotated tag, you can call // TagObject on the hash Reference passed in through ForEach: // -// iter, err := r.Tags() -// if err != nil { -// // Handle error -// } -// -// if err := iter.ForEach(func (ref *plumbing.Reference) error { -// obj, err := r.TagObject(ref.Hash()) -// switch err { -// case nil: -// // Tag object present -// case plumbing.ErrObjectNotFound: -// // Not a tag object -// default: -// // Some other error -// return err -// } -// }); err != nil { -// // Handle outer iterator error -// } +// iter, err := r.Tags() +// if err != nil { +// // Handle error +// } // +// if err := iter.ForEach(func (ref *plumbing.Reference) error { +// obj, err := r.TagObject(ref.Hash()) +// switch err { +// case nil: +// // Tag object present +// case plumbing.ErrObjectNotFound: +// // Not a tag object +// default: +// // Some other error +// return err +// } +// }); err != nil { +// // Handle outer iterator error +// } func (r *Repository) Tags() (storer.ReferenceIter, error) { refIter, err := r.Storer.IterReferences() if err != nil { @@ -1424,9 +1422,13 @@ func (r *Repository) Worktree() (*Worktree, error) { // // Implemented resolvers : HEAD, branch, tag, heads/branch, refs/heads/branch, // refs/tags/tag, refs/remotes/origin/branch, refs/remotes/origin/HEAD, tilde and caret (HEAD~1, master~^, tag~2, ref/heads/master~1, ...), selection by text (HEAD^{/fix nasty bug}), hash (prefix and full) -func (r *Repository) ResolveRevision(rev plumbing.Revision) (*plumbing.Hash, error) { - p := revision.NewParserFromString(string(rev)) +func (r *Repository) ResolveRevision(in plumbing.Revision) (*plumbing.Hash, error) { + rev := in.String() + if rev == "" { + return &plumbing.ZeroHash, plumbing.ErrReferenceNotFound + } + p := revision.NewParserFromString(rev) items, err := p.Parse() if err != nil { @@ -1557,6 +1559,10 @@ func (r *Repository) ResolveRevision(rev plumbing.Revision) (*plumbing.Hash, err } } + if commit == nil { + return &plumbing.ZeroHash, plumbing.ErrReferenceNotFound + } + return &commit.Hash, nil } diff --git a/repository_test.go b/repository_test.go index 7a9db151d..468ce33b0 100644 --- a/repository_test.go +++ b/repository_test.go @@ -2953,6 +2953,15 @@ func (s *RepositorySuite) TestDotGitToOSFilesystemsInvalidPath(c *C) { c.Assert(err, NotNil) } +func (s *RepositorySuite) TestIssue674(c *C) { + r, _ := Init(memory.NewStorage(), nil) + h, err := r.ResolveRevision(plumbing.Revision("")) + + c.Assert(err, NotNil) + c.Assert(h, NotNil) + c.Check(h.IsZero(), Equals, true) +} + func BenchmarkObjects(b *testing.B) { defer fixtures.Clean() diff --git a/storage/filesystem/dotgit/dotgit.go b/storage/filesystem/dotgit/dotgit.go index 6c386f799..2be2bae3e 100644 --- a/storage/filesystem/dotgit/dotgit.go +++ b/storage/filesystem/dotgit/dotgit.go @@ -943,6 +943,7 @@ func (d *DotGit) walkReferencesTree(refs *[]*plumbing.Reference, relPath []strin files, err := d.fs.ReadDir(d.fs.Join(relPath...)) if err != nil { if os.IsNotExist(err) { + // a race happened, and our directory is gone now return nil } @@ -960,6 +961,10 @@ func (d *DotGit) walkReferencesTree(refs *[]*plumbing.Reference, relPath []strin } ref, err := d.readReferenceFile(".", strings.Join(newRelPath, "/")) + if os.IsNotExist(err) { + // a race happened, and our file is gone now + continue + } if err != nil { return err } diff --git a/storage/filesystem/dotgit/dotgit_test.go b/storage/filesystem/dotgit/dotgit_test.go index a8f0eb754..63c9eb015 100644 --- a/storage/filesystem/dotgit/dotgit_test.go +++ b/storage/filesystem/dotgit/dotgit_test.go @@ -864,3 +864,71 @@ func (s *SuiteDotGit) TestIncBytes(c *C) { c.Assert(overflow, Equals, test.overflow) } } + +// this filesystem wrapper returns os.ErrNotExist if the file matches +// the provided paths list +type notExistsFS struct { + billy.Filesystem + + paths []string +} + +func (f *notExistsFS) matches(path string) bool { + p := filepath.ToSlash(path) + for _, n := range f.paths { + if p == n { + return true + } + } + return false +} + +func (f *notExistsFS) Open(filename string) (billy.File, error) { + if f.matches(filename) { + return nil, os.ErrNotExist + } + + return f.Filesystem.Open(filename) +} + +func (f *notExistsFS) ReadDir(path string) ([]os.FileInfo, error) { + if f.matches(path) { + return nil, os.ErrNotExist + } + + return f.Filesystem.ReadDir(path) +} + +func (s *SuiteDotGit) TestDeletedRefs(c *C) { + fs, clean := s.TemporalFilesystem() + defer clean() + + dir := New(¬ExistsFS{ + Filesystem: fs, + paths: []string{ + "refs/heads/bar", + "refs/heads/baz", + }, + }) + + err := dir.SetRef(plumbing.NewReferenceFromStrings( + "refs/heads/foo", + "e8d3ffab552895c19b9fcf7aa264d277cde33881", + ), nil) + c.Assert(err, IsNil) + err = dir.SetRef(plumbing.NewReferenceFromStrings( + "refs/heads/bar", + "a8d3ffab552895c19b9fcf7aa264d277cde33881", + ), nil) + c.Assert(err, IsNil) + err = dir.SetRef(plumbing.NewReferenceFromStrings( + "refs/heads/baz/baz", + "a8d3ffab552895c19b9fcf7aa264d277cde33881", + ), nil) + c.Assert(err, IsNil) + + refs, err := dir.Refs() + c.Assert(err, IsNil) + c.Assert(refs, HasLen, 1) + c.Assert(refs[0].Name(), Equals, plumbing.ReferenceName("refs/heads/foo")) +} diff --git a/worktree_test.go b/worktree_test.go index b57a77dbf..ac56a4688 100644 --- a/worktree_test.go +++ b/worktree_test.go @@ -259,7 +259,7 @@ func (s *RepositorySuite) TestPullAdd(c *C) { ExecuteOnPath(c, path, "touch foo", "git add foo", - "git commit -m foo foo", + "git commit --no-gpg-sign -m foo foo", ) w, err := r.Worktree()