From 5fca1636cda0ebc323168d8d46746ce02786280c Mon Sep 17 00:00:00 2001 From: Kashif Khan <70996046+kashifkhan0771@users.noreply.github.com> Date: Mon, 2 Jun 2025 11:54:56 +0500 Subject: [PATCH] remove prefix regex for JIRA V2 patterns (#4197) --- pkg/detectors/jiratoken/v2/jiratoken_v2.go | 6 +- .../jiratoken/v2/jiratoken_v2_test.go | 208 +++++++++++++++--- 2 files changed, 183 insertions(+), 31 deletions(-) diff --git a/pkg/detectors/jiratoken/v2/jiratoken_v2.go b/pkg/detectors/jiratoken/v2/jiratoken_v2.go index ca300a6c8..cddfa9a45 100644 --- a/pkg/detectors/jiratoken/v2/jiratoken_v2.go +++ b/pkg/detectors/jiratoken/v2/jiratoken_v2.go @@ -30,9 +30,9 @@ var ( // https://support.atlassian.com/atlassian-account/docs/manage-api-tokens-for-your-atlassian-account/ // Tokens created after Jan 18 2023 use a variable length - tokenPat = regexp.MustCompile(detectors.PrefixRegex([]string{"atlassian", "confluence", "jira"}) + `\b(ATATT[A-Za-z0-9+/=_-]+=[A-Za-z0-9]{8})\b`) - domainPat = regexp.MustCompile(detectors.PrefixRegex([]string{"atlassian", "confluence", "jira"}) + `\b((?:[a-zA-Z0-9-]{1,24}\.)+[a-zA-Z0-9-]{2,24}\.[a-zA-Z0-9-]{2,16})\b`) - emailPat = regexp.MustCompile(detectors.PrefixRegex([]string{"atlassian", "confluence", "jira"}) + common.EmailPattern) + tokenPat = regexp.MustCompile(`\b(ATATT[A-Za-z0-9+/=_-]+=[A-Za-z0-9]{8})\b`) + domainPat = regexp.MustCompile(`\b((?:[a-zA-Z0-9-]{1,24}\.)+[a-zA-Z0-9-]{2,24}\.[a-zA-Z0-9-]{2,16})\b`) + emailPat = regexp.MustCompile(common.EmailPattern) ) func (s Scanner) getClient() *http.Client { diff --git a/pkg/detectors/jiratoken/v2/jiratoken_v2_test.go b/pkg/detectors/jiratoken/v2/jiratoken_v2_test.go index cbf1e8ad9..4249d15b5 100644 --- a/pkg/detectors/jiratoken/v2/jiratoken_v2_test.go +++ b/pkg/detectors/jiratoken/v2/jiratoken_v2_test.go @@ -2,8 +2,6 @@ package jiratoken import ( "context" - "fmt" - "strings" "testing" "github.com/google/go-cmp/cmp" @@ -12,44 +10,193 @@ import ( "github.com/trufflesecurity/trufflehog/v3/pkg/engine/ahocorasick" ) -var ( - validTokenPattern = "ATATT9nsCADa7812Z7VoIsYJ0K4rFWLBfk=1rhOsLAW" - invalidTokenPattern = "9nsCA?a7812Z7VoI%YJ0K4rFWLBfk91rhOsLAW" - validDomainPattern = "hereisavalidsubdomain.heresalongdomain.com" - validDomainPattern2 = "jira.hereisavalidsubdomain.heresalongdomain.com" - invalidDomainPattern = "?y4r3fs1ewqec12v1e3tl.5Hcsrcehic89saXd.ro@" - validEmailPattern = "xfKF_BZq7@grum.com" - invalidEmailPattern = "xfKF_BZq7/grum.com" - keyword = "jira" -) - func TestJiraToken_Pattern(t *testing.T) { d := Scanner{} ahoCorasickCore := ahocorasick.NewAhoCorasickCore([]detectors.Detector{d}) tests := []struct { - name string - input string - want []string + name string + input string + want []string + noKeywords bool }{ { - name: "valid pattern - with keyword jira", - input: fmt.Sprintf("%s %s \n%s %s\n%s %s", keyword, validTokenPattern, keyword, validDomainPattern, keyword, validEmailPattern), - want: []string{strings.ToLower(validEmailPattern) + ":" + validTokenPattern + ":" + validDomainPattern}, + name: "valid pattern", + input: ` + { + "expand": "schema,names", + "startAt": 0, + "maxResults": 50, + "total": 1, + "issues": [ + { + "expand": "operations,versionedRepresentations,editmeta,changelog,renderedFields", + "id": "fake454", + "self": "https://example.atlassian.net/rest/api/2/issue/fake454", + "key": "ESI-5555", + "fields": { + "statuscategorychangedate": "2016-06-01T01:25:35.807-0700", + "issuetype": { + "self": "https://example.atlassian.net/rest/api/2/issuetype/09090", + "id": "09090", + "description": "This is an example ticket. Here's the token to test JIRA APIs: ATATThktLkSzzcXi1xt19IlU6gAchV1TS83w11YOqAXqFUeA2=Yx3ssoNC", + "name": "Example Pattern test", + "subtask": false, + "avatarId": 1298, + "entityId": "93a51c1d-fake-4673-a71d-0889fake1238", + "hierarchyLevel": 0, + "emailAddress": "trufflesecurity@example.com", + }, + } + ]}`, + want: []string{"trufflesecurity@example.com" + ":" + "ATATThktLkSzzcXi1xt19IlU6gAchV1TS83w11YOqAXqFUeA2=Yx3ssoNC" + ":" + "example.atlassian.net"}, }, { - name: "valid pattern - with multiple subdomains", - input: fmt.Sprintf("%s %s \n%s %s\n%s %s", keyword, validTokenPattern, keyword, validDomainPattern2, keyword, validEmailPattern), - want: []string{strings.ToLower(validEmailPattern) + ":" + validTokenPattern + ":" + validDomainPattern2}, + name: "valid pattern - without domain", + input: ` + { + "expand": "schema,names", + "startAt": 0, + "maxResults": 50, + "total": 1, + "issues": [ + { + "expand": "operations,versionedRepresentations,editmeta,changelog,renderedFields", + "id": "fake454", + "key": "ESI-5555", + "fields": { + "statuscategorychangedate": "2016-06-01T01:25:35.807-0700", + "issuetype": { + "id": "09090", + "description": "This is an example ticket. Here's the token to test JIRA APIs: ATATThktLkSzzcXi1xt19IlU6gAchV1TS83w11YOqAXqFUeA2=Yx3ssoNC", + "name": "Example Pattern test 2", + "subtask": false, + "avatarId": 1298, + "entityId": "93a51c1d-fake-4673-a71d-0889fake1238", + "hierarchyLevel": 0, + "emailAddress": "trufflesecurity@example.com", + }, + } + ]}`, + want: []string{"trufflesecurity@example.com" + ":" + "ATATThktLkSzzcXi1xt19IlU6gAchV1TS83w11YOqAXqFUeA2=Yx3ssoNC" + ":" + "api.atlassian.com"}, }, { - name: "valid pattern - key out of prefix range", - input: fmt.Sprintf("%s keyword is not close to the real key in the data\n = '%s' domain = '%s' email = '%s'", keyword, validTokenPattern, validDomainPattern, validEmailPattern), - want: []string{}, + name: "valid pattern - without token", + input: ` + { + "expand": "schema,names", + "startAt": 0, + "maxResults": 50, + "total": 1, + "issues": [ + { + "expand": "operations,versionedRepresentations,editmeta,changelog,renderedFields", + "id": "fake454", + "key": "ESI-5555", + "fields": { + "statuscategorychangedate": "2016-06-01T01:25:35.807-0700", + "issuetype": { + "id": "09090", + "self": "https://example.atlassian.net/rest/api/2/issuetype/09090", + "description": "This is an example ticket", + "name": "Example Pattern test 2", + "subtask": false, + "avatarId": 1298, + "entityId": "93a51c1d-fake-4673-a71d-0889fake1238", + "hierarchyLevel": 0, + "emailAddress": "trufflesecurity@example.com", + }, + } + ]}`, + want: []string{}, }, { - name: "invalid pattern", - input: fmt.Sprintf("%s key = '%s' domain = '%s' email = '%s'", keyword, invalidTokenPattern, invalidDomainPattern, invalidEmailPattern), - want: []string{}, + name: "valid pattern - without email", + input: ` + { + "expand": "schema,names", + "startAt": 0, + "maxResults": 50, + "total": 1, + "issues": [ + { + "expand": "operations,versionedRepresentations,editmeta,changelog,renderedFields", + "id": "fake454", + "key": "ESI-5555", + "fields": { + "statuscategorychangedate": "2016-06-01T01:25:35.807-0700", + "issuetype": { + "id": "09090", + "self": "https://example.atlassian.net/rest/api/2/issuetype/09090", + "description": "This is an example ticket. Here's the token to test JIRA APIs: ATATThktLkSzzcXi1xt19IlU6gAchV1TS83w11YOqAXqFUeA2=Yx3ssoNC", + "name": "Example Pattern test 2", + "subtask": false, + "avatarId": 1298, + "entityId": "93a51c1d-fake-4673-a71d-0889fake1238", + "hierarchyLevel": 0, + "emailAddress": "", + }, + } + ]}`, + want: []string{}, + }, + { + name: "valid pattern - without keywords", + input: ` + { + "expand": "schema,names", + "startAt": 0, + "maxResults": 50, + "total": 1, + "issues": [ + { + "expand": "operations,versionedRepresentations,editmeta,changelog,renderedFields", + "id": "fake454", + "key": "ESI-5555", + "fields": { + "statuscategorychangedate": "2016-06-01T01:25:35.807-0700", + "issuetype": { + "id": "09090", + "description": "ATATThktLkSzzcXi1xt19IlU6gAchV1TS83w11YOqAXqFUeA2=Yx3ssoNC", + "name": "Example Pattern test 2", + "subtask": false, + "avatarId": 1298, + "entityId": "93a51c1d-fake-4673-a71d-0889fake1238", + "hierarchyLevel": 0, + "emailAddress": "trufflesecurity@example.com", + }, + } + ]}`, + want: []string{}, + noKeywords: true, + }, + { + name: "invalid pattern", + input: ` + { + "expand": "schema,names", + "startAt": 0, + "maxResults": 50, + "total": 1, + "issues": [ + { + "expand": "operations,versionedRepresentations,editmeta,changelog,renderedFields", + "id": "fake454", + "key": "ESI-5555", + "fields": { + "statuscategorychangedate": "2016-06-01T01:25:35.807-0700", + "issuetype": { + "id": "09090", + "description": "This is an example ticket. Here's the token to test JIRA APIs: ATATTA9nsCA?a7812Z7VoI%YJ0K4rFWLBfk91rhOsLAW=Yx3ssoNC", + "name": "Example Pattern test 2", + "subtask": false, + "avatarId": 1298, + "entityId": "93a51c1d-fake-4673-a71d-0889fake1238", + "hierarchyLevel": 0, + "emailAddress": "?y4r3fs1ewqec12v1e3tl.5Hcsrcehic89saXd.ro@", + }, + } + ]}`, + want: []string{}, }, } @@ -57,6 +204,11 @@ func TestJiraToken_Pattern(t *testing.T) { t.Run(test.name, func(t *testing.T) { matchedDetectors := ahoCorasickCore.FindDetectorMatches([]byte(test.input)) if len(matchedDetectors) == 0 { + // if intentionally no keywords are passed + if test.noKeywords { + return + } + t.Errorf("keywords '%v' not matched by: %s", d.Keywords(), test.input) return }