Skip to content

Commit 2549f2e

Browse files
authored
added interseller detector (#504)
* added interseller detector * fix comment
1 parent 5f9c9f4 commit 2549f2e

2 files changed

Lines changed: 188 additions & 0 deletions

File tree

Lines changed: 72 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,72 @@
1+
package interseller
2+
3+
import (
4+
"context"
5+
"net/http"
6+
"regexp"
7+
"strings"
8+
"github.com/trufflesecurity/trufflehog/v3/pkg/common"
9+
"github.com/trufflesecurity/trufflehog/v3/pkg/detectors"
10+
"github.com/trufflesecurity/trufflehog/v3/pkg/pb/detectorspb"
11+
)
12+
13+
type Scanner struct{}
14+
15+
// Ensure the Scanner satisfies the interface at compile time
16+
var _ detectors.Detector = (*Scanner)(nil)
17+
18+
var (
19+
client = common.SaneHttpClient()
20+
21+
//Make sure that your group is surrounded in boundry characters such as below to reduce false positives
22+
keyPat = regexp.MustCompile(detectors.PrefixRegex([]string{"interseller"}) + `\b([0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12})\b`)
23+
)
24+
25+
// Keywords are used for efficiently pre-filtering chunks.
26+
// Use identifiers in the secret preferably, or the provider name.
27+
func (s Scanner) Keywords() []string {
28+
return []string{"interseller"}
29+
}
30+
31+
// FromData will find and optionally verify Interseller secrets in a given set of bytes.
32+
func (s Scanner) FromData(ctx context.Context, verify bool, data []byte) (results []detectors.Result, err error) {
33+
dataStr := string(data)
34+
35+
matches := keyPat.FindAllStringSubmatch(dataStr, -1)
36+
37+
for _, match := range matches {
38+
if len(match) != 2 {
39+
continue
40+
}
41+
resMatch := strings.TrimSpace(match[1])
42+
43+
s1 := detectors.Result{
44+
DetectorType: detectorspb.DetectorType_Interseller,
45+
Raw: []byte(resMatch),
46+
}
47+
if verify {
48+
req, err := http.NewRequestWithContext(ctx, "GET", "https://interseller.io/api/campaigns/list", nil)
49+
if err != nil {
50+
continue
51+
}
52+
req.Header.Add("Accept", "application/json")
53+
req.Header.Add("X-API-Key", resMatch)
54+
res, err := client.Do(req)
55+
if err == nil {
56+
defer res.Body.Close()
57+
if res.StatusCode >= 200 && res.StatusCode < 300 {
58+
s1.Verified = true
59+
} else {
60+
//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
61+
// if detectors.IsKnownFalsePositive(resMatch, detectors.DefaultFalsePositives, true) {
62+
// continue
63+
// }
64+
}
65+
}
66+
}
67+
68+
results = append(results, s1)
69+
}
70+
71+
return detectors.CleanResults(results), nil
72+
}
Lines changed: 116 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,116 @@
1+
package interseller
2+
3+
import (
4+
"context"
5+
"fmt"
6+
"testing"
7+
"time"
8+
9+
"github.com/kylelemons/godebug/pretty"
10+
"github.com/trufflesecurity/trufflehog/v3/pkg/detectors"
11+
12+
"github.com/trufflesecurity/trufflehog/v3/pkg/common"
13+
"github.com/trufflesecurity/trufflehog/v3/pkg/pb/detectorspb"
14+
)
15+
16+
func TestInterseller_FromChunk(t *testing.T) {
17+
ctx, cancel := context.WithTimeout(context.Background(), time.Second*5)
18+
defer cancel()
19+
testSecrets, err := common.GetSecret(ctx, "trufflehog-testing", "detectors4")
20+
if err != nil {
21+
t.Fatalf("could not get test secrets from GCP: %s", err)
22+
}
23+
secret := testSecrets.MustGetField("INTERSELLER")
24+
inactiveSecret := testSecrets.MustGetField("INTERSELLER_INACTIVE")
25+
26+
type args struct {
27+
ctx context.Context
28+
data []byte
29+
verify bool
30+
}
31+
tests := []struct {
32+
name string
33+
s Scanner
34+
args args
35+
want []detectors.Result
36+
wantErr bool
37+
}{
38+
{
39+
name: "found, verified",
40+
s: Scanner{},
41+
args: args{
42+
ctx: context.Background(),
43+
data: []byte(fmt.Sprintf("You can find a interseller secret %s within", secret)),
44+
verify: true,
45+
},
46+
want: []detectors.Result{
47+
{
48+
DetectorType: detectorspb.DetectorType_Interseller,
49+
Verified: true,
50+
},
51+
},
52+
wantErr: false,
53+
},
54+
{
55+
name: "found, unverified",
56+
s: Scanner{},
57+
args: args{
58+
ctx: context.Background(),
59+
data: []byte(fmt.Sprintf("You can find a interseller secret %s within but not valid", inactiveSecret)), // the secret would satisfy the regex but not pass validation
60+
verify: true,
61+
},
62+
want: []detectors.Result{
63+
{
64+
DetectorType: detectorspb.DetectorType_Interseller,
65+
Verified: false,
66+
},
67+
},
68+
wantErr: false,
69+
},
70+
{
71+
name: "not found",
72+
s: Scanner{},
73+
args: args{
74+
ctx: context.Background(),
75+
data: []byte("You cannot find the secret within"),
76+
verify: true,
77+
},
78+
want: nil,
79+
wantErr: false,
80+
},
81+
}
82+
for _, tt := range tests {
83+
t.Run(tt.name, func(t *testing.T) {
84+
s := Scanner{}
85+
got, err := s.FromData(tt.args.ctx, tt.args.verify, tt.args.data)
86+
if (err != nil) != tt.wantErr {
87+
t.Errorf("Interseller.FromData() error = %v, wantErr %v", err, tt.wantErr)
88+
return
89+
}
90+
for i := range got {
91+
if len(got[i].Raw) == 0 {
92+
t.Fatalf("no raw secret present: \n %+v", got[i])
93+
}
94+
got[i].Raw = nil
95+
}
96+
if diff := pretty.Compare(got, tt.want); diff != "" {
97+
t.Errorf("Interseller.FromData() %s diff: (-got +want)\n%s", tt.name, diff)
98+
}
99+
})
100+
}
101+
}
102+
103+
func BenchmarkFromData(benchmark *testing.B) {
104+
ctx := context.Background()
105+
s := Scanner{}
106+
for name, data := range detectors.MustGetBenchmarkData() {
107+
benchmark.Run(name, func(b *testing.B) {
108+
for n := 0; n < b.N; n++ {
109+
_, err := s.FromData(ctx, false, data)
110+
if err != nil {
111+
b.Fatal(err)
112+
}
113+
}
114+
})
115+
}
116+
}

0 commit comments

Comments
 (0)