From 88655cbe3c0badae76b20ad12fc59ee3291515f6 Mon Sep 17 00:00:00 2001 From: Bassel Mbariky Date: Wed, 25 Feb 2026 12:57:39 +0200 Subject: [PATCH 1/6] Added-install-for-curation-aduit --- cli/docs/flags.go | 4 +- cli/scancommands.go | 2 + commands/curation/curationaudit.go | 50 +++++ commands/curation/curationaudit_test.go | 121 ++++++++++ commands/curation/install.go | 156 +++++++++++++ commands/curation/install_test.go | 209 ++++++++++++++++++ sca/bom/buildinfo/technologies/common.go | 6 + sca/bom/buildinfo/technologies/go/golang.go | 26 ++- sca/bom/buildinfo/technologies/go/install.go | 50 +++++ .../buildinfo/technologies/go/install_test.go | 158 +++++++++++++ sca/bom/buildinfo/technologies/npm/install.go | 71 ++++++ .../technologies/npm/install_test.go | 191 ++++++++++++++++ 12 files changed, 1035 insertions(+), 9 deletions(-) create mode 100644 commands/curation/install.go create mode 100644 commands/curation/install_test.go create mode 100644 sca/bom/buildinfo/technologies/go/install.go create mode 100644 sca/bom/buildinfo/technologies/go/install_test.go create mode 100644 sca/bom/buildinfo/technologies/npm/install.go create mode 100644 sca/bom/buildinfo/technologies/npm/install_test.go diff --git a/cli/docs/flags.go b/cli/docs/flags.go index 8e74a45c4..b3ad301d8 100644 --- a/cli/docs/flags.go +++ b/cli/docs/flags.go @@ -157,6 +157,7 @@ const ( DockerImageName = "image" SolutionPath = "solution-path" IncludeCachedPackages = "include-cached-packages" + InstallPackage = "install" // Unique git flags InputFile = "input-file" @@ -215,7 +216,7 @@ var commandFlags = map[string][]string{ StaticSca, XrayLibPluginBinaryCustomPath, AnalyzerManagerCustomPath, AddSastRules, }, CurationAudit: { - CurationOutput, WorkingDirs, Threads, RequirementsFile, InsecureTls, useWrapperAudit, UseIncludedBuilds, SolutionPath, DockerImageName, IncludeCachedPackages, + CurationOutput, WorkingDirs, Threads, RequirementsFile, InsecureTls, useWrapperAudit, UseIncludedBuilds, SolutionPath, DockerImageName, IncludeCachedPackages, InstallPackage, }, GitCountContributors: { InputFile, ScmType, ScmApiUrl, Token, Owner, RepoName, Months, DetailedSummary, InsecureTls, @@ -333,6 +334,7 @@ var flagsMap = map[string]components.Flag{ CurationOutput: components.NewStringFlag(OutputFormat, "Defines the output format of the command. Acceptable values are: table, json.", components.WithStrDefaultValue("table")), SolutionPath: components.NewStringFlag(SolutionPath, "Path to the .NET solution file (.sln) to use when multiple solution files are present in the directory."), IncludeCachedPackages: components.NewBoolFlag(IncludeCachedPackages, "When set to true, the system will audit cached packages. This configuration is mandatory for Curation on-demand workflows, which rely on package caching."), + InstallPackage: components.NewStringFlag(InstallPackage, "Run curation audit on a specific package and its transitive dependencies. Format: @ (e.g. express@4.18.2 for npm, github.com/pkg/errors@v0.9.1 for Go)."), binarySca: components.NewBoolFlag(Sca, fmt.Sprintf("Selective scanners mode: Execute SCA (Software Composition Analysis) sub-scan. Use --%s to run both SCA and Contextual Analysis. Use --%s --%s to to run SCA. Can be combined with --%s.", Sca, Sca, WithoutCA, Secrets)), binarySecrets: components.NewBoolFlag(Secrets, fmt.Sprintf("Selective scanners mode: Execute Secrets sub-scan. Can be combined with --%s.", Sca)), binaryWithoutCA: components.NewBoolFlag(WithoutCA, fmt.Sprintf("Selective scanners mode: Disable Contextual Analysis scanner after SCA. Relevant only with --%s flag.", Sca)), diff --git a/cli/scancommands.go b/cli/scancommands.go index df549e023..4f8086bca 100644 --- a/cli/scancommands.go +++ b/cli/scancommands.go @@ -22,6 +22,7 @@ import ( flags "github.com/jfrog/jfrog-cli-security/cli/docs" auditSpecificDocs "github.com/jfrog/jfrog-cli-security/cli/docs/auditspecific" enrichDocs "github.com/jfrog/jfrog-cli-security/cli/docs/enrich" + // maliciousScanDocs "github.com/jfrog/jfrog-cli-security/cli/docs/maliciousscan" mcpDocs "github.com/jfrog/jfrog-cli-security/cli/docs/mcp" auditDocs "github.com/jfrog/jfrog-cli-security/cli/docs/scan/audit" @@ -703,6 +704,7 @@ func getCurationCommand(c *components.Context) (*curation.CurationAuditCommand, SetSolutionFilePath(c.GetStringFlagValue(flags.SolutionPath)) curationAuditCommand.SetDockerImageName(c.GetStringFlagValue(flags.DockerImageName)) curationAuditCommand.SetIncludeCachedPackages(c.GetBoolFlagValue(flags.IncludeCachedPackages)) + curationAuditCommand.SetInstallPackage(c.GetStringFlagValue(flags.InstallPackage)) return curationAuditCommand, nil } diff --git a/commands/curation/curationaudit.go b/commands/curation/curationaudit.go index ab066d8e9..48099cf12 100644 --- a/commands/curation/curationaudit.go +++ b/commands/curation/curationaudit.go @@ -224,6 +224,7 @@ type CurationAuditCommand struct { parallelRequests int dockerImageName string includeCachedPackages bool + installPackage string audit.AuditParamsInterface } @@ -274,7 +275,15 @@ func (ca *CurationAuditCommand) SetIncludeCachedPackages(includeCachedPackages b return ca } +func (ca *CurationAuditCommand) SetInstallPackage(spec string) *CurationAuditCommand { + ca.installPackage = spec + return ca +} + func (ca *CurationAuditCommand) Run() (err error) { + if ca.installPackage != "" { + return ca.runInstallMode() + } rootDir, err := os.Getwd() if err != nil { return errorutils.CheckError(err) @@ -443,6 +452,7 @@ func (ca *CurationAuditCommand) getBuildInfoParamsByTech() (technologies.BuildIn InstallCommandArgs: ca.InstallCommandArgs(), // Curation params IsCurationCmd: true, + IsInstallMode: ca.installPackage != "", // Java params IsMavenDepTreeInstalled: true, UseWrapper: ca.UseWrapper(), @@ -475,6 +485,9 @@ func (ca *CurationAuditCommand) auditTree(tech techutils.Technology, results map if len(depTreeResult.FullDepTrees) == 0 { return errorutils.CheckErrorf("found no dependencies for the audited project using '%v' as the package manager", tech.String()) } + if params.IsInstallMode && ca.installPackage != "" { + filterTreeForInstallPackage(&depTreeResult, tech, ca.installPackage) + } rtManager, serverDetails, err := ca.getRtManagerAndAuth(tech) if err != nil { return err @@ -542,6 +555,43 @@ func (ca *CurationAuditCommand) auditTree(tech techutils.Technology, results map return err } +// filterTreeForInstallPackage modifies depTreeResult in place to keep only the +// install package and its transitive dependencies. This ensures that in --install mode +// we only audit the new package, not the customer's existing dependencies. +func filterTreeForInstallPackage(depTreeResult *buildinfo.DependencyTreeResult, tech techutils.Technology, installPkgName string) { + prefix := tech.GetXrayPackageTypeId() + installPkgName + ":" + for _, tree := range depTreeResult.FullDepTrees { + for _, child := range tree.Nodes { + if strings.HasPrefix(child.Id, prefix) { + tree.Nodes = []*xrayUtils.GraphNode{child} + break + } + } + } + reachable := map[string]struct{}{} + for _, tree := range depTreeResult.FullDepTrees { + reachable[tree.Id] = struct{}{} + collectReachableNodes(tree, reachable) + } + var filteredFlat []*xrayUtils.GraphNode + for _, node := range depTreeResult.FlatTree.Nodes { + if _, ok := reachable[node.Id]; ok { + filteredFlat = append(filteredFlat, node) + } + } + depTreeResult.FlatTree.Nodes = filteredFlat +} + +func collectReachableNodes(node *xrayUtils.GraphNode, reachable map[string]struct{}) { + for _, child := range node.Nodes { + if _, seen := reachable[child.Id]; seen { + continue + } + reachable[child.Id] = struct{}{} + collectReachableNodes(child, reachable) + } +} + func getSelectedPackages(requestedRows string, blockedPackages []*PackageStatus) (selectedPackages []*PackageStatus, ok bool) { // Accepts the following formats: "all", or a comma-separated list of row numbers, or ranges of row numbers." validFormat := regexp.MustCompile(`^(all|(\d+(-\d+)?)(,\d+(-\d+)?)*$)`) diff --git a/commands/curation/curationaudit_test.go b/commands/curation/curationaudit_test.go index 461e664ec..9ba36e61e 100644 --- a/commands/curation/curationaudit_test.go +++ b/commands/curation/curationaudit_test.go @@ -16,6 +16,7 @@ import ( "sync" "testing" + "github.com/jfrog/jfrog-cli-security/sca/bom/buildinfo" "github.com/jfrog/jfrog-cli-security/sca/bom/buildinfo/technologies/java" "github.com/jfrog/jfrog-cli-security/utils/formats" @@ -545,6 +546,9 @@ func createCurationCmdAndRun(tt testCase) (cmdResults map[string]*CurationReport curationCmd.SetInsecureTls(true) curationCmd.SetIgnoreConfigFile(tt.shouldIgnoreConfigFile) curationCmd.SetInsecureTls(tt.allowInsecureTls) + if tt.installPackage != "" { + curationCmd.SetInstallPackage(tt.installPackage) + } cmdResults = map[string]*CurationReport{} err = curationCmd.doCurateAudit(cmdResults) return @@ -600,6 +604,7 @@ type testCase struct { tech techutils.Technology createServerWithoutCreds bool allowInsecureTls bool + installPackage string } func (tc testCase) getPathToTests() string { @@ -1636,3 +1641,119 @@ func TestSendWaiverRequests(t *testing.T) { }) } } + +func TestFilterTreeForInstallPackage(t *testing.T) { + tests := []struct { + name string + installPkgName string + tech techutils.Technology + tree *xrayUtils.GraphNode + flatNodes []*xrayUtils.GraphNode + expectedChildren []string + expectedFlatCount int + }{ + { + name: "npm - keep install package and transitives, drop others", + installPkgName: "express", + tech: techutils.Npm, + tree: &xrayUtils.GraphNode{ + Id: "npm://my-project:1.0.0", + Nodes: []*xrayUtils.GraphNode{ + {Id: "npm://react:18.0.0"}, + { + Id: "npm://express:4.18.2", + Nodes: []*xrayUtils.GraphNode{ + { + Id: "npm://body-parser:1.20.1", + Nodes: []*xrayUtils.GraphNode{ + {Id: "npm://raw-body:2.5.1"}, + }, + }, + {Id: "npm://cookie:0.5.0"}, + }, + }, + {Id: "npm://lodash:4.17.21"}, + }, + }, + flatNodes: []*xrayUtils.GraphNode{ + {Id: "npm://react:18.0.0"}, + {Id: "npm://express:4.18.2"}, + {Id: "npm://body-parser:1.20.1"}, + {Id: "npm://raw-body:2.5.1"}, + {Id: "npm://cookie:0.5.0"}, + {Id: "npm://lodash:4.17.21"}, + }, + expectedChildren: []string{"npm://express:4.18.2"}, + expectedFlatCount: 4, + }, + { + name: "go - keep install package and deep transitives", + installPkgName: "rsc.io/quote", + tech: techutils.Go, + tree: &xrayUtils.GraphNode{ + Id: "go://my-project", + Nodes: []*xrayUtils.GraphNode{ + {Id: "go://github.com/existing/dep:v1.0.0"}, + { + Id: "go://rsc.io/quote:v1.5.2", + Nodes: []*xrayUtils.GraphNode{ + { + Id: "go://rsc.io/sampler:v1.3.0", + Nodes: []*xrayUtils.GraphNode{ + {Id: "go://golang.org/x/text:v0.0.0-20170915032832-14c0d48ead0c"}, + }, + }, + }, + }, + }, + }, + flatNodes: []*xrayUtils.GraphNode{ + {Id: "go://github.com/existing/dep:v1.0.0"}, + {Id: "go://rsc.io/quote:v1.5.2"}, + {Id: "go://rsc.io/sampler:v1.3.0"}, + {Id: "go://golang.org/x/text:v0.0.0-20170915032832-14c0d48ead0c"}, + }, + expectedChildren: []string{"go://rsc.io/quote:v1.5.2"}, + expectedFlatCount: 3, + }, + { + name: "npm - no match leaves tree unchanged", + installPkgName: "nonexistent", + tech: techutils.Npm, + tree: &xrayUtils.GraphNode{ + Id: "npm://my-project:1.0.0", + Nodes: []*xrayUtils.GraphNode{ + {Id: "npm://react:18.0.0"}, + {Id: "npm://lodash:4.17.21"}, + }, + }, + flatNodes: []*xrayUtils.GraphNode{ + {Id: "npm://react:18.0.0"}, + {Id: "npm://lodash:4.17.21"}, + }, + expectedChildren: []string{"npm://react:18.0.0", "npm://lodash:4.17.21"}, + expectedFlatCount: 2, + }, + } + + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + depTreeResult := &buildinfo.DependencyTreeResult{ + FullDepTrees: []*xrayUtils.GraphNode{tt.tree}, + FlatTree: &xrayUtils.GraphNode{Nodes: tt.flatNodes}, + } + + filterTreeForInstallPackage(depTreeResult, tt.tech, tt.installPkgName) + + // Verify the filtered direct children + var childIds []string + for _, child := range depTreeResult.FullDepTrees[0].Nodes { + childIds = append(childIds, child.Id) + } + assert.Equal(t, tt.expectedChildren, childIds) + + // Verify the flat tree only contains reachable nodes + assert.Equal(t, tt.expectedFlatCount, len(depTreeResult.FlatTree.Nodes)) + }) + } +} diff --git a/commands/curation/install.go b/commands/curation/install.go new file mode 100644 index 000000000..f3f5c3ddb --- /dev/null +++ b/commands/curation/install.go @@ -0,0 +1,156 @@ +package curation + +import ( + "errors" + "fmt" + "os" + "path/filepath" + "strings" + + "github.com/jfrog/jfrog-client-go/utils/errorutils" + "github.com/jfrog/jfrog-client-go/utils/log" + + "github.com/jfrog/jfrog-cli-security/sca/bom/buildinfo/technologies" + gotech "github.com/jfrog/jfrog-cli-security/sca/bom/buildinfo/technologies/go" + "github.com/jfrog/jfrog-cli-security/sca/bom/buildinfo/technologies/npm" + "github.com/jfrog/jfrog-cli-security/utils" + "github.com/jfrog/jfrog-cli-security/utils/results/output" + "github.com/jfrog/jfrog-cli-security/utils/techutils" +) + +type PackageInstallHandler interface { + ParsePackageSpec(spec string) (technologies.InstalledPackage, error) + CreateTempProject(projectDir, tempDir, pkgName, pkgVersion string) error + Technology() techutils.Technology +} + +var installHandlers = map[techutils.Technology]PackageInstallHandler{ + techutils.Npm: &npm.NpmInstallHandler{}, + techutils.Go: &gotech.GoInstallHandler{}, +} + +func (ca *CurationAuditCommand) runInstallMode() (err error) { + originalDir, err := os.Getwd() + if err != nil { + return errorutils.CheckError(err) + } + + projectDir := originalDir + if len(ca.workingDirs) > 0 { + projectDir, err = filepath.Abs(ca.workingDirs[0]) + if err != nil { + return errorutils.CheckError(err) + } + if err = os.Chdir(projectDir); err != nil { + return errorutils.CheckError(err) + } + defer func() { + if e := errorutils.CheckError(os.Chdir(originalDir)); err == nil { + err = e + } + }() + } + + handler, err := detectInstallHandler() + if err != nil { + return err + } + tech := handler.Technology() + pkg, parseErr := handler.ParsePackageSpec(ca.installPackage) + if parseErr != nil { + return parseErr + } + ca.installPackage = pkg.Name + log.Info(fmt.Sprintf("Running curation audit for %s package %s@%s", tech.ToFormal(), pkg.Name, pkg.Version)) + + if err = ca.SetRepo(tech); err != nil { + return err + } + ca.AuditParamsInterface = ca.SetDepsRepo(ca.PackageManagerConfig.TargetRepo()) + + tempDir, err := os.MkdirTemp("", "curation-install-*") + if err != nil { + return errorutils.CheckError(err) + } + defer func() { + if removeErr := os.RemoveAll(tempDir); removeErr != nil { + log.Warn(fmt.Sprintf("Failed to remove temporary directory %s: %s", tempDir, removeErr.Error())) + } + }() + + if err = handler.CreateTempProject(projectDir, tempDir, pkg.Name, pkg.Version); err != nil { + return err + } + + if err = os.Chdir(tempDir); err != nil { + return errorutils.CheckError(err) + } + defer func() { + if e := errorutils.CheckError(os.Chdir(projectDir)); err == nil { + err = e + } + }() + + results := map[string]*CurationReport{} + auditErr := ca.doCurateAudit(results) + + if ca.Progress() != nil { + err = errors.Join(err, ca.Progress().Quit()) + } + + hasBlockedPackages := false + for _, report := range results { + if len(report.packagesStatus) > 0 { + hasBlockedPackages = true + break + } + } + + for projectPath, report := range results { + err = errors.Join(err, printResult(ca.OutputFormat(), projectPath, report.packagesStatus)) + } + + if hasBlockedPackages { + for _, report := range results { + for _, ps := range report.packagesStatus { + if ps.WaiverAllowed && !utils.IsCI() { + err = errors.Join(err, ca.requestWaiver(report.packagesStatus)) + break + } + } + } + } + + err = errors.Join(err, output.RecordSecurityCommandSummary(output.NewCurationSummary(convertResultsToSummary(results)))) + + if auditErr != nil { + return errors.Join(err, auditErr) + } + + if hasBlockedPackages { + return err + } + + return err +} + +func detectInstallHandler() (PackageInstallHandler, error) { + detectedTechs := techutils.DetectedTechnologiesList() + for _, tech := range detectedTechs { + if handler, ok := installHandlers[techutils.Technology(tech)]; ok { + return handler, nil + } + } + supportedTechs := getSupportedInstallTechnologies() + return nil, errorutils.CheckErrorf( + "could not detect a supported technology in the current directory for --install. Currently supported: %s", + strings.Join(supportedTechs, ", ")) +} + +func getSupportedInstallTechnologies() []string { + techs := make([]string, 0, len(installHandlers)) + for tech := range installHandlers { + techs = append(techs, tech.ToFormal()) + } + return techs +} diff --git a/commands/curation/install_test.go b/commands/curation/install_test.go new file mode 100644 index 000000000..feff9086b --- /dev/null +++ b/commands/curation/install_test.go @@ -0,0 +1,209 @@ +package curation + +import ( + "fmt" + "os" + "path/filepath" + "strings" + "testing" + + "github.com/jfrog/jfrog-cli-security/utils/techutils" + + gotech "github.com/jfrog/jfrog-cli-security/sca/bom/buildinfo/technologies/go" + "github.com/jfrog/jfrog-cli-security/sca/bom/buildinfo/technologies/npm" + "github.com/stretchr/testify/assert" + "github.com/stretchr/testify/require" +) + +func TestDetectInstallHandler(t *testing.T) { + originalDir, err := os.Getwd() + require.NoError(t, err) + defer func() { require.NoError(t, os.Chdir(originalDir)) }() + + tests := []struct { + name string + files map[string]string + expectedType interface{} + expectError bool + errorContains string + }{ + { + name: "detect npm from package.json", + files: map[string]string{ + "package.json": `{"name": "test-project", "version": "1.0.0"}`, + }, + expectedType: &npm.NpmInstallHandler{}, + }, + { + name: "detect go from go.mod", + files: map[string]string{ + "go.mod": "module example.com/test\n\ngo 1.21\n", + }, + expectedType: &gotech.GoInstallHandler{}, + }, + { + name: "no supported technology detected", + files: map[string]string{}, + expectError: true, + errorContains: "could not detect a supported technology", + }, + { + name: "unsupported technology only (e.g. Python)", + files: map[string]string{ + "requirements.txt": "flask==2.0.0\n", + }, + expectError: true, + errorContains: "could not detect a supported technology", + }, + } + + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + tempDir := t.TempDir() + for name, content := range tt.files { + require.NoError(t, os.WriteFile(filepath.Join(tempDir, name), []byte(content), 0644)) + } + require.NoError(t, os.Chdir(tempDir)) + + handler, err := detectInstallHandler() + + if tt.expectError { + assert.Error(t, err) + assert.Nil(t, handler) + if tt.errorContains != "" { + assert.Contains(t, err.Error(), tt.errorContains) + } + } else { + assert.NoError(t, err) + assert.IsType(t, tt.expectedType, handler) + } + }) + } +} + +func TestGetSupportedInstallTechnologies(t *testing.T) { + techs := getSupportedInstallTechnologies() + assert.Len(t, techs, len(installHandlers)) + for tech := range installHandlers { + assert.Contains(t, techs, tech.ToFormal()) + } +} + +func TestInstallModeFiltersAuditResults(t *testing.T) { + basePathToTests, err := filepath.Abs(TestDataDir) + require.NoError(t, err) + + cleanUpFlags := setCurationFlagsForTest(t) + defer cleanUpFlags() + + tests := []testCase{ + { + name: "npm install mode - only audits installed package", + tech: techutils.Npm, + pathToProject: filepath.Join("projects", "package-managers", "npm", "npm-project"), + shouldIgnoreConfigFile: true, + installPackage: "underscore@1.13.6", + expectedRequest: map[string]bool{ + "/api/npm/npms/underscore/-/underscore-1.13.6.tgz": false, + }, + requestToFail: map[string]bool{ + "/api/npm/npms/underscore/-/underscore-1.13.6.tgz": false, + }, + expectedResp: map[string]*CurationReport{ + "npm_test:1.0.0": { + packagesStatus: []*PackageStatus{ + { + Action: "blocked", + ParentVersion: "1.13.6", + ParentName: "underscore", + BlockedPackageUrl: "/api/npm/npms/underscore/-/underscore-1.13.6.tgz", + PackageName: "underscore", + PackageVersion: "1.13.6", + BlockingReason: "Policy violations", + PkgType: "npm", + DepRelation: "direct", + Policy: []Policy{ + { + Policy: "pol1", + Condition: "cond1", + }, + }, + }, + }, + totalNumberOfPackages: 1, + }, + }, + }, + { + name: "go install mode - only audits installed package and its transitives", + tech: techutils.Go, + pathToProject: filepath.Join("projects", "package-managers", "go", "curation-project"), + createServerWithoutCreds: true, + installPackage: "rsc.io/quote@v1.5.2", + serveResources: map[string]string{ + "v1.5.2.mod": filepath.Join("resources", "quote-v1.5.2.mod"), + "v1.5.2.zip": filepath.Join("resources", "quote-v1.5.2.zip"), + "v1.5.2.info": filepath.Join("resources", "quote-v1.5.2.info"), + "v1.3.0.mod": filepath.Join("resources", "sampler-v1.3.0.mod"), + "v1.3.0.zip": filepath.Join("resources", "sampler-v1.3.0.zip"), + "v1.3.0.info": filepath.Join("resources", "sampler-v1.3.0.info"), + "v0.0.0-20170915032832-14c0d48ead0c.mod": filepath.Join("resources", "text-v0.0.0-20170915032832-14c0d48ead0c.mod"), + "v0.0.0-20170915032832-14c0d48ead0c.zip": filepath.Join("resources", "text-v0.0.0-20170915032832-14c0d48ead0c.zip"), + "v0.0.0-20170915032832-14c0d48ead0c.info": filepath.Join("resources", "text-v0.0.0-20170915032832-14c0d48ead0c.info"), + }, + requestToFail: map[string]bool{ + "/api/go/go-virtual/rsc.io/sampler/@v/v1.3.0.zip": false, + }, + expectedResp: map[string]*CurationReport{ + "github.com/you/hello": { + packagesStatus: []*PackageStatus{ + { + Action: "blocked", + ParentName: "rsc.io/quote", + ParentVersion: "v1.5.2", + BlockedPackageUrl: "/api/go/go-virtual/rsc.io/sampler/@v/v1.3.0.zip", + PackageName: "rsc.io/sampler", + PackageVersion: "v1.3.0", + BlockingReason: "Policy violations", + DepRelation: "indirect", + PkgType: "go", + Policy: []Policy{ + { + Policy: "pol1", + Condition: "cond1", + }, + }, + }, + }, + totalNumberOfPackages: 3, + }, + }, + }, + } + + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + mockServer, serverConfig := curationServer(t, tt.expectedBuildRequest, tt.expectedRequest, tt.requestToFail, tt.requestToError, tt.serveResources) + defer mockServer.Close() + + cleanUp := createCurationTestEnv(t, basePathToTests, tt, serverConfig) + defer cleanUp() + + results, err := createCurationCmdAndRun(tt) + assert.NoError(t, err) + + for key := range tt.expectedResp { + for index := range tt.expectedResp[key].packagesStatus { + tt.expectedResp[key].packagesStatus[index].BlockedPackageUrl = fmt.Sprintf("%s%s", + strings.TrimSuffix(serverConfig.GetArtifactoryUrl(), "/"), + tt.expectedResp[key].packagesStatus[index].BlockedPackageUrl) + } + } + assert.Equal(t, tt.expectedResp, results) + + for _, requestDone := range tt.expectedRequest { + assert.True(t, requestDone) + } + }) + } +} diff --git a/sca/bom/buildinfo/technologies/common.go b/sca/bom/buildinfo/technologies/common.go index 28180c75c..05455c04b 100644 --- a/sca/bom/buildinfo/technologies/common.go +++ b/sca/bom/buildinfo/technologies/common.go @@ -32,6 +32,11 @@ const ( var CurationErrorMsgToUserTemplate = "Failed to retrieve the dependencies tree for the %s project. Please contact your " + "Artifactory administrator to verify pass-through for Curation audit is enabled for your project" +type InstalledPackage struct { + Name string + Version string +} + type BuildInfoBomGeneratorParams struct { XrayVersion string Progress ioUtils.ProgressMgr @@ -49,6 +54,7 @@ type BuildInfoBomGeneratorParams struct { InstallCommandArgs []string // Curation params IsCurationCmd bool + IsInstallMode bool // Java params IsMavenDepTreeInstalled bool UseWrapper bool diff --git a/sca/bom/buildinfo/technologies/go/golang.go b/sca/bom/buildinfo/technologies/go/golang.go index 59cbe9431..e314977e4 100644 --- a/sca/bom/buildinfo/technologies/go/golang.go +++ b/sca/bom/buildinfo/technologies/go/golang.go @@ -55,10 +55,12 @@ func BuildDependencyTree(params technologies.BuildInfoBomGeneratorParams) (depen if err != nil || len(dependenciesGraph) == 0 { return } - // Calculate go dependencies list - dependenciesList, err := getDependenciesList(currentDir, handleCurationGoError) - if err != nil { - return + var dependenciesList map[string]bool + if !params.IsInstallMode { + dependenciesList, err = getDependenciesList(currentDir, handleCurationGoError) + if err != nil { + return + } } // Get root module name rootModuleName, err := goutils.GetModuleName(currentDir) @@ -71,7 +73,7 @@ func BuildDependencyTree(params technologies.BuildInfoBomGeneratorParams) (depen Nodes: []*xrayUtils.GraphNode{}, } uniqueDepsSet := datastructures.MakeSet[string]() - populateGoDependencyTree(rootNode, dependenciesGraph, dependenciesList, uniqueDepsSet) + populateGoDependencyTree(rootNode, dependenciesGraph, dependenciesList, uniqueDepsSet, params.IsInstallMode) // In case of curation command, go version is not relevant as it can't be resolved from go repo if !params.IsCurationCmd { @@ -106,7 +108,7 @@ func handleCurationGoError(err error) (bool, error) { return false, nil } -func populateGoDependencyTree(currNode *xrayUtils.GraphNode, dependenciesGraph map[string][]string, dependenciesList map[string]bool, uniqueDepsSet *datastructures.Set[string]) { +func populateGoDependencyTree(currNode *xrayUtils.GraphNode, dependenciesGraph map[string][]string, dependenciesList map[string]bool, uniqueDepsSet *datastructures.Set[string], isInstallMode bool) { if currNode.NodeHasLoop() { return } @@ -114,7 +116,11 @@ func populateGoDependencyTree(currNode *xrayUtils.GraphNode, dependenciesGraph m currDepChildren := dependenciesGraph[strings.TrimPrefix(currNode.Id, goPackageTypeIdentifier)] // Recursively create & append all node's dependencies. for _, childName := range currDepChildren { - if !dependenciesList[childName] { + if isInstallMode { + if isGoToolchainDep(childName) { + continue + } + } else if !dependenciesList[childName] { // 'go list all' is more accurate than 'go graph' so we filter out deps that don't exist in go list continue } @@ -124,7 +130,7 @@ func populateGoDependencyTree(currNode *xrayUtils.GraphNode, dependenciesGraph m Parent: currNode, } currNode.Nodes = append(currNode.Nodes, childNode) - populateGoDependencyTree(childNode, dependenciesGraph, dependenciesList, uniqueDepsSet) + populateGoDependencyTree(childNode, dependenciesGraph, dependenciesList, uniqueDepsSet, isInstallMode) } } @@ -155,3 +161,7 @@ func getDependenciesGraph(projectDir string) (map[string][]string, error) { } return deps, nil } + +func isGoToolchainDep(dep string) bool { + return strings.HasPrefix(dep, "toolchain@") || strings.HasPrefix(dep, "go@") +} diff --git a/sca/bom/buildinfo/technologies/go/install.go b/sca/bom/buildinfo/technologies/go/install.go new file mode 100644 index 000000000..bb82708ef --- /dev/null +++ b/sca/bom/buildinfo/technologies/go/install.go @@ -0,0 +1,50 @@ +package _go + +import ( + "fmt" + "os/exec" + "strings" + + biutils "github.com/jfrog/build-info-go/utils" + "github.com/jfrog/jfrog-client-go/utils/errorutils" + + "github.com/jfrog/jfrog-cli-security/sca/bom/buildinfo/technologies" + "github.com/jfrog/jfrog-cli-security/utils/techutils" +) + +type GoInstallHandler struct{} + +func (h *GoInstallHandler) Technology() techutils.Technology { + return techutils.Go +} + +// ParsePackageSpec parses a Go package spec: @. +func (h *GoInstallHandler) ParsePackageSpec(spec string) (technologies.InstalledPackage, error) { + atIdx := strings.LastIndex(spec, "@") + if atIdx <= 0 { + return technologies.InstalledPackage{}, errorutils.CheckErrorf( + "invalid Go package spec: '%s'. Expected format: @ (e.g. github.com/pkg/errors@v0.9.1)", spec) + } + name := spec[:atIdx] + version := spec[atIdx+1:] + if name == "" || version == "" { + return technologies.InstalledPackage{}, errorutils.CheckErrorf( + "invalid Go package spec: '%s'. Both module path and version are required", spec) + } + return technologies.InstalledPackage{Name: name, Version: version}, nil +} + +func (h *GoInstallHandler) CreateTempProject(projectDir, tempDir, pkgName, pkgVersion string) error { + if err := biutils.CopyDir(projectDir, tempDir, true, []string{".git", "vendor"}); err != nil { + return err + } + requireArg := fmt.Sprintf("-require=%s@%s", pkgName, pkgVersion) + cmd := exec.Command("go", "mod", "edit", requireArg) + cmd.Dir = tempDir + output, err := cmd.CombinedOutput() + if err != nil { + return errorutils.CheckErrorf("failed to add require to temp go.mod: %s\n%s", err.Error(), string(output)) + } + return nil +} + diff --git a/sca/bom/buildinfo/technologies/go/install_test.go b/sca/bom/buildinfo/technologies/go/install_test.go new file mode 100644 index 000000000..fa4a11e33 --- /dev/null +++ b/sca/bom/buildinfo/technologies/go/install_test.go @@ -0,0 +1,158 @@ +package _go + +import ( + "os" + "path/filepath" + "strings" + "testing" + + "github.com/stretchr/testify/assert" + "github.com/stretchr/testify/require" +) + +func TestGoParsePackageSpec(t *testing.T) { + handler := &GoInstallHandler{} + + tests := []struct { + name string + spec string + wantName string + wantVersion string + wantErr bool + }{ + { + name: "standard module", + spec: "github.com/pkg/errors@v0.9.1", + wantName: "github.com/pkg/errors", + wantVersion: "v0.9.1", + }, + { + name: "short module path", + spec: "rsc.io/quote@v1.5.2", + wantName: "rsc.io/quote", + wantVersion: "v1.5.2", + }, + { + name: "module with many path segments", + spec: "github.com/jfrog/jfrog-cli-core/v2@v2.50.0", + wantName: "github.com/jfrog/jfrog-cli-core/v2", + wantVersion: "v2.50.0", + }, + { + name: "pre-release version", + spec: "golang.org/x/text@v0.14.0-rc.1", + wantName: "golang.org/x/text", + wantVersion: "v0.14.0-rc.1", + }, + { + name: "pseudo-version", + spec: "golang.org/x/text@v0.0.0-20170915032832-14c0d48ead0c", + wantName: "golang.org/x/text", + wantVersion: "v0.0.0-20170915032832-14c0d48ead0c", + }, + { + name: "missing version - no @", + spec: "github.com/pkg/errors", + wantErr: true, + }, + { + name: "empty spec", + spec: "", + wantErr: true, + }, + { + name: "only @", + spec: "@", + wantErr: true, + }, + { + name: "@ at start with version", + spec: "@v1.0.0", + wantErr: true, + }, + } + + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + pkg, err := handler.ParsePackageSpec(tt.spec) + if tt.wantErr { + assert.Error(t, err) + return + } + require.NoError(t, err) + assert.Equal(t, tt.wantName, pkg.Name) + assert.Equal(t, tt.wantVersion, pkg.Version) + }) + } +} + +func TestGoCreateTempProject(t *testing.T) { + handler := &GoInstallHandler{} + + tests := []struct { + name string + pkgName string + pkgVersion string + existDeps []string + }{ + { + name: "add package to project with existing dependency", + pkgName: "github.com/pkg/errors", + pkgVersion: "v0.9.1", + existDeps: []string{"rsc.io/quote v1.5.2"}, + }, + { + name: "add multi-segment module to project with multiple deps", + pkgName: "github.com/jfrog/jfrog-cli-core/v2", + pkgVersion: "v2.50.0", + existDeps: []string{"rsc.io/quote v1.5.2", "rsc.io/sampler v1.3.0"}, + }, + } + + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + projectDir := t.TempDir() + tempDir := t.TempDir() + + goMod := "module my-project\n\ngo 1.21\n\nrequire (\n" + for _, dep := range tt.existDeps { + goMod += "\t" + dep + "\n" + } + goMod += ")\n" + require.NoError(t, os.WriteFile(filepath.Join(projectDir, "go.mod"), []byte(goMod), 0644)) + + require.NoError(t, os.MkdirAll(filepath.Join(projectDir, ".git", "objects"), 0755)) + require.NoError(t, os.WriteFile(filepath.Join(projectDir, ".git", "HEAD"), []byte("ref: refs/heads/main"), 0644)) + require.NoError(t, os.MkdirAll(filepath.Join(projectDir, "vendor"), 0755)) + require.NoError(t, os.WriteFile(filepath.Join(projectDir, "vendor", "modules.txt"), []byte("# vendor"), 0644)) + + err := handler.CreateTempProject(projectDir, tempDir, tt.pkgName, tt.pkgVersion) + require.NoError(t, err) + + data, err := os.ReadFile(filepath.Join(tempDir, "go.mod")) + require.NoError(t, err) + + content := string(data) + assert.True(t, strings.Contains(content, "module my-project"), + "go.mod should keep the original module name") + assert.True(t, strings.Contains(content, tt.pkgName), + "go.mod should contain the new dependency: %s", tt.pkgName) + for _, dep := range tt.existDeps { + depName := strings.Fields(dep)[0] + assert.True(t, strings.Contains(content, depName), + "go.mod should still contain existing dependency: %s", depName) + } + + _, err = os.Stat(filepath.Join(tempDir, ".git")) + assert.True(t, os.IsNotExist(err), ".git should not be copied") + _, err = os.Stat(filepath.Join(tempDir, "vendor")) + assert.True(t, os.IsNotExist(err), "vendor should not be copied") + }) + } +} + +func TestGoCreateTempProject_InvalidDir(t *testing.T) { + handler := &GoInstallHandler{} + err := handler.CreateTempProject("/nonexistent/path/that/does/not/exist", t.TempDir(), "github.com/pkg/errors", "v0.9.1") + assert.Error(t, err) +} diff --git a/sca/bom/buildinfo/technologies/npm/install.go b/sca/bom/buildinfo/technologies/npm/install.go new file mode 100644 index 000000000..76e1c3080 --- /dev/null +++ b/sca/bom/buildinfo/technologies/npm/install.go @@ -0,0 +1,71 @@ +package npm + +import ( + "encoding/json" + "os" + "path/filepath" + "strings" + + biutils "github.com/jfrog/build-info-go/utils" + "github.com/jfrog/jfrog-client-go/utils/errorutils" + + "github.com/jfrog/jfrog-cli-security/sca/bom/buildinfo/technologies" + "github.com/jfrog/jfrog-cli-security/utils/techutils" +) + +type NpmInstallHandler struct{} + +func (h *NpmInstallHandler) Technology() techutils.Technology { + return techutils.Npm +} + +// ParsePackageSpec parses an npm package spec: @ or @/@. +func (h *NpmInstallHandler) ParsePackageSpec(spec string) (technologies.InstalledPackage, error) { + lastAt := strings.LastIndex(spec, "@") + if lastAt <= 0 { + return technologies.InstalledPackage{}, errorutils.CheckErrorf( + "invalid npm package spec: '%s'. Expected format: @ (e.g. express@4.18.2 or @scope/name@1.0.0)", spec) + } + name := spec[:lastAt] + version := spec[lastAt+1:] + if name == "" || version == "" { + return technologies.InstalledPackage{}, errorutils.CheckErrorf( + "invalid npm package spec: '%s'. Both package name and version are required", spec) + } + return technologies.InstalledPackage{Name: name, Version: version}, nil +} + +// CreateTempProject copies the project directory (excluding node_modules) to the temp directory, +// then adds the requested package to the copied package.json. +func (h *NpmInstallHandler) CreateTempProject(projectDir, tempDir, pkgName, pkgVersion string) error { + if err := biutils.CopyDir(projectDir, tempDir, true, []string{"node_modules", ".git"}); err != nil { + return err + } + return addDependencyToPackageJson(filepath.Join(tempDir, "package.json"), pkgName, pkgVersion) +} + +func addDependencyToPackageJson(packageJsonPath, pkgName, pkgVersion string) error { + data, err := os.ReadFile(packageJsonPath) + if err != nil { + return errorutils.CheckErrorf("failed to read package.json at '%s': %s", packageJsonPath, err.Error()) + } + + var packageJson map[string]interface{} + if err = json.Unmarshal(data, &packageJson); err != nil { + return errorutils.CheckErrorf("failed to parse package.json: %s", err.Error()) + } + + deps, ok := packageJson["dependencies"].(map[string]interface{}) + if !ok { + deps = make(map[string]interface{}) + } + deps[pkgName] = pkgVersion + packageJson["dependencies"] = deps + + updatedData, err := json.MarshalIndent(packageJson, "", " ") + if err != nil { + return errorutils.CheckError(err) + } + updatedData = append(updatedData, '\n') + return errorutils.CheckError(os.WriteFile(packageJsonPath, updatedData, 0644)) +} diff --git a/sca/bom/buildinfo/technologies/npm/install_test.go b/sca/bom/buildinfo/technologies/npm/install_test.go new file mode 100644 index 000000000..b7f0c516c --- /dev/null +++ b/sca/bom/buildinfo/technologies/npm/install_test.go @@ -0,0 +1,191 @@ +package npm + +import ( + "encoding/json" + "os" + "path/filepath" + "testing" + + "github.com/stretchr/testify/assert" + "github.com/stretchr/testify/require" +) + +func TestParsePackageSpec(t *testing.T) { + handler := &NpmInstallHandler{} + + tests := []struct { + name string + spec string + wantName string + wantVersion string + wantErr bool + }{ + { + name: "simple package", + spec: "express@4.18.2", + wantName: "express", + wantVersion: "4.18.2", + }, + { + name: "scoped package", + spec: "@angular/core@17.0.0", + wantName: "@angular/core", + wantVersion: "17.0.0", + }, + { + name: "scoped package with nested scope", + spec: "@morgan-stanley/fdc3-web@1.0.0", + wantName: "@morgan-stanley/fdc3-web", + wantVersion: "1.0.0", + }, + { + name: "wildcard version", + spec: "lodash@*", + wantName: "lodash", + wantVersion: "*", + }, + { + name: "caret range", + spec: "express@^4.18.0", + wantName: "express", + wantVersion: "^4.18.0", + }, + { + name: "tilde range", + spec: "express@~4.18.0", + wantName: "express", + wantVersion: "~4.18.0", + }, + { + name: "latest tag", + spec: "next@latest", + wantName: "next", + wantVersion: "latest", + }, + { + name: "greater-equal range", + spec: "react@>=18.0.0", + wantName: "react", + wantVersion: ">=18.0.0", + }, + { + name: "missing version - no @", + spec: "express", + wantErr: true, + }, + { + name: "scoped package without version", + spec: "@angular/core", + wantErr: true, + }, + { + name: "empty string", + spec: "", + wantErr: true, + }, + { + name: "only @", + spec: "@", + wantErr: true, + }, + { + name: "trailing @ with no version", + spec: "express@", + wantErr: true, + }, + } + + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + pkg, err := handler.ParsePackageSpec(tt.spec) + if tt.wantErr { + assert.Error(t, err) + return + } + assert.NoError(t, err) + assert.Equal(t, tt.wantName, pkg.Name) + assert.Equal(t, tt.wantVersion, pkg.Version) + }) + } +} + +func TestCreateTempProject(t *testing.T) { + handler := &NpmInstallHandler{} + + tests := []struct { + name string + pkgName string + pkgVersion string + existDeps map[string]string + }{ + { + name: "add to existing dependencies", + pkgName: "express", + pkgVersion: "4.18.2", + existDeps: map[string]string{"react": "^18.0.0"}, + }, + { + name: "add scoped package to empty deps", + pkgName: "@angular/core", + pkgVersion: "17.0.0", + existDeps: map[string]string{}, + }, + { + name: "add wildcard version alongside existing", + pkgName: "lodash", + pkgVersion: "*", + existDeps: map[string]string{"express": "4.18.2", "react": "^18.0.0"}, + }, + } + + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + projectDir := t.TempDir() + tempDir := t.TempDir() + + srcPkg := map[string]interface{}{ + "name": "my-project", + "version": "1.0.0", + "dependencies": tt.existDeps, + } + srcData, _ := json.MarshalIndent(srcPkg, "", " ") + require.NoError(t, os.WriteFile(filepath.Join(projectDir, "package.json"), srcData, 0644)) + + require.NoError(t, os.WriteFile(filepath.Join(projectDir, ".npmrc"), []byte("registry=https://registry.npmjs.org/"), 0644)) + + // node_modules and .git should be excluded + require.NoError(t, os.MkdirAll(filepath.Join(projectDir, "node_modules", "react"), 0755)) + require.NoError(t, os.WriteFile(filepath.Join(projectDir, "node_modules", "react", "index.js"), []byte("module.exports = {}"), 0644)) + require.NoError(t, os.MkdirAll(filepath.Join(projectDir, ".git", "objects"), 0755)) + require.NoError(t, os.WriteFile(filepath.Join(projectDir, ".git", "HEAD"), []byte("ref: refs/heads/main"), 0644)) + + err := handler.CreateTempProject(projectDir, tempDir, tt.pkgName, tt.pkgVersion) + require.NoError(t, err) + + // Verify package.json was copied and has the new dependency + data, err := os.ReadFile(filepath.Join(tempDir, "package.json")) + require.NoError(t, err) + + var packageJson map[string]interface{} + require.NoError(t, json.Unmarshal(data, &packageJson)) + + assert.Equal(t, "my-project", packageJson["name"]) + + deps, ok := packageJson["dependencies"].(map[string]interface{}) + require.True(t, ok, "dependencies should be a map") + assert.Equal(t, tt.pkgVersion, deps[tt.pkgName]) + + for k, v := range tt.existDeps { + assert.Equal(t, v, deps[k]) + } + _, err = os.Stat(filepath.Join(tempDir, ".npmrc")) + assert.NoError(t, err, ".npmrc should be copied") + + _, err = os.Stat(filepath.Join(tempDir, "node_modules")) + assert.True(t, os.IsNotExist(err), "node_modules should not be copied") + _, err = os.Stat(filepath.Join(tempDir, ".git")) + assert.True(t, os.IsNotExist(err), ".git should not be copied") + }) + } +} + From 53e0cce20ab993fc687f6fa815dd4e79089f2824 Mon Sep 17 00:00:00 2001 From: Bassel Mbariky Date: Wed, 25 Feb 2026 13:31:41 +0200 Subject: [PATCH 2/6] change-command-name --- cli/docs/flags.go | 6 +++--- cli/scancommands.go | 2 +- commands/curation/curationaudit.go | 20 ++++++++++---------- commands/curation/curationaudit_test.go | 6 +++--- commands/curation/install.go | 6 +++--- sca/bom/buildinfo/technologies/common.go | 2 +- sca/bom/buildinfo/technologies/go/golang.go | 4 ++-- 7 files changed, 23 insertions(+), 23 deletions(-) diff --git a/cli/docs/flags.go b/cli/docs/flags.go index b3ad301d8..99ddfc322 100644 --- a/cli/docs/flags.go +++ b/cli/docs/flags.go @@ -157,7 +157,7 @@ const ( DockerImageName = "image" SolutionPath = "solution-path" IncludeCachedPackages = "include-cached-packages" - InstallPackage = "install" + AuditPackage = "package" // Unique git flags InputFile = "input-file" @@ -216,7 +216,7 @@ var commandFlags = map[string][]string{ StaticSca, XrayLibPluginBinaryCustomPath, AnalyzerManagerCustomPath, AddSastRules, }, CurationAudit: { - CurationOutput, WorkingDirs, Threads, RequirementsFile, InsecureTls, useWrapperAudit, UseIncludedBuilds, SolutionPath, DockerImageName, IncludeCachedPackages, InstallPackage, + CurationOutput, WorkingDirs, Threads, RequirementsFile, InsecureTls, useWrapperAudit, UseIncludedBuilds, SolutionPath, DockerImageName, IncludeCachedPackages, AuditPackage, }, GitCountContributors: { InputFile, ScmType, ScmApiUrl, Token, Owner, RepoName, Months, DetailedSummary, InsecureTls, @@ -334,7 +334,7 @@ var flagsMap = map[string]components.Flag{ CurationOutput: components.NewStringFlag(OutputFormat, "Defines the output format of the command. Acceptable values are: table, json.", components.WithStrDefaultValue("table")), SolutionPath: components.NewStringFlag(SolutionPath, "Path to the .NET solution file (.sln) to use when multiple solution files are present in the directory."), IncludeCachedPackages: components.NewBoolFlag(IncludeCachedPackages, "When set to true, the system will audit cached packages. This configuration is mandatory for Curation on-demand workflows, which rely on package caching."), - InstallPackage: components.NewStringFlag(InstallPackage, "Run curation audit on a specific package and its transitive dependencies. Format: @ (e.g. express@4.18.2 for npm, github.com/pkg/errors@v0.9.1 for Go)."), + AuditPackage: components.NewStringFlag(AuditPackage, "Run curation audit on a specific package and its transitive dependencies. Format: @ (e.g. express@4.18.2 for npm, github.com/pkg/errors@v0.9.1 for Go)."), binarySca: components.NewBoolFlag(Sca, fmt.Sprintf("Selective scanners mode: Execute SCA (Software Composition Analysis) sub-scan. Use --%s to run both SCA and Contextual Analysis. Use --%s --%s to to run SCA. Can be combined with --%s.", Sca, Sca, WithoutCA, Secrets)), binarySecrets: components.NewBoolFlag(Secrets, fmt.Sprintf("Selective scanners mode: Execute Secrets sub-scan. Can be combined with --%s.", Sca)), binaryWithoutCA: components.NewBoolFlag(WithoutCA, fmt.Sprintf("Selective scanners mode: Disable Contextual Analysis scanner after SCA. Relevant only with --%s flag.", Sca)), diff --git a/cli/scancommands.go b/cli/scancommands.go index 4f8086bca..f02a6e8c7 100644 --- a/cli/scancommands.go +++ b/cli/scancommands.go @@ -704,7 +704,7 @@ func getCurationCommand(c *components.Context) (*curation.CurationAuditCommand, SetSolutionFilePath(c.GetStringFlagValue(flags.SolutionPath)) curationAuditCommand.SetDockerImageName(c.GetStringFlagValue(flags.DockerImageName)) curationAuditCommand.SetIncludeCachedPackages(c.GetBoolFlagValue(flags.IncludeCachedPackages)) - curationAuditCommand.SetInstallPackage(c.GetStringFlagValue(flags.InstallPackage)) + curationAuditCommand.SetAuditPackage(c.GetStringFlagValue(flags.AuditPackage)) return curationAuditCommand, nil } diff --git a/commands/curation/curationaudit.go b/commands/curation/curationaudit.go index 48099cf12..d1e6485bd 100644 --- a/commands/curation/curationaudit.go +++ b/commands/curation/curationaudit.go @@ -224,7 +224,7 @@ type CurationAuditCommand struct { parallelRequests int dockerImageName string includeCachedPackages bool - installPackage string + auditPackage string audit.AuditParamsInterface } @@ -275,13 +275,13 @@ func (ca *CurationAuditCommand) SetIncludeCachedPackages(includeCachedPackages b return ca } -func (ca *CurationAuditCommand) SetInstallPackage(spec string) *CurationAuditCommand { - ca.installPackage = spec +func (ca *CurationAuditCommand) SetAuditPackage(spec string) *CurationAuditCommand { + ca.auditPackage = spec return ca } func (ca *CurationAuditCommand) Run() (err error) { - if ca.installPackage != "" { + if ca.auditPackage != "" { return ca.runInstallMode() } rootDir, err := os.Getwd() @@ -452,7 +452,7 @@ func (ca *CurationAuditCommand) getBuildInfoParamsByTech() (technologies.BuildIn InstallCommandArgs: ca.InstallCommandArgs(), // Curation params IsCurationCmd: true, - IsInstallMode: ca.installPackage != "", + IsPackageMode: ca.auditPackage != "", // Java params IsMavenDepTreeInstalled: true, UseWrapper: ca.UseWrapper(), @@ -485,8 +485,8 @@ func (ca *CurationAuditCommand) auditTree(tech techutils.Technology, results map if len(depTreeResult.FullDepTrees) == 0 { return errorutils.CheckErrorf("found no dependencies for the audited project using '%v' as the package manager", tech.String()) } - if params.IsInstallMode && ca.installPackage != "" { - filterTreeForInstallPackage(&depTreeResult, tech, ca.installPackage) + if params.IsPackageMode && ca.auditPackage != "" { + filterAuditPackageTree(&depTreeResult, tech, ca.auditPackage) } rtManager, serverDetails, err := ca.getRtManagerAndAuth(tech) if err != nil { @@ -555,10 +555,10 @@ func (ca *CurationAuditCommand) auditTree(tech techutils.Technology, results map return err } -// filterTreeForInstallPackage modifies depTreeResult in place to keep only the -// install package and its transitive dependencies. This ensures that in --install mode +// filterAuditPackageTree modifies depTreeResult in place to keep only the +// requested package and its transitive dependencies. This ensures that in --package mode // we only audit the new package, not the customer's existing dependencies. -func filterTreeForInstallPackage(depTreeResult *buildinfo.DependencyTreeResult, tech techutils.Technology, installPkgName string) { +func filterAuditPackageTree(depTreeResult *buildinfo.DependencyTreeResult, tech techutils.Technology, installPkgName string) { prefix := tech.GetXrayPackageTypeId() + installPkgName + ":" for _, tree := range depTreeResult.FullDepTrees { for _, child := range tree.Nodes { diff --git a/commands/curation/curationaudit_test.go b/commands/curation/curationaudit_test.go index 9ba36e61e..ffd2eae46 100644 --- a/commands/curation/curationaudit_test.go +++ b/commands/curation/curationaudit_test.go @@ -547,7 +547,7 @@ func createCurationCmdAndRun(tt testCase) (cmdResults map[string]*CurationReport curationCmd.SetIgnoreConfigFile(tt.shouldIgnoreConfigFile) curationCmd.SetInsecureTls(tt.allowInsecureTls) if tt.installPackage != "" { - curationCmd.SetInstallPackage(tt.installPackage) + curationCmd.SetAuditPackage(tt.installPackage) } cmdResults = map[string]*CurationReport{} err = curationCmd.doCurateAudit(cmdResults) @@ -1642,7 +1642,7 @@ func TestSendWaiverRequests(t *testing.T) { } } -func TestFilterTreeForInstallPackage(t *testing.T) { +func TestFilterAuditPackageTree(t *testing.T) { tests := []struct { name string installPkgName string @@ -1743,7 +1743,7 @@ func TestFilterTreeForInstallPackage(t *testing.T) { FlatTree: &xrayUtils.GraphNode{Nodes: tt.flatNodes}, } - filterTreeForInstallPackage(depTreeResult, tt.tech, tt.installPkgName) + filterAuditPackageTree(depTreeResult, tt.tech, tt.installPkgName) // Verify the filtered direct children var childIds []string diff --git a/commands/curation/install.go b/commands/curation/install.go index f3f5c3ddb..8ae8284f7 100644 --- a/commands/curation/install.go +++ b/commands/curation/install.go @@ -56,11 +56,11 @@ func (ca *CurationAuditCommand) runInstallMode() (err error) { return err } tech := handler.Technology() - pkg, parseErr := handler.ParsePackageSpec(ca.installPackage) + pkg, parseErr := handler.ParsePackageSpec(ca.auditPackage) if parseErr != nil { return parseErr } - ca.installPackage = pkg.Name + ca.auditPackage = pkg.Name log.Info(fmt.Sprintf("Running curation audit for %s package %s@%s", tech.ToFormal(), pkg.Name, pkg.Version)) if err = ca.SetRepo(tech); err != nil { @@ -143,7 +143,7 @@ func detectInstallHandler() (PackageInstallHandler, error) { } supportedTechs := getSupportedInstallTechnologies() return nil, errorutils.CheckErrorf( - "could not detect a supported technology in the current directory for --install. Currently supported: %s", + "could not detect a supported technology in the current directory for --package flag. Currently supported: %s", strings.Join(supportedTechs, ", ")) } diff --git a/sca/bom/buildinfo/technologies/common.go b/sca/bom/buildinfo/technologies/common.go index 05455c04b..788470309 100644 --- a/sca/bom/buildinfo/technologies/common.go +++ b/sca/bom/buildinfo/technologies/common.go @@ -54,7 +54,7 @@ type BuildInfoBomGeneratorParams struct { InstallCommandArgs []string // Curation params IsCurationCmd bool - IsInstallMode bool + IsPackageMode bool // Java params IsMavenDepTreeInstalled bool UseWrapper bool diff --git a/sca/bom/buildinfo/technologies/go/golang.go b/sca/bom/buildinfo/technologies/go/golang.go index e314977e4..58e02cbe2 100644 --- a/sca/bom/buildinfo/technologies/go/golang.go +++ b/sca/bom/buildinfo/technologies/go/golang.go @@ -56,7 +56,7 @@ func BuildDependencyTree(params technologies.BuildInfoBomGeneratorParams) (depen return } var dependenciesList map[string]bool - if !params.IsInstallMode { + if !params.IsPackageMode { dependenciesList, err = getDependenciesList(currentDir, handleCurationGoError) if err != nil { return @@ -73,7 +73,7 @@ func BuildDependencyTree(params technologies.BuildInfoBomGeneratorParams) (depen Nodes: []*xrayUtils.GraphNode{}, } uniqueDepsSet := datastructures.MakeSet[string]() - populateGoDependencyTree(rootNode, dependenciesGraph, dependenciesList, uniqueDepsSet, params.IsInstallMode) + populateGoDependencyTree(rootNode, dependenciesGraph, dependenciesList, uniqueDepsSet, params.IsPackageMode) // In case of curation command, go version is not relevant as it can't be resolved from go repo if !params.IsCurationCmd { From a65926df3374f2866b6cc305b4161fb8a04d8298 Mon Sep 17 00:00:00 2001 From: Bassel Mbariky Date: Wed, 25 Feb 2026 16:01:25 +0200 Subject: [PATCH 3/6] fixed-test --- commands/curation/install_test.go | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/commands/curation/install_test.go b/commands/curation/install_test.go index feff9086b..e94470a7d 100644 --- a/commands/curation/install_test.go +++ b/commands/curation/install_test.go @@ -130,7 +130,7 @@ func TestInstallModeFiltersAuditResults(t *testing.T) { }, }, }, - totalNumberOfPackages: 1, + totalNumberOfPackages: 2, }, }, }, @@ -152,7 +152,7 @@ func TestInstallModeFiltersAuditResults(t *testing.T) { "v0.0.0-20170915032832-14c0d48ead0c.info": filepath.Join("resources", "text-v0.0.0-20170915032832-14c0d48ead0c.info"), }, requestToFail: map[string]bool{ - "/api/go/go-virtual/rsc.io/sampler/@v/v1.3.0.zip": false, + "/api/go/go-virtual/rsc.io/quote/@v/v1.5.2.zip": false, }, expectedResp: map[string]*CurationReport{ "github.com/you/hello": { @@ -161,11 +161,11 @@ func TestInstallModeFiltersAuditResults(t *testing.T) { Action: "blocked", ParentName: "rsc.io/quote", ParentVersion: "v1.5.2", - BlockedPackageUrl: "/api/go/go-virtual/rsc.io/sampler/@v/v1.3.0.zip", - PackageName: "rsc.io/sampler", - PackageVersion: "v1.3.0", + BlockedPackageUrl: "/api/go/go-virtual/rsc.io/quote/@v/v1.5.2.zip", + PackageName: "rsc.io/quote", + PackageVersion: "v1.5.2", BlockingReason: "Policy violations", - DepRelation: "indirect", + DepRelation: "direct", PkgType: "go", Policy: []Policy{ { From 09007c3a6df28c22a886d7a82fc95e7141259ff2 Mon Sep 17 00:00:00 2001 From: Bassel Mbariky Date: Wed, 25 Feb 2026 16:53:34 +0200 Subject: [PATCH 4/6] Fixed-windows-issue --- commands/curation/install_test.go | 1 + 1 file changed, 1 insertion(+) diff --git a/commands/curation/install_test.go b/commands/curation/install_test.go index e94470a7d..84e7b683c 100644 --- a/commands/curation/install_test.go +++ b/commands/curation/install_test.go @@ -64,6 +64,7 @@ func TestDetectInstallHandler(t *testing.T) { require.NoError(t, os.WriteFile(filepath.Join(tempDir, name), []byte(content), 0644)) } require.NoError(t, os.Chdir(tempDir)) + defer func() { require.NoError(t, os.Chdir(originalDir)) }() handler, err := detectInstallHandler() From a283691cccef7f9d5059679f5d14b20ff89c968f Mon Sep 17 00:00:00 2001 From: Bassel Mbariky Date: Tue, 3 Mar 2026 15:34:19 +0200 Subject: [PATCH 5/6] rename-file-names --- commands/curation/curationaudit.go | 2 +- commands/curation/curationaudit_test.go | 6 ++--- .../{install.go => specfic-packge.go} | 24 ++++++++--------- ...install_test.go => specfic-packge_test.go} | 26 +++++++++---------- .../go/{install.go => go-specfic-package.go} | 8 +++--- ...all_test.go => go-specfic-package_test.go} | 6 ++--- .../{install.go => npm-specfic-package.go} | 8 +++--- ...ll_test.go => npm-specfic-package_test.go} | 4 +-- 8 files changed, 42 insertions(+), 42 deletions(-) rename commands/curation/{install.go => specfic-packge.go} (83%) rename commands/curation/{install_test.go => specfic-packge_test.go} (88%) rename sca/bom/buildinfo/technologies/go/{install.go => go-specfic-package.go} (80%) rename sca/bom/buildinfo/technologies/go/{install_test.go => go-specfic-package_test.go} (97%) rename sca/bom/buildinfo/technologies/npm/{install.go => npm-specfic-package.go} (87%) rename sca/bom/buildinfo/technologies/npm/{install_test.go => npm-specfic-package_test.go} (98%) diff --git a/commands/curation/curationaudit.go b/commands/curation/curationaudit.go index d1e6485bd..9ca1cc6cc 100644 --- a/commands/curation/curationaudit.go +++ b/commands/curation/curationaudit.go @@ -282,7 +282,7 @@ func (ca *CurationAuditCommand) SetAuditPackage(spec string) *CurationAuditComma func (ca *CurationAuditCommand) Run() (err error) { if ca.auditPackage != "" { - return ca.runInstallMode() + return ca.runSpecificPackageMode() } rootDir, err := os.Getwd() if err != nil { diff --git a/commands/curation/curationaudit_test.go b/commands/curation/curationaudit_test.go index ffd2eae46..2eeead371 100644 --- a/commands/curation/curationaudit_test.go +++ b/commands/curation/curationaudit_test.go @@ -546,8 +546,8 @@ func createCurationCmdAndRun(tt testCase) (cmdResults map[string]*CurationReport curationCmd.SetInsecureTls(true) curationCmd.SetIgnoreConfigFile(tt.shouldIgnoreConfigFile) curationCmd.SetInsecureTls(tt.allowInsecureTls) - if tt.installPackage != "" { - curationCmd.SetAuditPackage(tt.installPackage) + if tt.specificPackage != "" { + curationCmd.SetAuditPackage(tt.specificPackage) } cmdResults = map[string]*CurationReport{} err = curationCmd.doCurateAudit(cmdResults) @@ -604,7 +604,7 @@ type testCase struct { tech techutils.Technology createServerWithoutCreds bool allowInsecureTls bool - installPackage string + specificPackage string } func (tc testCase) getPathToTests() string { diff --git a/commands/curation/install.go b/commands/curation/specfic-packge.go similarity index 83% rename from commands/curation/install.go rename to commands/curation/specfic-packge.go index 8ae8284f7..215526725 100644 --- a/commands/curation/install.go +++ b/commands/curation/specfic-packge.go @@ -18,18 +18,18 @@ import ( "github.com/jfrog/jfrog-cli-security/utils/techutils" ) -type PackageInstallHandler interface { +type SpecificPackageHandler interface { ParsePackageSpec(spec string) (technologies.InstalledPackage, error) CreateTempProject(projectDir, tempDir, pkgName, pkgVersion string) error Technology() techutils.Technology } -var installHandlers = map[techutils.Technology]PackageInstallHandler{ - techutils.Npm: &npm.NpmInstallHandler{}, - techutils.Go: &gotech.GoInstallHandler{}, +var specificPackageHandlers = map[techutils.Technology]SpecificPackageHandler{ + techutils.Npm: &npm.NpmSpecificPackageHandler{}, + techutils.Go: &gotech.GoSpecificPackageHandler{}, } -func (ca *CurationAuditCommand) runInstallMode() (err error) { +func (ca *CurationAuditCommand) runSpecificPackageMode() (err error) { originalDir, err := os.Getwd() if err != nil { return errorutils.CheckError(err) @@ -51,7 +51,7 @@ func (ca *CurationAuditCommand) runInstallMode() (err error) { }() } - handler, err := detectInstallHandler() + handler, err := detectSpecificPackageHandler() if err != nil { return err } @@ -134,22 +134,22 @@ func (ca *CurationAuditCommand) runInstallMode() (err error) { return err } -func detectInstallHandler() (PackageInstallHandler, error) { +func detectSpecificPackageHandler() (SpecificPackageHandler, error) { detectedTechs := techutils.DetectedTechnologiesList() for _, tech := range detectedTechs { - if handler, ok := installHandlers[techutils.Technology(tech)]; ok { + if handler, ok := specificPackageHandlers[techutils.Technology(tech)]; ok { return handler, nil } } - supportedTechs := getSupportedInstallTechnologies() + supportedTechs := getSupportedSpecificPackageTechnologies() return nil, errorutils.CheckErrorf( "could not detect a supported technology in the current directory for --package flag. Currently supported: %s", strings.Join(supportedTechs, ", ")) } -func getSupportedInstallTechnologies() []string { - techs := make([]string, 0, len(installHandlers)) - for tech := range installHandlers { +func getSupportedSpecificPackageTechnologies() []string { + techs := make([]string, 0, len(specificPackageHandlers)) + for tech := range specificPackageHandlers { techs = append(techs, tech.ToFormal()) } return techs diff --git a/commands/curation/install_test.go b/commands/curation/specfic-packge_test.go similarity index 88% rename from commands/curation/install_test.go rename to commands/curation/specfic-packge_test.go index 84e7b683c..2cd11d407 100644 --- a/commands/curation/install_test.go +++ b/commands/curation/specfic-packge_test.go @@ -15,7 +15,7 @@ import ( "github.com/stretchr/testify/require" ) -func TestDetectInstallHandler(t *testing.T) { +func TestDetectSpecificPackageHandler(t *testing.T) { originalDir, err := os.Getwd() require.NoError(t, err) defer func() { require.NoError(t, os.Chdir(originalDir)) }() @@ -32,14 +32,14 @@ func TestDetectInstallHandler(t *testing.T) { files: map[string]string{ "package.json": `{"name": "test-project", "version": "1.0.0"}`, }, - expectedType: &npm.NpmInstallHandler{}, + expectedType: &npm.NpmSpecificPackageHandler{}, }, { name: "detect go from go.mod", files: map[string]string{ "go.mod": "module example.com/test\n\ngo 1.21\n", }, - expectedType: &gotech.GoInstallHandler{}, + expectedType: &gotech.GoSpecificPackageHandler{}, }, { name: "no supported technology detected", @@ -66,7 +66,7 @@ func TestDetectInstallHandler(t *testing.T) { require.NoError(t, os.Chdir(tempDir)) defer func() { require.NoError(t, os.Chdir(originalDir)) }() - handler, err := detectInstallHandler() + handler, err := detectSpecificPackageHandler() if tt.expectError { assert.Error(t, err) @@ -82,15 +82,15 @@ func TestDetectInstallHandler(t *testing.T) { } } -func TestGetSupportedInstallTechnologies(t *testing.T) { - techs := getSupportedInstallTechnologies() - assert.Len(t, techs, len(installHandlers)) - for tech := range installHandlers { +func TestGetSupportedSpecificPackageTechnologies(t *testing.T) { + techs := getSupportedSpecificPackageTechnologies() + assert.Len(t, techs, len(specificPackageHandlers)) + for tech := range specificPackageHandlers { assert.Contains(t, techs, tech.ToFormal()) } } -func TestInstallModeFiltersAuditResults(t *testing.T) { +func TestSpecificPackageModeFiltersAuditResults(t *testing.T) { basePathToTests, err := filepath.Abs(TestDataDir) require.NoError(t, err) @@ -99,11 +99,11 @@ func TestInstallModeFiltersAuditResults(t *testing.T) { tests := []testCase{ { - name: "npm install mode - only audits installed package", + name: "npm specific package mode - only audits specific package", tech: techutils.Npm, pathToProject: filepath.Join("projects", "package-managers", "npm", "npm-project"), shouldIgnoreConfigFile: true, - installPackage: "underscore@1.13.6", + specificPackage: "underscore@1.13.6", expectedRequest: map[string]bool{ "/api/npm/npms/underscore/-/underscore-1.13.6.tgz": false, }, @@ -136,11 +136,11 @@ func TestInstallModeFiltersAuditResults(t *testing.T) { }, }, { - name: "go install mode - only audits installed package and its transitives", + name: "go specific package mode - only audits specific package and its transitives", tech: techutils.Go, pathToProject: filepath.Join("projects", "package-managers", "go", "curation-project"), createServerWithoutCreds: true, - installPackage: "rsc.io/quote@v1.5.2", + specificPackage: "rsc.io/quote@v1.5.2", serveResources: map[string]string{ "v1.5.2.mod": filepath.Join("resources", "quote-v1.5.2.mod"), "v1.5.2.zip": filepath.Join("resources", "quote-v1.5.2.zip"), diff --git a/sca/bom/buildinfo/technologies/go/install.go b/sca/bom/buildinfo/technologies/go/go-specfic-package.go similarity index 80% rename from sca/bom/buildinfo/technologies/go/install.go rename to sca/bom/buildinfo/technologies/go/go-specfic-package.go index bb82708ef..7c0182df6 100644 --- a/sca/bom/buildinfo/technologies/go/install.go +++ b/sca/bom/buildinfo/technologies/go/go-specfic-package.go @@ -12,14 +12,14 @@ import ( "github.com/jfrog/jfrog-cli-security/utils/techutils" ) -type GoInstallHandler struct{} +type GoSpecificPackageHandler struct{} -func (h *GoInstallHandler) Technology() techutils.Technology { +func (h *GoSpecificPackageHandler) Technology() techutils.Technology { return techutils.Go } // ParsePackageSpec parses a Go package spec: @. -func (h *GoInstallHandler) ParsePackageSpec(spec string) (technologies.InstalledPackage, error) { +func (h *GoSpecificPackageHandler) ParsePackageSpec(spec string) (technologies.InstalledPackage, error) { atIdx := strings.LastIndex(spec, "@") if atIdx <= 0 { return technologies.InstalledPackage{}, errorutils.CheckErrorf( @@ -34,7 +34,7 @@ func (h *GoInstallHandler) ParsePackageSpec(spec string) (technologies.Installed return technologies.InstalledPackage{Name: name, Version: version}, nil } -func (h *GoInstallHandler) CreateTempProject(projectDir, tempDir, pkgName, pkgVersion string) error { +func (h *GoSpecificPackageHandler) CreateTempProject(projectDir, tempDir, pkgName, pkgVersion string) error { if err := biutils.CopyDir(projectDir, tempDir, true, []string{".git", "vendor"}); err != nil { return err } diff --git a/sca/bom/buildinfo/technologies/go/install_test.go b/sca/bom/buildinfo/technologies/go/go-specfic-package_test.go similarity index 97% rename from sca/bom/buildinfo/technologies/go/install_test.go rename to sca/bom/buildinfo/technologies/go/go-specfic-package_test.go index fa4a11e33..914f728f8 100644 --- a/sca/bom/buildinfo/technologies/go/install_test.go +++ b/sca/bom/buildinfo/technologies/go/go-specfic-package_test.go @@ -11,7 +11,7 @@ import ( ) func TestGoParsePackageSpec(t *testing.T) { - handler := &GoInstallHandler{} + handler := &GoSpecificPackageHandler{} tests := []struct { name string @@ -87,7 +87,7 @@ func TestGoParsePackageSpec(t *testing.T) { } func TestGoCreateTempProject(t *testing.T) { - handler := &GoInstallHandler{} + handler := &GoSpecificPackageHandler{} tests := []struct { name string @@ -152,7 +152,7 @@ func TestGoCreateTempProject(t *testing.T) { } func TestGoCreateTempProject_InvalidDir(t *testing.T) { - handler := &GoInstallHandler{} + handler := &GoSpecificPackageHandler{} err := handler.CreateTempProject("/nonexistent/path/that/does/not/exist", t.TempDir(), "github.com/pkg/errors", "v0.9.1") assert.Error(t, err) } diff --git a/sca/bom/buildinfo/technologies/npm/install.go b/sca/bom/buildinfo/technologies/npm/npm-specfic-package.go similarity index 87% rename from sca/bom/buildinfo/technologies/npm/install.go rename to sca/bom/buildinfo/technologies/npm/npm-specfic-package.go index 76e1c3080..bb03921ab 100644 --- a/sca/bom/buildinfo/technologies/npm/install.go +++ b/sca/bom/buildinfo/technologies/npm/npm-specfic-package.go @@ -13,14 +13,14 @@ import ( "github.com/jfrog/jfrog-cli-security/utils/techutils" ) -type NpmInstallHandler struct{} +type NpmSpecificPackageHandler struct{} -func (h *NpmInstallHandler) Technology() techutils.Technology { +func (h *NpmSpecificPackageHandler) Technology() techutils.Technology { return techutils.Npm } // ParsePackageSpec parses an npm package spec: @ or @/@. -func (h *NpmInstallHandler) ParsePackageSpec(spec string) (technologies.InstalledPackage, error) { +func (h *NpmSpecificPackageHandler) ParsePackageSpec(spec string) (technologies.InstalledPackage, error) { lastAt := strings.LastIndex(spec, "@") if lastAt <= 0 { return technologies.InstalledPackage{}, errorutils.CheckErrorf( @@ -37,7 +37,7 @@ func (h *NpmInstallHandler) ParsePackageSpec(spec string) (technologies.Installe // CreateTempProject copies the project directory (excluding node_modules) to the temp directory, // then adds the requested package to the copied package.json. -func (h *NpmInstallHandler) CreateTempProject(projectDir, tempDir, pkgName, pkgVersion string) error { +func (h *NpmSpecificPackageHandler) CreateTempProject(projectDir, tempDir, pkgName, pkgVersion string) error { if err := biutils.CopyDir(projectDir, tempDir, true, []string{"node_modules", ".git"}); err != nil { return err } diff --git a/sca/bom/buildinfo/technologies/npm/install_test.go b/sca/bom/buildinfo/technologies/npm/npm-specfic-package_test.go similarity index 98% rename from sca/bom/buildinfo/technologies/npm/install_test.go rename to sca/bom/buildinfo/technologies/npm/npm-specfic-package_test.go index b7f0c516c..e5eed05a1 100644 --- a/sca/bom/buildinfo/technologies/npm/install_test.go +++ b/sca/bom/buildinfo/technologies/npm/npm-specfic-package_test.go @@ -11,7 +11,7 @@ import ( ) func TestParsePackageSpec(t *testing.T) { - handler := &NpmInstallHandler{} + handler := &NpmSpecificPackageHandler{} tests := []struct { name string @@ -110,7 +110,7 @@ func TestParsePackageSpec(t *testing.T) { } func TestCreateTempProject(t *testing.T) { - handler := &NpmInstallHandler{} + handler := &NpmSpecificPackageHandler{} tests := []struct { name string From 32dc4a7784ce32764255dd3f00f1cf957cd12ac5 Mon Sep 17 00:00:00 2001 From: Bassel Mbariky Date: Tue, 3 Mar 2026 15:40:34 +0200 Subject: [PATCH 6/6] fixed-files-names --- commands/curation/{specfic-packge.go => specific-package.go} | 0 .../curation/{specfic-packge_test.go => specific-package_test.go} | 0 .../go/{go-specfic-package.go => go-specific-package.go} | 0 .../{go-specfic-package_test.go => go-specific-package_test.go} | 0 .../npm/{npm-specfic-package.go => npm-specific-package.go} | 0 .../{npm-specfic-package_test.go => npm-specific-package_test.go} | 0 6 files changed, 0 insertions(+), 0 deletions(-) rename commands/curation/{specfic-packge.go => specific-package.go} (100%) rename commands/curation/{specfic-packge_test.go => specific-package_test.go} (100%) rename sca/bom/buildinfo/technologies/go/{go-specfic-package.go => go-specific-package.go} (100%) rename sca/bom/buildinfo/technologies/go/{go-specfic-package_test.go => go-specific-package_test.go} (100%) rename sca/bom/buildinfo/technologies/npm/{npm-specfic-package.go => npm-specific-package.go} (100%) rename sca/bom/buildinfo/technologies/npm/{npm-specfic-package_test.go => npm-specific-package_test.go} (100%) diff --git a/commands/curation/specfic-packge.go b/commands/curation/specific-package.go similarity index 100% rename from commands/curation/specfic-packge.go rename to commands/curation/specific-package.go diff --git a/commands/curation/specfic-packge_test.go b/commands/curation/specific-package_test.go similarity index 100% rename from commands/curation/specfic-packge_test.go rename to commands/curation/specific-package_test.go diff --git a/sca/bom/buildinfo/technologies/go/go-specfic-package.go b/sca/bom/buildinfo/technologies/go/go-specific-package.go similarity index 100% rename from sca/bom/buildinfo/technologies/go/go-specfic-package.go rename to sca/bom/buildinfo/technologies/go/go-specific-package.go diff --git a/sca/bom/buildinfo/technologies/go/go-specfic-package_test.go b/sca/bom/buildinfo/technologies/go/go-specific-package_test.go similarity index 100% rename from sca/bom/buildinfo/technologies/go/go-specfic-package_test.go rename to sca/bom/buildinfo/technologies/go/go-specific-package_test.go diff --git a/sca/bom/buildinfo/technologies/npm/npm-specfic-package.go b/sca/bom/buildinfo/technologies/npm/npm-specific-package.go similarity index 100% rename from sca/bom/buildinfo/technologies/npm/npm-specfic-package.go rename to sca/bom/buildinfo/technologies/npm/npm-specific-package.go diff --git a/sca/bom/buildinfo/technologies/npm/npm-specfic-package_test.go b/sca/bom/buildinfo/technologies/npm/npm-specific-package_test.go similarity index 100% rename from sca/bom/buildinfo/technologies/npm/npm-specfic-package_test.go rename to sca/bom/buildinfo/technologies/npm/npm-specific-package_test.go