From c573bdc145448c6e28ff7341bdba6940cb10c4fc Mon Sep 17 00:00:00 2001 From: James Telfer <792299+jamestelfer@users.noreply.github.com> Date: Mon, 27 May 2024 21:12:22 +1000 Subject: [PATCH] fix: allow HTTPS credentials when SSH configured Allow a pipeline to request HTTPS credentials of a repository that has been configured for SSH, as long as the requested repository matches the configured repository. --- internal/vendor/vendor.go | 15 ++++++++ internal/vendor/vendor_test.go | 66 ++++++++++++++++++++++++++++++++++ 2 files changed, 81 insertions(+) diff --git a/internal/vendor/vendor.go b/internal/vendor/vendor.go index 6f056f4..f828731 100644 --- a/internal/vendor/vendor.go +++ b/internal/vendor/vendor.go @@ -4,6 +4,7 @@ import ( "context" "fmt" "net/url" + "regexp" "strconv" "time" @@ -60,6 +61,9 @@ func New( return nil, fmt.Errorf("could not find repository for pipeline %s: %w", claims.PipelineSlug, err) } + // allow HTTPS credentials if the pipeline is configured for an equivalent SSH URL + pipelineRepoURL = TranslateSSHToHTTPS(pipelineRepoURL) + if requestedRepoURL != "" && pipelineRepoURL != requestedRepoURL { // git is asking for a different repo than we can handle: return nil // to indicate that the handler should return a successful (but @@ -89,3 +93,14 @@ func New( }, nil } } + +var sshUrl = regexp.MustCompile(`^git@github.com:([^/].+)$`) + +func TranslateSSHToHTTPS(url string) string { + groups := sshUrl.FindStringSubmatch(url) + if groups == nil { + return url + } + + return fmt.Sprintf("https://github.com/%s", groups[1]) +} diff --git a/internal/vendor/vendor_test.go b/internal/vendor/vendor_test.go index 038e35c..4f5c035 100644 --- a/internal/vendor/vendor_test.go +++ b/internal/vendor/vendor_test.go @@ -151,3 +151,69 @@ func TestPipelineRepositoryToken_ExpiryUnix(t *testing.T) { }) } } + +func TestTransformSSHToHTTPS(t *testing.T) { + testCases := []struct { + name string + url string + expected string + }{ + { + name: "ssh, valid GitHub", + url: "git@github.com:organization/chinmina.git", + expected: "https://github.com/organization/chinmina.git", + }, + { + name: "ssh, no user", + url: "github.com:organization/chinmina.git", + expected: "github.com:organization/chinmina.git", + }, + { + name: "ssh, different host", + url: "git@githab.com:organization/chinmina.git", + expected: "git@githab.com:organization/chinmina.git", + }, + { + name: "ssh, invalid path specifier", + url: "git@github.com/organization/chinmina.git", + expected: "git@github.com/organization/chinmina.git", + }, + { + name: "ssh, zero length path", + url: "git@github.com:", + expected: "git@github.com:", + }, + { + name: "ssh, no extension", + url: "git@github.com:organization/chinmina", + expected: "https://github.com/organization/chinmina", + }, + { + name: "https, valid", + url: "https://github.com/organization/chinmina.git", + expected: "https://github.com/organization/chinmina.git", + }, + { + name: "https, nonsense", + url: "https://github.com/organization/chinmina.git", + expected: "https://github.com/organization/chinmina.git", + }, + { + name: "http, valid", + url: "http://github.com/organization/chinmina.git", + expected: "http://github.com/organization/chinmina.git", + }, + { + name: "pure nonsense", + url: "molybdenum://mo", + expected: "molybdenum://mo", + }, + } + + for _, tc := range testCases { + t.Run(tc.name, func(t *testing.T) { + actual := vendor.TranslateSSHToHTTPS(tc.url) + assert.Equal(t, tc.expected, actual) + }) + } +}