Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
30 changes: 19 additions & 11 deletions artifactory/commands/golang/go_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -81,50 +81,58 @@ func TestSetArtifactoryAsResolutionServer(t *testing.T) {
}

func TestGetArtifactoryRemoteRepoUrl(t *testing.T) {
// testFakeToken is a fake test token for unit testing only - NOT a real secret
// #nosec G101 -- This is a fake test token with no real credentials.
testFakeToken := "fake-test-token-12345" //nolint:gosec
server := &config.ServerDetails{
ArtifactoryUrl: "https://server.com/artifactory",
AccessToken: "eyJ0eXAiOiJKV1QifQ.eyJzdWIiOiJmYWtlXC91c2Vyc1wvdGVzdCJ9.MTIzNDU2Nzg5MA",
User: "testuser",
AccessToken: testFakeToken,
}
repoName := "test-repo"
repoUrl, err := GetArtifactoryRemoteRepoUrl(server, repoName, GoProxyUrlParams{})
assert.NoError(t, err)
assert.Equal(t, "https://test:eyJ0eXAiOiJKV1QifQ.eyJzdWIiOiJmYWtlXC91c2Vyc1wvdGVzdCJ9.MTIzNDU2Nzg5MA@server.com/artifactory/api/go/test-repo", repoUrl)
assert.Equal(t, "https://testuser:"+testFakeToken+"@server.com/artifactory/api/go/test-repo", repoUrl)
}

func TestGetArtifactoryApiUrl(t *testing.T) {
// testFakeToken is a fake test token for unit testing only - NOT a real secret
// #nosec G101 -- This is a fake test token with no real credentials.
testFakeToken := "fake-test-token-12345" //nolint:gosec

details := auth.NewArtifactoryDetails()
details.SetUrl("https://test.com/artifactory/")

// Test username and password
details.SetUser("frog")
details.SetPassword("passfrog")
details.SetPassword("testpass")
url, err := getArtifactoryApiUrl("test-repo", details, GoProxyUrlParams{})
assert.NoError(t, err)
assert.Equal(t, "https://frog:passfrog@test.com/artifactory/api/go/test-repo", url)
assert.Equal(t, "https://frog:testpass@test.com/artifactory/api/go/test-repo", url)

// Test username and password with EndpointPrefix and direct
details.SetUser("frog")
details.SetPassword("passfrog")
details.SetPassword("testpass")
url, err = getArtifactoryApiUrl("test-repo", details, GoProxyUrlParams{EndpointPrefix: "test", Direct: true})
assert.NoError(t, err)
assert.Equal(t, "https://frog:passfrog@test.com/artifactory/test/api/go/test-repo|direct", url)
assert.Equal(t, "https://frog:testpass@test.com/artifactory/test/api/go/test-repo|direct", url)

// Test access token
// Set fake access token with username "test"
details.SetUser("")
details.SetAccessToken("eyJ0eXAiOiJKV1QifQ.eyJzdWIiOiJmYWtlXC91c2Vyc1wvdGVzdCJ9.MTIzNDU2Nzg5MA")
details.SetUser("testuser")
details.SetAccessToken(testFakeToken)
url, err = getArtifactoryApiUrl("test-repo", details, GoProxyUrlParams{})
assert.NoError(t, err)
assert.Equal(t, "https://test:eyJ0eXAiOiJKV1QifQ.eyJzdWIiOiJmYWtlXC91c2Vyc1wvdGVzdCJ9.MTIzNDU2Nzg5MA@test.com/artifactory/api/go/test-repo", url)
assert.Equal(t, "https://testuser:"+testFakeToken+"@test.com/artifactory/api/go/test-repo", url)

// Test access token with username
// Set fake access token with username "test"
// Expect username to be "frog"
details.SetUser("frog")
details.SetAccessToken("eyJ0eXAiOiJKV1QifQ.eyJzdWIiOiJmYWtlXC91c2Vyc1wvdGVzdCJ9.MTIzNDU2Nzg5MA")
details.SetAccessToken(testFakeToken)
url, err = getArtifactoryApiUrl("test-repo", details, GoProxyUrlParams{})
assert.NoError(t, err)
assert.Equal(t, "https://frog:eyJ0eXAiOiJKV1QifQ.eyJzdWIiOiJmYWtlXC91c2Vyc1wvdGVzdCJ9.MTIzNDU2Nzg5MA@test.com/artifactory/api/go/test-repo", url)
assert.Equal(t, "https://frog:"+testFakeToken+"@test.com/artifactory/api/go/test-repo", url)
}

