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
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:
@@ -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{
|
||||
{
|
||||
|
||||
@@ -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)
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user