Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Addeded 403 account block status code handling for gitlab #3471

Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
35 changes: 25 additions & 10 deletions pkg/detectors/gitlab/v1/gitlab.go
Original file line number Diff line number Diff line change
Expand Up @@ -34,6 +34,8 @@ func (Scanner) CloudEndpoint() string { return "https://gitlab.com" }
var (
defaultClient = common.SaneHttpClient()
keyPat = regexp.MustCompile(detectors.PrefixRegex([]string{"gitlab"}) + `\b([a-zA-Z0-9\-=_]{20,22})\b`)

BlockedUserMessage = "403 Forbidden - Your account has been blocked"
)

// Keywords are used for efficiently pre-filtering chunks.
Expand All @@ -60,15 +62,18 @@ func (s Scanner) FromData(ctx context.Context, verify bool, data []byte) (result
s1 := detectors.Result{
DetectorType: detectorspb.DetectorType_Gitlab,
Raw: []byte(resMatch),
ExtraData: map[string]string{},
}
s1.ExtraData = map[string]string{
"rotation_guide": "https://howtorotate.com/docs/tutorials/gitlab/",
"version": fmt.Sprintf("%d", s.Version()),
}

if verify {
isVerified, verificationErr := s.verifyGitlab(ctx, resMatch)
isVerified, extraData, verificationErr := s.verifyGitlab(ctx, resMatch)
s1.Verified = isVerified
s1.ExtraData = extraData

s1.SetVerificationError(verificationErr, resMatch)
}

Expand All @@ -78,7 +83,7 @@ func (s Scanner) FromData(ctx context.Context, verify bool, data []byte) (result
return results, nil
}

func (s Scanner) verifyGitlab(ctx context.Context, resMatch string) (bool, error) {
func (s Scanner) verifyGitlab(ctx context.Context, resMatch string) (bool, map[string]string, error) {
// there are 4 read 'scopes' for a gitlab token: api, read_user, read_repo, and read_registry
// they all grant access to different parts of the API. I couldn't find an endpoint that every
// one of these scopes has access to, so we just check an example endpoint for each scope. If any
Expand All @@ -98,33 +103,43 @@ func (s Scanner) verifyGitlab(ctx context.Context, resMatch string) (bool, error
req.Header.Add("Authorization", fmt.Sprintf("Bearer %s", resMatch))
res, err := client.Do(req)
if err != nil {
return false, err
return false, nil, err
}

defer res.Body.Close()
body, err := io.ReadAll(res.Body)

bodyBytes, err := io.ReadAll(res.Body)
if err != nil {
return false, err
return false, nil, err
}

// 200 means good key and has `read_user` scope
// 403 means good key but not the right scope
// 401 is bad key
switch res.StatusCode {
case http.StatusOK:
return json.Valid(body), nil
return json.Valid(bodyBytes), nil, nil
case http.StatusForbidden:
// check if the user account is blocked or not
stringBody := string(bodyBytes)
if strings.Contains(stringBody, BlockedUserMessage) {
return true, map[string]string{
"blocked": "True",
}, nil
}

// Good key but not the right scope
return true, nil
return true, nil, nil
case http.StatusUnauthorized:
// Nothing to do; zero values are the ones we want
return false, nil
return false, nil, nil
default:
return false, fmt.Errorf("unexpected HTTP response status %d", res.StatusCode)
return false, nil, fmt.Errorf("unexpected HTTP response status %d", res.StatusCode)
}

}
return false, nil

return false, nil, nil
}

func (s Scanner) Type() detectorspb.DetectorType {
Expand Down
36 changes: 27 additions & 9 deletions pkg/detectors/gitlab/v2/gitlab_v2.go
Original file line number Diff line number Diff line change
Expand Up @@ -3,13 +3,15 @@ package gitlab
import (
"context"
"fmt"
"io"
"net/http"
"strings"

regexp "github.com/wasilibs/go-re2"

"github.com/trufflesecurity/trufflehog/v3/pkg/common"
"github.com/trufflesecurity/trufflehog/v3/pkg/detectors"
v1 "github.com/trufflesecurity/trufflehog/v3/pkg/detectors/gitlab/v1"
"github.com/trufflesecurity/trufflehog/v3/pkg/pb/detectorspb"
)

Expand Down Expand Up @@ -49,15 +51,18 @@ func (s Scanner) FromData(ctx context.Context, verify bool, data []byte) (result
s1 := detectors.Result{
DetectorType: detectorspb.DetectorType_Gitlab,
Raw: []byte(resMatch),
ExtraData: map[string]string{},
}
s1.ExtraData = map[string]string{
"rotation_guide": "https://howtorotate.com/docs/tutorials/gitlab/",
"version": fmt.Sprintf("%d", s.Version()),
}

if verify {
isVerified, verificationErr := s.verifyGitlab(ctx, resMatch)
isVerified, extraData, verificationErr := s.verifyGitlab(ctx, resMatch)
s1.Verified = isVerified
s1.ExtraData = extraData

s1.SetVerificationError(verificationErr, resMatch)
}

Expand All @@ -67,7 +72,7 @@ func (s Scanner) FromData(ctx context.Context, verify bool, data []byte) (result
return results, nil
}

func (s Scanner) verifyGitlab(ctx context.Context, resMatch string) (bool, error) {
func (s Scanner) verifyGitlab(ctx context.Context, resMatch string) (bool, map[string]string, error) {
// there are 4 read 'scopes' for a gitlab token: api, read_user, read_repo, and read_registry
// they all grant access to different parts of the API. I couldn't find an endpoint that every
// one of these scopes has access to, so we just check an example endpoint for each scope. If any
Expand All @@ -86,28 +91,41 @@ func (s Scanner) verifyGitlab(ctx context.Context, resMatch string) (bool, error
req.Header.Add("Authorization", fmt.Sprintf("Bearer %s", resMatch))
res, err := client.Do(req)
if err != nil {
return false, err
return false, nil, err
}
defer res.Body.Close()

bodyBytes, err := io.ReadAll(res.Body)
if err != nil {
return false, nil, err
}
defer res.Body.Close() // The request body is unused.

// 200 means good key and has `read_user` scope
// 403 means good key but not the right scope
// 401 is bad key
switch res.StatusCode {
case http.StatusOK:
return true, nil
return true, nil, nil
case http.StatusForbidden:
// check if the user account is blocked or not
stringBody := string(bodyBytes)
if strings.Contains(stringBody, v1.BlockedUserMessage) {
return true, map[string]string{
"blocked": "True",
}, nil
}

// Good key but not the right scope
return true, nil
return true, nil, nil
case http.StatusUnauthorized:
// Nothing to do; zero values are the ones we want
return false, nil
return false, nil, nil
default:
return false, fmt.Errorf("unexpected HTTP response status %d", res.StatusCode)
return false, nil, fmt.Errorf("unexpected HTTP response status %d", res.StatusCode)
}

}
return false, nil
return false, nil, nil
}

func (s Scanner) Type() detectorspb.DetectorType {
Expand Down
Loading