Skip to content

Commit 21126db

Browse files
Enhanced smartsheets detector (#4168)
* enhanced smartsheets detector * removed sheet as keyword * only keep sheet as prefix
1 parent 3bba277 commit 21126db

File tree

2 files changed

+79
-39
lines changed

2 files changed

+79
-39
lines changed

pkg/detectors/smartsheets/smartsheets.go

Lines changed: 42 additions & 20 deletions
Original file line numberDiff line numberDiff line change
@@ -3,9 +3,10 @@ package smartsheets
33
import (
44
"context"
55
"fmt"
6-
regexp "github.com/wasilibs/go-re2"
6+
"io"
77
"net/http"
8-
"strings"
8+
9+
regexp "github.com/wasilibs/go-re2"
910

1011
"github.com/trufflesecurity/trufflehog/v3/pkg/common"
1112
"github.com/trufflesecurity/trufflehog/v3/pkg/detectors"
@@ -21,42 +22,35 @@ var (
2122
client = common.SaneHttpClient()
2223

2324
// Make sure that your group is surrounded in boundary characters such as below to reduce false positives.
24-
keyPat = regexp.MustCompile(detectors.PrefixRegex([]string{"smartsheets"}) + `\b([a-zA-Z0-9]{37})\b`)
25+
keyPat = regexp.MustCompile(detectors.PrefixRegex([]string{"sheet"}) + `\b([a-zA-Z0-9]{37})\b`)
2526
)
2627

2728
// Keywords are used for efficiently pre-filtering chunks.
2829
// Use identifiers in the secret preferably, or the provider name.
2930
func (s Scanner) Keywords() []string {
30-
return []string{"smartsheets"}
31+
return []string{"smartsheet"}
3132
}
3233

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

37-
matches := keyPat.FindAllStringSubmatch(dataStr, -1)
38+
var uniqueKeys = make(map[string]struct{})
3839

39-
for _, match := range matches {
40-
resMatch := strings.TrimSpace(match[1])
40+
for _, matche := range keyPat.FindAllStringSubmatch(dataStr, -1) {
41+
uniqueKeys[matche[1]] = struct{}{}
42+
}
4143

44+
for key := range uniqueKeys {
4245
s1 := detectors.Result{
4346
DetectorType: detectorspb.DetectorType_Smartsheets,
44-
Raw: []byte(resMatch),
47+
Raw: []byte(key),
4548
}
4649

4750
if verify {
48-
req, err := http.NewRequestWithContext(ctx, "GET", "https://api.smartsheet.com/2.0/sheets", nil)
49-
if err != nil {
50-
continue
51-
}
52-
req.Header.Add("Authorization", fmt.Sprintf("Bearer %s", resMatch))
53-
res, err := client.Do(req)
54-
if err == nil {
55-
defer res.Body.Close()
56-
if res.StatusCode >= 200 && res.StatusCode < 300 {
57-
s1.Verified = true
58-
}
59-
}
51+
isVerified, verificationErr := verifySmartSheetsToken(ctx, client, key)
52+
s1.Verified = isVerified
53+
s1.SetVerificationError(verificationErr)
6054
}
6155

6256
results = append(results, s1)
@@ -72,3 +66,31 @@ func (s Scanner) Type() detectorspb.DetectorType {
7266
func (s Scanner) Description() string {
7367
return "Smartsheets is a platform for work management and automation. Smartsheets API keys can be used to access and modify data and automate workflows within the platform."
7468
}
69+
70+
func verifySmartSheetsToken(ctx context.Context, client *http.Client, token string) (bool, error) {
71+
req, err := http.NewRequestWithContext(ctx, http.MethodGet, "https://api.smartsheet.com/2.0/sheets", http.NoBody)
72+
if err != nil {
73+
return false, err
74+
}
75+
76+
req.Header.Add("Authorization", fmt.Sprintf("Bearer %s", token))
77+
78+
resp, err := client.Do(req)
79+
if err != nil {
80+
return false, err
81+
}
82+
83+
defer func() {
84+
_, _ = io.Copy(io.Discard, resp.Body)
85+
_ = resp.Body.Close()
86+
}()
87+
88+
switch resp.StatusCode {
89+
case http.StatusOK:
90+
return true, nil
91+
case http.StatusUnauthorized:
92+
return false, nil
93+
default:
94+
return false, fmt.Errorf("unexpected status code: %d", resp.StatusCode)
95+
}
96+
}

pkg/detectors/smartsheets/smartsheets_test.go

Lines changed: 37 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,6 @@ package smartsheets
22

33
import (
44
"context"
5-
"fmt"
65
"testing"
76

87
"github.com/google/go-cmp/cmp"
@@ -11,12 +10,6 @@ import (
1110
"github.com/trufflesecurity/trufflehog/v3/pkg/engine/ahocorasick"
1211
)
1312

14-
var (
15-
validPattern = "MVE7zmdxouvunYkowLzaudyX7tvMpkqJ3q52C"
16-
invalidPattern = "MVE7?mdxouvunYkowLzaudyX7tvMpkqJ3q52C"
17-
keyword = "smartsheets"
18-
)
19-
2013
func TestSmartsheets_Pattern(t *testing.T) {
2114
d := Scanner{}
2215
ahoCorasickCore := ahocorasick.NewAhoCorasickCore([]detectors.Detector{d})
@@ -26,24 +19,49 @@ func TestSmartsheets_Pattern(t *testing.T) {
2619
want []string
2720
}{
2821
{
29-
name: "valid pattern - with keyword smartsheets",
30-
input: fmt.Sprintf("%s token = '%s'", keyword, validPattern),
31-
want: []string{validPattern},
22+
name: "valid pattern - with keyword smartsheet and sheet",
23+
input: `
24+
# do not share these secrets
25+
# list all sheets
26+
sheets := getsmartsheet("MVE7zmdxouvunYkowLzaudyX7tvMpkqJ3q52C")
27+
`,
28+
want: []string{"MVE7zmdxouvunYkowLzaudyX7tvMpkqJ3q52C"},
3229
},
3330
{
34-
name: "valid pattern - ignore duplicate",
35-
input: fmt.Sprintf("%s token = '%s' | '%s'", keyword, validPattern, validPattern),
36-
want: []string{validPattern},
31+
name: "valid pattern - with prefixRegex sheet",
32+
input: `
33+
# smartsheet credentials
34+
sheet_id := "MVE7zmdxouvunFAKELzaudyX7tvMpkqJ3q52d"
35+
`,
36+
want: []string{"MVE7zmdxouvunFAKELzaudyX7tvMpkqJ3q52d"},
3737
},
3838
{
39-
name: "valid pattern - key out of prefix range",
40-
input: fmt.Sprintf("%s keyword is not close to the real key in the data\n = '%s'", keyword, validPattern),
41-
want: []string{},
39+
name: "valid pattern - ignore duplicate",
40+
input: `
41+
# smartsheet duplicate credentials
42+
sheet_id1 := "MVE7zmdxouvunFAKELzaudyX7tvMpkqJ3q52d"
43+
sheet_id2 := "MVE7zmdxouvunFAKELzaudyX7tvMpkqJ3q52d"
44+
`,
45+
want: []string{"MVE7zmdxouvunFAKELzaudyX7tvMpkqJ3q52d"},
46+
},
47+
{
48+
name: "valid pattern - key out of prefix range",
49+
input: `
50+
# below is the smartsheet secret
51+
# use this secret to list sheets
52+
# do not share this
53+
54+
sslist := listAll("MVE7zmdxouvunFAKELzaudyX7tvMpkqJ3q52d")
55+
`,
56+
want: []string{},
4257
},
4358
{
44-
name: "invalid pattern",
45-
input: fmt.Sprintf("%s = '%s'", keyword, invalidPattern),
46-
want: []string{},
59+
name: "invalid pattern",
60+
input: `
61+
# smartsheet secret
62+
sheet_id = MVE7?mdxouvunYkowLzaudyX7tvMpkqJ3q52C
63+
`,
64+
want: []string{},
4765
},
4866
}
4967

0 commit comments

Comments
 (0)