From f30d58707a4ef315a5b9aad888e3f21fb2af2131 Mon Sep 17 00:00:00 2001 From: Curtis Vogt Date: Mon, 29 Mar 2021 16:33:05 -0500 Subject: [PATCH] Fix issue where approved credentials were not sent to git credential helpers (#40161) Due to the bug in the `GitCredential` constructor we were never writing credential data to the credential helper effectively making our use of git credential helpers read-only. (cherry picked from commit ac50ac6058caa97c047c1751ca9cbbe0c1ceea2a) --- stdlib/LibGit2/src/gitcredential.jl | 7 +++- stdlib/LibGit2/test/libgit2.jl | 61 +++++++++++++++++++++++++++++ 2 files changed, 67 insertions(+), 1 deletion(-) diff --git a/stdlib/LibGit2/src/gitcredential.jl b/stdlib/LibGit2/src/gitcredential.jl index 0a442337531a79..1b97c29cd933e6 100644 --- a/stdlib/LibGit2/src/gitcredential.jl +++ b/stdlib/LibGit2/src/gitcredential.jl @@ -30,7 +30,12 @@ function GitCredential(cfg::GitConfig, url::AbstractString) fill!(cfg, parse(GitCredential, url)) end -GitCredential(cred::UserPasswordCredential, url::AbstractString) = parse(GitCredential, url) +function GitCredential(user_pass_cred::UserPasswordCredential, url::AbstractString) + cred = parse(GitCredential, url) + cred.username = user_pass_cred.user + cred.password = deepcopy(user_pass_cred.pass) + return cred +end Base.:(==)(c1::GitCredential, c2::GitCredential) = (c1.protocol, c1.host, c1.path, c1.username, c1.password, c1.use_http_path) == (c2.protocol, c2.host, c2.path, c2.username, c2.password, c2.use_http_path) diff --git a/stdlib/LibGit2/test/libgit2.jl b/stdlib/LibGit2/test/libgit2.jl index bc678b2dc7160b..f39c0a0e3b27aa 100644 --- a/stdlib/LibGit2/test/libgit2.jl +++ b/stdlib/LibGit2/test/libgit2.jl @@ -600,6 +600,23 @@ end github_regex_test("ssh://git@github.com/$user/$repo", user, repo) @test !occursin(LibGit2.GITHUB_REGEX, "git@notgithub.com/$user/$repo.git") end + + @testset "UserPasswordCredential/url constructor" begin + user_pass_cred = LibGit2.UserPasswordCredential("user", "*******") + url = "https://github.com" + expected_cred = LibGit2.GitCredential("https", "github.com", nothing, "user", "*******") + + cred = LibGit2.GitCredential(user_pass_cred, url) + @test cred == expected_cred + + # Shredding the UserPasswordCredential shouldn't result in information being lost + # inside of a GitCredential. + Base.shred!(user_pass_cred) + @test cred == expected_cred + + Base.shred!(cred) + Base.shred!(expected_cred) + end end mktempdir() do dir @@ -2121,6 +2138,50 @@ mktempdir() do dir end end end + + @testset "approve/reject with UserPasswordCredential" begin + # In order to use the "store" credential helper `git` needs to be installed and + # on the path. + if GIT_INSTALLED + config_path = joinpath(dir, config_file) + isfile(config_path) && rm(config_path) + + credential_path = joinpath(dir, ".git-credentials") + isfile(credential_path) && rm(credential_path) + + LibGit2.with(LibGit2.GitConfig(config_path, LibGit2.Consts.CONFIG_LEVEL_APP)) do cfg + query = LibGit2.GitCredential("https", "mygithost") + filled = LibGit2.GitCredential("https", "mygithost", nothing, "alice", "1234") + user_pass_cred = LibGit2.UserPasswordCredential("alice", "1234") + url = "https://mygithost" + + # Requires `git` to be installed and available on the path. + LibGit2.set!(cfg, "credential.helper", "store --file \"$credential_path\"") + helper = only(LibGit2.credential_helpers(cfg, query)) + + @test !isfile(credential_path) + + Base.shred!(LibGit2.fill!(helper, deepcopy(query))) do result + @test result == query + end + + LibGit2.approve(cfg, user_pass_cred, url) + @test isfile(credential_path) + Base.shred!(LibGit2.fill!(helper, deepcopy(query))) do result + @test result == filled + end + + LibGit2.reject(cfg, user_pass_cred, url) + Base.shred!(LibGit2.fill!(helper, deepcopy(query))) do result + @test result == query + end + + Base.shred!(query) + Base.shred!(filled) + Base.shred!(user_pass_cred) + end + end + end end # The following tests require that we can fake a TTY so that we can provide passwords