Detect Organization ID to pass into AnalysisInfo for Atlassian Detector (#4480)
* detect organization id and pass into analysis info * add non integration tests for analysis info, examples of credentials matching the regexes * switch from tabular tests to separate tests * move gock setup outside test functions --------- Co-authored-by: Shahzad Haider <[email protected]> Co-authored-by: Amaan Ullah <[email protected]> Co-authored-by: Kashif Khan <[email protected]>
This commit is contained in:
co-authored by
Shahzad Haider
Amaan Ullah
Kashif Khan
parent
d005847359
commit
b140e2b58e
@@ -35,7 +35,11 @@ var _ detectors.Versioner = (*Scanner)(nil)
|
||||
var (
|
||||
defaultClient = common.SaneHttpClient()
|
||||
// Make sure that your group is surrounded in boundary characters such as below to reduce false positives.
|
||||
|
||||
// Example: ATCTT3xFfGN0GsZNgOGrQSHSnxiJVi00oHlRicyM0yMNuKCBfw6qOHVcCy4Hm89GnclGb_W-1qAkxqCn5XbuyoX54bNhpK5yFKGFR7ocV6FByvL_P9Sb3tFnbUg3T3I3S_RGCBLMSN7Nsa4GJv8JEJ6bzvDmX-oJ8AnrazMU-zZ5hb-u3t2ERew=366BFE3A
|
||||
keyPat = regexp.MustCompile(`\b(ATCTT3xFfG[A-Za-z0-9+/=_-]+=[A-Za-z0-9]{8})\b`)
|
||||
// Example: 123e4567-e89b-12d3-a456-426614174000
|
||||
organizationIdPat = regexp.MustCompile(detectors.PrefixRegex([]string{"org", "id"}) + `\b([0-9a-z]{8}-[0-9a-z]{4}-[0-9a-z]{4}-[0-9a-z]{4}-[0-9a-z]{12})\b`)
|
||||
)
|
||||
|
||||
// Keywords are used for efficiently pre-filtering chunks.
|
||||
@@ -58,31 +62,52 @@ func (s Scanner) FromData(ctx context.Context, verify bool, data []byte) (result
|
||||
uniqueMatches[match[1]] = struct{}{}
|
||||
}
|
||||
|
||||
uniqueOrgIdMatches := make(map[string]struct{})
|
||||
for _, match := range organizationIdPat.FindAllStringSubmatch(dataStr, -1) {
|
||||
uniqueOrgIdMatches[match[1]] = struct{}{}
|
||||
}
|
||||
if len(uniqueOrgIdMatches) == 0 {
|
||||
// we only need an org ID to pass into AnalysisInfo
|
||||
// if we don't find one, we can still verify the key
|
||||
// we can add a dummy entry here just to make sure a result is returned
|
||||
uniqueOrgIdMatches[""] = struct{}{}
|
||||
}
|
||||
|
||||
for match := range uniqueMatches {
|
||||
s1 := detectors.Result{
|
||||
DetectorType: detectorspb.DetectorType_Atlassian,
|
||||
Raw: []byte(match),
|
||||
ExtraData: map[string]string{
|
||||
"rotation_guide": "https://howtorotate.com/docs/tutorials/atlassian/",
|
||||
"version": fmt.Sprintf("%d", s.Version()),
|
||||
},
|
||||
}
|
||||
|
||||
if verify {
|
||||
client := s.client
|
||||
if client == nil {
|
||||
client = defaultClient
|
||||
for orgId := range uniqueOrgIdMatches {
|
||||
s1 := detectors.Result{
|
||||
DetectorType: detectorspb.DetectorType_Atlassian,
|
||||
Raw: []byte(match),
|
||||
ExtraData: map[string]string{
|
||||
"rotation_guide": "https://howtorotate.com/docs/tutorials/atlassian/",
|
||||
"version": fmt.Sprintf("%d", s.Version()),
|
||||
},
|
||||
}
|
||||
|
||||
isVerified, orgResponse, verificationErr := verifyMatch(ctx, client, match)
|
||||
s1.Verified = isVerified
|
||||
if orgResponse != nil && len(orgResponse.Data) > 0 {
|
||||
s1.ExtraData["Organization"] = orgResponse.Data[0].Attributes.Name
|
||||
}
|
||||
s1.SetVerificationError(verificationErr, match)
|
||||
}
|
||||
if verify {
|
||||
client := s.client
|
||||
if client == nil {
|
||||
client = defaultClient
|
||||
}
|
||||
|
||||
results = append(results, s1)
|
||||
isVerified, orgResponse, verificationErr := verifyMatch(ctx, client, match)
|
||||
s1.Verified = isVerified
|
||||
if orgResponse != nil && len(orgResponse.Data) > 0 {
|
||||
s1.ExtraData["Organization"] = orgResponse.Data[0].Attributes.Name
|
||||
}
|
||||
s1.SetVerificationError(verificationErr, match)
|
||||
if s1.Verified {
|
||||
s1.AnalysisInfo = map[string]string{
|
||||
"key": match,
|
||||
}
|
||||
if orgId != "" {
|
||||
s1.AnalysisInfo["organization_id"] = orgId
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
results = append(results, s1)
|
||||
}
|
||||
}
|
||||
|
||||
return
|
||||
|
||||
@@ -136,7 +136,7 @@ func TestAtlassian_FromChunk(t *testing.T) {
|
||||
t.Fatalf("wantVerificationError = %v, verification error = %v", tt.wantVerificationErr, got[i].VerificationError())
|
||||
}
|
||||
}
|
||||
ignoreOpts := cmpopts.IgnoreFields(detectors.Result{}, "Raw", "verificationError", "ExtraData")
|
||||
ignoreOpts := cmpopts.IgnoreFields(detectors.Result{}, "Raw", "verificationError", "ExtraData", "primarySecret", "AnalysisInfo")
|
||||
if diff := cmp.Diff(got, tt.want, ignoreOpts); diff != "" {
|
||||
t.Errorf("Atlassian.FromData() %s diff: (-got +want)\n%s", tt.name, diff)
|
||||
}
|
||||
|
||||
@@ -2,12 +2,17 @@ package atlassian
|
||||
|
||||
import (
|
||||
"context"
|
||||
"fmt"
|
||||
"net/http"
|
||||
"testing"
|
||||
|
||||
"github.com/google/go-cmp/cmp"
|
||||
"github.com/stretchr/testify/assert"
|
||||
"github.com/stretchr/testify/require"
|
||||
"github.com/trufflesecurity/trufflehog/v3/pkg/common"
|
||||
"github.com/trufflesecurity/trufflehog/v3/pkg/detectors"
|
||||
"github.com/trufflesecurity/trufflehog/v3/pkg/engine/ahocorasick"
|
||||
"gopkg.in/h2non/gock.v1"
|
||||
)
|
||||
|
||||
func TestAtlassian_Pattern(t *testing.T) {
|
||||
@@ -79,3 +84,80 @@ func TestAtlassian_Pattern(t *testing.T) {
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
// TestAtlassian_AnalysisInfo_KeyAndOrgId tests if both the key and organization id are populated into AnalysisInfo
|
||||
// given that they are present in the input data chunk
|
||||
func TestAtlassian_AnalysisInfo_KeyAndOrgId(t *testing.T) {
|
||||
client := common.SaneHttpClient()
|
||||
d := Scanner{client: client}
|
||||
|
||||
key := "ATCTT3xFfGN0GsZNgOGrQSHSnxiJVi00oHlRicyM0yMNuKCBfw6qOHVcCy4Hm89GnclGb_W-1qAkxqCn5XbuyoX54bNhpK5yFKGFR7ocV6FByvL_P9Sb3tFnbUg3T3I3S_RGCBLMSN7Nsa4GJv8JEJ6bzvDmX-oJ8AnrazMU-zZ5hb-u3t2ERew=366BFE3A"
|
||||
orgId := "123j4567-e89b-12d3-a456-426614174000"
|
||||
|
||||
defer gock.Off()
|
||||
defer gock.RestoreClient(client)
|
||||
gock.InterceptClient(client)
|
||||
gock.New("https://api.atlassian.com").
|
||||
Get("/admin/v1/orgs").
|
||||
MatchHeader("Accept", "application/json").
|
||||
MatchHeader("Authorization", fmt.Sprintf("Bearer %s", key)).
|
||||
Reply(http.StatusOK).
|
||||
JSON(map[string]any{
|
||||
"Data": []map[string]any{},
|
||||
})
|
||||
|
||||
t.Run("key and organization id both present", func(t *testing.T) {
|
||||
input := fmt.Sprintf(`
|
||||
[INFO] Sending request to the atlassian API
|
||||
[DEBUG] Using Key=%s
|
||||
[DEBUG] Using Organization ID=%s
|
||||
[INFO] Response received: 200 OK
|
||||
`, key, orgId)
|
||||
|
||||
results, err := d.FromData(context.Background(), true, []byte(input))
|
||||
require.NoError(t, err)
|
||||
require.Len(t, results, 1, "mismatch in result count: expected %d, got %d", 1, len(results))
|
||||
result := results[0]
|
||||
require.NotNil(t, result.AnalysisInfo, "AnalysisInfo is nil")
|
||||
|
||||
assert.Equal(t, key, result.AnalysisInfo["key"], "mismatch in key")
|
||||
assert.Equal(t, orgId, result.AnalysisInfo["organization_id"], "mismatch in organization_id")
|
||||
})
|
||||
}
|
||||
|
||||
// TestAtlassian_AnalysisInfo_KeyOnly tests if only key is populated into AnalysisInfo
|
||||
// given that only the key and no organization_id is present in the input data chunk
|
||||
func TestAtlassian_AnalysisInfo_KeyOnly(t *testing.T) {
|
||||
client := common.SaneHttpClient()
|
||||
d := Scanner{client: client}
|
||||
|
||||
key := "ATCTT3xFfGN0GsZNgOGrQSHSnxiJVi00oHlRicyM0yMNuKCBfw6qOHVcCy4Hm89GnclGb_W-1qAkxqCn5XbuyoX54bNhpK5yFKGFR7ocV6FByvL_P9Sb3tFnbUg3T3I3S_RGCBLMSN7Nsa4GJv8JEJ6bzvDmX-oJ8AnrazMU-zZ5hb-u3t2ERew=366BFE3A"
|
||||
|
||||
defer gock.Off()
|
||||
defer gock.RestoreClient(client)
|
||||
gock.InterceptClient(client)
|
||||
gock.New("https://api.atlassian.com").
|
||||
Get("/admin/v1/orgs").
|
||||
MatchHeader("Accept", "application/json").
|
||||
MatchHeader("Authorization", fmt.Sprintf("Bearer %s", key)).
|
||||
Reply(http.StatusOK).
|
||||
JSON(map[string]any{
|
||||
"Data": []map[string]any{},
|
||||
})
|
||||
t.Run("only key present", func(t *testing.T) {
|
||||
|
||||
input := fmt.Sprintf(`
|
||||
[INFO] Sending request to the atlassian API
|
||||
[DEBUG] Using Key=%s
|
||||
[INFO] Response received: 200 OK
|
||||
`, key)
|
||||
|
||||
results, err := d.FromData(context.Background(), true, []byte(input))
|
||||
require.NoError(t, err)
|
||||
require.Len(t, results, 1, "mismatch in result count: expected %d, got %d", 1, len(results))
|
||||
result := results[0]
|
||||
require.NotNil(t, result.AnalysisInfo, "AnalysisInfo is nil")
|
||||
|
||||
assert.Equal(t, key, result.AnalysisInfo["key"], "mismatch in key")
|
||||
})
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user