Skip to content

Commit e18edee

Browse files
zakiskchmouel
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 e7a2b7b commit e18edee

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
@@ -952,6 +952,12 @@ func TestGetFiles(t *testing.T) {
952952
}
953953
}
954954

955+
type roundTripperFunc func(*http.Request) (*http.Response, error)
956+
957+
func (f roundTripperFunc) RoundTrip(r *http.Request) (*http.Response, error) {
958+
return f(r)
959+
}
960+
955961
func TestProvider_checkWebhookSecretValidity(t *testing.T) {
956962
t1 := time.Date(1999, time.February, 3, 4, 5, 6, 7, time.UTC)
957963
cw := clockwork.NewFakeClockAt(t1)
@@ -963,7 +969,9 @@ func TestProvider_checkWebhookSecretValidity(t *testing.T) {
963969
expHeaderSet bool
964970
apiNotEnabled bool
965971
wantLogSnippet string
966-
report500 bool
972+
statusCode int
973+
wantNilSCIM bool
974+
wantNilResp bool
967975
}{
968976
{
969977
name: "remaining scim calls",
@@ -988,6 +996,22 @@ func TestProvider_checkWebhookSecretValidity(t *testing.T) {
988996
name: "no header mean unlimited",
989997
remaining: 5,
990998
},
999+
{
1000+
name: "skipping api rate limit is not enabled",
1001+
remaining: 0,
1002+
statusCode: http.StatusNotFound,
1003+
},
1004+
{
1005+
name: "skipping because scim is not available",
1006+
remaining: 0,
1007+
wantNilSCIM: true,
1008+
},
1009+
{
1010+
name: "resp is nil",
1011+
remaining: 0,
1012+
wantNilResp: true,
1013+
wantSubErr: "error making request to the GitHub API checking rate limit",
1014+
},
9911015
{
9921016
name: "no header but no remaining scim calls",
9931017
remaining: 0,
@@ -996,7 +1020,12 @@ func TestProvider_checkWebhookSecretValidity(t *testing.T) {
9961020
{
9971021
name: "api error",
9981022
wantSubErr: "error making request to the GitHub API checking rate limit",
999-
report500: true,
1023+
statusCode: http.StatusInternalServerError,
1024+
},
1025+
{
1026+
name: "not enabled",
1027+
apiNotEnabled: true,
1028+
wantLogSnippet: "skipping checking",
10001029
},
10011030
{
10021031
name: "not enabled",
@@ -1012,14 +1041,15 @@ func TestProvider_checkWebhookSecretValidity(t *testing.T) {
10121041

10131042
if !tt.apiNotEnabled {
10141043
mux.HandleFunc("/rate_limit", func(rw http.ResponseWriter, _ *http.Request) {
1015-
if tt.report500 {
1016-
rw.WriteHeader(http.StatusInternalServerError)
1044+
if tt.statusCode != 0 {
1045+
rw.WriteHeader(tt.statusCode)
10171046
return
10181047
}
1019-
s := &github.RateLimits{
1020-
SCIM: &github.Rate{
1048+
s := &github.RateLimits{}
1049+
if !tt.wantNilSCIM {
1050+
s.SCIM = &github.Rate{
10211051
Remaining: tt.remaining,
1022-
},
1052+
}
10231053
}
10241054
st := new(struct {
10251055
Resources *github.RateLimits `json:"resources"`
@@ -1034,6 +1064,16 @@ func TestProvider_checkWebhookSecretValidity(t *testing.T) {
10341064
})
10351065
}
10361066
defer teardown()
1067+
1068+
// create bad round tripper to make response nil and test that it handles that case.
1069+
if tt.wantNilResp {
1070+
errRT := roundTripperFunc(func(*http.Request) (*http.Response, error) {
1071+
return nil, fmt.Errorf("network down")
1072+
})
1073+
httpClient := &http.Client{Transport: errRT}
1074+
fakeclient = github.NewClient(httpClient)
1075+
}
1076+
10371077
v := &Provider{
10381078
ghClient: fakeclient,
10391079
Logger: logger,

0 commit comments

Comments
 (0)