Skip to content

Commit

Permalink
update protos
Browse files Browse the repository at this point in the history
  • Loading branch information
lc committed Nov 2, 2023
2 parents d509743 + ab89689 commit d92de1d
Show file tree
Hide file tree
Showing 34 changed files with 1,343 additions and 684 deletions.
2 changes: 1 addition & 1 deletion Dockerfile
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@ RUN --mount=type=cache,target=/go/pkg/mod \
GOOS=${TARGETOS} GOARCH=${TARGETARCH} go build -o trufflehog .

FROM alpine:3.18
RUN apk add --no-cache bash git openssh-client ca-certificates rpm2cpio \
RUN apk add --no-cache bash git openssh-client ca-certificates rpm2cpio binutils cpio \
&& rm -rf /var/cache/apk/* && update-ca-certificates
COPY --from=builder /build/trufflehog /usr/bin/trufflehog
COPY entrypoint.sh /etc/entrypoint.sh
Expand Down
2 changes: 1 addition & 1 deletion Dockerfile.goreleaser
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
FROM alpine:3.18

RUN apk add --no-cache bash git openssh-client ca-certificates rpm2cpio \
RUN apk add --no-cache bash git openssh-client ca-certificates rpm2cpio binutils cpio \
&& rm -rf /var/cache/apk/* && update-ca-certificates
WORKDIR /usr/bin/
COPY trufflehog .
Expand Down
2 changes: 1 addition & 1 deletion hack/snifftest/main.go
Original file line number Diff line number Diff line change
Expand Up @@ -204,7 +204,7 @@ func main() {
})

logger.Info("scanning repo", "repo", r)
err = s.ScanRepo(ctx, repo, path, git.NewScanOptions(), chunksChan)
err = s.ScanRepo(ctx, repo, path, git.NewScanOptions(), sources.ChanReporter{Ch: chunksChan})
if err != nil {
logFatal(err, "error scanning repo")
}
Expand Down
2 changes: 1 addition & 1 deletion pkg/cache/memory/memory.go
Original file line number Diff line number Diff line change
Expand Up @@ -94,7 +94,7 @@ func (c *Cache) Values() []string {
return res
}

// Contents returns all key-value pairs in the cache encodes as a string.
// Contents returns a comma-separated string containing all keys in the cache.
func (c *Cache) Contents() string {
items := c.c.Items()
res := make([]string, 0, len(items))
Expand Down
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
}
Original file line number Diff line number Diff line change
@@ -1,76 +1,80 @@
//go:build detectors
// +build detectors

package sentiment
package azurebatch

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

"github.com/kylelemons/godebug/pretty"
"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 TestSentiment_FromChunk(t *testing.T) {
func TestAzurebatch_FromChunk(t *testing.T) {
ctx, cancel := context.WithTimeout(context.Background(), time.Second*5)
defer cancel()
testSecrets, err := common.GetSecret(ctx, "trufflehog-testing", "detectors2")
testSecrets, err := common.GetSecret(ctx, "trufflehog-testing", "detectors5")
if err != nil {
t.Fatalf("could not get test secrets from GCP: %s", err)
}
token := testSecrets.MustGetField("SENTIMENT_TOKEN")
inactiveToken := testSecrets.MustGetField("SENTIMENT_TOKEN_INACTIVE")
key := testSecrets.MustGetField("SENTIMENT_KEY")
inactiveKey := testSecrets.MustGetField("SENTIMENT_KEY_INACTIVE")
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
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 sentiment key %s with sentiment user token %s within", key, token)),
data: []byte(fmt.Sprintf("You can find a azurebatch secret %s and %s within", url, secret)),
verify: true,
},
want: []detectors.Result{
{
DetectorType: detectorspb.DetectorType_Sentiment,
DetectorType: detectorspb.DetectorType_AzureBatch,
Verified: true,
},
},
wantErr: false,
wantErr: false,
wantVerificationErr: false,
},
{
name: "found, unverified",
s: Scanner{},
args: args{
ctx: context.Background(),
data: []byte(fmt.Sprintf("You can find a sentiment key %s with sentiment user token %s within but not valid", inactiveKey, inactiveToken)), // the secret would satisfy the regex but not pass validation
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_Sentiment,
DetectorType: detectorspb.DetectorType_AzureBatch,
Verified: false,
},
},
wantErr: false,
wantErr: false,
wantVerificationErr: false,
},
{
name: "not found",
Expand All @@ -80,26 +84,30 @@ func TestSentiment_FromChunk(t *testing.T) {
data: []byte("You cannot find the secret within"),
verify: true,
},
want: nil,
wantErr: false,
want: nil,
wantErr: false,
wantVerificationErr: false,
},
}
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
s := Scanner{}
got, err := s.FromData(tt.args.ctx, tt.args.verify, tt.args.data)
got, err := tt.s.FromData(tt.args.ctx, tt.args.verify, tt.args.data)
if (err != nil) != tt.wantErr {
t.Errorf("Sentiment.FromData() error = %v, wantErr %v", err, 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])
}
got[i].Raw = nil
if (got[i].VerificationError != nil) != tt.wantVerificationErr {
t.Fatalf("wantVerificationError = %v, verification error = %v", tt.wantVerificationErr, got[i].VerificationError)
}
}
if diff := pretty.Compare(got, tt.want); diff != "" {
t.Errorf("Sentiment.FromData() %s diff: (-got +want)\n%s", tt.name, diff)
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)
}
})
}
Expand Down
101 changes: 101 additions & 0 deletions pkg/detectors/azurecontainerregistry/azurecontainerregistry.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,101 @@
package azurecontainerregistry

import (
"context"
"encoding/base64"
"fmt"
"net/http"
"regexp"

"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.
url = regexp.MustCompile(`([a-zA-Z0-9-]{1,100})\.azurecr\.io`)
password = regexp.MustCompile(`\b[A-Za-z0-9+/=]{52}\b`)
)

// 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{".azurecr.io"}
}

// FromData will find and optionally verify Azurecontainerregistry 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 := url.FindAllStringSubmatch(dataStr, -1)
passwordMatches := password.FindAllStringSubmatch(dataStr, -1)

for _, urlMatch := range urlMatches {
for _, passwordMatch := range passwordMatches {

endpoint := urlMatch[0]
username := urlMatch[1]
password := passwordMatch[0]

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

if verify {
client := s.client
if client == nil {
client = defaultClient
}

auth := base64.StdEncoding.EncodeToString([]byte(fmt.Sprintf("%s:%s", username, password)))
url := fmt.Sprintf("https://%s/v2/", endpoint)
req, err := http.NewRequest("GET", url, nil)
if err != nil {
continue
}

req.Header.Set("Authorization", fmt.Sprintf("Basic %s", auth))
res, err := client.Do(req)
if err == nil {
defer res.Body.Close()
if res.StatusCode >= 200 && res.StatusCode < 300 {
s1.Verified = true
} else if res.StatusCode == 401 {
// The secret is determinately not verified (nothing to do)
} else {
s1.VerificationError = fmt.Errorf("unexpected HTTP response status %d", res.StatusCode)
}
} else {
s1.VerificationError = err
}
}

if !s1.Verified && detectors.IsKnownFalsePositive(password, detectors.DefaultFalsePositives, true) {
continue
}

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

return results, nil
}

func (s Scanner) Type() detectorspb.DetectorType {
return detectorspb.DetectorType_AzureContainerRegistry
}
Loading

0 comments on commit d92de1d

Please sign in to comment.