Skip to content
2 changes: 1 addition & 1 deletion audit_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -680,7 +680,7 @@ func TestAuditJasCycloneDx(t *testing.T) {
SbomComponents: &validations.SbomCount{Direct: 2, Transitive: 4},
Vulnerabilities: &validations.VulnerabilityCount{
ValidateScan: &validations.ScanCount{Sca: 3, Sast: 2, Secrets: 1},
ValidateApplicabilityStatus: &validations.ApplicabilityStatusCount{NotCovered: 2, NotApplicable: 1},
ValidateApplicabilityStatus: &validations.ApplicabilityStatusCount{NotCovered: 1, NotApplicable: 1},
},
})
}
Expand Down
4 changes: 3 additions & 1 deletion cli/docs/flags.go
Original file line number Diff line number Diff line change
Expand Up @@ -140,6 +140,7 @@ const (

// Unique curation flags
CurationOutput = "curation-format"
SolutionPath = "solution-path"

// Unique git flags
InputFile = "input-file"
Expand Down Expand Up @@ -193,7 +194,7 @@ var commandFlags = map[string][]string{
StaticSca, XrayLibPluginBinaryCustomPath, AnalyzerManagerCustomPath, AddSastRules,
},
CurationAudit: {
CurationOutput, WorkingDirs, Threads, RequirementsFile, InsecureTls, useWrapperAudit,
CurationOutput, WorkingDirs, Threads, RequirementsFile, InsecureTls, useWrapperAudit, SolutionPath,
},
GitCountContributors: {
InputFile, ScmType, ScmApiUrl, Token, Owner, RepoName, Months, DetailedSummary, InsecureTls,
Expand Down Expand Up @@ -303,6 +304,7 @@ var flagsMap = map[string]components.Flag{
XrayLibPluginBinaryCustomPath: components.NewStringFlag(XrayLibPluginBinaryCustomPath, "Defines the custom path to the xray-lib-plugin binary.", components.SetHiddenStrFlag()),
StaticSca: components.NewBoolFlag(StaticSca, "Set to true to use the new SCA engine which is based on lock files.", components.SetHiddenBoolFlag()),
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."),
Sca: 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, --%s, --%s.", Sca, Sca, WithoutCA, Secrets, Sast, Iac)),
Iac: components.NewBoolFlag(Iac, fmt.Sprintf("Selective scanners mode: Execute IaC sub-scan. Can be combined with --%s, --%s and --%s.", Sca, Secrets, Sast)),
Sast: components.NewBoolFlag(Sast, fmt.Sprintf("Selective scanners mode: Execute SAST sub-scan. Can be combined with --%s, --%s and --%s.", Sca, Secrets, Iac)),
Expand Down
3 changes: 2 additions & 1 deletion cli/scancommands.go
Original file line number Diff line number Diff line change
Expand Up @@ -646,7 +646,8 @@ func getCurationCommand(c *components.Context) (*curation.CurationAuditCommand,
SetUseWrapper(c.GetBoolFlagValue(flags.UseWrapper)).
SetInsecureTls(c.GetBoolFlagValue(flags.InsecureTls)).
SetNpmScope(c.GetStringFlagValue(flags.DepType)).
SetPipRequirementsFile(c.GetStringFlagValue(flags.RequirementsFile))
SetPipRequirementsFile(c.GetStringFlagValue(flags.RequirementsFile)).
SetSolutionFilePath(c.GetStringFlagValue(flags.SolutionPath))
return curationAuditCommand, nil
}

Expand Down
12 changes: 12 additions & 0 deletions commands/audit/auditbasicparams.go
Original file line number Diff line number Diff line change
Expand Up @@ -49,6 +49,8 @@ type AuditParamsInterface interface {
AllowPartialResults() bool
GetXrayVersion() string
GetConfigProfile() *xscservices.ConfigProfile
SolutionFilePath() string
SetSolutionFilePath(solutionFilePath string) *AuditBasicParams
}

type AuditBasicParams struct {
Expand Down Expand Up @@ -79,6 +81,7 @@ type AuditBasicParams struct {
xrayVersion string
xscVersion string
configProfile *xscservices.ConfigProfile
solutionFilePath string
}

func (abp *AuditBasicParams) DirectDependencies() *[]string {
Expand Down Expand Up @@ -330,3 +333,12 @@ func (abp *AuditBasicParams) SetConfigProfile(profile *xscservices.ConfigProfile
func (abp *AuditBasicParams) GetConfigProfile() *xscservices.ConfigProfile {
return abp.configProfile
}

func (abp *AuditBasicParams) SolutionFilePath() string {
return abp.solutionFilePath
}

func (abp *AuditBasicParams) SetSolutionFilePath(solutionFilePath string) *AuditBasicParams {
abp.solutionFilePath = solutionFilePath
return abp
}
2 changes: 2 additions & 0 deletions commands/curation/curationaudit.go
Original file line number Diff line number Diff line change
Expand Up @@ -426,6 +426,8 @@ func (ca *CurationAuditCommand) getBuildInfoParamsByTech() (technologies.BuildIn
NpmOverwritePackageLock: true,
// Python params
PipRequirementsFile: ca.PipRequirementsFile(),
// NuGet params
SolutionFilePath: ca.SolutionFilePath(),
}, err
}

Expand Down
2 changes: 2 additions & 0 deletions sca/bom/buildinfo/technologies/common.go
Original file line number Diff line number Diff line change
Expand Up @@ -58,6 +58,8 @@ type BuildInfoBomGeneratorParams struct {
NpmOverwritePackageLock bool
// Pnpm params
MaxTreeDepth string
// NuGet params
SolutionFilePath string
}

func (bbp *BuildInfoBomGeneratorParams) SetNpmScope(depType string) *BuildInfoBomGeneratorParams {
Expand Down
7 changes: 7 additions & 0 deletions sca/bom/buildinfo/technologies/nuget/nuget.go
Original file line number Diff line number Diff line change
Expand Up @@ -267,6 +267,13 @@ func runDotnetRestore(wd string, params technologies.BuildInfoBomGeneratorParams
completeCommandArgs = append(completeCommandArgs, toolType.String(), installCommandName)
}

// Check for solution file path from JF CA arguments for specific solution when we have more than one solution file
if params.SolutionFilePath != "" {
solutionFileName := filepath.Base(params.SolutionFilePath)
completeCommandArgs = append(completeCommandArgs, solutionFileName)
log.Info(fmt.Sprintf("Using solution file: %s", solutionFileName))
}

// We include the flag that allows resolution from an Artifactory server, if it exists.
completeCommandArgs = append(completeCommandArgs, commandExtraArgs...)
command := exec.Command(completeCommandArgs[0], completeCommandArgs[1:]...)
Expand Down
57 changes: 57 additions & 0 deletions sca/bom/buildinfo/technologies/nuget/nuget_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@ import (
"strings"
"testing"

bidotnet "github.com/jfrog/build-info-go/build/utils/dotnet"
"github.com/jfrog/build-info-go/build/utils/dotnet/solution"
"github.com/jfrog/build-info-go/utils"
"github.com/jfrog/jfrog-cli-security/sca/bom/buildinfo/technologies"
Expand Down Expand Up @@ -229,3 +230,59 @@ func TestSkipBuildDepTreeWhenInstallForbidden(t *testing.T) {
})
}
}

func TestSolutionFilePathParameter(t *testing.T) {
testCases := []struct {
name string
solutionFilePath string
expectedFileName string
}{
{
name: "solution file path from params",
solutionFilePath: "/path/to/my-solution.sln",
expectedFileName: "my-solution.sln",
},
{
name: "no solution file path",
solutionFilePath: "",
expectedFileName: "",
},
}

for _, test := range testCases {
t.Run(test.name, func(t *testing.T) {
params := technologies.BuildInfoBomGeneratorParams{
SolutionFilePath: test.solutionFilePath,
}

// Get the solution file path using the same logic as runDotnetRestore
var solutionFilePath string
if params.SolutionFilePath != "" {
solutionFilePath = params.SolutionFilePath
}
var solutionFileName string
if solutionFilePath != "" {
solutionFileName = filepath.Base(solutionFilePath)
}

assert.Equal(t, test.expectedFileName, solutionFileName)
})
}
}

func TestRunDotnetRestoreWithRealSolutionFile(t *testing.T) {
testDataDir := filepath.Join("..", "..", "..", "..", "..", "tests", "testdata", "projects", "package-managers")
multiProjectPath := filepath.Join(testDataDir, "nuget", "multi")
solutionFilePath := filepath.Join(multiProjectPath, "TestSolution.sln")
_, err := os.Stat(solutionFilePath)
assert.NoError(t, err, "Test solution file should exist")

params := technologies.BuildInfoBomGeneratorParams{
SolutionFilePath: solutionFilePath,
}
toolType := bidotnet.ConvertNameToToolType("dotnet")
err = runDotnetRestore(multiProjectPath, params, toolType, []string{})
if err != nil {
assert.NotContains(t, err.Error(), "this folder contains more than one project or solution file")
}
}
Loading