Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

modified Alibaba detector to use standard library #568

Merged
merged 4 commits into from
May 24, 2022
Merged
Show file tree
Hide file tree
Changes from 1 commit
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
120 changes: 84 additions & 36 deletions pkg/detectors/alibaba/alibaba.go
Original file line number Diff line number Diff line change
Expand Up @@ -2,11 +2,18 @@ package alibaba

import (
"context"
"crypto/hmac"
"crypto/sha1"
"encoding/base64"
"math/rand"
"net/http"
"net/url"
"regexp"
"strconv"
"strings"
"time"

"github.com/aliyun/alibaba-cloud-sdk-go/services/ecs"
log "github.com/sirupsen/logrus"
"github.com/trufflesecurity/trufflehog/v3/pkg/common"
"github.com/trufflesecurity/trufflehog/v3/pkg/detectors"
"github.com/trufflesecurity/trufflehog/v3/pkg/pb/detectorspb"
)
Expand All @@ -17,68 +24,109 @@ type Scanner struct{}
var _ detectors.Detector = (*Scanner)(nil)

var (
keyPat = regexp.MustCompile(`\b(LTAI[a-zA-Z0-9]{17,21})[\"' ;\s]*`)
secretPat = regexp.MustCompile(`\b([a-zA-Z0-9]{30})\b`)
client = common.SaneHttpClient()

// Make sure that your group is surrounded in boundary characters such as below to reduce false positives.
keyPat = regexp.MustCompile(`\b([a-zA-Z0-9]{30})\b`)
idPat = regexp.MustCompile(`\b(LTAI[a-zA-Z0-9]{17,21})[\"';\s]*`)
)

// 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{"LTAI"}
return []string{"alibaba"}
roxanne-tampus marked this conversation as resolved.
Show resolved Hide resolved
}

func randString(n int) string {
rand.Seed(time.Now().UnixNano())
const alphanum = "0123456789abcdefghijklmnopqrstuvwxyz"
var bytes = make([]byte, n)
rand.Read(bytes)
for i, b := range bytes {
bytes[i] = alphanum[b%byte(len(alphanum))]
}
return string(bytes)
}

func GetSignature(input, key string) string {
key_for_sign := []byte(key)
h := hmac.New(sha1.New, key_for_sign)
h.Write([]byte(input))
return base64.StdEncoding.EncodeToString(h.Sum(nil))
}
func buildStringToSign(input string) string {
filter := strings.Replace(input, "+", "%20", -1)
filter = strings.Replace(filter, "*", "%2A", -1)
filter = strings.Replace(filter, "%7E", "~", -1)
roxanne-tampus marked this conversation as resolved.
Show resolved Hide resolved
method := "GET"
filter = method + "&%2F&" + url.QueryEscape(filter)
return filter
}

