Skip to content

Commit

Permalink
Detector-Competition-Feat: Adding Azure Batch keys (#1956)
Browse files Browse the repository at this point in the history
* adding azure batch

* fmt

* fix lint

---------

Co-authored-by: Chair <[email protected]>
Co-authored-by: Zachary Rice <[email protected]>
  • Loading branch information
3 people authored Oct 31, 2023
1 parent 499cb64 commit 8bac2b1
Show file tree
Hide file tree
Showing 5 changed files with 258 additions and 6 deletions.
114 changes: 114 additions & 0 deletions pkg/detectors/azurebatch/azurebatch.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,114 @@
package azurebatch

import (
"context"
"crypto/hmac"
"crypto/sha256"
"encoding/base64"
"fmt"
"net/http"
"regexp"
"strings"
"time"

"github.com/trufflesecurity/trufflehog/v3/pkg/common"
"github.com/trufflesecurity/trufflehog/v3/pkg/detectors"
"github.com/trufflesecurity/trufflehog/v3/pkg/pb/detectorspb"
)

type Scanner struct {
client *http.Client
}

// Ensure the Scanner satisfies the interface at compile time.
var _ detectors.Detector = (*Scanner)(nil)

var (
defaultClient = common.SaneHttpClient()
// Make sure that your group is surrounded in boundary characters such as below to reduce false positives.
urlPat = regexp.MustCompile(`https://(.{1,50})\.(.{1,50})\.batch\.azure\.com`)
secretPat = regexp.MustCompile(`[A-Za-z0-9+/=]{88}`)
)

// Keywords are used for efficiently pre-filtering chunks.
// Use identifiers in the secret preferably, or the provider name.
func (s Scanner) Keywords() []string {
return []string{".batch.azure.com"}
}

// FromData will find and optionally verify Azurebatch secrets in a given set of bytes.
func (s Scanner) FromData(ctx context.Context, verify bool, data []byte) (results []detectors.Result, err error) {
dataStr := string(data)

urlMatches := urlPat.FindAllStringSubmatch(dataStr, -1)
secretMatches := secretPat.FindAllStringSubmatch(dataStr, -1)

for _, urlMatch := range urlMatches {

for _, secretMatch := range secretMatches {

endpoint := urlMatch[0]
accountName := urlMatch[1]
accountKey := secretMatch[0]

s1 := detectors.Result{
DetectorType: detectorspb.DetectorType_AzureBatch,
Raw: []byte(endpoint),
Redacted: endpoint,
RawV2: []byte(endpoint + accountKey),
}

if verify {
client := s.client
if client == nil {
client = defaultClient
}
url := fmt.Sprintf("%s/applications?api-version=2020-09-01.12.0", endpoint)
date := time.Now().UTC().Format(http.TimeFormat)
stringToSign := fmt.Sprintf(
"GET\n\n\n\n\napplication/json\n%s\n\n\n\n\n\n%s\napi-version:%s",
date,
strings.ToLower(fmt.Sprintf("/%s/applications", accountName)),
"2020-09-01.12.0",
)
key, _ := base64.StdEncoding.DecodeString(accountKey)
h := hmac.New(sha256.New, key)
h.Write([]byte(stringToSign))
signature := base64.StdEncoding.EncodeToString(h.Sum(nil))
req, err := http.NewRequest("GET", url, nil)
if err != nil {
continue
}
req.Header.Set("Content-Type", "application/json")
req.Header.Set("Authorization", fmt.Sprintf("SharedKey %s:%s", accountName, signature))
req.Header.Set("Date", date)
resp, err := client.Do(req)
if err != nil {
continue
}
defer resp.Body.Close()

if resp.StatusCode == http.StatusOK {
s1.Verified = true
}

}

// 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.
if !s1.Verified && detectors.IsKnownFalsePositive(accountKey, detectors.DefaultFalsePositives, true) {
continue
}

results = append(results, s1)
if s1.Verified {
break
}
}
}

return results, nil
}

func (s Scanner) Type() detectorspb.DetectorType {
return detectorspb.DetectorType_AzureBatch
}
130 changes: 130 additions & 0 deletions pkg/detectors/azurebatch/azurebatch_test.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,130 @@
//go:build detectors
// +build detectors

package azurebatch

import (
"context"
"fmt"
"testing"
"time"

"github.com/google/go-cmp/cmp"
"github.com/google/go-cmp/cmp/cmpopts"

"github.com/trufflesecurity/trufflehog/v3/pkg/detectors"

"github.com/trufflesecurity/trufflehog/v3/pkg/common"
"github.com/trufflesecurity/trufflehog/v3/pkg/pb/detectorspb"
)

func TestAzurebatch_FromChunk(t *testing.T) {
ctx, cancel := context.WithTimeout(context.Background(), time.Second*5)
defer cancel()
testSecrets, err := common.GetSecret(ctx, "trufflehog-testing", "detectors5")
if err != nil {
t.Fatalf("could not get test secrets from GCP: %s", err)
}
url := testSecrets.MustGetField("AZUREBATCH_URL")
secret := testSecrets.MustGetField("AZUREBATCH_KEY")
inactiveSecret := testSecrets.MustGetField("AZUREBATCH_KEY_INACTIVE")

type args struct {
ctx context.Context
data []byte
verify bool
}
tests := []struct {
name string
s Scanner
args args
want []detectors.Result
wantErr bool
wantVerificationErr bool
}{
{
name: "found, verified",
s: Scanner{},
args: args{
ctx: context.Background(),
data: []byte(fmt.Sprintf("You can find a azurebatch secret %s and %s within", url, secret)),
verify: true,
},
want: []detectors.Result{
{
DetectorType: detectorspb.DetectorType_AzureBatch,
Verified: true,
},
},
wantErr: false,
wantVerificationErr: false,
},
{
name: "found, unverified",
s: Scanner{},
args: args{
ctx: context.Background(),
data: []byte(fmt.Sprintf("You can find a azurebatch secret %s and %s within but not valid", url, inactiveSecret)), // the secret would satisfy the regex but not pass validation
verify: true,
},
want: []detectors.Result{
{
DetectorType: detectorspb.DetectorType_AzureBatch,
Verified: false,
},
},
wantErr: false,
wantVerificationErr: false,
},
{
name: "not found",
s: Scanner{},
args: args{
ctx: context.Background(),
data: []byte("You cannot find the secret within"),
verify: true,
},
want: nil,
wantErr: false,
wantVerificationErr: false,
},
}
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
got, err := tt.s.FromData(tt.args.ctx, tt.args.verify, tt.args.data)
if (err != nil) != tt.wantErr {
t.Errorf("AzureBatch.FromData() error = %v, wantErr %v", err, tt.wantErr)
return
}
for i := range got {
if len(got[i].Raw) == 0 {
t.Fatalf("no raw secret present: \n %+v", got[i])
}
if (got[i].VerificationError != nil) != tt.wantVerificationErr {
t.Fatalf("wantVerificationError = %v, verification error = %v", tt.wantVerificationErr, got[i].VerificationError)
}
}
ignoreOpts := cmpopts.IgnoreFields(detectors.Result{}, "RawV2", "Raw", "Redacted", "VerificationError")

if diff := cmp.Diff(got, tt.want, ignoreOpts); diff != "" {
t.Errorf("AzureBatch.FromData() %s diff: (-got +want)\n%s", tt.name, diff)
}
})
}
}

