diff --git a/pkg/common/utils.go b/pkg/common/utils.go index e6a02e963eb0..6c0796397439 100644 --- a/pkg/common/utils.go +++ b/pkg/common/utils.go @@ -1,5 +1,11 @@ package common +import ( + "bufio" + "io" + "strings" +) + func AddStringSliceItem(item string, slice *[]string) { for _, i := range *slice { if i == item { @@ -17,3 +23,16 @@ func RemoveStringSliceItem(item string, slice *[]string) { } } } + +func ResponseContainsSubstring(reader io.ReadCloser, target string) (bool, error) { + scanner := bufio.NewScanner(reader) + for scanner.Scan() { + if strings.Contains(scanner.Text(), target) { + return true, nil + } + } + if err := scanner.Err(); err != nil { + return false, err + } + return false, nil +} diff --git a/pkg/common/utils_test.go b/pkg/common/utils_test.go index 1185a330ead7..02087854b2fc 100644 --- a/pkg/common/utils_test.go +++ b/pkg/common/utils_test.go @@ -1,7 +1,9 @@ package common import ( + "io" "reflect" + "strings" "testing" ) @@ -74,3 +76,60 @@ func TestRemoveItem(t *testing.T) { } } } + +// Test ParseResponseForKeywords with a reader that contains the keyword and a reader that doesn't. +func TestParseResponseForKeywords(t *testing.T) { + testCases := []struct { + name string + input string + keyword string + expected bool + }{ + { + name: "Should find keyword", + input: "ey: abc", + keyword: "ey", + expected: true, + }, + { + name: "Should not find keyword", + input: "fake response", + keyword: "ey", + expected: false, + }, + { + name: "Empty string", + input: "", + keyword: "ey", + expected: false, + }, + { + name: "Keyword at end", + input: "abc ey", + keyword: "ey", + expected: true, + }, + { + name: "Keyword at start", + input: "ey abc", + keyword: "ey", + expected: true, + }, + } + + for _, tc := range testCases { + t.Run(tc.name, func(t *testing.T) { + testReader := strings.NewReader(tc.input) + testReadCloser := io.NopCloser(testReader) + found, err := ResponseContainsSubstring(testReadCloser, tc.keyword) + + if err != nil { + t.Errorf("Error: %v", err) + } + + if found != tc.expected { + t.Errorf("Expected %v, got %v", tc.expected, found) + } + }) + } +} diff --git a/pkg/detectors/docusign/docusign.go b/pkg/detectors/docusign/docusign.go index 18bf32ccb7fb..92008dee46b3 100644 --- a/pkg/detectors/docusign/docusign.go +++ b/pkg/detectors/docusign/docusign.go @@ -3,10 +3,8 @@ package docusign import ( "context" "encoding/base64" - "encoding/json" "fmt" "github.com/go-errors/errors" - "io" "net/http" "regexp" "strings" @@ -83,25 +81,15 @@ func (s Scanner) FromData(ctx context.Context, verify bool, data []byte) (result return nil, errors.WrapPrefix(err, "Error making request", 0) } - // Read the response body - body, err := io.ReadAll(res.Body) - - if err != nil { - return nil, errors.WrapPrefix(err, "Error reading response body", 0) - } - - // Close the response body + verifiedBodyResponse, err := common.ResponseContainsSubstring(res.Body, "ey") res.Body.Close() - // Parse the response body into a Response struct - var parsedResponse Response - err = json.Unmarshal(body, &parsedResponse) if err != nil { - return nil, errors.WrapPrefix(err, "Error parsing response", 0) + return nil, err } if err == nil { - if res.StatusCode >= 200 && res.StatusCode < 300 && strings.HasPrefix(parsedResponse.AccessToken, "ey") { + if res.StatusCode >= 200 && res.StatusCode < 300 && verifiedBodyResponse { s1.Verified = true } else { // This function will check false positives for common test words, but also it will make sure the key appears 'random' enough to be a real key.