diff --git a/sca/bom/buildinfo/buildinfobom.go b/sca/bom/buildinfo/buildinfobom.go index bc0c7be4f..3c5865b09 100644 --- a/sca/bom/buildinfo/buildinfobom.go +++ b/sca/bom/buildinfo/buildinfobom.go @@ -157,12 +157,12 @@ func (b *BuildInfoBomGenerator) buildDependencyTree(scan results.ScanTarget) (*D hasAnyTree := false for _, tech := range techs { log.Debug(fmt.Sprintf("Generating '%s' dependency tree for '%s'...", tech.ToFormal(), scan.Target)) - serverDetails, err := SetResolutionRepoInParamsIfExists(&b.params, tech) + techParams, serverDetails, err := b.resolveTechParams(tech) if err != nil { buildErr = errors.Join(buildErr, fmt.Errorf("failed to set resolution repo in params: %w", err)) continue } - treeResult, err := GetTechDependencyTree(b.params, serverDetails, tech) + treeResult, err := GetTechDependencyTree(techParams, serverDetails, tech) if err != nil { buildErr = errors.Join(buildErr, fmt.Errorf("failed while building '%s' dependency tree: %w", tech, err)) continue @@ -183,6 +183,16 @@ func (b *BuildInfoBomGenerator) buildDependencyTree(scan results.ScanTarget) (*D return merged, buildErr } +func (b *BuildInfoBomGenerator) resolveTechParams(tech techutils.Technology) (techParams technologies.BuildInfoBomGeneratorParams, serverDetails *config.ServerDetails, err error) { + techParams = b.params + if b.params.ServerDetails != nil { + copied := *b.params.ServerDetails + techParams.ServerDetails = &copied + } + serverDetails, err = SetResolutionRepoInParamsIfExists(&techParams, tech) + return +} + func mergeResults(existing, additional *DependencyTreeResult) *DependencyTreeResult { if additional == nil { return existing diff --git a/sca/bom/buildinfo/buildinfobom_test.go b/sca/bom/buildinfo/buildinfobom_test.go index 5c64592af..75b45fa16 100644 --- a/sca/bom/buildinfo/buildinfobom_test.go +++ b/sca/bom/buildinfo/buildinfobom_test.go @@ -2,16 +2,86 @@ package buildinfo import ( "fmt" + "os" + "path/filepath" "testing" "github.com/CycloneDX/cyclonedx-go" + "github.com/jfrog/jfrog-cli-core/v2/utils/config" + coreutils "github.com/jfrog/jfrog-cli-core/v2/utils/coreutils" + "github.com/jfrog/jfrog-cli-security/sca/bom/buildinfo/technologies" "github.com/jfrog/jfrog-cli-security/utils/results" + "github.com/jfrog/jfrog-cli-security/utils/techutils" xrayUtils "github.com/jfrog/jfrog-client-go/xray/services/utils" "github.com/stretchr/testify/assert" "github.com/stretchr/testify/require" ) +func isolateResolverConfig(t *testing.T) string { + t.Helper() + t.Setenv(coreutils.HomeDir, t.TempDir()) + dummyHome := t.TempDir() + t.Setenv("HOME", dummyHome) + t.Setenv("USERPROFILE", dummyHome) + require.NoError(t, config.SaveServersConf([]*config.ServerDetails{{ + ServerId: "test", + Url: "http://localhost/", + ArtifactoryUrl: "http://localhost/artifactory/", + }})) + projectRoot := t.TempDir() + originalCwd, err := os.Getwd() + require.NoError(t, err) + require.NoError(t, os.Chdir(projectRoot)) + t.Cleanup(func() { require.NoError(t, os.Chdir(originalCwd)) }) + return projectRoot +} + +func TestResolveTechParams(t *testing.T) { + t.Run("isolates DependenciesRepository per technology", func(t *testing.T) { + projectRoot := isolateResolverConfig(t) + projectsDir := filepath.Join(projectRoot, ".jfrog", "projects") + require.NoError(t, os.MkdirAll(projectsDir, 0o755)) + require.NoError(t, os.WriteFile(filepath.Join(projectsDir, "go.yaml"), + []byte("version: 1\ntype: go\nresolver:\n serverId: test\n repo: go-vir\n"), 0o644)) + require.NoError(t, os.WriteFile(filepath.Join(projectsDir, "npm.yaml"), + []byte("version: 1\ntype: npm\nresolver:\n serverId: test\n repo: npm-remote\n"), 0o644)) + + generator := NewBuildInfoBomGenerator() + + goParams, _, err := generator.resolveTechParams(techutils.Go) + require.NoError(t, err) + assert.Equal(t, "go-vir", goParams.DependenciesRepository) + + npmParams, _, err := generator.resolveTechParams(techutils.Npm) + require.NoError(t, err) + assert.Equal(t, "npm-remote", npmParams.DependenciesRepository) + assert.Empty(t, generator.params.DependenciesRepository) + }) + + t.Run("isolates ServerDetails from in-place mutation", func(t *testing.T) { + shared := &config.ServerDetails{ + ServerId: "test", + Url: "http://localhost/", + ArtifactoryUrl: "http://localhost/artifactory/", + } + generator := NewBuildInfoBomGenerator() + generator.params = technologies.BuildInfoBomGeneratorParams{ + ServerDetails: shared, + DependenciesRepository: "cli-deps-repo", + } + + techParams, _, err := generator.resolveTechParams(techutils.Nuget) + require.NoError(t, err) + require.NotNil(t, techParams.ServerDetails) + assert.NotSame(t, shared, techParams.ServerDetails) + + techParams.ServerDetails.ArtifactoryUrl += "api/curation/audit" + assert.Equal(t, "http://localhost/artifactory/", shared.ArtifactoryUrl) + assert.Equal(t, "http://localhost/artifactory/", generator.params.ServerDetails.ArtifactoryUrl) + }) +} + func TestMergeResults(t *testing.T) { nodeA := &xrayUtils.GraphNode{Id: "npm://a:1"} nodeB := &xrayUtils.GraphNode{Id: "gav://b:2"}