Skip to content

Commit 3f6e5b4

Browse files
authored
Digitaloceanv2 detector (#832)
1 parent 2bc4985 commit 3f6e5b4

1 file changed

Lines changed: 47 additions & 15 deletions

File tree

pkg/detectors/digitaloceanv2/digitaloceanv2.go

Lines changed: 47 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,7 @@ package digitaloceanv2
33
import (
44
"context"
55
"fmt"
6+
"io"
67
"net/http"
78
"regexp"
89
"strings"
@@ -21,13 +22,13 @@ var (
2122
client = common.SaneHttpClient()
2223

2324
// Make sure that your group is surrounded in boundary characters such as below to reduce false positives.
24-
keyPat = regexp.MustCompile(`\b(dop\_v1\_[a-f0-9]{64})\b`)
25+
keyPat = regexp.MustCompile(`\b((?:dop|doo|dor)_v1_[a-f0-9]{64})\b`)
2526
)
2627

2728
// Keywords are used for efficiently pre-filtering chunks.
2829
// Use identifiers in the secret preferably, or the provider name.
2930
func (s Scanner) Keywords() []string {
30-
return []string{"dop_v1_"}
31+
return []string{"dop_v1_", "doo_v1_", "dor_v1_"}
3132
}
3233

3334
// FromData will find and optionally verify DigitalOceanV2 secrets in a given set of bytes.
@@ -48,21 +49,52 @@ func (s Scanner) FromData(ctx context.Context, verify bool, data []byte) (result
4849
}
4950

5051
if verify {
51-
req, err := http.NewRequestWithContext(ctx, "GET", "https://api.digitalocean.com/v2/account", nil)
52-
if err != nil {
53-
continue
54-
}
55-
req.Header.Add("Authorization", fmt.Sprintf("Bearer %s", resMatch))
56-
res, err := client.Do(req)
57-
if err == nil {
58-
defer res.Body.Close()
59-
if res.StatusCode >= 200 && res.StatusCode < 300 {
60-
s1.Verified = true
61-
} else {
62-
// 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.
63-
if detectors.IsKnownFalsePositive(resMatch, detectors.DefaultFalsePositives, true) {
52+
switch {
53+
case strings.HasPrefix(resMatch, "dor_v1_"):
54+
req, err := http.NewRequestWithContext(ctx, "GET", "https://cloud.digitalocean.com/v1/oauth/token?grant_type=refresh_token&refresh_token="+resMatch, nil)
55+
if err != nil {
56+
continue
57+
}
58+
59+
res, err := client.Do(req)
60+
if err == nil {
61+
bodyBytes, err := io.ReadAll(res.Body)
62+
63+
if err != nil {
6464
continue
6565
}
66+
67+
bodyString := string(bodyBytes)
68+
validResponse := strings.Contains(bodyString, `"access_token"`)
69+
defer res.Body.Close()
70+
71+
if res.StatusCode >= 200 && res.StatusCode < 300 && validResponse {
72+
s1.Verified = true
73+
} else {
74+
// 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
75+
if detectors.IsKnownFalsePositive(resMatch, detectors.DefaultFalsePositives, true) {
76+
continue
77+
}
78+
}
79+
}
80+
81+
case strings.HasPrefix(resMatch, "doo_v1_"), strings.HasPrefix(resMatch, "dop_v1_"):
82+
req, err := http.NewRequestWithContext(ctx, "GET", "https://api.digitalocean.com/v2/account", nil)
83+
if err != nil {
84+
continue
85+
}
86+
req.Header.Add("Authorization", fmt.Sprintf("Bearer %s", resMatch))
87+
res, err := client.Do(req)
88+
if err == nil {
89+
defer res.Body.Close()
90+
if res.StatusCode >= 200 && res.StatusCode < 300 {
91+
s1.Verified = true
92+
} else {
93+
// 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.
94+
if detectors.IsKnownFalsePositive(resMatch, detectors.DefaultFalsePositives, true) {
95+
continue
96+
}
97+
}
6698
}
6799
}
68100
}

0 commit comments

Comments
 (0)