Skip to content

Commit 89f152f

Browse files
Added conversiontools scanner (#351)
* added new protos * added conversiontools scanner
1 parent 92c34ee commit 89f152f

File tree

2 files changed

+192
-0
lines changed

2 files changed

+192
-0
lines changed
Lines changed: 76 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,76 @@
1+
package conversiontools
2+
3+
import (
4+
"context"
5+
"fmt"
6+
"net/http"
7+
"regexp"
8+
"strings"
9+
10+
"github.com/trufflesecurity/trufflehog/v3/pkg/common"
11+
"github.com/trufflesecurity/trufflehog/v3/pkg/detectors"
12+
"github.com/trufflesecurity/trufflehog/v3/pkg/pb/detectorspb"
13+
)
14+
15+
type Scanner struct{}
16+
17+
// Ensure the Scanner satisfies the interface at compile time
18+
var _ detectors.Detector = (*Scanner)(nil)
19+
20+
var (
21+
client = common.SaneHttpClient()
22+
23+
//Make sure that your group is surrounded in boundry characters such as below to reduce false positives
24+
keyPat = regexp.MustCompile(detectors.PrefixRegex([]string{"conversiontools"}) + `\b(ey[a-zA-Z0-9_.]{157,165})\b`)
25+
)
26+
27+
// Keywords are used for efficiently pre-filtering chunks.
28+
// Use identifiers in the secret preferably, or the provider name.
29+
func (s Scanner) Keywords() []string {
30+
return []string{"conversiontools"}
31+
}
32+
33+
// FromData will find and optionally verify ConversionTools secrets in a given set of bytes.
34+
func (s Scanner) FromData(ctx context.Context, verify bool, data []byte) (results []detectors.Result, err error) {
35+
dataStr := string(data)
36+
37+
matches := keyPat.FindAllStringSubmatch(dataStr, -1)
38+
39+
for _, match := range matches {
40+
if len(match) != 2 {
41+
continue
42+
}
43+
resMatch := strings.TrimSpace(match[1])
44+
45+
s1 := detectors.Result{
46+
DetectorType: detectorspb.DetectorType_ConversionTools,
47+
Raw: []byte(resMatch),
48+
}
49+
50+
if verify {
51+
payload := strings.NewReader(`{ "type": "convert.website_to_jpg", "options": { "url": "http://google.com", "images": "yes" }}`)
52+
req, err := http.NewRequestWithContext(ctx, "POST", "https://api.conversiontools.io/v1/tasks", payload)
53+
if err != nil {
54+
continue
55+
}
56+
req.Header.Add("Content-Type", "application/json")
57+
req.Header.Add("Authorization", fmt.Sprintf("Bearer %s", resMatch))
58+
res, err := client.Do(req)
59+
if err == nil {
60+
defer res.Body.Close()
61+
if res.StatusCode >= 200 && res.StatusCode < 300 {
62+
s1.Verified = true
63+
} else {
64+
//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
65+
if detectors.IsKnownFalsePositive(resMatch, detectors.DefaultFalsePositives, true) {
66+
continue
67+
}
68+
}
69+
}
70+
}
71+
72+
results = append(results, s1)
73+
}
74+
75+
return detectors.CleanResults(results), nil
76+
}
Lines changed: 116 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,116 @@
1+
package conversiontools
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 TestConversionTools_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", "detectors3")
20+
if err != nil {
21+
t.Fatalf("could not get test secrets from GCP: %s", err)
22+
}
23+
secret := testSecrets.MustGetField("CONVERSIONTOOLS")
24+
inactiveSecret := testSecrets.MustGetField("CONVERSIONTOOLS_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 conversiontools secret %s within", secret)),
44+
verify: true,
45+
},
46+
want: []detectors.Result{
47+
{
48+
DetectorType: detectorspb.DetectorType_ConversionTools,
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 conversiontools 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_ConversionTools,
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("ConversionTools.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("ConversionTools.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)