From f4810ec03f75bb6ef38e3f3bd9900d333ea61423 Mon Sep 17 00:00:00 2001 From: Jack Rose Date: Thu, 27 Mar 2025 13:41:42 +1100 Subject: [PATCH 1/2] improve messaging with github authed requests --- internal/plugin/github.go | 35 +++++++++++++++++++++++++++++- internal/plugin/github_test.go | 39 ++++++++++++++++++++++++++++++++++ 2 files changed, 73 insertions(+), 1 deletion(-) diff --git a/internal/plugin/github.go b/internal/plugin/github.go index a2ed6ab6ad6..984627bf1ab 100644 --- a/internal/plugin/github.go +++ b/internal/plugin/github.go @@ -4,6 +4,7 @@ import ( "cmp" "fmt" "io" + "log/slog" "net/http" "net/url" "os" @@ -104,12 +105,20 @@ func (p *githubPlugin) FileContent(subpath string) ([]byte, error) { } defer res.Body.Close() if res.StatusCode != http.StatusOK { + authInfo := "No auth header was send with this request." + if req.Header.Get("Authorization") != "" { + authInfo = fmt.Sprintf( + "The auth header `%s` was send with this request.", + getRedactedAuthHeader(req), + ) + } return nil, 0, usererr.New( - "failed to get plugin %s @ %s (Status code %d). \nPlease make "+ + "failed to get plugin %s @ %s (Status code %d).\n%s\nPlease make "+ "sure a plugin.json file exists in plugin directory.", p.LockfileKey(), req.URL.String(), res.StatusCode, + authInfo, ) } body, err := io.ReadAll(res.Body) @@ -147,6 +156,11 @@ func (p *githubPlugin) request(contentURL string) (*http.Request, error) { if ghToken != "" { authValue := fmt.Sprintf("token %s", ghToken) req.Header.Add("Authorization", authValue) + slog.Debug( + "GITHUB_TOKEN env var found, adding to request's auth header", + "headerValue", + getRedactedAuthHeader(req), + ) } return req, nil @@ -155,3 +169,22 @@ func (p *githubPlugin) request(contentURL string) (*http.Request, error) { func (p *githubPlugin) LockfileKey() string { return p.ref.String() } + +func getRedactedAuthHeader(req *http.Request) string { + authHeader := req.Header.Get("Authorization") + parts := strings.SplitN(authHeader, " ", 2) + + if len(authHeader) < 10 || len(parts) < 2 { + // too short to safely reveal any part + return strings.Repeat("*", len(authHeader)) + } + + authType, token := parts[0], parts[1] + if len(token) < 10 { + // second word to short to reveal any, but show first word + return authType + " " + strings.Repeat("*", len(token)) + } + + // show first 4 chars of token to help with debugging (will often be "ghp_") + return authType + " " + token[:4] + strings.Repeat("*", len(token)-4) +} diff --git a/internal/plugin/github_test.go b/internal/plugin/github_test.go index f9671429035..7aaebcd8415 100644 --- a/internal/plugin/github_test.go +++ b/internal/plugin/github_test.go @@ -1,6 +1,7 @@ package plugin import ( + "net/http" "strings" "testing" @@ -134,3 +135,41 @@ func TestGithubPluginAuth(t *testing.T) { assert.Equal(t, "token gh_abcd", actual.Header.Get("Authorization")) }) } + +func TestGetRedactedAuthHeader(t *testing.T) { + testCases := []struct { + name string + authHeader string + expected string + }{ + { + "normal length token partially readable for debugging", + "token ghp_61b296fb898349778e20532cb65ce38e", + "token ghp_********************************", + }, + { + "short token redacted", + "token ghp_61b29", + "token *********", + }, + { + "short header fully redacted", + "token xyz", + "*********", + }, + { + "no token returns empty string", + "", + "", + }, + } + + for _, testCase := range testCases { + t.Run(testCase.name, func(t *testing.T) { + req, err := http.NewRequest(http.MethodGet, "https://example.com", nil) + assert.NoError(t, err) + req.Header.Add("Authorization", testCase.authHeader) + assert.Equal(t, testCase.expected, getRedactedAuthHeader(req)) + }) + } +} From d19822ebd05cb8d18dabc5aacb126eb4f4ce49f3 Mon Sep 17 00:00:00 2001 From: Jack Rose Date: Fri, 28 Mar 2025 09:50:57 +1100 Subject: [PATCH 2/2] fix grammar --- internal/plugin/github.go | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/internal/plugin/github.go b/internal/plugin/github.go index 984627bf1ab..b3c14f9f2c3 100644 --- a/internal/plugin/github.go +++ b/internal/plugin/github.go @@ -105,10 +105,10 @@ func (p *githubPlugin) FileContent(subpath string) ([]byte, error) { } defer res.Body.Close() if res.StatusCode != http.StatusOK { - authInfo := "No auth header was send with this request." + authInfo := "No auth header was sent with this request." if req.Header.Get("Authorization") != "" { authInfo = fmt.Sprintf( - "The auth header `%s` was send with this request.", + "The auth header `%s` was sent with this request.", getRedactedAuthHeader(req), ) } @@ -181,7 +181,7 @@ func getRedactedAuthHeader(req *http.Request) string { authType, token := parts[0], parts[1] if len(token) < 10 { - // second word to short to reveal any, but show first word + // second word too short to reveal any, but show first word return authType + " " + strings.Repeat("*", len(token)) }