@@ -3,6 +3,7 @@ package digitaloceanv2
33import (
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.
2930func (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