@@ -2,6 +2,9 @@ package copper
22
33import (
44 "context"
5+ "encoding/json"
6+ "fmt"
7+ "io"
58 "net/http"
69 "strings"
710
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.
3240func (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+
86125func (s Scanner ) Type () detectorspb.DetectorType {
87126 return detectorspb .DetectorType_Copper
88127}
0 commit comments