Skip to content

Commit ce555b0

Browse files
authored
Okta Detector - Added Check for Indeterminate Verification Results (#4045)
* updated okta detector; add indeterminate verification error return * refactored, added adjustable client, updated integration test to include verification error test
1 parent a1243a4 commit ce555b0

File tree

2 files changed

+98
-52
lines changed

2 files changed

+98
-52
lines changed

pkg/detectors/okta/okta.go

Lines changed: 57 additions & 36 deletions
Original file line numberDiff line numberDiff line change
@@ -3,26 +3,26 @@ package okta
33
import (
44
"context"
55
"fmt"
6-
"io"
76
"net/http"
8-
"strings"
97

108
regexp "github.com/wasilibs/go-re2"
119

1210
"github.com/trufflesecurity/trufflehog/v3/pkg/detectors"
1311
"github.com/trufflesecurity/trufflehog/v3/pkg/pb/detectorspb"
1412
)
1513

16-
type Scanner struct{
14+
type Scanner struct {
15+
client *http.Client
1716
detectors.DefaultMultiPartCredentialProvider
1817
}
1918

2019
// Ensure the Scanner satisfies the interface at compile time.
2120
var _ detectors.Detector = (*Scanner)(nil)
2221

2322
var (
24-
domainPat = regexp.MustCompile(`\b[a-z0-9-]{1,40}\.okta(?:preview|-emea){0,1}\.com\b`)
25-
tokenPat = regexp.MustCompile(`\b00[a-zA-Z0-9_-]{40}\b`)
23+
defaultClient = detectors.DetectorHttpClientWithNoLocalAddresses
24+
domainPat = regexp.MustCompile(`\b[a-z0-9-]{1,40}\.okta(?:preview|-emea){0,1}\.com\b`)
25+
tokenPat = regexp.MustCompile(`\b00[a-zA-Z0-9_-]{40}\b`)
2626
// TODO: Oauth client secrets
2727
)
2828

@@ -34,55 +34,76 @@ func (s Scanner) Keywords() []string {
3434

3535
// FromData will find and optionally verify Okta secrets in a given set of bytes.
3636
func (s Scanner) FromData(ctx context.Context, verify bool, data []byte) (results []detectors.Result, err error) {
37-
for _, tokenMatch := range tokenPat.FindAll(data, -1) {
38-
token := string(tokenMatch)
37+
dataStr := string(data)
3938

40-
for _, domainMatch := range domainPat.FindAll(data, -1) {
41-
domain := string(domainMatch)
39+
var uniqueTokens, uniqueDomains = make(map[string]struct{}), make(map[string]struct{})
4240

43-
result := detectors.Result{
41+
for _, matches := range tokenPat.FindAllStringSubmatch(dataStr, -1) {
42+
uniqueTokens[matches[0]] = struct{}{}
43+
}
44+
45+
for _, matches := range domainPat.FindAllStringSubmatch(dataStr, -1) {
46+
uniqueDomains[matches[0]] = struct{}{}
47+
}
48+
49+
for token := range uniqueTokens {
50+
for domain := range uniqueDomains {
51+
s1 := detectors.Result{
4452
DetectorType: detectorspb.DetectorType_Okta,
4553
Raw: []byte(token),
4654
RawV2: []byte(fmt.Sprintf("%s:%s", domain, token)),
4755
}
4856

4957
if verify {
50-
// curl -v -X GET \
51-
// -H "Accept: application/json" \
52-
// -H "Content-Type: application/json" \
53-
// -H "Authorization: Bearer token" \
54-
// "https://subdomain.okta.com/api/v1/users/me"
55-
//
56-
57-
url := fmt.Sprintf("https://%s/api/v1/users/me", domain)
58-
req, err := http.NewRequestWithContext(ctx, "GET", url, nil)
59-
if err != nil {
60-
return results, err
58+
client := s.client
59+
if client == nil {
60+
client = defaultClient
6161
}
62-
req.Header.Set("Accept", "application/json")
63-
req.Header.Set("Content-Type", "application/json")
64-
req.Header.Set("Authorization", fmt.Sprintf("SSWS %s", token))
6562

66-
resp, err := detectors.DetectorHttpClientWithNoLocalAddresses.Do(req)
67-
if err != nil {
68-
continue
69-
}
70-
defer resp.Body.Close()
71-
if resp.StatusCode >= 200 && resp.StatusCode < 300 {
72-
body, _ := io.ReadAll(resp.Body)
73-
if strings.Contains(string(body), "activated") {
74-
result.Verified = true
75-
}
76-
}
63+
isVerified, verificationErr := verifyOktaToken(ctx, client, domain, token)
64+
s1.Verified = isVerified
65+
s1.SetVerificationError(verificationErr)
7766
}
7867

79-
results = append(results, result)
68+
results = append(results, s1)
8069
}
8170
}
8271

8372
return
8473
}
8574

75+
func verifyOktaToken(ctx context.Context, client *http.Client, domain string, token string) (bool, error) {
76+
// curl -v -X GET \
77+
// -H "Accept: application/json" \
78+
// -H "Content-Type: application/json" \
79+
// -H "Authorization: SSWS token" \
80+
// "https://subdomain.okta.com/api/v1/users/me"
81+
82+
url := fmt.Sprintf("https://%s/api/v1/users/me", domain)
83+
req, err := http.NewRequestWithContext(ctx, http.MethodGet, url, nil)
84+
if err != nil {
85+
return false, err
86+
}
87+
req.Header.Set("Accept", "application/json")
88+
req.Header.Set("Content-Type", "application/json")
89+
req.Header.Set("Authorization", fmt.Sprintf("SSWS %s", token))
90+
91+
resp, err := client.Do(req)
92+
if err != nil {
93+
return false, err
94+
}
95+
defer resp.Body.Close()
96+
97+
switch resp.StatusCode {
98+
case http.StatusOK:
99+
return true, nil
100+
case http.StatusUnauthorized, http.StatusForbidden:
101+
return false, nil
102+
default:
103+
return false, fmt.Errorf("unexpected status code: %d", resp.StatusCode)
104+
}
105+
}
106+
86107
func (s Scanner) Type() detectorspb.DetectorType {
87108
return detectorspb.DetectorType_Okta
88109
}

pkg/detectors/okta/okta_integration_test.go

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

12-
"github.com/kylelemons/godebug/pretty"
12+
"github.com/google/go-cmp/cmp"
13+
"github.com/google/go-cmp/cmp/cmpopts"
14+
1315
"github.com/trufflesecurity/trufflehog/v3/pkg/common"
1416
"github.com/trufflesecurity/trufflehog/v3/pkg/detectors"
1517
"github.com/trufflesecurity/trufflehog/v3/pkg/pb/detectorspb"
@@ -32,11 +34,12 @@ func TestOkta_FromChunk(t *testing.T) {
3234
verify bool
3335
}
3436
tests := []struct {
35-
name string
36-
s Scanner
37-
args args
38-
want []detectors.Result
39-
wantErr bool
37+
name string
38+
s Scanner
39+
args args
40+
want []detectors.Result
41+
wantErr bool
42+
wantVerificationErr bool
4043
}{
4144
{
4245
name: "found token, verified",
@@ -52,7 +55,8 @@ func TestOkta_FromChunk(t *testing.T) {
5255
Verified: true,
5356
},
5457
},
55-
wantErr: false,
58+
wantErr: false,
59+
wantVerificationErr: false,
5660
},
5761
{
5862
name: "found, unverified",
@@ -68,7 +72,8 @@ func TestOkta_FromChunk(t *testing.T) {
6872
Verified: false,
6973
},
7074
},
71-
wantErr: false,
75+
wantErr: false,
76+
wantVerificationErr: false,
7277
},
7378
{
7479
name: "not found",
@@ -78,26 +83,46 @@ func TestOkta_FromChunk(t *testing.T) {
7883
data: []byte("You cannot find the secret within"),
7984
verify: true,
8085
},
81-
want: nil,
82-
wantErr: false,
86+
want: nil,
87+
wantErr: false,
88+
wantVerificationErr: false,
89+
},
90+
{
91+
name: "found, verified but unexpected api surface",
92+
s: Scanner{client: common.ConstantResponseHttpClient(404, "")},
93+
args: args{
94+
ctx: context.Background(),
95+
data: []byte(fmt.Sprintf("You can find a okta secret %s within oktaDomain %s", secretInactive, domain)),
96+
verify: true,
97+
},
98+
want: []detectors.Result{
99+
{
100+
DetectorType: detectorspb.DetectorType_Okta,
101+
Verified: false,
102+
},
103+
},
104+
wantErr: false,
105+
wantVerificationErr: true,
83106
},
84107
}
85108
for _, tt := range tests {
86109
t.Run(tt.name, func(t *testing.T) {
87-
s := Scanner{}
88-
got, err := s.FromData(tt.args.ctx, tt.args.verify, tt.args.data)
110+
got, err := tt.s.FromData(tt.args.ctx, tt.args.verify, tt.args.data)
89111
if (err != nil) != tt.wantErr {
90112
t.Errorf("Okta.FromData) error = %v, wantErr %v", err, tt.wantErr)
91113
return
92114
}
93115
for i := range got {
94116
if len(got[i].Raw) == 0 {
95-
t.Fatal("no raw secret present")
117+
t.Fatalf("no raw secret present: \n %+v", got[i])
118+
}
119+
if (got[i].VerificationError() != nil) != tt.wantVerificationErr {
120+
t.Fatalf("wantVerificationError = %v, verification error = %v", tt.wantVerificationErr, got[i].VerificationError())
96121
}
97-
got[i].Raw = nil
98122
}
99-
if diff := pretty.Compare(got, tt.want); diff != "" {
100-
t.Errorf("Okta.FromData) %s diff: (-got +want)\n%s", tt.name, diff)
123+
ignoreOpts := cmpopts.IgnoreFields(detectors.Result{}, "Raw", "RawV2", "verificationError")
124+
if diff := cmp.Diff(got, tt.want, ignoreOpts); diff != "" {
125+
t.Errorf("Okta.FromData() %s diff: (-got +want)\n%s", tt.name, diff)
101126
}
102127
})
103128
}

0 commit comments

Comments
 (0)