6
6
"crypto/sha256"
7
7
"encoding/base64"
8
8
"fmt"
9
+ "io"
9
10
"net/http"
10
11
"regexp"
11
- "strings"
12
12
"time"
13
13
14
14
"github.com/trufflesecurity/trufflehog/v3/pkg/detectors"
@@ -23,72 +23,113 @@ var _ detectors.Detector = (*Scanner)(nil)
23
23
24
24
var (
25
25
defaultClient = http .DefaultClient
26
- keyPat = regexp .MustCompile (`DefaultEndpointsProtocol=https;AccountName=(?P<account_name>[^;]+);AccountKey=(?P<account_key>[^;]+);EndpointSuffix=core\.windows\.net` )
26
+ namePat = regexp .MustCompile (`AccountName=([a-z0-9]{3,24})` ) // Names can only be lowercase alphanumeric.
27
+ keyPat = regexp .MustCompile (`AccountKey=([a-zA-Z0-9+/-]{86,88}={0,2})` )
28
+ key1Pat = regexp .MustCompile (`DefaultEndpointsProtocol=https;AccountName=(?P<account_name>[^;]+);AccountKey=(?P<account_key>[^;]+);EndpointSuffix=core\.windows\.net` )
29
+
30
+ // https://learn.microsoft.com/en-us/azure/storage/common/storage-use-emulator
31
+ testNames = map [string ]struct {}{
32
+ "devstoreaccount1" : {},
33
+ "storagesample" : {},
34
+ }
35
+ testKeys = map [string ]struct {}{
36
+ "Eby8vdM02xNOcqFlqUwJPLlmEtlCDXJ1OUzFT50uSRZ6IFsuFq2UVErCz4I6tq/K1SZFPTOtr/KBHBeksoGMGw==" : {},
37
+ }
27
38
)
28
39
29
40
func (s Scanner ) Keywords () []string {
30
- return []string {"DefaultEndpointsProtocol=https;AccountName =" }
41
+ return []string {"DefaultEndpointsProtocol=http" , "EndpointSuffix =" }
31
42
}
32
43
33
44
func (s Scanner ) FromData (ctx context.Context , verify bool , data []byte ) (results []detectors.Result , err error ) {
34
45
dataStr := string (data )
35
46
36
- matches := keyPat .FindAllStringSubmatch (dataStr , - 1 )
47
+ // Deduplicate results.
48
+ names := make (map [string ]struct {})
49
+ for _ , matches := range namePat .FindAllStringSubmatch (dataStr , - 1 ) {
50
+ name := matches [1 ]
51
+ if _ , ok := testNames [name ]; ok {
52
+ continue
53
+ }
54
+ names [name ] = struct {}{}
55
+ }
56
+ if len (names ) == 0 {
57
+ return results , nil
58
+ }
37
59
38
- for _ , match := range matches {
39
- if len (match ) != 3 {
60
+ keys := make (map [string ]struct {})
61
+ for _ , matches := range keyPat .FindAllStringSubmatch (dataStr , - 1 ) {
62
+ key := matches [1 ]
63
+ if _ , ok := testKeys [key ]; ok {
40
64
continue
41
65
}
42
- accountName := strings .TrimSpace (match [1 ])
43
- accountKey := strings .TrimSpace (match [2 ])
66
+ keys [key ] = struct {}{}
67
+ }
68
+ if len (keys ) == 0 {
69
+ return results , nil
70
+ }
44
71
72
+ // Check results.
73
+ for name := range names {
45
74
s1 := detectors.Result {
46
- DetectorType : detectorspb . DetectorType_AzureStorage ,
47
- Raw : []byte (accountName ),
75
+ DetectorType : s . Type () ,
76
+ Raw : []byte (name ),
48
77
}
49
78
50
- if verify {
51
- client := s .client
52
- if client == nil {
53
- client = defaultClient
79
+ for key := range keys {
80
+ if verify {
81
+ isVerified , verificationErr := s .verifyMatch (ctx , name , key )
82
+ s1 .Verified = isVerified
83
+ s1 .VerificationError = verificationErr
54
84
}
55
85
56
- now := time .Now ().UTC ().Format (http .TimeFormat )
57
- stringToSign := "GET\n \n \n \n \n \n \n \n \n \n \n \n x-ms-date:" + now + "\n x-ms-version:2019-12-12\n /" + accountName + "/\n comp:list"
58
- accountKeyBytes , _ := base64 .StdEncoding .DecodeString (accountKey )
59
- h := hmac .New (sha256 .New , accountKeyBytes )
60
- h .Write ([]byte (stringToSign ))
61
- signature := base64 .StdEncoding .EncodeToString (h .Sum (nil ))
62
-
63
- url := "https://" + accountName + ".blob.core.windows.net/?comp=list"
64
- req , err := http .NewRequestWithContext (ctx , "GET" , url , nil )
65
- req .Header .Set ("x-ms-date" , now )
66
- req .Header .Set ("x-ms-version" , "2019-12-12" )
67
- req .Header .Set ("Authorization" , "SharedKey " + accountName + ":" + signature )
68
-
69
- if err != nil {
70
- continue
71
- }
72
- res , err := client .Do (req )
73
- if err == nil {
74
- defer res .Body .Close ()
75
- if res .StatusCode >= 200 && res .StatusCode < 300 {
76
- s1 .Verified = true
77
- } else if res .StatusCode == 403 {
78
- } else {
79
- s1 .VerificationError = fmt .Errorf ("unexpected HTTP response status %d" , res .StatusCode )
80
- }
81
- } else {
82
- s1 .VerificationError = err
83
- }
86
+ results = append (results , s1 )
84
87
}
85
-
86
- results = append (results , s1 )
87
88
}
88
89
89
90
return results , nil
90
91
}
91
92
93
+ func (s Scanner ) verifyMatch (ctx context.Context , name string , key string ) (bool , error ) {
94
+ client := s .client
95
+ if client == nil {
96
+ client = defaultClient
97
+ }
98
+
99
+ now := time .Now ().UTC ().Format (http .TimeFormat )
100
+ stringToSign := "GET\n \n \n \n \n \n \n \n \n \n \n \n x-ms-date:" + now + "\n x-ms-version:2019-12-12\n /" + name + "/\n comp:list"
101
+ accountKeyBytes , _ := base64 .StdEncoding .DecodeString (key )
102
+ h := hmac .New (sha256 .New , accountKeyBytes )
103
+ h .Write ([]byte (stringToSign ))
104
+ signature := base64 .StdEncoding .EncodeToString (h .Sum (nil ))
105
+
106
+ url := "https://" + name + ".blob.core.windows.net/?comp=list"
107
+ req , err := http .NewRequestWithContext (ctx , "GET" , url , nil )
108
+ if err != nil {
109
+ return false , err
110
+ }
111
+
112
+ req .Header .Set ("x-ms-date" , now )
113
+ req .Header .Set ("x-ms-version" , "2019-12-12" )
114
+ req .Header .Set ("Authorization" , "SharedKey " + name + ":" + signature )
115
+
116
+ res , err := client .Do (req )
117
+ if err == nil {
118
+ _ , _ = io .Copy (io .Discard , res .Body )
119
+ _ = res .Body .Close ()
120
+
121
+ if res .StatusCode >= 200 && res .StatusCode < 300 {
122
+ return true , nil
123
+ } else if res .StatusCode == 403 {
124
+ return false , nil
125
+ } else {
126
+ return false , fmt .Errorf ("unexpected HTTP response status %d" , res .StatusCode )
127
+ }
128
+ } else {
129
+ return false , err
130
+ }
131
+ }
132
+
92
133
func (s Scanner ) Type () detectorspb.DetectorType {
93
134
return detectorspb .DetectorType_AzureStorage
94
135
}
0 commit comments