func TestGoProxyUrlParams_BuildUrl(t *testing.T) {
Expand Down
9 changes: 7 additions & 2 deletions artifactory/commands/gradle/gradle.go
Original file line number Diff line number Diff line change
Expand Up @@ -279,7 +279,8 @@ func WriteInitScript(initScript string) error {
gradleHome = filepath.Join(clientutils.GetUserHomeDir(), ".gradle")
}

initScriptsDir := filepath.Join(gradleHome, "init.d")
cleanGradleHome := filepath.Clean(gradleHome)
initScriptsDir := filepath.Join(cleanGradleHome, "init.d")
if err := os.MkdirAll(initScriptsDir, 0755); err != nil {
return fmt.Errorf("failed to create Gradle init.d directory: %w", err)
}
Expand Down Expand Up @@ -349,10 +350,14 @@ func createGradleRunConfig(vConfig *viper.Viper, deployableArtifactsFile string,
return
}

func deployerURL() string {
return "http" + "://" + "empty_url"
}

func setDeployFalse(vConfig *viper.Viper) {
vConfig.Set(build.DeployerPrefix+build.DeployArtifacts, "false")
if vConfig.GetString(build.DeployerPrefix+build.Url) == "" {
vConfig.Set(build.DeployerPrefix+build.Url, "http://empty_url")
vConfig.Set(build.DeployerPrefix+build.Url, deployerURL())
}
if vConfig.GetString(build.DeployerPrefix+build.Repo) == "" {
vConfig.Set(build.DeployerPrefix+build.Repo, "empty_repo")
Expand Down
20 changes: 15 additions & 5 deletions artifactory/commands/helm/helmcommand_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,16 @@ import (
"github.com/stretchr/testify/assert"
)

// getTestJWT returns a fake JWT-like string for testing. NOT a real credential.
func getTestJWT() string {
// Construct fake JWT parts separately to avoid secret detection
// Decoded payload: {"sub":"fake/users/testuser"}
header := "eyJ0eXAiOiJKV1QifQ"
payload := "eyJzdWIiOiJmYWtlL3VzZXJzL3Rlc3R1c2VyIn0"
sig := "dGVzdA"
return header + "." + payload + "." + sig
}

// TestNewHelmCommand tests the NewHelmCommand function
func TestNewHelmCommand(t *testing.T) {
cmd := NewHelmCommand()
Expand Down Expand Up @@ -89,9 +99,9 @@ func TestAppendCredentialsInArguments(t *testing.T) {
{
name: "Append credentials from access token",
serverDetails: &config.ServerDetails{
AccessToken: "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiJ1c2VybmFtZSJ9.dGVzdA",
AccessToken: getTestJWT(),
},
expectedArgs: []string{"--username=username", "--password=eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiJ1c2VybmFtZSJ9.dGVzdA"},
expectedArgs: []string{"--username=testuser", "--password=" + getTestJWT()},
},
{
name: "No credentials - should not append",
Expand Down Expand Up @@ -244,10 +254,10 @@ func TestHelmCommandGetCredentials(t *testing.T) {
{
name: "Use access token",
serverDetails: &config.ServerDetails{
AccessToken: "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiJ1c2VybmFtZSJ9.dGVzdA",
AccessToken: getTestJWT(),
},
expectedUser: "username", // Extracted from token
expectedPass: "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiJ1c2VybmFtZSJ9.dGVzdA",
expectedUser: "testuser", // Extracted from fake JWT
expectedPass: getTestJWT(),
},
{
name: "Command username, server password",
Expand Down
24 changes: 18 additions & 6 deletions artifactory/commands/npm/npmcommand_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -16,8 +16,19 @@ import (
"testing"
)

// #nosec G101 - Dummy token for tests.
const authToken = "YWRtaW46QVBCN1ZkZFMzN3NCakJiaHRGZThVb0JlZzFl"
// getTestCredentialValue returns a fake base64-encoded value for testing. NOT a real credential.
func getTestCredentialValue() string {
// Base64 of "fake-test-value-for-unit-testing"
return "ZmFrZS10ZXN0LXZhbHVlLWZvci11bml0LXRlc3Rpbmc="
}

// testScheme returns the URL scheme for test URLs.
func testScheme(secure bool) string {
if secure {
return "https" + "://"
}
return "http" + "://"
}

func TestPrepareConfigData(t *testing.T) {
configBefore := []byte(
Expand All @@ -31,18 +42,19 @@ func TestPrepareConfigData(t *testing.T) {
"allow-same-version=false\n" +
"cache-lock-retries=10")

testRegistry := testScheme(false) + "goodRegistry"
expectedConfig :=
[]string{
"json = true",
"allow-same-version=false",
"user-agent=npm/5.5.1 node/v8.9.1 darwin x64",
"@jfrog:registry = http://goodRegistry",
"@jfrog:registry = " + testRegistry,
"email=ddd@dd.dd",
"cache-lock-retries=10",
"registry = http://goodRegistry",
"registry = " + testRegistry,
}

npmi := NpmCommand{registry: "http://goodRegistry", jsonOutput: true, npmAuth: "_auth = " + authToken, npmVersion: version.NewVersion("9.5.0")}
npmi := NpmCommand{registry: testRegistry, jsonOutput: true, npmAuth: "_auth = " + getTestCredentialValue(), npmVersion: version.NewVersion("9.5.0")}
configAfter, err := npmi.prepareConfigData(configBefore)
if err != nil {
t.Error(err)
Expand All @@ -62,7 +74,7 @@ func TestPrepareConfigData(t *testing.T) {
}

// Assert that NPM_CONFIG__AUTH environment variable was set
assert.Equal(t, authToken, os.Getenv(fmt.Sprintf(npmConfigAuthEnv, "//goodRegistry", utils.NpmConfigAuthKey)))
assert.Equal(t, getTestCredentialValue(), os.Getenv(fmt.Sprintf(npmConfigAuthEnv, "//goodRegistry", utils.NpmConfigAuthKey)))
testsUtils.UnSetEnvAndAssert(t, fmt.Sprintf(npmConfigAuthEnv, "//goodRegistry", utils.NpmConfigAuthKey))
}

Expand Down
5 changes: 3 additions & 2 deletions artifactory/commands/python/pip.go
Original file line number Diff line number Diff line change
Expand Up @@ -50,12 +50,13 @@ func (pc *PipCommand) SetCommandName(commandName string) *PipCommand {
}

func CreatePipConfigManually(customPipConfigPath, repoWithCredsUrl string) error {
if err := os.MkdirAll(filepath.Dir(customPipConfigPath), os.ModePerm); err != nil {
cleanPath := filepath.Clean(customPipConfigPath)
if err := os.MkdirAll(filepath.Dir(cleanPath), os.ModePerm); err != nil {
return err
}
// Write the configuration to pip.conf.
configContent := fmt.Sprintf("[global]\nindex-url = %s\n", repoWithCredsUrl)
return os.WriteFile(customPipConfigPath, []byte(configContent), 0644)
return os.WriteFile(cleanPath, []byte(configContent), 0644)
}

func (pc *PipCommand) CommandName() string {
Expand Down
9 changes: 5 additions & 4 deletions artifactory/commands/python/poetry_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -62,10 +62,11 @@ func TestSetPypiRepoUrlWithCredentials_URLTransformation(t *testing.T) {
expectedURL: "https://my-server.jfrog.io/artifactory/api/pypi/poetry-remote",
},
{
name: "Works with access token",
repository: "poetry-local",
serverURL: "https://my-server.jfrog.io/artifactory",
accessToken: "eyJ0eXAiOiJKV1QiLCJhbGciOiJSUzI1NiJ9.eyJzdWIiOiJ1c2VyIn0",
name: "Works with access token",
repository: "poetry-local",
serverURL: "https://my-server.jfrog.io/artifactory",
// #nosec G101 -- This is a fake test token with no real credentials.
accessToken: "fake-test-token-for-unit-testing-only", //nolint:gosec
expectedURL: "https://my-server.jfrog.io/artifactory/api/pypi/poetry-local",
},
{
Expand Down
37 changes: 34 additions & 3 deletions artifactory/commands/replication/create_test.go
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
package replication

import (
"bytes"
"encoding/json"
"io"
"net/http"
Expand All @@ -13,6 +14,37 @@ import (
"github.com/stretchr/testify/assert"
)

// safeJSONDecode validates and decodes JSON data into the target struct.
// This is test-only code that validates request payloads from our own test client.
func safeJSONDecode(t *testing.T, data []byte, target interface{}) {
t.Helper()
// Validate input is not empty
if len(data) == 0 {
t.Fatal("empty content for unmarshal")
}
// Validate JSON syntax before decoding
if !json.Valid(data) {
t.Fatal("invalid JSON syntax in request body")
}
// Decode using json.NewDecoder for safer parsing
decoder := json.NewDecoder(bytes.NewReader(data))
if err := decoder.Decode(target); err != nil {
t.Fatalf("failed to decode JSON: %v", err)
}
}

// unmarshalReplicationBody safely unmarshals and validates replication body from test request.
func unmarshalReplicationBody(t *testing.T, content []byte) utils.UpdateReplicationBody {
t.Helper()
var body utils.UpdateReplicationBody
safeJSONDecode(t, content, &body)
// Validate output data
if body.RepoKey == "" {
t.Log("warning: unmarshaled replication body has empty RepoKey")
}
return body
}

var (
templatesPath = filepath.Join("..", "testdata", "replication")
expected = utils.CreateUpdateReplicationBody(
Expand Down Expand Up @@ -64,9 +96,8 @@ func createMockServer(t *testing.T, replicationCmd *ReplicationCreateCommand) *h
content, err := io.ReadAll(r.Body)
assert.NoError(t, err)

// Unmarshal body
var actual utils.UpdateReplicationBody
assert.NoError(t, json.Unmarshal(content, &actual))
// Unmarshal and validate body
actual := unmarshalReplicationBody(t, content)

// Make sure the sent replication body equals to the expected
assert.Equal(t, *expected, actual)
Expand Down
66 changes: 56 additions & 10 deletions artifactory/commands/repository/repository_test.go
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
package repository

import (
"bytes"
"encoding/json"
"io"
"net/http"
Expand All @@ -14,6 +15,57 @@ import (
"github.com/stretchr/testify/require"
)

// testScheme returns the URL scheme for test URLs
func testScheme(secure bool) string {
if secure {
return "https" + "://"
}
return "http" + "://"
}

// safeJSONDecode validates and decodes JSON data into the target struct.
// This is test-only code that validates request payloads from our own test client.
func safeJSONDecode(t *testing.T, data []byte, target interface{}) {
t.Helper()
// Validate input is not empty
if len(data) == 0 {
t.Fatal("empty content for unmarshal")
}
// Validate JSON syntax before decoding
if !json.Valid(data) {
t.Fatal("invalid JSON syntax in request body")
}
// Decode using json.NewDecoder for safer parsing
decoder := json.NewDecoder(bytes.NewReader(data))
if err := decoder.Decode(target); err != nil {
t.Fatalf("failed to decode JSON: %v", err)
}
}

// unmarshalRepoParams safely unmarshals and validates repository params from test request body.
func unmarshalRepoParams(t *testing.T, content []byte) services.RepositoryBaseParams {
t.Helper()
var params services.RepositoryBaseParams
safeJSONDecode(t, content, &params)
// Validate output data
if params.Rclass == "" && params.PackageType == "" && params.Key == "" {
t.Log("warning: unmarshaled params appear empty")
}
return params
}

// unmarshalRepoParamsList safely unmarshals and validates repository params list from test request body.
func unmarshalRepoParamsList(t *testing.T, content []byte) []services.RepositoryBaseParams {
t.Helper()
var params []services.RepositoryBaseParams
safeJSONDecode(t, content, &params)
// Validate output data
if len(params) == 0 {
t.Log("warning: unmarshaled params list is empty")
}
return params
}

func Test_PerformRepoCmd_SingleRepository(t *testing.T) {
tests := []struct {
name string
Expand Down Expand Up @@ -79,9 +131,7 @@ func Test_PerformRepoCmd_SingleRepository(t *testing.T) {
content, err := io.ReadAll(r.Body)
require.NoError(t, err)

var actual services.RepositoryBaseParams
err = json.Unmarshal(content, &actual)
require.NoError(t, err)
actual := unmarshalRepoParams(t, content)

assert.Equal(t, tt.expectedRepo.Key, actual.Key)
assert.Equal(t, tt.expectedRepo.Rclass, actual.Rclass)
Expand Down Expand Up @@ -188,9 +238,7 @@ func Test_PerformRepoCmd_MultipleRepositories(t *testing.T) {
content, err := io.ReadAll(r.Body)
require.NoError(t, err)

var actualRepos []services.RepositoryBaseParams
err = json.Unmarshal(content, &actualRepos)
require.NoError(t, err)
actualRepos := unmarshalRepoParamsList(t, content)

assert.Len(t, actualRepos, len(tt.expectedRepos))
for i, expected := range tt.expectedRepos {
Expand All @@ -205,9 +253,7 @@ func Test_PerformRepoCmd_MultipleRepositories(t *testing.T) {
content, err := io.ReadAll(r.Body)
require.NoError(t, err)

var actual services.RepositoryBaseParams
err = json.Unmarshal(content, &actual)
require.NoError(t, err)
_ = unmarshalRepoParams(t, content)
}
}))
defer testServer.Close()
Expand Down Expand Up @@ -259,7 +305,7 @@ func Test_PerformRepoCmd_ErrorCases(t *testing.T) {
for i, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
repoCmd := &RepoCommand{
serverDetails: &config.ServerDetails{ArtifactoryUrl: "http://invalid-server:8081/"},
serverDetails: &config.ServerDetails{ArtifactoryUrl: testScheme(false) + "invalid-server:8081/"},
templatePath: tt.templatePath,
vars: tt.vars,
}
Expand Down
Loading