Skip to content

Commit f9dd660

Browse files
authored
add support for account api token (#4495)
1 parent b140e2b commit f9dd660

File tree

2 files changed

+112
-18
lines changed

2 files changed

+112
-18
lines changed

pkg/detectors/postmark/postmark.go

Lines changed: 90 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -2,10 +2,14 @@ package postmark
22

33
import (
44
"context"
5-
regexp "github.com/wasilibs/go-re2"
5+
"errors"
6+
"fmt"
7+
"io"
68
"net/http"
79
"strings"
810

11+
regexp "github.com/wasilibs/go-re2"
12+
913
"github.com/trufflesecurity/trufflehog/v3/pkg/common"
1014
"github.com/trufflesecurity/trufflehog/v3/pkg/detectors"
1115
"github.com/trufflesecurity/trufflehog/v3/pkg/pb/detectorspb"
@@ -43,20 +47,10 @@ func (s Scanner) FromData(ctx context.Context, verify bool, data []byte) (result
4347
}
4448

4549
if verify {
46-
req, err := http.NewRequestWithContext(ctx, "GET", "https://api.postmarkapp.com/deliverystats", nil)
47-
if err != nil {
48-
continue
49-
}
50-
req.Header.Add("Accept", "application/json")
51-
req.Header.Add("Content-Type", "application/json")
52-
req.Header.Add("X-Postmark-Server-Token", 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-
}
50+
valid, extraData, err := verifyKey(ctx, client, resMatch)
51+
s1.Verified = valid
52+
s1.ExtraData = extraData
53+
s1.SetVerificationError(err)
6054
}
6155

6256
results = append(results, s1)
@@ -65,6 +59,87 @@ func (s Scanner) FromData(ctx context.Context, verify bool, data []byte) (result
6559
return results, nil
6660
}
6761

62+
// verifyKey verifies a Postmark key by making requests to the Postmark API.
63+
// It tries both server and account API key verification.
64+
func verifyKey(ctx context.Context, client *http.Client, key string) (bool, map[string]string, error) {
65+
errs := make([]error, 0, 2)
66+
67+
// Try verifying as server API key first
68+
valid, err := verifyServerAPIKey(ctx, client, key)
69+
if valid {
70+
// If valid as server API key, return immediately
71+
return true, map[string]string{"type": "server"}, nil
72+
}
73+
if err != nil {
74+
errs = append(errs, err)
75+
}
76+
77+
// Try verifying as account API key next
78+
valid, err = verifyAccountAPIKey(ctx, client, key)
79+
if valid {
80+
// If valid as account API key, return immediately
81+
return true, map[string]string{"type": "account"}, nil
82+
}
83+
if err != nil {
84+
errs = append(errs, err)
85+
}
86+
if len(errs) > 0 {
87+
// If there were errors during verification, return them
88+
return false, nil, errors.Join(errs...)
89+
}
90+
return false, nil, nil
91+
}
92+
93+
// verifyServerAPIKey verifies a Postmark server API key by making a request to the Postmark API.
94+
func verifyServerAPIKey(ctx context.Context, client *http.Client, key string) (bool, error) {
95+
return verifyKeyWithOptions(
96+
ctx,
97+
client,
98+
key,
99+
"/deliverystats",
100+
"X-Postmark-Server-Token",
101+
)
102+
}
103+
104+
// verifyAccountAPIKey verifies a Postmark account API key by making a request to the Postmark API.
105+
func verifyAccountAPIKey(ctx context.Context, client *http.Client, key string) (bool, error) {
106+
return verifyKeyWithOptions(
107+
ctx,
108+
client,
109+
key,
110+
"/domains?count=10&offset=0",
111+
"X-Postmark-Account-Token",
112+
)
113+
}
114+
115+
// verifyKeyWithOptions is a generic function to verify a Postmark key with given endpoint and header.
116+
func verifyKeyWithOptions(ctx context.Context, client *http.Client, key, endpoint, authHeader string) (bool, error) {
117+
req, err := http.NewRequestWithContext(ctx, http.MethodGet, "https://api.postmarkapp.com"+endpoint, nil)
118+
if err != nil {
119+
return false, err
120+
}
121+
req.Header.Add("Accept", "application/json")
122+
req.Header.Add("Content-Type", "application/json")
123+
req.Header.Add(authHeader, key)
124+
res, err := client.Do(req)
125+
if err != nil {
126+
return false, err
127+
}
128+
defer func() {
129+
_, _ = io.Copy(io.Discard, res.Body)
130+
_ = res.Body.Close()
131+
}()
132+
133+
switch res.StatusCode {
134+
case http.StatusOK:
135+
return true, nil
136+
case http.StatusUnauthorized:
137+
return false, nil
138+
default:
139+
return false, fmt.Errorf("unexpected status code: %d", res.StatusCode)
140+
}
141+
}
142+
68143
func (s Scanner) Type() detectorspb.DetectorType {
69144
return detectorspb.DetectorType_Postmark
70145
}

pkg/detectors/postmark/postmark_integration_test.go

Lines changed: 22 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -23,7 +23,8 @@ func TestPostmark_FromChunk(t *testing.T) {
2323
if err != nil {
2424
t.Fatalf("could not get test secrets from GCP: %s", err)
2525
}
26-
secret := testSecrets.MustGetField("POSTMARK_TOKEN")
26+
secretServerApi := testSecrets.MustGetField("POSTMARK_TOKEN")
27+
secretAccountApi := testSecrets.MustGetField("POSTMARK_ACCOUNT_API_TOKEN")
2728
inactiveSecret := testSecrets.MustGetField("POSTMARK_INACTIVE")
2829

2930
type args struct {
@@ -39,17 +40,35 @@ func TestPostmark_FromChunk(t *testing.T) {
3940
wantErr bool
4041
}{
4142
{
42-
name: "found, verified",
43+
name: "found server api key, verified",
4344
s: Scanner{},
4445
args: args{
4546
ctx: context.Background(),
46-
data: []byte(fmt.Sprintf("You can find a postmark secret %s within", secret)),
47+
data: []byte(fmt.Sprintf("You can find a postmark secret %s within", secretServerApi)),
4748
verify: true,
4849
},
4950
want: []detectors.Result{
5051
{
5152
DetectorType: detectorspb.DetectorType_Postmark,
5253
Verified: true,
54+
ExtraData: map[string]string{"type": "server"},
55+
},
56+
},
57+
wantErr: false,
58+
},
59+
{
60+
name: "found account api key, verified",
61+
s: Scanner{},
62+
args: args{
63+
ctx: context.Background(),
64+
data: []byte(fmt.Sprintf("You can find a postmark secret %s within", secretAccountApi)),
65+
verify: true,
66+
},
67+
want: []detectors.Result{
68+
{
69+
DetectorType: detectorspb.DetectorType_Postmark,
70+
Verified: true,
71+
ExtraData: map[string]string{"type": "account"},
5372
},
5473
},
5574
wantErr: false,

0 commit comments

Comments
 (0)