// FromData will find and optionally verify Alibaba 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)
matches := keyPat.FindAllStringSubmatch(dataStr, -1)
for _, match := range matches {
//Plausible key pat found, look for secrets match
secMatches := secretPat.FindAllStringSubmatch(dataStr, -1)
idMatches := idPat.FindAllStringSubmatch(dataStr, -1)

for _, secMatch := range secMatches {
for _, match := range matches {
if len(match) != 2 {
continue
}
resMatch := strings.TrimSpace(match[1])

if len(match) != 2 {
for _, idMatch := range idMatches {
if len(idMatch) != 2 {
continue
}

s := detectors.Result{
resIdMatch := strings.TrimSpace(idMatch[1])

s1 := detectors.Result{
DetectorType: detectorspb.DetectorType_Alibaba,
Raw: []byte(match[1]),
Redacted: match[1],
Raw: []byte(resMatch),
}

if verify {
ecsClient, err := ecs.NewClientWithAccessKey(
"us-east-1", // your region ID
match[1], // your AccessKey ID
secMatch[1]) // your AccessKey Secret
req, err := http.NewRequestWithContext(ctx, "GET", "http://ecs.aliyuncs.com/?", nil)
if err != nil {
log.WithError(err).Debug("error creating alibaba client, skipping")
continue
}
// Create an API request and set parameters
request := ecs.CreateDescribeInstancesRequest()
request.ConnectTimeout = time.Duration(5) * time.Second
request.Scheme = "https"
request.Domain = "ecs.aliyuncs.com"
// Initiate the request and handle exceptions
_, err = ecsClient.DescribeInstances(request)
if err != nil {
s.Verified = false
dateISO := time.Now().UTC().Format("2006-01-02T15:04:05Z07:00")
params := req.URL.Query()
params.Add("AccessKeyId", resIdMatch)
params.Add("Action", "DescribeRegions")
params.Add("Format", "JSON")
params.Add("SignatureMethod", "HMAC-SHA1")
params.Add("SignatureNonce", randString(16))
params.Add("SignatureVersion", "1.0")
params.Add("Timestamp", dateISO)
params.Add("Version", "2014-05-26")

} else {
s.Verified = true
}
}
stringToSign := buildStringToSign(params.Encode())
signature := GetSignature(stringToSign, resMatch+"&") //Get Signature HMAC SHA1
params.Add("Signature", signature)
req.URL.RawQuery = params.Encode()

if !s.Verified {
if detectors.IsKnownFalsePositive(string(s.Raw), detectors.DefaultFalsePositives, true) {
continue
req.Header.Add("Content-Type", "text/xml;charset=utf-8")
req.Header.Add("Content-Length", strconv.Itoa(len(params.Encode())))
res, err := client.Do(req)
if err == nil {
defer res.Body.Close()
if res.StatusCode >= 200 && res.StatusCode < 300 {
s1.Verified = true
} else {
// 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 detectors.IsKnownFalsePositive(resMatch, detectors.DefaultFalsePositives, true) {
continue
}
}
}
}

results = append(results, s)
results = append(results, s1)
}
}
return

return detectors.CleanResults(results), nil
}
30 changes: 22 additions & 8 deletions pkg/detectors/alibaba/alibaba_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -7,20 +7,21 @@ import (
"time"

"github.com/kylelemons/godebug/pretty"
"github.com/trufflesecurity/trufflehog/v3/pkg/common"
"github.com/trufflesecurity/trufflehog/v3/pkg/detectors"

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

func TestAlibaba_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", "detectors4")
if err != nil {
t.Fatalf("could not get test secrets from GCP: %s", err)
}
secret := testSecrets.MustGetField("ALIBABA_SECRET")
secretInactive := testSecrets.MustGetField("ALIBABA_SECRET_INACTIVE")
inactiveSecret := testSecrets.MustGetField("ALIBABA_SECRET_INACTIVE")
id := testSecrets.MustGetField("ALIBABA_ID")

type args struct {
Expand All @@ -40,14 +41,13 @@ func TestAlibaba_FromChunk(t *testing.T) {
s: Scanner{},
args: args{
ctx: context.Background(),
data: []byte(fmt.Sprintf("Alibaba keys are here: %s\n %s within", id, secret)),
data: []byte(fmt.Sprintf("You can find a alibaba secret %s within alibaba %s", secret, id)),
verify: true,
},
want: []detectors.Result{
{
DetectorType: detectorspb.DetectorType_Alibaba,
Verified: true,
Redacted: id,
},
},
wantErr: false,
Expand All @@ -57,14 +57,13 @@ func TestAlibaba_FromChunk(t *testing.T) {
s: Scanner{},
args: args{
ctx: context.Background(),
data: []byte(fmt.Sprintf("Alibaba keys are here: %s\n %s within", id, secretInactive)),
data: []byte(fmt.Sprintf("You can find a alibaba secret %s within alibaba %s but not valid", inactiveSecret, id)), // the secret would satisfy the regex but not pass validation
verify: true,
},
want: []detectors.Result{
{
DetectorType: detectorspb.DetectorType_Alibaba,
Verified: false,
Redacted: id,
},
},
wantErr: false,
Expand All @@ -91,7 +90,7 @@ func TestAlibaba_FromChunk(t *testing.T) {
}
for i := range got {
if len(got[i].Raw) == 0 {
t.Fatal("no raw secret present")
t.Fatalf("no raw secret present: \n %+v", got[i])
}
got[i].Raw = nil
}
Expand All @@ -101,3 +100,18 @@ func TestAlibaba_FromChunk(t *testing.T) {
})
}
}

func BenchmarkFromData(benchmark *testing.B) {
ctx := context.Background()
s := Scanner{}
for name, data := range detectors.MustGetBenchmarkData() {
benchmark.Run(name, func(b *testing.B) {
for n := 0; n < b.N; n++ {
_, err := s.FromData(ctx, false, data)
if err != nil {
b.Fatal(err)
}
}
})
}
}