Skip to content

Commit 7583da5

Browse files
Improve Copper Detector verification with stricter status code and email matching (#4594)
* feat(detector/copper): improve verification with stricter email matching * revert tags in int test --------- Co-authored-by: Kashif Khan <[email protected]>
1 parent 1d87fba commit 7583da5

File tree

2 files changed

+65
-24
lines changed

2 files changed

+65
-24
lines changed

pkg/detectors/copper/copper.go

Lines changed: 59 additions & 20 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,9 @@ package copper
22

33
import (
44
"context"
5+
"encoding/json"
6+
"fmt"
7+
"io"
58
"net/http"
69
"strings"
710

@@ -27,6 +30,11 @@ var (
2730
idPat = regexp.MustCompile(`\b([a-z0-9]{4,25}@[a-zA-Z0-9]{2,12}.[a-zA-Z0-9]{2,6})\b`)
2831
)
2932

33+
type UserApiResponse struct {
34+
Id int `json:"id"`
35+
Email string `json:"email"`
36+
}
37+
3038
// Keywords are used for efficiently pre-filtering chunks.
3139
// Use identifiers in the secret preferably, or the provider name.
3240
func (s Scanner) Keywords() []string {
@@ -52,26 +60,9 @@ func (s Scanner) FromData(ctx context.Context, verify bool, data []byte) (result
5260
}
5361

5462
if verify {
55-
56-
payload := strings.NewReader(`{
57-
"page_size": 25,
58-
"sort_by": "name"
59-
}`)
60-
req, err := http.NewRequestWithContext(ctx, "POST", "https://api.copper.com/developer_api/v1/tasks/search", payload)
61-
if err != nil {
62-
continue
63-
}
64-
req.Header.Add("X-PW-AccessToken", resMatch)
65-
req.Header.Add("X-PW-Application", "developer_api")
66-
req.Header.Add("X-PW-UserEmail", resIdMatch)
67-
req.Header.Add("Content-Type", "application/json")
68-
res, err := client.Do(req)
69-
if err == nil {
70-
defer res.Body.Close()
71-
if res.StatusCode >= 200 && res.StatusCode < 300 {
72-
s1.Verified = true
73-
}
74-
}
63+
isVerified, verificationErr := verifyCopper(ctx, client, resIdMatch, resMatch)
64+
s1.Verified = isVerified
65+
s1.SetVerificationError(verificationErr, resMatch)
7566
}
7667

7768
results = append(results, s1)
@@ -83,6 +74,54 @@ func (s Scanner) FromData(ctx context.Context, verify bool, data []byte) (result
8374
return results, nil
8475
}
8576

77+
func verifyCopper(ctx context.Context, client *http.Client, email, apiKey string) (bool, error) {
78+
req, err := http.NewRequestWithContext(
79+
ctx,
80+
http.MethodGet,
81+
"https://api.copper.com/developer_api/v1/users/me",
82+
http.NoBody,
83+
)
84+
if err != nil {
85+
return false, err
86+
}
87+
req.Header.Add("X-PW-AccessToken", apiKey)
88+
req.Header.Add("X-PW-Application", "developer_api")
89+
req.Header.Add("X-PW-UserEmail", email)
90+
req.Header.Add("Content-Type", "application/json")
91+
res, err := client.Do(req)
92+
if err != nil {
93+
return false, err
94+
}
95+
defer func() {
96+
_, _ = io.Copy(io.Discard, res.Body)
97+
_ = res.Body.Close()
98+
}()
99+
100+
switch res.StatusCode {
101+
case http.StatusOK:
102+
respBytes, err := io.ReadAll(res.Body)
103+
if err != nil {
104+
return false, err
105+
}
106+
107+
var respBody UserApiResponse
108+
if err := json.Unmarshal(respBytes, &respBody); err != nil {
109+
return false, err
110+
}
111+
112+
// strict verification with email in credentials
113+
if respBody.Email == email {
114+
return true, nil
115+
}
116+
117+
return false, fmt.Errorf("email mismatch in verification response")
118+
case http.StatusUnauthorized:
119+
return false, nil
120+
default:
121+
return false, fmt.Errorf("unexpected status code :%d", res.StatusCode)
122+
}
123+
}
124+
86125
func (s Scanner) Type() detectorspb.DetectorType {
87126
return detectorspb.DetectorType_Copper
88127
}

pkg/detectors/copper/copper_integration_test.go

Lines changed: 6 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -9,8 +9,8 @@ import (
99
"testing"
1010
"time"
1111

12-
"github.com/kylelemons/godebug/pretty"
13-
12+
"github.com/google/go-cmp/cmp"
13+
"github.com/google/go-cmp/cmp/cmpopts"
1414
"github.com/trufflesecurity/trufflehog/v3/pkg/common"
1515
"github.com/trufflesecurity/trufflehog/v3/pkg/detectors"
1616
"github.com/trufflesecurity/trufflehog/v3/pkg/pb/detectorspb"
@@ -96,9 +96,11 @@ func TestCopper_FromChunk(t *testing.T) {
9696
t.Fatalf("no raw secret present: \n %+v", got[i])
9797
}
9898
got[i].Raw = nil
99+
got[i].RawV2 = nil
99100
}
100-
if diff := pretty.Compare(got, tt.want); diff != "" {
101-
t.Errorf("Copper.FromData() %s diff: (-got +want)\n%s", tt.name, diff)
101+
ignoreOpts := cmpopts.IgnoreFields(detectors.Result{}, "Raw", "RawV2", "verificationError", "primarySecret")
102+
if diff := cmp.Diff(got, tt.want, ignoreOpts); diff != "" {
103+
t.Errorf("Abstract.FromData() %s diff: (-got +want)\n%s", tt.name, diff)
102104
}
103105
})
104106
}

0 commit comments

Comments
 (0)