Skip to content

Commit

Permalink
fix: added correct verification endpoint & validation logic for alegra (
Browse files Browse the repository at this point in the history
#3437)

* fix: added correct verification endpoint for alegra

* fix: fixed email regex

* fix: added correct tests and validation

* fix: fixed alegra tests
  • Loading branch information
sahil9001 authored Oct 24, 2024
1 parent 0608b22 commit c37edcd
Show file tree
Hide file tree
Showing 3 changed files with 62 additions and 41 deletions.
80 changes: 52 additions & 28 deletions pkg/detectors/alegra/alegra.go
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,8 @@ package alegra

import (
"context"
"fmt"
"io"
"net/http"
"strings"

Expand All @@ -13,18 +15,17 @@ import (
)

type Scanner struct {
detectors.DefaultMultiPartCredentialProvider
client *http.Client
}

// Ensure the Scanner satisfies the interface at compile time.
var _ detectors.Detector = (*Scanner)(nil)

var (
client = common.SaneHttpClient()

defaultClient = common.SaneHttpClient()
// Make sure that your group is surrounded in boundary characters such as below to reduce false positives.
keyPat = regexp.MustCompile(detectors.PrefixRegex([]string{"alegra"}) + `\b([a-z0-9-]{20})\b`)
idPat = regexp.MustCompile(detectors.PrefixRegex([]string{"alegra"}) + `\b([a-zA-Z0-9\.\-\@]{25,30})\b`)
idPat = regexp.MustCompile(detectors.PrefixRegex([]string{"alegra"}) + common.EmailPattern)
)

// Keywords are used for efficiently pre-filtering chunks.
Expand All @@ -37,41 +38,38 @@ func (s Scanner) Keywords() []string {
func (s Scanner) FromData(ctx context.Context, verify bool, data []byte) (results []detectors.Result, err error) {
dataStr := string(data)

matches := keyPat.FindAllStringSubmatch(dataStr, -1)
keyMatches := keyPat.FindAllStringSubmatch(dataStr, -1)
idMatches := idPat.FindAllStringSubmatch(dataStr, -1)

for _, match := range matches {
if len(match) != 2 {
continue
}
tokenPatMatch := strings.TrimSpace(match[1])
uniqueTokens := make(map[string]struct{})
uniqueIDs := make(map[string]struct{})

for _, idMatch := range idMatches {
if len(idMatch) != 2 {
continue
}
for _, match := range keyMatches {
uniqueTokens[match[1]] = struct{}{}
}

userPatMatch := strings.TrimSpace(idMatch[1])
for _, match := range idMatches {
id := match[0][strings.LastIndex(match[0], " ")+1:]
uniqueIDs[id] = struct{}{}
}

for token := range uniqueTokens {
for id := range uniqueIDs {
s1 := detectors.Result{
DetectorType: detectorspb.DetectorType_Alegra,
Raw: []byte(tokenPatMatch),
RawV2: []byte(tokenPatMatch + userPatMatch),
Raw: []byte(token),
RawV2: []byte(token + ":" + id),
}

if verify {
req, err := http.NewRequestWithContext(ctx, "GET", "https://api.alegra.com/api/v1/users", nil)
if err != nil {
continue
}
req.SetBasicAuth(userPatMatch, tokenPatMatch)
res, err := client.Do(req)
if err == nil {
defer res.Body.Close()
if res.StatusCode >= 200 && res.StatusCode < 300 {
s1.Verified = true
}
client := s.client
if client == nil {
client = defaultClient
}

isVerified, verificationErr := verifyCredentials(ctx, client, id, token)
s1.Verified = isVerified
s1.SetVerificationError(verificationErr, token)
}

results = append(results, s1)
Expand All @@ -81,6 +79,32 @@ func (s Scanner) FromData(ctx context.Context, verify bool, data []byte) (result
return results, nil
}

func verifyCredentials(ctx context.Context, client *http.Client, username, token string) (bool, error) {
req, err := http.NewRequestWithContext(ctx, http.MethodGet, "https://api.alegra.com/api/v1/users/self", nil)
if err != nil {
return false, nil
}
req.SetBasicAuth(username, token)

res, err := client.Do(req)
if err != nil {
return false, err
}
defer func() {
_, _ = io.Copy(io.Discard, res.Body)
_ = res.Body.Close()
}()

switch res.StatusCode {
case http.StatusOK:
return true, nil
case http.StatusUnauthorized:
return false, nil
default:
return false, fmt.Errorf("unexpected HTTP response status %d", res.StatusCode)
}
}

func (s Scanner) Type() detectorspb.DetectorType {
return detectorspb.DetectorType_Alegra
}
Expand Down
1 change: 1 addition & 0 deletions pkg/detectors/alegra/alegra_integration_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -96,6 +96,7 @@ func TestAlegra_FromChunk(t *testing.T) {
t.Fatalf("no raw secret present: \n %+v", got[i])
}
got[i].Raw = nil
got[i].RawV2 = nil
}
if diff := pretty.Compare(got, tt.want); diff != "" {
t.Errorf("Alegra.FromData() %s diff: (-got +want)\n%s", tt.name, diff)
Expand Down
22 changes: 9 additions & 13 deletions pkg/detectors/alegra/alegra_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,7 @@ import (
var (
validPattern = "wdvn-usa87a-fxp9ioas/testUser.1005@example.com"
validSpecialCharPattern = "wdvn-usa87a-fxp9ioas / test-User.1005@example.com"
invalidPattern = "wdvn-usa87a-fxp9ioas/testUser$1005@example.com"
invalidPattern = "wdvn-usa87a-fxp9ioasQQsstestUsQQ@example"
)

func TestAlegra_Pattern(t *testing.T) {
Expand All @@ -28,30 +28,30 @@ func TestAlegra_Pattern(t *testing.T) {
}{
{
name: "valid pattern",
input: fmt.Sprintf("alegra: '%s'", validPattern),
want: []string{"wdvn-usa87a-fxp9ioastestUser.1005@example.com"},
input: fmt.Sprintf("alegra: %s", validPattern),
want: []string{"wdvn-usa87a-fxp9ioas:wdvn-usa87a-fxp9ioas/testUser.1005@example.com"},
},
{
name: "valid pattern - with special characters",
input: fmt.Sprintf("alegra: '%s'", validSpecialCharPattern),
want: []string{"wdvn-usa87a-fxp9ioastest-User.1005@example.com"},
input: fmt.Sprintf("alegra: %s", validSpecialCharPattern),
want: []string{"wdvn-usa87a-fxp9ioas:test-User.1005@example.com"},
},
{
name: "valid pattern - key out of prefix range",
input: fmt.Sprintf("alegra keyword is not close to the real key and id = '%s'", validPattern),
input: fmt.Sprintf("alegra keyword is not close to the real key and id = %s", validPattern),
want: nil,
},
{
name: "invalid pattern",
input: fmt.Sprintf("alegra: '%s'", invalidPattern),
input: fmt.Sprintf("alegra: %s", invalidPattern),
want: nil,
},
}

for _, test := range tests {
t.Run(test.name, func(t *testing.T) {
matchedDetectors := ahoCorasickCore.FindDetectorMatches([]byte(test.input))
if len(matchedDetectors) == 0 {
if len(matchedDetectors) == 0 && test.want != nil {
t.Errorf("keywords '%v' not matched by: %s", d.Keywords(), test.input)
return
}
Expand All @@ -63,11 +63,7 @@ func TestAlegra_Pattern(t *testing.T) {
}

if len(results) != len(test.want) {
if len(results) == 0 {
t.Errorf("did not receive result")
} else {
t.Errorf("expected %d results, only received %d", len(test.want), len(results))
}
t.Errorf("expected %d results, got %d", len(test.want), len(results))
return
}

Expand Down

0 comments on commit c37edcd

Please sign in to comment.