Skip to content

Commit 0f9db66

Browse files
committed
fix(detectors): report Todoist verifier errors
1 parent 26eae1f commit 0f9db66

2 files changed

Lines changed: 116 additions & 12 deletions

File tree

pkg/detectors/todoist/todoist.go

Lines changed: 33 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,7 @@ package todoist
33
import (
44
"context"
55
"fmt"
6+
"io"
67
"net/http"
78
"strings"
89

@@ -47,18 +48,9 @@ func (s Scanner) FromData(ctx context.Context, verify bool, data []byte) (result
4748
}
4849

4950
if verify {
50-
req, err := http.NewRequestWithContext(ctx, "GET", "https://api.todoist.com/api/v1/projects", nil)
51-
if err != nil {
52-
continue
53-
}
54-
req.Header.Add("Authorization", fmt.Sprintf("Bearer %s", resMatch))
55-
res, err := client.Do(req)
56-
if err == nil {
57-
defer func() { _ = res.Body.Close() }()
58-
if res.StatusCode >= 200 && res.StatusCode < 300 {
59-
s1.Verified = true
60-
}
61-
}
51+
isVerified, verificationErr := verifyMatch(ctx, resMatch)
52+
s1.Verified = isVerified
53+
s1.SetVerificationError(verificationErr, resMatch)
6254
}
6355

6456
results = append(results, s1)
@@ -67,6 +59,35 @@ func (s Scanner) FromData(ctx context.Context, verify bool, data []byte) (result
6759
return results, nil
6860
}
6961

62+
func verifyMatch(ctx context.Context, token string) (bool, error) {
63+
req, err := http.NewRequestWithContext(ctx, http.MethodGet, "https://api.todoist.com/api/v1/projects", nil)
64+
if err != nil {
65+
return false, err
66+
}
67+
req.Header.Add("Authorization", fmt.Sprintf("Bearer %s", token))
68+
69+
res, err := client.Do(req)
70+
if err != nil {
71+
return false, err
72+
}
73+
defer func() {
74+
_, _ = io.Copy(io.Discard, res.Body)
75+
_ = res.Body.Close()
76+
}()
77+
78+
switch res.StatusCode {
79+
case http.StatusOK:
80+
return true, nil
81+
case http.StatusUnauthorized, http.StatusForbidden:
82+
return false, nil
83+
default:
84+
if res.StatusCode >= 200 && res.StatusCode < 300 {
85+
return true, nil
86+
}
87+
return false, fmt.Errorf("unexpected HTTP response status %d", res.StatusCode)
88+
}
89+
}
90+
7091
func (s Scanner) Type() detector_typepb.DetectorType {
7192
return detector_typepb.DetectorType_Todoist
7293
}

pkg/detectors/todoist/todoist_test.go

Lines changed: 83 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,7 @@ package todoist
22

33
import (
44
"context"
5+
"errors"
56
"fmt"
67
"io"
78
"net/http"
@@ -141,3 +142,85 @@ func TestTodoist_VerificationEndpoint(t *testing.T) {
141142
t.Fatal("expected result to be verified")
142143
}
143144
}
145+
146+
func TestTodoist_VerificationResponses(t *testing.T) {
147+
d := Scanner{}
148+
input := fmt.Sprintf("%s token = '%s'", keyword, validPattern)
149+
150+
tests := []struct {
151+
name string
152+
response *http.Response
153+
responseErr error
154+
wantVerified bool
155+
wantVerificationErr bool
156+
}{
157+
{
158+
name: "valid token",
159+
response: &http.Response{
160+
StatusCode: http.StatusOK,
161+
Body: io.NopCloser(strings.NewReader("{}")),
162+
Header: make(http.Header),
163+
},
164+
wantVerified: true,
165+
},
166+
{
167+
name: "unauthorized token",
168+
response: &http.Response{
169+
StatusCode: http.StatusUnauthorized,
170+
Body: io.NopCloser(strings.NewReader("{}")),
171+
Header: make(http.Header),
172+
},
173+
},
174+
{
175+
name: "forbidden token",
176+
response: &http.Response{
177+
StatusCode: http.StatusForbidden,
178+
Body: io.NopCloser(strings.NewReader("{}")),
179+
Header: make(http.Header),
180+
},
181+
},
182+
{
183+
name: "client error",
184+
responseErr: errors.New("network failure"),
185+
wantVerificationErr: true,
186+
},
187+
{
188+
name: "unexpected status",
189+
response: &http.Response{
190+
StatusCode: http.StatusInternalServerError,
191+
Body: io.NopCloser(strings.NewReader("{}")),
192+
Header: make(http.Header),
193+
},
194+
wantVerificationErr: true,
195+
},
196+
}
197+
198+
for _, tt := range tests {
199+
t.Run(tt.name, func(t *testing.T) {
200+
prevClient := client
201+
t.Cleanup(func() {
202+
client = prevClient
203+
})
204+
205+
client = &http.Client{
206+
Transport: roundTripFunc(func(req *http.Request) (*http.Response, error) {
207+
return tt.response, tt.responseErr
208+
}),
209+
}
210+
211+
results, err := d.FromData(context.Background(), true, []byte(input))
212+
if err != nil {
213+
t.Fatalf("FromData returned error: %v", err)
214+
}
215+
if len(results) != 1 {
216+
t.Fatalf("expected 1 result, got %d", len(results))
217+
}
218+
if results[0].Verified != tt.wantVerified {
219+
t.Fatalf("Verified = %v, want %v", results[0].Verified, tt.wantVerified)
220+
}
221+
if (results[0].VerificationError() != nil) != tt.wantVerificationErr {
222+
t.Fatalf("wantVerificationError = %v, verification error = %v", tt.wantVerificationErr, results[0].VerificationError())
223+
}
224+
})
225+
}
226+
}

0 commit comments

Comments
 (0)