Skip to content

Commit

Permalink
feat(detectors): create azure_entra base package (#2985)
Browse files Browse the repository at this point in the history
feat(azure): finish pending items for review
  • Loading branch information
rgmz authored Nov 20, 2024
1 parent c9780de commit 4e243a0
Show file tree
Hide file tree
Showing 17 changed files with 1,290 additions and 406 deletions.
10 changes: 0 additions & 10 deletions go.mod
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,6 @@ replace github.com/STARRY-S/zip => github.com/STARRY-S/zip v0.1.0
require (
cloud.google.com/go/secretmanager v1.14.2
cloud.google.com/go/storage v1.47.0
github.com/Azure/go-autorest/autorest/azure/auth v0.5.13
github.com/AzureAD/microsoft-authentication-library-for-go v1.3.1
github.com/BobuSumisu/aho-corasick v1.0.3
github.com/TheZeroSlave/zapsentry v1.23.0
Expand Down Expand Up @@ -136,13 +135,6 @@ require (
github.com/99designs/go-keychain v0.0.0-20191008050251-8e49817e8af4 // indirect
github.com/99designs/keyring v1.2.2 // indirect
github.com/Azure/go-ansiterm v0.0.0-20210617225240-d185dfc1b5a1 // indirect
github.com/Azure/go-autorest v14.2.0+incompatible // indirect
github.com/Azure/go-autorest/autorest v0.11.28 // indirect
github.com/Azure/go-autorest/autorest/adal v0.9.22 // indirect
github.com/Azure/go-autorest/autorest/azure/cli v0.4.6 // indirect
github.com/Azure/go-autorest/autorest/date v0.3.0 // indirect
github.com/Azure/go-autorest/logger v0.2.1 // indirect
github.com/Azure/go-autorest/tracing v0.6.0 // indirect
github.com/Azure/go-ntlmssp v0.0.0-20221128193559-754e69321358 // indirect
github.com/DataDog/zstd v1.5.5 // indirect
github.com/GoogleCloudPlatform/opentelemetry-operations-go/detectors/gcp v1.24.1 // indirect
Expand Down Expand Up @@ -186,7 +178,6 @@ require (
github.com/cyphar/filepath-securejoin v0.2.4 // indirect
github.com/danieljoos/wincred v1.1.2 // indirect
github.com/davecgh/go-spew v1.1.1 // indirect
github.com/dimchansky/utfbom v1.1.1 // indirect
github.com/distribution/reference v0.6.0 // indirect
github.com/dlclark/regexp2 v1.4.0 // indirect
github.com/docker/cli v27.1.1+incompatible // indirect
Expand Down Expand Up @@ -221,7 +212,6 @@ require (
github.com/golang/groupcache v0.0.0-20210331224755-41bb18bfe9da // indirect
github.com/golang/snappy v0.0.4 // indirect
github.com/google/flatbuffers v23.5.26+incompatible // indirect
github.com/google/go-github/v62 v62.0.0 // indirect
github.com/google/go-querystring v1.1.0 // indirect
github.com/google/pprof v0.0.0-20240227163752-401108e1b7e7 // indirect
github.com/google/s2a-go v0.1.8 // indirect
Expand Down
212 changes: 6 additions & 206 deletions go.sum

Large diffs are not rendered by default.

92 changes: 0 additions & 92 deletions pkg/detectors/azure/azure.go

This file was deleted.

90 changes: 0 additions & 90 deletions pkg/detectors/azure/azure_test.go

This file was deleted.

Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
package azurebatch
package azure_batch

import (
"context"
Expand Down
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
//go:build detectors
// +build detectors

package azurebatch
package azure_batch

import (
"context"
Expand Down
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
package azurebatch
package azure_batch

import (
"context"
Expand Down
127 changes: 127 additions & 0 deletions pkg/detectors/azure_entra/common.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,127 @@
package azure_entra

import (
"fmt"
"io"
"net/http"
"strings"

regexp "github.com/wasilibs/go-re2"
"golang.org/x/sync/singleflight"

"github.com/trufflesecurity/trufflehog/v3/pkg/cache/simple"
"github.com/trufflesecurity/trufflehog/v3/pkg/context"
"github.com/trufflesecurity/trufflehog/v3/pkg/detectors"
)

const uuidStr = `[a-f0-9]{8}-[a-f0-9]{4}-[a-f0-9]{4}-[a-f0-9]{4}-[a-f0-9]{12}`

var (
// Tenants can be identified with a UUID or an `*.onmicrosoft.com` domain.
//
// See:
// https://learn.microsoft.com/en-us/partner-center/account-settings/find-ids-and-domain-names#find-the-microsoft-azure-ad-tenant-id-and-primary-domain-name
// https://learn.microsoft.com/en-us/microsoft-365/admin/setup/domains-faq?view=o365-worldwide#why-do-i-have-an--onmicrosoft-com--domain
tenantIdPat = regexp.MustCompile(fmt.Sprintf(
//language=regexp
`(?i)(?:(?:login\.microsoftonline\.com/|(?:login|sts)\.windows\.net/|(?:t[ae]n[ae]nt(?:[ ._-]?id)?|\btid)(?:.|\s){0,60}?)(%s)|https?://(%s)|X-AnchorMailbox(?:.|\s){0,60}?@(%s))`,
uuidStr,
uuidStr,
uuidStr,
))
tenantOnMicrosoftPat = regexp.MustCompile(`([\w-]+\.onmicrosoft\.com)`)

clientIdPat = regexp.MustCompile(fmt.Sprintf(
`(?i)(?:(?:app(?:lication)?|client)(?:[ ._-]?id)?|username| -u)(?:.|\s){0,45}?(%s)`, uuidStr))
)

// FindTenantIdMatches returns a list of potential tenant IDs in the provided |data|.
func FindTenantIdMatches(data string) map[string]struct{} {
uniqueMatches := make(map[string]struct{})

for _, match := range tenantIdPat.FindAllStringSubmatch(data, -1) {
var m string
if match[1] != "" {
m = strings.ToLower(match[1])
} else if match[2] != "" {
m = strings.ToLower(match[2])
} else if match[3] != "" {
m = strings.ToLower(match[3])
}
if _, ok := detectors.UuidFalsePositives[detectors.FalsePositive(m)]; ok {
continue
}
uniqueMatches[m] = struct{}{}
}
for _, match := range tenantOnMicrosoftPat.FindAllStringSubmatch(data, -1) {
uniqueMatches[match[1]] = struct{}{}
}
return uniqueMatches
}

// FindClientIdMatches returns a list of potential client UUIDs in the provided |data|.
func FindClientIdMatches(data string) map[string]struct{} {
uniqueMatches := make(map[string]struct{})
for _, match := range clientIdPat.FindAllStringSubmatch(data, -1) {
m := strings.ToLower(match[1])
if _, ok := detectors.UuidFalsePositives[detectors.FalsePositive(m)]; ok {
continue
}
uniqueMatches[m] = struct{}{}
}
return uniqueMatches
}

var (
tenantCache = simple.NewCache[bool]()
tenantGroup singleflight.Group
)

// TenantExists returns whether the tenant exists according to Microsoft's well-known OpenID endpoint.
func TenantExists(ctx context.Context, client *http.Client, tenant string) bool {
// Use cached value where possible.
if tenantExists, isCached := tenantCache.Get(tenant); isCached {
return tenantExists
}

// https://www.codingexplorations.com/blog/understanding-singleflight-in-golang-a-solution-for-eliminating-redundant-work
tenantExists, _, _ := tenantGroup.Do(tenant, func() (interface{}, error) {
result := queryTenant(ctx, client, tenant)
tenantCache.Set(tenant, result)
return result, nil
})

return tenantExists.(bool)
}

func queryTenant(ctx context.Context, client *http.Client, tenant string) bool {
logger := ctx.Logger().WithName("azure").WithValues("tenant", tenant)

tenantUrl := fmt.Sprintf("https://login.microsoftonline.com/%s/.well-known/openid-configuration", tenant)
req, err := http.NewRequestWithContext(ctx, http.MethodGet, tenantUrl, nil)
if err != nil {
return false
}

res, err := client.Do(req)
if err != nil {
logger.Error(err, "Failed to check if tenant exists")
return false
}
defer func() {
_, _ = io.Copy(io.Discard, res.Body)
_ = res.Body.Close()
}()

switch res.StatusCode {
case http.StatusOK:
return true
case http.StatusBadRequest:
logger.V(4).Info("Tenant does not exist.")
return false
default:
bodyBytes, _ := io.ReadAll(res.Body)
logger.Error(nil, "WARNING: Unexpected response when checking if tenant exists", "status_code", res.StatusCode, "body", string(bodyBytes))
return false
}
}
Loading

0 comments on commit 4e243a0

Please sign in to comment.