@@ -17,6 +17,8 @@ import (
17
17
"github.com/trufflesecurity/trufflehog/v3/pkg/pb/detectorspb"
18
18
)
19
19
20
+ const RFC3339WithoutMicroseconds = "2006-01-02T15:04:05"
21
+
20
22
type Scanner struct {
21
23
client * http.Client
22
24
detectors.DefaultMultiPartCredentialProvider
@@ -28,7 +30,7 @@ var _ detectors.Detector = (*Scanner)(nil)
28
30
var (
29
31
defaultClient = common .SaneHttpClient ()
30
32
urlPat = regexp .MustCompile (`https://([a-z0-9][a-z0-9-]{0,48}[a-z0-9])\.management\.azure-api\.net` ) // https://azure.github.io/PSRule.Rules.Azure/en/rules/Azure.APIM.Name/
31
- keyPat = regexp .MustCompile (detectors .PrefixRegex ([]string {"azure" }) + `\b([a-zA-Z0-9+\/-]{86,88}\b={0,2})` ) // Base64-encoded primary key
33
+ primaryKeyPat = regexp .MustCompile (detectors .PrefixRegex ([]string {"azure" }) + `\b([a-zA-Z0-9+\/-]{86,88}\b={0,2})` ) // Base64-encoded primary key
32
34
)
33
35
34
36
// Keywords are used for efficiently pre-filtering chunks.
@@ -41,30 +43,21 @@ func (s Scanner) Keywords() []string {
41
43
func (s Scanner ) FromData (ctx context.Context , verify bool , data []byte ) (results []detectors.Result , err error ) {
42
44
dataStr := string (data )
43
45
44
- urlMatches := urlPat .FindAllStringSubmatch (dataStr , - 1 )
45
- keyMatches := keyPat .FindAllStringSubmatch (dataStr , - 1 )
46
-
47
46
urlMatchesUnique := make (map [string ]string )
48
- for _ , urlMatch := range urlMatches {
49
- urlMatchesUnique [urlMatch [0 ]] = urlMatch [1 ]
47
+ for _ , urlMatch := range urlPat . FindAllStringSubmatch ( dataStr , - 1 ) {
48
+ urlMatchesUnique [urlMatch [0 ]] = urlMatch [1 ] // urlMatch[0] is the full url, urlMatch[1] is the service name
50
49
}
51
- keyMatchesUnique := make (map [string ]struct {})
52
- for _ , keyMatch := range keyMatches {
53
- keyMatchesUnique [ keyMatch [1 ]] = struct {}{}
50
+ primaryKeyMatchesUnique := make (map [string ]struct {})
51
+ for _ , keyMatch := range primaryKeyPat . FindAllStringSubmatch ( dataStr , - 1 ) {
52
+ primaryKeyMatchesUnique [ strings . TrimSpace ( keyMatch [1 ]) ] = struct {}{}
54
53
}
55
54
56
55
for baseUrl , serviceName := range urlMatchesUnique {
57
- for key , _ := range keyMatchesUnique {
58
- resMatch := strings .TrimSpace (key )
59
- url := fmt .Sprintf (
60
- "%s/subscriptions/default/resourceGroups/default/providers/Microsoft.ApiManagement/service/%s/apis?api-version=2024-05-01" ,
61
- baseUrl , serviceName ,
62
- )
56
+ for primaryKey := range primaryKeyMatchesUnique {
63
57
s1 := detectors.Result {
64
58
DetectorType : detectorspb .DetectorType_AzureDirectManagementKey ,
65
59
Raw : []byte (baseUrl ),
66
- RawV2 : []byte (baseUrl + resMatch ),
67
- Redacted : baseUrl ,
60
+ RawV2 : []byte (baseUrl + primaryKey ),
68
61
}
69
62
70
63
if verify {
@@ -73,9 +66,9 @@ func (s Scanner) FromData(ctx context.Context, verify bool, data []byte) (result
73
66
client = defaultClient
74
67
}
75
68
76
- isVerified , verificationErr := s .verifyMatch (ctx , client , url , resMatch )
69
+ isVerified , verificationErr := s .verifyMatch (ctx , client , baseUrl , serviceName , primaryKey )
77
70
s1 .Verified = isVerified
78
- s1 .SetVerificationError (verificationErr , resMatch )
71
+ s1 .SetVerificationError (verificationErr , primaryKey )
79
72
}
80
73
81
74
results = append (results , s1 )
@@ -88,19 +81,19 @@ func (s Scanner) FromData(ctx context.Context, verify bool, data []byte) (result
88
81
return results , nil
89
82
}
90
83
91
- func (s Scanner ) IsFalsePositive (_ detectors.Result ) (bool , string ) {
92
- return false , ""
93
- }
94
-
95
84
func (s Scanner ) Type () detectorspb.DetectorType {
96
85
return detectorspb .DetectorType_AzureDirectManagementKey
97
86
}
98
87
99
88
func (s Scanner ) Description () string {
100
- return "The Azure Management API is a RESTful interface for managing Azure resources programmatically through Azure Resource Manager (ARM), supporting automation with tools like Azure CLI and PowerShell. An Azure Management Direct Access API Key enables secure, non-interactive authentication, allowing direct access to manage resources via Azure Active Directory (AAD) ."
89
+ return "Azure API Management provides a direct management REST API for performing operations on selected entities, such as users, groups, products, and subscriptions ."
101
90
}
102
91
103
- func (s Scanner ) verifyMatch (ctx context.Context , client * http.Client , url , key string ) (bool , error ) {
92
+ func (s Scanner ) verifyMatch (ctx context.Context , client * http.Client , baseUrl , serviceName , key string ) (bool , error ) {
93
+ url := fmt .Sprintf (
94
+ "%s/subscriptions/default/resourceGroups/default/providers/Microsoft.ApiManagement/service/%s/apis?api-version=2024-05-01" ,
95
+ baseUrl , serviceName ,
96
+ )
104
97
accessToken , err := generateAccessToken (key )
105
98
if err != nil {
106
99
return false , err
@@ -129,8 +122,8 @@ func (s Scanner) verifyMatch(ctx context.Context, client *http.Client, url, key
129
122
130
123
// https://learn.microsoft.com/en-us/rest/api/apimanagement/apimanagementrest/azure-api-management-rest-api-authentication
131
124
func generateAccessToken (key string ) (string , error ) {
132
- expiry := time .Now ().UTC ().Add (time .Minute ).Format (time . RFC3339Nano )
133
- expiry = expiry [: 27 ] + "Z" // 7 decimals precision for miliseconds
125
+ expiry := time .Now ().UTC ().Add (5 * time .Second ).Format (RFC3339WithoutMicroseconds ) // expires in 5 seconds
126
+ expiry = expiry + ".0000000Z" // 7 decimals microsecond's precision is must for access token
134
127
135
128
// Construct the string-to-sign
136
129
stringToSign := fmt .Sprintf ("integration\n %s" , expiry )
0 commit comments