optimize the regex pattern in the artifactory access token detector (#4685)
Lint / golangci-lint (push) Waiting to run
Lint / semgrep (push) Waiting to run
Release / Release (push) Waiting to run
Scan for secrets / test (push) Waiting to run
Test / test (push) Waiting to run
Test / test-community (push) Waiting to run

This commit is contained in:
Shahzad Haider
2026-02-19 15:16:35 +01:00
committed by GitHub
parent cbdff3fa9c
commit 7c0734f987
3 changed files with 52 additions and 49 deletions
+2 -2
View File
@@ -29,7 +29,7 @@ var (
defaultClient = detectors.DetectorHttpClientWithNoLocalAddresses
// Make sure that your group is surrounded in boundary characters such as below to reduce false positives.
keyPat = regexp.MustCompile(`\b([a-zA-Z0-9]{64,73})\b`)
keyPat = regexp.MustCompile(`\b(AKCp[a-zA-Z0-9]{69})\b`)
URLPat = regexp.MustCompile(`\b([A-Za-z0-9][A-Za-z0-9\-]{0,61}[A-Za-z0-9]\.jfrog\.io)`)
invalidHosts = simple.NewCache[struct{}]()
@@ -42,7 +42,7 @@ func (Scanner) CloudEndpoint() string { return "" }
// Keywords are used for efficiently pre-filtering chunks.
// Use identifiers in the secret preferably, or the provider name.
func (s Scanner) Keywords() []string {
return []string{"artifactory", "jfrog.io"}
return []string{"artifactory", "jfrog.io", "AKCp"}
}
func (s Scanner) getClient() *http.Client {
@@ -7,26 +7,21 @@ import (
"context"
"fmt"
"testing"
"time"
"github.com/google/go-cmp/cmp"
"github.com/google/go-cmp/cmp/cmpopts"
"github.com/trufflesecurity/trufflehog/v3/pkg/detectors"
"github.com/trufflesecurity/trufflehog/v3/pkg/common"
"github.com/trufflesecurity/trufflehog/v3/pkg/pb/detectorspb"
)
func TestArtifactory_FromChunk(t *testing.T) {
ctx, cancel := context.WithTimeout(context.Background(), time.Second*5)
defer cancel()
testSecrets, err := common.GetSecret(ctx, "trufflehog-testing", "detectors5")
if err != nil {
t.Fatalf("could not get test secrets from GCP: %s", err)
}
secret := testSecrets.MustGetField("ARTIFACTORY_TOKEN")
inactiveSecret := testSecrets.MustGetField("ARTIFACTORY_INACTIVE")
appURL := testSecrets.MustGetField("ARTIFACTORY_URL")
// NOTE: Using mock secrets because JFrog deprecated AKCp API keys (disabled creation end of Q3 2024).
// Real AKCp keys can no longer be generated, so we cannot test actual verification scenarios.
// These mock keys follow the correct format: AKCp + 69 alphanumeric characters = 73 total
// Reference: https://jfrog.com/help/r/jfrog-release-information/artifactory-7.47.10-cloud-self-hosted
mockSecret := "AKCp5bueTFpfypEqQbGJPp7eHFi28fBivfWczrjbPb9erDff9LbXZbj6UsRExVXA8asWGc9fM"
appURL := "trufflehog.jfrog.io"
type args struct {
ctx context.Context
@@ -41,28 +36,12 @@ func TestArtifactory_FromChunk(t *testing.T) {
wantErr bool
}{
{
name: "found, verified",
name: "found, unverified - mock key (cannot verify deprecated AKCp format)",
s: Scanner{},
args: args{
ctx: context.Background(),
data: []byte(fmt.Sprintf("You can find a artifactory secret %s and domain %s", secret, appURL)),
verify: true,
},
want: []detectors.Result{
{
DetectorType: detectorspb.DetectorType_ArtifactoryAccessToken,
Verified: true,
},
},
wantErr: false,
},
{
name: "found, unverified",
s: Scanner{},
args: args{
ctx: context.Background(),
data: []byte(fmt.Sprintf("You can find a artifactory secret %s but not valid on endpoint %s", inactiveSecret, appURL)), // the secret would satisfy the regex but not pass validation
verify: true,
data: []byte(fmt.Sprintf("You can find a artifactory secret %s and domain %s", mockSecret, appURL)),
verify: false, // Cannot verify - AKCp API keys are deprecated and no valid keys available
},
want: []detectors.Result{
{
+41 -17
View File
@@ -27,13 +27,16 @@ func TestArtifactory_Pattern(t *testing.T) {
name: "valid pattern",
input: `
[INFO] Sending request to the artifactory API
[DEBUG] Using Key=cmVmdGtuOjAxOjE3ODA1NTFAKEM6S2J2MGswemNzZzhaRnFlVUFAKEk3amlLcGZg
[DEBUG] Using Key=AKCp5e2gMx8TtJNDtrsuPq7Jz24Rqjkjf1d1iiy1GuEjmdsY8ghxFGgehZcK3UGNgy5TxHWdE
[INFO] rwxtOp.jfrog.io
[INFO] Response received: 200 OK
`,
useCloudEndpoint: false,
useFoundEndpoint: true,
want: []string{"cmVmdGtuOjAxOjE3ODA1NTFAKEM6S2J2MGswemNzZzhaRnFlVUFAKEk3amlLcGZgrwxtOp.jfrog.io"},
want: []string{
"AKCp5e2gMx8TtJNDtrsuPq7Jz24Rqjkjf1d1iiy1GuEjmdsY8ghxFGgehZcK3UGNgy5TxHWdE" +
"rwxtOp.jfrog.io",
},
},
{
name: "valid pattern - xml",
@@ -41,7 +44,7 @@ func TestArtifactory_Pattern(t *testing.T) {
<com.cloudbees.plugins.credentials.impl.StringCredentialsImpl>
<scope>GLOBAL</scope>
<id>{artifactory}</id>
<secret>{AQAAABAAA KUd8GOVfcXnIv1nJ5qmnNzrqkLvseoPRMuwsdDVr9QthonFogtMaoJ3pgtO4eHXC}</secret>
<secret>AKCp8budTFpbypBqQbGJPp7eHFi28fBivfWczrjbPb9erDff9LbXZbj6UsRExVXA8asWGc9fM</secret>
<domain>{HTTPnGQZ79vjWXze.jfrog.io}</domain>
<description>configuration for production</description>
<creationDate>2023-05-18T14:32:10Z</creationDate>
@@ -50,25 +53,31 @@ func TestArtifactory_Pattern(t *testing.T) {
`,
useCloudEndpoint: false,
useFoundEndpoint: true,
want: []string{"KUd8GOVfcXnIv1nJ5qmnNzrqkLvseoPRMuwsdDVr9QthonFogtMaoJ3pgtO4eHXCHTTPnGQZ79vjWXze.jfrog.io"},
want: []string{
"AKCp8budTFpbypBqQbGJPp7eHFi28fBivfWczrjbPb9erDff9LbXZbj6UsRExVXA8asWGc9fM" +
"HTTPnGQZ79vjWXze.jfrog.io",
},
},
{
name: "valid pattern - with cloud endpoints",
input: `
[INFO] Sending request to the artifactory API
[DEBUG] Using Key=cmVmdGtuOjAxOjE3ODA1NTFAKEM6S2J2MGswemNzZzhaRnFlVUFAKEk3amlLcGZg
[DEBUG] Using Key=AKCp5e2gMx8TtJNDtrsuPq7Jz24Rqjkjf1d1iiy1GuEjmdsY8ghxFGgehZcK3UGNgy5TxHWdE
[INFO] Response received: 200 OK
`,
cloudEndpoint: "cloudendpoint.jfrog.io",
useCloudEndpoint: true,
useFoundEndpoint: false,
want: []string{"cmVmdGtuOjAxOjE3ODA1NTFAKEM6S2J2MGswemNzZzhaRnFlVUFAKEk3amlLcGZgcloudendpoint.jfrog.io"},
want: []string{
"AKCp5e2gMx8TtJNDtrsuPq7Jz24Rqjkjf1d1iiy1GuEjmdsY8ghxFGgehZcK3UGNgy5TxHWdE" +
"cloudendpoint.jfrog.io",
},
},
{
name: "valid pattern - with cloud and found endpoints",
input: `
[INFO] Sending request to the artifactory API
[DEBUG] Using Key=cmVmdGtuOjAxOjE3ODA1NTFAKEM6S2J2MGswemNzZzhaRnFlVUFAKEk3amlLcGZg
[DEBUG] Using Key=AKCp5e2gMx8TtJNDtrsuPq7Jz24Rqjkjf1d1iiy1GuEjmdsY8ghxFGgehZcK3UGNgy5TxHWdE
[INFO] rwxtOp.jfrog.io
[INFO] Response received: 200 OK
`,
@@ -76,15 +85,17 @@ func TestArtifactory_Pattern(t *testing.T) {
useCloudEndpoint: true,
useFoundEndpoint: true,
want: []string{
"cmVmdGtuOjAxOjE3ODA1NTFAKEM6S2J2MGswemNzZzhaRnFlVUFAKEk3amlLcGZgcloudendpoint.jfrog.io",
"cmVmdGtuOjAxOjE3ODA1NTFAKEM6S2J2MGswemNzZzhaRnFlVUFAKEk3amlLcGZgrwxtOp.jfrog.io",
"AKCp5e2gMx8TtJNDtrsuPq7Jz24Rqjkjf1d1iiy1GuEjmdsY8ghxFGgehZcK3UGNgy5TxHWdE" +
"cloudendpoint.jfrog.io",
"AKCp5e2gMx8TtJNDtrsuPq7Jz24Rqjkjf1d1iiy1GuEjmdsY8ghxFGgehZcK3UGNgy5TxHWdE" +
"rwxtOp.jfrog.io",
},
},
{
name: "valid pattern - with disabled found endpoints",
input: `
[INFO] Sending request to the artifactory API
[DEBUG] Using Key=cmVmdGtuOjAxOjE3ODA1NTFAKEM6S2J2MGswemNzZzhaRnFlVUFAKEk3amlLcGZg
[DEBUG] Using Key=AKCp5e2gMx8TtJNDtrsuPq7Jz24Rqjkjf1d1iiy1GuEjmdsY8ghxFGgehZcK3UGNgy5TxHWdE
[INFO] rwxtOp.jfrog.io
[INFO] Response received: 200 OK
`,
@@ -92,28 +103,41 @@ func TestArtifactory_Pattern(t *testing.T) {
useCloudEndpoint: true,
useFoundEndpoint: false,
want: []string{
"cmVmdGtuOjAxOjE3ODA1NTFAKEM6S2J2MGswemNzZzhaRnFlVUFAKEk3amlLcGZgcloudendpoint.jfrog.io",
"AKCp5e2gMx8TtJNDtrsuPq7Jz24Rqjkjf1d1iiy1GuEjmdsY8ghxFGgehZcK3UGNgy5TxHWdE" +
"cloudendpoint.jfrog.io",
},
},
{
name: "valid pattern - with https in configured endpoint",
input: `
[INFO] Sending request to the artifactory API
[DEBUG] Using Key=cmVmdGtuOjAxOjE3ODA1NTFAKEM6S2J2MGswemNzZzhaRnFlVUFAKEk3amlLcGZg
[DEBUG] Using Key=AKCp5e2gMx8TtJNDtrsuPq7Jz24Rqjkjf1d1iiy1GuEjmdsY8ghxFGgehZcK3UGNgy5TxHWdE
[INFO] Response received: 200 OK
`,
cloudEndpoint: "https://cloudendpoint.jfrog.io",
useCloudEndpoint: true,
useFoundEndpoint: false,
want: []string{
"cmVmdGtuOjAxOjE3ODA1NTFAKEM6S2J2MGswemNzZzhaRnFlVUFAKEk3amlLcGZgcloudendpoint.jfrog.io",
"AKCp5e2gMx8TtJNDtrsuPq7Jz24Rqjkjf1d1iiy1GuEjmdsY8ghxFGgehZcK3UGNgy5TxHWdE" +
"cloudendpoint.jfrog.io",
},
},
{
name: "invalid pattern",
name: "invalid pattern - wrong prefix",
input: `
[INFO] Sending request to the artifactory API
[DEBUG] Using Key=cmVmdGtuOjAxOjEODA_1NTFAKEM6S2J2MGswemNzZzhaRnFlVUFAKEk3amlLcGZg
[DEBUG] Using Key=XYZp5e2gMx8TtJNDtrsuPq7Jz24Rqjkjf1d1iiy1GuEjmdsY8ghxFGgehZcK3UGNgy5TxHWdE
[INFO] rwxtOp.jfrog.io
[INFO] Response received: 200 OK
`,
useFoundEndpoint: true,
want: nil,
},
{
name: "invalid pattern - too short",
input: `
[INFO] Sending request to the artifactory API
[DEBUG] Using Key=AKCp5e2gMx8TtJNDtrsuPq7Jz24Rqjkjf1d1iiy1GuEjmd
[INFO] rwxtOp.jfrog.io
[INFO] Response received: 200 OK
`,
@@ -124,10 +148,10 @@ func TestArtifactory_Pattern(t *testing.T) {
for _, test := range tests {
t.Run(test.name, func(t *testing.T) {
// this detector use endpoint customizer interface so we need to enable them based on test case
// this detector uses endpoint customizer interface so we need to enable them based on test case
d.UseFoundEndpoints(test.useFoundEndpoint)
d.UseCloudEndpoint(test.useCloudEndpoint)
// if test case provide cloud endpoint use it
// if the test case provides cloud endpoint, then use it
if test.useCloudEndpoint && test.cloudEndpoint != "" {
d.SetCloudEndpoint(test.cloudEndpoint)
}