Addeded 403 account block status code handling for gitlab (#3471)
* Addeded 403 account block status code handling for gitlab * resolved comments * removed unmarshalling logic
This commit is contained in:
@@ -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.
|
||||
@@ -60,6 +62,7 @@ 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/",
|
||||
@@ -67,8 +70,10 @@ func (s Scanner) FromData(ctx context.Context, verify bool, data []byte) (result
|
||||
}
|
||||
|
||||
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)
|
||||
}
|
||||
|
||||
@@ -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
|
||||
@@ -98,13 +103,14 @@ 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
|
||||
@@ -112,19 +118,28 @@ func (s Scanner) verifyGitlab(ctx context.Context, resMatch string) (bool, error
|
||||
// 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 {
|
||||
|
||||
@@ -3,6 +3,7 @@ package gitlab
|
||||
import (
|
||||
"context"
|
||||
"fmt"
|
||||
"io"
|
||||
"net/http"
|
||||
"strings"
|
||||
|
||||
@@ -10,6 +11,7 @@ import (
|
||||
|
||||
"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"
|
||||
)
|
||||
|
||||
@@ -49,6 +51,7 @@ 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/",
|
||||
@@ -56,8 +59,10 @@ func (s Scanner) FromData(ctx context.Context, verify bool, data []byte) (result
|
||||
}
|
||||
|
||||
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)
|
||||
}
|
||||
|
||||
@@ -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
|
||||
@@ -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 {
|
||||
|
||||
Reference in New Issue
Block a user