func BenchmarkFromData(benchmark *testing.B) {
ctx := context.Background()
s := Scanner{}
for name, data := range detectors.MustGetBenchmarkData() {
benchmark.Run(name, func(b *testing.B) {
b.ResetTimer()
for n := 0; n < b.N; n++ {
_, err := s.FromData(ctx, false, data)
if err != nil {
b.Fatal(err)
}
}
})
}
}
2 changes: 2 additions & 0 deletions pkg/engine/defaults.go
Original file line number Diff line number Diff line change
Expand Up @@ -62,6 +62,7 @@ import (
"github.com/trufflesecurity/trufflehog/v3/pkg/detectors/aylien"
"github.com/trufflesecurity/trufflehog/v3/pkg/detectors/ayrshare"
"github.com/trufflesecurity/trufflehog/v3/pkg/detectors/azure"
"github.com/trufflesecurity/trufflehog/v3/pkg/detectors/azurebatch"
"github.com/trufflesecurity/trufflehog/v3/pkg/detectors/bannerbear"
"github.com/trufflesecurity/trufflehog/v3/pkg/detectors/baremetrics"
"github.com/trufflesecurity/trufflehog/v3/pkg/detectors/beamer"
Expand Down Expand Up @@ -780,6 +781,7 @@ func DefaultDetectors() []detectors.Detector {
&alibaba.Scanner{},
aws.New(),
&azure.Scanner{},
&azurebatch.Scanner{},
&slack.Scanner{}, // has 4 secret types
&gitlab.Scanner{},
&gitlabv2.Scanner{},
Expand Down
17 changes: 11 additions & 6 deletions pkg/pb/detectorspb/detectors.pb.go

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

1 change: 1 addition & 0 deletions proto/detectors.proto
Original file line number Diff line number Diff line change
Expand Up @@ -965,6 +965,7 @@ enum DetectorType {
DenoDeploy = 954;
Stripo = 955;
ReplyIO = 956;
AzureBatch = 957;
}

message Result {
Expand Down

0 comments on commit 8bac2b1

Please sign in to comment.