Skip to content

Commit 165d92a

Browse files
zakisksavitaashture
authored andcommitted
fix(github): guard checkWebhookSecretValidity against nil response
Fix nil-pointer panic when /rate_limit returns success without SCIM data or when the HTTP response itself is nil. Add early err/resp checks and ensure rl and rl.SCIM are non-nil before accessing them. Jira: https://issues.redhat.com/browse/SRVKP-8075 Signed-off-by: Zaki Shaikh <[email protected]>
1 parent 0437463 commit 165d92a

File tree

2 files changed

+58
-11
lines changed

2 files changed

+58
-11
lines changed

pkg/provider/github/github.go

Lines changed: 11 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -258,26 +258,33 @@ func parseTS(headerTS string) (time.Time, error) {
258258
// the issue was.
259259
func (v *Provider) checkWebhookSecretValidity(ctx context.Context, cw clockwork.Clock) error {
260260
rl, resp, err := v.Client().RateLimit.Get(ctx)
261-
if resp.StatusCode == http.StatusNotFound {
261+
if resp != nil && resp.StatusCode == http.StatusNotFound {
262262
v.Logger.Info("skipping checking if token has expired, rate_limit api is not enabled on token")
263263
return nil
264264
}
265265

266266
if err != nil {
267267
return fmt.Errorf("error making request to the GitHub API checking rate limit: %w", err)
268268
}
269-
if resp.Header.Get("GitHub-Authentication-Token-Expiration") != "" {
269+
270+
if resp != nil && resp.Header.Get("GitHub-Authentication-Token-Expiration") != "" {
270271
ts, err := parseTS(resp.Header.Get("GitHub-Authentication-Token-Expiration"))
271272
if err != nil {
272273
return fmt.Errorf("error parsing token expiration date: %w", err)
273274
}
274275

275276
if cw.Now().After(ts) {
276-
errm := fmt.Sprintf("token has expired at %s", resp.TokenExpiration.Format(time.RFC1123))
277-
return fmt.Errorf("%s", errm)
277+
errMsg := fmt.Sprintf("token has expired at %s", resp.TokenExpiration.Format(time.RFC1123))
278+
return fmt.Errorf("%s", errMsg)
278279
}
279280
}
280281

282+
// Guard against nil rl or rl.SCIM which could lead to a panic.
283+
if rl == nil || rl.SCIM == nil {
284+
v.Logger.Info("skipping token expiration check, SCIM rate limit API is not available for this token")
285+
return nil
286+
}
287+
281288
if rl.SCIM.Remaining == 0 {
282289
return fmt.Errorf("api rate limit exceeded. Access will be restored at %s", rl.SCIM.Reset.Format(time.RFC1123))
283290
}

pkg/provider/github/github_test.go

Lines changed: 47 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -914,6 +914,12 @@ func TestGetFiles(t *testing.T) {
914914
}
915915
}
916916

917+
type roundTripperFunc func(*http.Request) (*http.Response, error)
918+
919+
func (f roundTripperFunc) RoundTrip(r *http.Request) (*http.Response, error) {
920+
return f(r)
921+
}
922+
917923
func TestProvider_checkWebhookSecretValidity(t *testing.T) {
918924
t1 := time.Date(1999, time.February, 3, 4, 5, 6, 7, time.UTC)
919925
cw := clockwork.NewFakeClockAt(t1)
@@ -925,7 +931,9 @@ func TestProvider_checkWebhookSecretValidity(t *testing.T) {
925931
expHeaderSet bool
926932
apiNotEnabled bool
927933
wantLogSnippet string
928-
report500 bool
934+
statusCode int
935+
wantNilSCIM bool
936+
wantNilResp bool
929937
}{
930938
{
931939
name: "remaining scim calls",
@@ -950,6 +958,22 @@ func TestProvider_checkWebhookSecretValidity(t *testing.T) {
950958
name: "no header mean unlimited",
951959
remaining: 5,
952960
},
961+
{
962+
name: "skipping api rate limit is not enabled",
963+
remaining: 0,
964+
statusCode: http.StatusNotFound,
965+
},
966+
{
967+
name: "skipping because scim is not available",
968+
remaining: 0,
969+
wantNilSCIM: true,
970+
},
971+
{
972+
name: "resp is nil",
973+
remaining: 0,
974+
wantNilResp: true,
975+
wantSubErr: "error making request to the GitHub API checking rate limit",
976+
},
953977
{
954978
name: "no header but no remaining scim calls",
955979
remaining: 0,
@@ -958,7 +982,12 @@ func TestProvider_checkWebhookSecretValidity(t *testing.T) {
958982
{
959983
name: "api error",
960984
wantSubErr: "error making request to the GitHub API checking rate limit",
961-
report500: true,
985+
statusCode: http.StatusInternalServerError,
986+
},
987+
{
988+
name: "not enabled",
989+
apiNotEnabled: true,
990+
wantLogSnippet: "skipping checking",
962991
},
963992
{
964993
name: "not enabled",
@@ -974,14 +1003,15 @@ func TestProvider_checkWebhookSecretValidity(t *testing.T) {
9741003

9751004
if !tt.apiNotEnabled {
9761005
mux.HandleFunc("/rate_limit", func(rw http.ResponseWriter, _ *http.Request) {
977-
if tt.report500 {
978-
rw.WriteHeader(http.StatusInternalServerError)
1006+
if tt.statusCode != 0 {
1007+
rw.WriteHeader(tt.statusCode)
9791008
return
9801009
}
981-
s := &github.RateLimits{
982-
SCIM: &github.Rate{
1010+
s := &github.RateLimits{}
1011+
if !tt.wantNilSCIM {
1012+
s.SCIM = &github.Rate{
9831013
Remaining: tt.remaining,
984-
},
1014+
}
9851015
}
9861016
st := new(struct {
9871017
Resources *github.RateLimits `json:"resources"`
@@ -996,6 +1026,16 @@ func TestProvider_checkWebhookSecretValidity(t *testing.T) {
9961026
})
9971027
}
9981028
defer teardown()
1029+
1030+
// create bad round tripper to make response nil and test that it handles that case.
1031+
if tt.wantNilResp {
1032+
errRT := roundTripperFunc(func(*http.Request) (*http.Response, error) {
1033+
return nil, fmt.Errorf("network down")
1034+
})
1035+
httpClient := &http.Client{Transport: errRT}
1036+
fakeclient = github.NewClient(httpClient)
1037+
}
1038+
9991039
v := &Provider{
10001040
ghClient: fakeclient,
10011041
Logger: logger,

0 commit comments

Comments
 (0)