diff --git a/audit_test.go b/audit_test.go index c922b801..8a5b4780 100644 --- a/audit_test.go +++ b/audit_test.go @@ -575,7 +575,7 @@ func getNoJasAuditMockCommand() components.Command { Name: docs.Audit, Flags: docs.GetCommandFlags(docs.Audit), Action: func(c *components.Context) error { - auditCmd, err := cli.CreateAuditCmd(c) + _, _, _, auditCmd, err := cli.CreateAuditCmd(c) if err != nil { return err } diff --git a/cli/scancommands.go b/cli/scancommands.go index fe553370..65569d63 100644 --- a/cli/scancommands.go +++ b/cli/scancommands.go @@ -206,6 +206,10 @@ func ScanCmd(c *components.Context) error { if err != nil { return err } + xrayVersion, xscVersion, err := GetJfrogServicesVersion(serverDetails) + if err != nil { + return err + } var specFile *spec.SpecFiles if c.IsFlagSet(flags.SpecFlag) && len(c.GetStringFlagValue(flags.SpecFlag)) > 0 { specFile, err = pluginsCommon.GetFileSystemSpec(c) @@ -233,6 +237,8 @@ func ScanCmd(c *components.Context) error { return err } scanCmd := scan.NewScanCommand(). + SetXrayVersion(xrayVersion). + SetXscVersion(xscVersion). SetServerDetails(serverDetails). SetThreads(threads). SetSpec(specFile). @@ -369,7 +375,7 @@ func BuildScan(c *components.Context) error { } func AuditCmd(c *components.Context) error { - auditCmd, err := CreateAuditCmd(c) + xrayVersion, xscVersion, serverDetails, auditCmd, err := CreateAuditCmd(c) if err != nil { return err } @@ -419,7 +425,7 @@ func AuditCmd(c *components.Context) error { auditCmd.SetThreads(threads) err = progressbar.ExecWithProgress(auditCmd) // Reporting error if Xsc service is enabled - reportErrorIfExists(err, auditCmd) + reportErrorIfExists(xrayVersion, xscVersion, serverDetails, err) return err } @@ -428,46 +434,42 @@ func shouldAddSubScan(subScan utils.SubScanType, c *components.Context) bool { (subScan == utils.ContextualAnalysisScan && c.GetBoolFlagValue(flags.Sca) && !c.GetBoolFlagValue(flags.WithoutCA)) || (subScan == utils.SecretTokenValidationScan && c.GetBoolFlagValue(flags.Secrets) && c.GetBoolFlagValue(flags.SecretValidation)) } -func reportErrorIfExists(err error, auditCmd *audit.AuditCommand) { +func reportErrorIfExists(xrayVersion, xscVersion string, serverDetails *coreConfig.ServerDetails, err error) { if err == nil || !usage.ShouldReportUsage() { return } - var serverDetails *coreConfig.ServerDetails - serverDetails, innerError := auditCmd.ServerDetails() - if innerError != nil { - log.Debug(fmt.Sprintf("failed to get server details for error report: %q", innerError)) - return - } - if reportError := xsc.ReportError(serverDetails, err, "cli"); reportError != nil { + if reportError := xsc.ReportError(xrayVersion, xscVersion, serverDetails, err, "cli"); reportError != nil { log.Debug("failed to report error log:" + reportError.Error()) } } -func CreateAuditCmd(c *components.Context) (*audit.AuditCommand, error) { +func CreateAuditCmd(c *components.Context) (string, string, *coreConfig.ServerDetails, *audit.AuditCommand, error) { auditCmd := audit.NewGenericAuditCommand() serverDetails, err := createServerDetailsWithConfigOffer(c) if err != nil { - return nil, err + return "", "", nil, nil, err } err = validateXrayContext(c, serverDetails) if err != nil { - return nil, err + return "", "", nil, nil, err + } + xrayVersion, xscVersion, err := GetJfrogServicesVersion(serverDetails) + if err != nil { + return "", "", nil, nil, err } format, err := outputFormat.GetOutputFormat(c.GetStringFlagValue(flags.OutputFormat)) if err != nil { - return nil, err + return "", "", nil, nil, err } minSeverity, err := getMinimumSeverity(c) if err != nil { - return nil, err + return "", "", nil, nil, err } scansOutputDir, err := getAndValidateOutputDirExistsIfProvided(c) if err != nil { - return nil, err + return "", "", nil, nil, err } - auditCmd.SetAnalyticsMetricsService(xsc.NewAnalyticsMetricsService(serverDetails)) - auditCmd.SetTargetRepoPath(addTrailingSlashToRepoPathIfNeeded(c)). SetProject(getProject(c)). SetIncludeVulnerabilities(c.GetBoolFlagValue(flags.Vuln) || shouldIncludeVulnerabilities(c)). @@ -489,6 +491,8 @@ func CreateAuditCmd(c *components.Context) (*audit.AuditCommand, error) { auditCmd.SetWorkingDirs(splitByCommaAndTrim(c.GetStringFlagValue(flags.WorkingDirs))) } auditCmd.SetServerDetails(serverDetails). + SetXrayVersion(xrayVersion). + SetXscVersion(xscVersion). SetExcludeTestDependencies(c.GetBoolFlagValue(flags.ExcludeTestDeps)). SetOutputFormat(format). SetUseJas(true). @@ -497,7 +501,7 @@ func CreateAuditCmd(c *components.Context) (*audit.AuditCommand, error) { SetNpmScope(c.GetStringFlagValue(flags.DepType)). SetPipRequirementsFile(c.GetStringFlagValue(flags.RequirementsFile)). SetExclusions(pluginsCommon.GetStringsArrFlagValue(c, flags.Exclusions)) - return auditCmd, err + return xrayVersion, xscVersion, serverDetails, auditCmd, err } func logNonGenericAuditCommandDeprecation(cmdName string) { @@ -513,7 +517,7 @@ func logNonGenericAuditCommandDeprecation(cmdName string) { func AuditSpecificCmd(c *components.Context, technology techutils.Technology) error { logNonGenericAuditCommandDeprecation(c.CommandName) - auditCmd, err := CreateAuditCmd(c) + xrayVersion, xscVersion, serverDetails, auditCmd, err := CreateAuditCmd(c) if err != nil { return err } @@ -522,7 +526,7 @@ func AuditSpecificCmd(c *components.Context, technology techutils.Technology) er err = progressbar.ExecWithProgress(auditCmd) // Reporting error if Xsc service is enabled - reportErrorIfExists(err, auditCmd) + reportErrorIfExists(xrayVersion, xscVersion, serverDetails, err) return err } @@ -709,6 +713,10 @@ func DockerScan(c *components.Context, image string) error { if err != nil { return err } + xrayVersion, xscVersion, err := GetJfrogServicesVersion(serverDetails) + if err != nil { + return err + } containerScanCommand := scan.NewDockerScanCommand() format, err := outputFormat.GetOutputFormat(c.GetStringFlagValue(flags.OutputFormat)) if err != nil { @@ -721,6 +729,8 @@ func DockerScan(c *components.Context, image string) error { containerScanCommand.SetImageTag(image). SetTargetRepoPath(addTrailingSlashToRepoPathIfNeeded(c)). SetServerDetails(serverDetails). + SetXrayVersion(xrayVersion). + SetXscVersion(xscVersion). SetOutputFormat(format). SetProject(getProject(c)). SetIncludeVulnerabilities(c.GetBoolFlagValue(flags.Vuln) || shouldIncludeVulnerabilities(c)). @@ -731,10 +741,32 @@ func DockerScan(c *components.Context, image string) error { SetFixableOnly(c.GetBoolFlagValue(flags.FixableOnly)). SetMinSeverityFilter(minSeverity). SetThreads(threads). - SetAnalyticsMetricsService(xsc.NewAnalyticsMetricsService(serverDetails)). SetSecretValidation(c.GetBoolFlagValue(flags.SecretValidation)) if c.GetStringFlagValue(flags.Watches) != "" { containerScanCommand.SetWatches(splitByCommaAndTrim(c.GetStringFlagValue(flags.Watches))) } return progressbar.ExecWithProgress(containerScanCommand) } + +func GetJfrogServicesVersion(serverDetails *coreConfig.ServerDetails) (xrayVersion, xscVersion string, err error) { + xrayManager, err := xray.CreateXrayServiceManager(serverDetails) + if err != nil { + return + } + xrayVersion, err = xrayManager.GetVersion() + if err != nil { + return + } + log.Debug("Xray version: " + xrayVersion) + xscService, err := xsc.CreateXscService(xrayVersion, serverDetails) + if err != nil { + return + } + xscVersion, e := xscService.GetVersion() + if e != nil { + log.Debug("Using Xray: " + e.Error()) + return + } + log.Debug("XSC version: " + xscVersion) + return +} diff --git a/commands/audit/audit.go b/commands/audit/audit.go index 557e2854..5d347f5f 100644 --- a/commands/audit/audit.go +++ b/commands/audit/audit.go @@ -33,15 +33,14 @@ import ( ) type AuditCommand struct { - watches []string - projectKey string - targetRepoPath string - IncludeVulnerabilities bool - IncludeLicenses bool - Fail bool - PrintExtendedTable bool - analyticsMetricsService *xsc.AnalyticsMetricsService - Threads int + watches []string + projectKey string + targetRepoPath string + IncludeVulnerabilities bool + IncludeLicenses bool + Fail bool + PrintExtendedTable bool + Threads int AuditParams } @@ -84,11 +83,6 @@ func (auditCmd *AuditCommand) SetPrintExtendedTable(printExtendedTable bool) *Au return auditCmd } -func (auditCmd *AuditCommand) SetAnalyticsMetricsService(analyticsMetricsService *xsc.AnalyticsMetricsService) *AuditCommand { - auditCmd.analyticsMetricsService = analyticsMetricsService - return auditCmd -} - func (auditCmd *AuditCommand) SetThreads(threads int) *AuditCommand { auditCmd.Threads = threads return auditCmd @@ -103,7 +97,6 @@ func (auditCmd *AuditCommand) CreateCommonGraphScanParams() *scangraph.CommonGra commonParams.ProjectKey = auditCmd.projectKey commonParams.IncludeVulnerabilities = auditCmd.IncludeVulnerabilities commonParams.IncludeLicenses = auditCmd.IncludeLicenses - commonParams.MultiScanId, commonParams.XscVersion = xsc.GetXscMsiAndVersion(auditCmd.analyticsMetricsService) return commonParams } @@ -114,9 +107,18 @@ func (auditCmd *AuditCommand) Run() (err error) { if err != nil { return } + serverDetails, err := auditCmd.ServerDetails() + if err != nil { + return + } + + multiScanId, startTime := xsc.SendNewScanEvent( + auditCmd.GetXrayVersion(), + auditCmd.GetXscVersion(), + serverDetails, + xsc.CreateAnalyticsEvent(xscservices.CliProduct, xscservices.CliEventType, serverDetails), + ) - // Should be called before creating the audit params, so the params will contain XSC information. - auditCmd.analyticsMetricsService.AddGeneralEvent(auditCmd.analyticsMetricsService.CreateGeneralEvent(xscservices.CliProduct, xscservices.CliEventType)) auditParams := NewAuditParams(). SetWorkingDirs(workingDirs). SetMinSeverityFilter(auditCmd.minSeverityFilter). @@ -125,11 +127,12 @@ func (auditCmd *AuditCommand) Run() (err error) { SetCommonGraphScanParams(auditCmd.CreateCommonGraphScanParams()). SetThirdPartyApplicabilityScan(auditCmd.thirdPartyApplicabilityScan). SetThreads(auditCmd.Threads). - SetScansResultsOutputDir(auditCmd.scanResultsOutputDir) + SetScansResultsOutputDir(auditCmd.scanResultsOutputDir).SetStartTime(startTime).SetMultiScanId(multiScanId) auditParams.SetIsRecursiveScan(isRecursiveScan).SetExclusions(auditCmd.Exclusions()) auditResults := RunAudit(auditParams) - auditCmd.analyticsMetricsService.UpdateGeneralEvent(auditCmd.analyticsMetricsService.CreateXscAnalyticsGeneralEventFinalizeFromAuditResults(auditResults)) + + xsc.SendScanEndedWithResults(serverDetails, auditResults) if auditCmd.Progress() != nil { if err = auditCmd.Progress().Quit(); err != nil { @@ -217,7 +220,7 @@ func isEntitledForJas(xrayManager *xray.XrayServicesManager, auditParams *AuditP // Dry run without JAS return false, nil } - return jas.IsEntitledForJas(xrayManager, auditParams.xrayVersion) + return jas.IsEntitledForJas(xrayManager, auditParams.GetXrayVersion()) } func RunJasScans(auditParallelRunner *utils.SecurityParallelRunner, auditParams *AuditParams, scanResults *results.SecurityCommandResults, jfrogAppsConfig *jfrogappsconfig.JFrogAppsConfig) (jasScanner *jas.JasScanner, generalError error) { @@ -231,7 +234,7 @@ func RunJasScans(auditParallelRunner *utils.SecurityParallelRunner, auditParams return } auditParallelRunner.ResultsMu.Lock() - jasScanner, err = jas.CreateJasScanner(serverDetails, scanResults.SecretValidation, auditParams.minSeverityFilter, jas.GetAnalyzerManagerXscEnvVars(auditParams.commonGraphScanParams.MultiScanId, scanResults.GetTechnologies()...), auditParams.Exclusions()...) + jasScanner, err = jas.CreateJasScanner(serverDetails, scanResults.SecretValidation, auditParams.minSeverityFilter, jas.GetAnalyzerManagerXscEnvVars(auditParams.GetMultiScanId(), scanResults.GetTechnologies()...), auditParams.Exclusions()...) auditParallelRunner.ResultsMu.Unlock() if err != nil { generalError = fmt.Errorf("failed to create jas scanner: %s", err.Error()) @@ -300,13 +303,16 @@ func initAuditCmdResults(params *AuditParams) (cmdResults *results.SecurityComma if err != nil { return cmdResults.AddGeneralError(err, false) } - var xrayManager *xray.XrayServicesManager - if xrayManager, params.xrayVersion, err = xrayutils.CreateXrayServiceManagerAndGetVersion(serverDetails); err != nil { + if err = clientutils.ValidateMinimumVersion(clientutils.Xray, params.GetXrayVersion(), scangraph.GraphScanMinXrayVersion); err != nil { return cmdResults.AddGeneralError(err, false) - } else { - cmdResults.SetXrayVersion(params.xrayVersion) } - if err = clientutils.ValidateMinimumVersion(clientutils.Xray, params.xrayVersion, scangraph.GraphScanMinXrayVersion); err != nil { + cmdResults.SetXrayVersion(params.GetXrayVersion()) + cmdResults.SetXscVersion(params.GetXscVersion()) + cmdResults.SetMultiScanId(params.GetMultiScanId()) + cmdResults.SetStartTime(params.StartTime()) + // Send entitlement requests + xrayManager, err := xrayutils.CreateXrayServiceManager(serverDetails) + if err != nil { return cmdResults.AddGeneralError(err, false) } entitledForJas, err := isEntitledForJas(xrayManager, params) @@ -316,9 +322,8 @@ func initAuditCmdResults(params *AuditParams) (cmdResults *results.SecurityComma cmdResults.SetEntitledForJas(entitledForJas) } if entitledForJas { - cmdResults.SetSecretValidation(jas.CheckForSecretValidation(xrayManager, params.xrayVersion, slices.Contains(params.AuditBasicParams.ScansToPerform(), utils.SecretTokenValidationScan))) + cmdResults.SetSecretValidation(jas.CheckForSecretValidation(xrayManager, params.GetXrayVersion(), slices.Contains(params.AuditBasicParams.ScansToPerform(), utils.SecretTokenValidationScan))) } - cmdResults.SetMultiScanId(params.commonGraphScanParams.MultiScanId) // Initialize targets detectScanTargets(cmdResults, params) if params.IsRecursiveScan() && len(params.workingDirs) == 1 && len(cmdResults.Targets) == 0 { diff --git a/commands/audit/audit_test.go b/commands/audit/audit_test.go index 248e4907..420bef09 100644 --- a/commands/audit/audit_test.go +++ b/commands/audit/audit_test.go @@ -405,7 +405,7 @@ func TestAuditWithConfigProfile(t *testing.T) { for _, testcase := range testcases { t.Run(testcase.name, func(t *testing.T) { - mockServer, serverDetails := validations.XrayServer(t, utils.EntitlementsMinVersion) + mockServer, serverDetails := validations.XrayServer(t, validations.MockServerParams{XrayVersion: utils.EntitlementsMinVersion, XscVersion: services.ConfigProfileMinXscVersion}) defer mockServer.Close() tempDirPath, createTempDirCallback := coreTests.CreateTempDirWithCallbackAndAssert(t) @@ -414,20 +414,21 @@ func TestAuditWithConfigProfile(t *testing.T) { auditBasicParams := (&utils.AuditBasicParams{}). SetServerDetails(serverDetails). + SetXrayVersion(utils.EntitlementsMinVersion). + SetXscVersion(services.ConfigProfileMinXscVersion). SetOutputFormat(format.Table). SetUseJas(true) configProfile := testcase.configProfile auditParams := NewAuditParams(). SetWorkingDirs([]string{tempDirPath}). + SetMultiScanId(validations.TestMsi). SetGraphBasicParams(auditBasicParams). SetConfigProfile(&configProfile). SetCommonGraphScanParams(&scangraph.CommonGraphScanParams{ RepoPath: "", ScanType: scanservices.Dependency, IncludeVulnerabilities: true, - XscVersion: services.ConfigProfileMinXscVersion, - MultiScanId: validations.TestMsi, }) auditParams.SetWorkingDirs([]string{tempDirPath}).SetIsRecursiveScan(true) @@ -462,7 +463,7 @@ func TestAuditWithConfigProfile(t *testing.T) { // This test tests audit flow when providing --output-dir flag func TestAuditWithScansOutputDir(t *testing.T) { - mockServer, serverDetails := validations.XrayServer(t, utils.EntitlementsMinVersion) + mockServer, serverDetails := validations.XrayServer(t, validations.MockServerParams{XrayVersion: utils.EntitlementsMinVersion}) defer mockServer.Close() outputDirPath, removeOutputDirCallback := coreTests.CreateTempDirWithCallbackAndAssert(t) @@ -476,15 +477,16 @@ func TestAuditWithScansOutputDir(t *testing.T) { auditBasicParams := (&utils.AuditBasicParams{}). SetServerDetails(serverDetails). SetOutputFormat(format.Table). + SetXrayVersion(utils.EntitlementsMinVersion). SetUseJas(true) auditParams := NewAuditParams(). SetWorkingDirs([]string{tempDirPath}). + SetMultiScanId(validations.TestScaScanId). SetGraphBasicParams(auditBasicParams). SetCommonGraphScanParams(&scangraph.CommonGraphScanParams{ ScanType: scanservices.Dependency, IncludeVulnerabilities: true, - MultiScanId: validations.TestScaScanId, }). SetScansResultsOutputDir(outputDirPath) auditParams.SetIsRecursiveScan(true) @@ -612,17 +614,18 @@ func TestAuditWithPartialResults(t *testing.T) { auditBasicParams := (&utils.AuditBasicParams{}). SetServerDetails(serverDetails). SetOutputFormat(format.Table). + SetXrayVersion("3.108.0"). SetUseJas(testcase.useJas). SetAllowPartialResults(testcase.allowPartialResults). SetPipRequirementsFile(testcase.pipRequirementsFile) auditParams := NewAuditParams(). SetWorkingDirs([]string{tempDirPath}). + SetMultiScanId(validations.TestScaScanId). SetGraphBasicParams(auditBasicParams). SetCommonGraphScanParams(&scangraph.CommonGraphScanParams{ ScanType: scanservices.Dependency, IncludeVulnerabilities: true, - MultiScanId: validations.TestScaScanId, }) auditParams.SetIsRecursiveScan(true) diff --git a/commands/audit/auditparams.go b/commands/audit/auditparams.go index a2f34678..595c9673 100644 --- a/commands/audit/auditparams.go +++ b/commands/audit/auditparams.go @@ -1,6 +1,8 @@ package audit import ( + "time" + xrayutils "github.com/jfrog/jfrog-cli-security/utils" "github.com/jfrog/jfrog-cli-security/utils/severityutils" "github.com/jfrog/jfrog-cli-security/utils/xray/scangraph" @@ -16,12 +18,13 @@ type AuditParams struct { fixableOnly bool minSeverityFilter severityutils.Severity *xrayutils.AuditBasicParams - xrayVersion string + multiScanId string // Include third party dependencies source code in the applicability scan. thirdPartyApplicabilityScan bool threads int configProfile *clientservices.ConfigProfile scanResultsOutputDir string + startTime time.Time } func NewAuditParams() *AuditParams { @@ -38,8 +41,22 @@ func (params *AuditParams) WorkingDirs() []string { return params.workingDirs } -func (params *AuditParams) XrayVersion() string { - return params.xrayVersion +func (params *AuditParams) SetMultiScanId(msi string) *AuditParams { + params.multiScanId = msi + return params +} + +func (params *AuditParams) GetMultiScanId() string { + return params.multiScanId +} + +func (params *AuditParams) SetStartTime(startTime time.Time) *AuditParams { + params.startTime = startTime + return params +} + +func (params *AuditParams) StartTime() time.Time { + return params.startTime } func (params *AuditParams) SetGraphBasicParams(gbp *xrayutils.AuditBasicParams) *AuditParams { @@ -113,7 +130,5 @@ func (params *AuditParams) createXrayGraphScanParams() *services.XrayGraphScanPa ProjectKey: params.commonGraphScanParams.ProjectKey, IncludeVulnerabilities: params.commonGraphScanParams.IncludeVulnerabilities, IncludeLicenses: params.commonGraphScanParams.IncludeLicenses, - XscVersion: params.commonGraphScanParams.XscVersion, - MultiScanId: params.commonGraphScanParams.MultiScanId, } } diff --git a/commands/audit/sca/common.go b/commands/audit/sca/common.go index 901ae18c..158e188b 100644 --- a/commands/audit/sca/common.go +++ b/commands/audit/sca/common.go @@ -38,6 +38,7 @@ func GetExcludePattern(params utils.AuditParams) string { } func RunXrayDependenciesTreeScanGraph(dependencyTree xrayUtils.GraphNode, technology techutils.Technology, scanGraphParams *scangraph.ScanGraphParams) (results []services.ScanResponse, err error) { + scanGraphParams.XrayGraphScanParams().XrayVersion = scanGraphParams.XrayVersion() scanGraphParams.XrayGraphScanParams().DependenciesGraph = &dependencyTree xscGitInfoContext := scanGraphParams.XrayGraphScanParams().XscGitInfoContext if xscGitInfoContext != nil { diff --git a/commands/audit/scarunner.go b/commands/audit/scarunner.go index c3f816c6..11f27237 100644 --- a/commands/audit/scarunner.go +++ b/commands/audit/scarunner.go @@ -146,10 +146,14 @@ func executeScaScanTask(auditParallelRunner *utils.SecurityParallelRunner, serve func runScaWithTech(tech techutils.Technology, params *AuditParams, serverDetails *config.ServerDetails, flatTree xrayCmdUtils.GraphNode, fullDependencyTrees []*xrayCmdUtils.GraphNode) (techResults []services.ScanResponse, err error) { + xrayScanGraphParams := params.createXrayGraphScanParams() + xrayScanGraphParams.MultiScanId = params.GetMultiScanId() + xrayScanGraphParams.XscVersion = params.GetXscVersion() + scanGraphParams := scangraph.NewScanGraphParams(). SetServerDetails(serverDetails). - SetXrayGraphScanParams(params.createXrayGraphScanParams()). - SetXrayVersion(params.xrayVersion). + SetXrayGraphScanParams(xrayScanGraphParams). + SetXrayVersion(params.GetXrayVersion()). SetFixableOnly(params.fixableOnly). SetSeverityLevel(params.minSeverityFilter.String()) techResults, err = sca.RunXrayDependenciesTreeScanGraph(flatTree, tech, scanGraphParams) diff --git a/commands/scan/dockerscan.go b/commands/scan/dockerscan.go index 421455c7..f200628e 100644 --- a/commands/scan/dockerscan.go +++ b/commands/scan/dockerscan.go @@ -3,17 +3,18 @@ package scan import ( "bytes" "fmt" - xscservices "github.com/jfrog/jfrog-client-go/xsc/services" "os" "os/exec" "path/filepath" "strings" + xscservices "github.com/jfrog/jfrog-client-go/xsc/services" + "github.com/jfrog/jfrog-cli-core/v2/common/spec" "github.com/jfrog/jfrog-cli-security/utils" "github.com/jfrog/jfrog-cli-security/utils/results" "github.com/jfrog/jfrog-cli-security/utils/results/output" - "github.com/jfrog/jfrog-cli-security/utils/xray" + "github.com/jfrog/jfrog-cli-security/utils/xsc" clientutils "github.com/jfrog/jfrog-client-go/utils" "github.com/jfrog/jfrog-client-go/utils/errorutils" "github.com/jfrog/jfrog-client-go/utils/io/fileutils" @@ -47,11 +48,7 @@ func (dsc *DockerScanCommand) SetTargetRepoPath(repoPath string) *DockerScanComm func (dsc *DockerScanCommand) Run() (err error) { // Validate Xray minimum version - _, xrayVersion, err := xray.CreateXrayServiceManagerAndGetVersion(dsc.ScanCommand.serverDetails) - if err != nil { - return err - } - err = clientutils.ValidateMinimumVersion(clientutils.Xray, xrayVersion, DockerScanMinXrayVersion) + err = clientutils.ValidateMinimumVersion(clientutils.Xray, dsc.xrayVersion, DockerScanMinXrayVersion) if err != nil { return err } @@ -82,7 +79,13 @@ func (dsc *DockerScanCommand) Run() (err error) { } // Perform scan on image.tar - dsc.analyticsMetricsService.AddGeneralEvent(dsc.analyticsMetricsService.CreateGeneralEvent(xscservices.CliProduct, xscservices.CliEventType)) + dsc.multiScanId, dsc.startTime = xsc.SendNewScanEvent( + dsc.xrayVersion, + dsc.xscVersion, + dsc.serverDetails, + xsc.CreateAnalyticsEvent(xscservices.CliProduct, xscservices.CliEventType, dsc.serverDetails), + ) + dsc.SetSpec(spec.NewBuilder(). Pattern(imageTarPath). Target(dsc.targetRepoPath). @@ -102,7 +105,7 @@ func (dsc *DockerScanCommand) Run() (err error) { if scanResults == nil { return } - dsc.analyticsMetricsService.UpdateGeneralEvent(dsc.analyticsMetricsService.CreateXscAnalyticsGeneralEventFinalizeFromAuditResults(scanResults)) + xsc.SendScanEndedWithResults(dsc.serverDetails, scanResults) return dsc.recordResults(scanResults) }) } diff --git a/commands/scan/scan.go b/commands/scan/scan.go index 6ee5a7c5..a661d8dd 100644 --- a/commands/scan/scan.go +++ b/commands/scan/scan.go @@ -9,8 +9,7 @@ import ( "path/filepath" "regexp" "strings" - - "github.com/jfrog/jfrog-cli-security/utils/xsc" + "time" "golang.org/x/exp/maps" "golang.org/x/exp/slices" @@ -74,9 +73,13 @@ type ScanCommand struct { fixableOnly bool progress ioUtils.ProgressMgr // JAS is only supported for Docker images. - commandSupportsJAS bool - targetNameOverride string - analyticsMetricsService *xsc.AnalyticsMetricsService + commandSupportsJAS bool + targetNameOverride string + + xrayVersion string + xscVersion string + multiScanId string + startTime time.Time } func (scanCmd *ScanCommand) SetMinSeverityFilter(minSeverityFilter severityutils.Severity) *ScanCommand { @@ -167,8 +170,13 @@ func (scanCmd *ScanCommand) SetBypassArchiveLimits(bypassArchiveLimits bool) *Sc return scanCmd } -func (scanCmd *ScanCommand) SetAnalyticsMetricsService(analyticsMetricsService *xsc.AnalyticsMetricsService) *ScanCommand { - scanCmd.analyticsMetricsService = analyticsMetricsService +func (scanCmd *ScanCommand) SetXrayVersion(xrayVersion string) *ScanCommand { + scanCmd.xrayVersion = xrayVersion + return scanCmd +} + +func (scanCmd *ScanCommand) SetXscVersion(xscVersion string) *ScanCommand { + scanCmd.xscVersion = xscVersion return scanCmd } @@ -269,7 +277,17 @@ func (scanCmd *ScanCommand) RunAndRecordResults(cmdType utils.CommandType, recor } func (scanCmd *ScanCommand) RunScan(cmdType utils.CommandType) (cmdResults *results.SecurityCommandResults) { - xrayManager, cmdResults := initScanCmdResults(cmdType, scanCmd.serverDetails, scanCmd.analyticsMetricsService, scanCmd.bypassArchiveLimits, scanCmd.validateSecrets, scanCmd.commandSupportsJAS) + xrayManager, cmdResults := initScanCmdResults( + cmdType, + scanCmd.serverDetails, + scanCmd.xrayVersion, + scanCmd.xscVersion, + scanCmd.multiScanId, + scanCmd.startTime, + scanCmd.bypassArchiveLimits, + scanCmd.validateSecrets, + scanCmd.commandSupportsJAS, + ) if cmdResults.GeneralError != nil { return } @@ -309,24 +327,28 @@ func (scanCmd *ScanCommand) RunScan(cmdType utils.CommandType) (cmdResults *resu return } -func initScanCmdResults(cmdType utils.CommandType, serverDetails *config.ServerDetails, analyticsMetricsService *xsc.AnalyticsMetricsService, bypassArchiveLimits, validateSecrets, useJas bool) (xrayManager *xrayClient.XrayServicesManager, cmdResults *results.SecurityCommandResults) { +func initScanCmdResults(cmdType utils.CommandType, serverDetails *config.ServerDetails, xrayVersion, xscVersion, msi string, startTime time.Time, bypassArchiveLimits, validateSecrets, useJas bool) (xrayManager *xrayClient.XrayServicesManager, cmdResults *results.SecurityCommandResults) { cmdResults = results.NewCommandResults(cmdType) - xrayManager, xrayVersion, err := xray.CreateXrayServiceManagerAndGetVersion(serverDetails) - if err != nil { - return xrayManager, cmdResults.AddGeneralError(err, false) - } else { - cmdResults.SetXrayVersion(xrayVersion) - } // Validate Xray minimum version for graph scan command - if err = clientutils.ValidateMinimumVersion(clientutils.Xray, xrayVersion, scangraph.GraphScanMinXrayVersion); err != nil { + if err := clientutils.ValidateMinimumVersion(clientutils.Xray, xrayVersion, scangraph.GraphScanMinXrayVersion); err != nil { return xrayManager, cmdResults.AddGeneralError(err, false) } if bypassArchiveLimits { // Validate Xray minimum version for BypassArchiveLimits flag for indexer - if err = clientutils.ValidateMinimumVersion(clientutils.Xray, xrayVersion, BypassArchiveLimitsMinXrayVersion); err != nil { + if err := clientutils.ValidateMinimumVersion(clientutils.Xray, xrayVersion, BypassArchiveLimitsMinXrayVersion); err != nil { return xrayManager, cmdResults.AddGeneralError(err, false) } } + xrayManager, err := xray.CreateXrayServiceManager(serverDetails) + if err != nil { + return xrayManager, cmdResults.AddGeneralError(err, false) + } + // Initialize general information + cmdResults.SetXrayVersion(xrayVersion) + cmdResults.SetXscVersion(xscVersion) + cmdResults.SetMultiScanId(msi) + cmdResults.SetStartTime(startTime) + // Send entitlement request if entitledForJas, err := isEntitledForJas(xrayManager, xrayVersion, useJas); err != nil { return xrayManager, cmdResults.AddGeneralError(err, false) } else { @@ -335,9 +357,6 @@ func initScanCmdResults(cmdType utils.CommandType, serverDetails *config.ServerD cmdResults.SetSecretValidation(jas.CheckForSecretValidation(xrayManager, xrayVersion, validateSecrets)) } } - if analyticsMetricsService != nil { - cmdResults.SetMultiScanId(analyticsMetricsService.GetMsi()) - } return } @@ -432,7 +451,8 @@ func (scanCmd *ScanCommand) createIndexerHandlerFunc(file *spec.File, cmdResults ProjectKey: scanCmd.projectKey, ScanType: services.Binary, } - params.MultiScanId, params.XscVersion = xsc.GetXscMsiAndVersion(scanCmd.analyticsMetricsService) + params.MultiScanId = cmdResults.MultiScanId + params.XscVersion = cmdResults.XscVersion if scanCmd.progress != nil { scanCmd.progress.SetHeadlineMsg("Scanning 🔍") } diff --git a/go.mod b/go.mod index d1cb19f1..cbf7b91d 100644 --- a/go.mod +++ b/go.mod @@ -1,6 +1,8 @@ module github.com/jfrog/jfrog-cli-security -go 1.22.7 +go 1.22.9 + +toolchain go1.23.3 require ( github.com/beevik/etree v1.4.0 @@ -16,9 +18,9 @@ require ( github.com/owenrumney/go-sarif/v2 v2.3.0 github.com/stretchr/testify v1.9.0 github.com/urfave/cli v1.22.16 - golang.org/x/exp v0.0.0-20241009180824-f66d83c29e7c - golang.org/x/sync v0.8.0 - golang.org/x/text v0.19.0 + golang.org/x/exp v0.0.0-20241108190413-2d47ceb2692f + golang.org/x/sync v0.9.0 + golang.org/x/text v0.20.0 gopkg.in/yaml.v3 v3.0.1 ) @@ -27,7 +29,7 @@ require ( github.com/BurntSushi/toml v1.4.0 // indirect github.com/CycloneDX/cyclonedx-go v0.9.0 // indirect github.com/Microsoft/go-winio v0.6.1 // indirect - github.com/ProtonMail/go-crypto v1.0.0 // indirect + github.com/ProtonMail/go-crypto v1.1.2 // indirect github.com/VividCortex/ewma v1.2.0 // indirect github.com/acarl005/stripansi v0.0.0-20180116102854-5a71ef0e047d // indirect github.com/andybalholm/brotli v1.1.0 // indirect @@ -99,21 +101,25 @@ require ( github.com/xo/terminfo v0.0.0-20210125001918-ca9a967f8778 // indirect go.uber.org/atomic v1.9.0 // indirect go.uber.org/multierr v1.9.0 // indirect - golang.org/x/crypto v0.28.0 // indirect - golang.org/x/mod v0.21.0 // indirect - golang.org/x/net v0.30.0 // indirect + golang.org/x/crypto v0.29.0 // indirect + golang.org/x/mod v0.22.0 // indirect + golang.org/x/net v0.31.0 // indirect golang.org/x/oauth2 v0.20.0 // indirect - golang.org/x/sys v0.26.0 // indirect - golang.org/x/term v0.25.0 // indirect + golang.org/x/sys v0.27.0 // indirect + golang.org/x/term v0.26.0 // indirect golang.org/x/time v0.5.0 // indirect - golang.org/x/tools v0.26.0 // indirect + golang.org/x/tools v0.27.0 // indirect gopkg.in/ini.v1 v1.67.0 // indirect gopkg.in/warnings.v0 v0.1.2 // indirect ) // replace github.com/jfrog/jfrog-cli-core/v2 => github.com/jfrog/jfrog-cli-core/v2 dev -// replace github.com/jfrog/jfrog-client-go => github.com/jfrog/jfrog-client-go dev +replace github.com/jfrog/jfrog-client-go => github.com/jfrog/jfrog-client-go v1.28.1-0.20241124172451-50bd3e54f1e0 + +// replace github.com/jfrog/jfrog-client-go => ../cli-projects/jfrog-client-go + +// replace github.com/jfrog/jfrog-client-go => github.com/attiasas/jfrog-client-go v0.0.0-20241118103817-1d4e8d19de4f // replace github.com/jfrog/build-info-go => github.com/jfrog/build-info-go dev diff --git a/go.sum b/go.sum index 114c02cb..22da96e3 100644 --- a/go.sum +++ b/go.sum @@ -9,8 +9,8 @@ github.com/CycloneDX/cyclonedx-go v0.9.0/go.mod h1:NE/EWvzELOFlG6+ljX/QeMlVt9VKc github.com/Microsoft/go-winio v0.5.2/go.mod h1:WpS1mjBmmwHBEWmogvA2mj8546UReBk4v8QkMxJ6pZY= github.com/Microsoft/go-winio v0.6.1 h1:9/kr64B9VUZrLm5YYwbGtUJnMgqWVOdUAXu6Migciow= github.com/Microsoft/go-winio v0.6.1/go.mod h1:LRdKpFKfdobln8UmuiYcKPot9D2v6svN5+sAH+4kjUM= -github.com/ProtonMail/go-crypto v1.0.0 h1:LRuvITjQWX+WIfr930YHG2HNfjR1uOfyf5vE0kC2U78= -github.com/ProtonMail/go-crypto v1.0.0/go.mod h1:EjAoLdwvbIOoOQr3ihjnSoLZRtE8azugULFRteWMNc0= +github.com/ProtonMail/go-crypto v1.1.2 h1:A7JbD57ThNqh7XjmHE+PXpQ3Dqt3BrSAC0AL0Go3KS0= +github.com/ProtonMail/go-crypto v1.1.2/go.mod h1:rA3QumHc/FZ8pAHreoekgiAbzpNsfQAosU5td4SnOrE= github.com/VividCortex/ewma v1.2.0 h1:f58SaIzcDXrSy3kWaHNvuJgJ3Nmz59Zji6XoJR/q1ow= github.com/VividCortex/ewma v1.2.0/go.mod h1:nz4BbCtbLyFDeC9SUHbtcT5644juEuWfUAUnGx7j5l4= github.com/acarl005/stripansi v0.0.0-20180116102854-5a71ef0e047d h1:licZJFw2RwpHMqeKTCYkitsPqHNxTmd4SNR5r94FGM8= @@ -28,7 +28,6 @@ github.com/bradleyjkemp/cupaloy/v2 v2.8.0 h1:any4BmKE+jGIaMpnU8YgH/I2LPiLBufr6oM github.com/bradleyjkemp/cupaloy/v2 v2.8.0/go.mod h1:bm7JXdkRd4BHJk9HpwqAI8BoAY1lps46Enkdqw6aRX0= github.com/buger/jsonparser v1.1.1 h1:2PnMjfWD7wBILjqQbt530v576A/cAbQvEW9gGIpYMUs= github.com/buger/jsonparser v1.1.1/go.mod h1:6RYKKt7H4d4+iWqouImQ9R2FZql3VbhNgx27UK13J/0= -github.com/bwesterb/go-ristretto v1.2.3/go.mod h1:fUIoIZaG73pV5biE2Blr2xEzDoMj7NFEuV9ekS419A0= github.com/c-bata/go-prompt v0.2.5 h1:3zg6PecEywxNn0xiqcXHD96fkbxghD+gdB2tbsYfl+Y= github.com/c-bata/go-prompt v0.2.5/go.mod h1:vFnjEGDIIA/Lib7giyE4E9c50Lvl8j0S+7FVlAwDAVw= github.com/chzyer/logex v1.1.10/go.mod h1:+Ywpsq7O8HXn0nuIou7OrIPyXbp3wmkHB+jjWRnGsAI= @@ -40,7 +39,6 @@ github.com/chzyer/readline v1.5.1/go.mod h1:Eh+b79XXUwfKfcPLepksvw2tcLE/Ct21YObk github.com/chzyer/test v0.0.0-20180213035817-a1ea475d72b1/go.mod h1:Q3SI9o4m/ZMnBNeIyt5eFwwo7qiLfzFZmjNmxjkiQlU= github.com/chzyer/test v1.0.0 h1:p3BQDXSxOhOG0P9z6/hGnII4LGiEPOYBhs8asl/fC04= github.com/chzyer/test v1.0.0/go.mod h1:2JlltgoNkt4TW/z9V/IzDdFaMTM2JPIi26O1pF38GC8= -github.com/cloudflare/circl v1.3.3/go.mod h1:5XYMA4rFBvNIrhs50XuiBJ15vF2pZn4nnUKZrLbUZFA= github.com/cloudflare/circl v1.4.0 h1:BV7h5MgrktNzytKmWjpOtdYrf0lkkbF8YMlBGPhJQrY= github.com/cloudflare/circl v1.4.0/go.mod h1:PDRU+oXvdD7KCtgKxW95M5Z8BpSCJXQORiZFnBQS5QU= github.com/cpuguy83/go-md2man/v2 v2.0.5 h1:ZtcqGrnekaHpVLArFSe4HK5DoKx1T0rq2DwVB0alcyc= @@ -131,8 +129,8 @@ github.com/jfrog/jfrog-apps-config v1.0.1 h1:mtv6k7g8A8BVhlHGlSveapqf4mJfonwvXYL github.com/jfrog/jfrog-apps-config v1.0.1/go.mod h1:8AIIr1oY9JuH5dylz2S6f8Ym2MaadPLR6noCBO4C22w= github.com/jfrog/jfrog-cli-core/v2 v2.56.7 h1:pB4ronzVk60k/lf9bUL9HxBZ8PbMW6LhbIFld9NXNNc= github.com/jfrog/jfrog-cli-core/v2 v2.56.7/go.mod h1:puLwWcnXYCJqUOvhscXRJiKNzPdj0adP+zadKy6A/gU= -github.com/jfrog/jfrog-client-go v1.47.6 h1:nEMwJvjsuuY6LpOV3e33P4c4irPHkG8Qxw27bgeCl/Y= -github.com/jfrog/jfrog-client-go v1.47.6/go.mod h1:jCpvS83DZHAin2aSG7VroTsILJsyq7AOcFfx++P241E= +github.com/jfrog/jfrog-client-go v1.28.1-0.20241124172451-50bd3e54f1e0 h1:YROG+bJY4QJEz9KdKUbBlbOHXY1vnDhhi0/cXrEgu9E= +github.com/jfrog/jfrog-client-go v1.28.1-0.20241124172451-50bd3e54f1e0/go.mod h1:1a7bmQHkRmPEza9wva2+WVrYzrGbosrMymq57kyG5gU= github.com/k0kubun/colorstring v0.0.0-20150214042306-9440f1994b88/go.mod h1:3w7q1U84EfirKl04SVQ/s7nPm1ZPhiXd34z40TNz36k= github.com/k0kubun/pp v3.0.1+incompatible/go.mod h1:GWse8YhT0p8pT4ir3ZgBbfZild3tgzSScAn6HmfYukg= github.com/kevinburke/ssh_config v1.2.0 h1:x584FjTGwHzMwvHx18PXxbBVzfnxogHaAReU4gf13a4= @@ -284,18 +282,16 @@ go.uber.org/multierr v1.9.0/go.mod h1:X2jQV1h+kxSjClGpnseKVIxpmcjrj7MNnI0bnlfKTV golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w= golang.org/x/crypto v0.0.0-20210921155107-089bfa567519/go.mod h1:GvvjBRRGRdwPK5ydBHafDWAxML/pGHZbMvKqRZ5+Abc= golang.org/x/crypto v0.0.0-20220622213112-05595931fe9d/go.mod h1:IxCIyHEi3zRg3s0A5j5BB6A9Jmi73HwBIUl50j+osU4= -golang.org/x/crypto v0.3.1-0.20221117191849-2c476679df9a/go.mod h1:hebNnKkNXi2UzZN1eVRvBB7co0a+JxK6XbPiWVs/3J4= -golang.org/x/crypto v0.7.0/go.mod h1:pYwdfH91IfpZVANVyUOhSIPZaFoJGxTFbZhFTx+dXZU= golang.org/x/crypto v0.19.0/go.mod h1:Iy9bg/ha4yyC70EfRS8jz+B6ybOBKMaSxLj6P6oBDfU= golang.org/x/crypto v0.22.0/go.mod h1:vr6Su+7cTlO45qkww3VDJlzDn0ctJvRgYbC2NvXHt+M= -golang.org/x/crypto v0.28.0 h1:GBDwsMXVQi34v5CCYUm2jkJvu4cbtru2U4TN2PSyQnw= -golang.org/x/crypto v0.28.0/go.mod h1:rmgy+3RHxRZMyY0jjAJShp2zgEdOqj2AO7U0pYmeQ7U= -golang.org/x/exp v0.0.0-20241009180824-f66d83c29e7c h1:7dEasQXItcW1xKJ2+gg5VOiBnqWrJc+rq0DPKyvvdbY= -golang.org/x/exp v0.0.0-20241009180824-f66d83c29e7c/go.mod h1:NQtJDoLvd6faHhE7m4T/1IY708gDefGGjR/iUW8yQQ8= +golang.org/x/crypto v0.29.0 h1:L5SG1JTTXupVV3n6sUqMTeWbjAyfPwoda2DLX8J8FrQ= +golang.org/x/crypto v0.29.0/go.mod h1:+F4F4N5hv6v38hfeYwTdx20oUvLLc+QfrE9Ax9HtgRg= +golang.org/x/exp v0.0.0-20241108190413-2d47ceb2692f h1:XdNn9LlyWAhLVp6P/i8QYBW+hlyhrhei9uErw2B5GJo= +golang.org/x/exp v0.0.0-20241108190413-2d47ceb2692f/go.mod h1:D5SMRVC3C2/4+F/DB1wZsLRnSNimn2Sp/NPsCrsv8ak= golang.org/x/mod v0.6.0-dev.0.20220419223038-86c51ed26bb4/go.mod h1:jJ57K6gSWd91VN4djpZkiMVwK6gcyfeH4XE8wZrZaV4= golang.org/x/mod v0.8.0/go.mod h1:iBbtSCu2XBx23ZKBPSOrRkjjQPZFPuis4dIYUhu/chs= -golang.org/x/mod v0.21.0 h1:vvrHzRwRfVKSiLrG+d4FMl/Qi4ukBCE6kZlTUkDYRT0= -golang.org/x/mod v0.21.0/go.mod h1:6SkKJ3Xj0I0BrPOZoBy3bdMptDDU9oJrpohJ3eWZ1fY= +golang.org/x/mod v0.22.0 h1:D4nJWe9zXqHOmWqj4VMOJhvzj7bEZg4wEYa759z1pH4= +golang.org/x/mod v0.22.0/go.mod h1:6SkKJ3Xj0I0BrPOZoBy3bdMptDDU9oJrpohJ3eWZ1fY= golang.org/x/net v0.0.0-20180724234803-3673e40ba225/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= golang.org/x/net v0.0.0-20190108225652-1e06a53dbb7e/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= golang.org/x/net v0.0.0-20190603091049-60506f45cf65/go.mod h1:HSz+uSET+XFnRR8LxR5pz3Of3rY3CfYBVs4xY44aLks= @@ -304,15 +300,13 @@ golang.org/x/net v0.0.0-20200301022130-244492dfa37a/go.mod h1:z5CRVTTTmAJ677TzLL golang.org/x/net v0.0.0-20210226172049-e18ecbb05110/go.mod h1:m0MpNAwzfU5UDzcl9v0D8zg8gWTRqZa9RBIspLL5mdg= golang.org/x/net v0.0.0-20211112202133-69e39bad7dc2/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y= golang.org/x/net v0.0.0-20220722155237-a158d28d115b/go.mod h1:XRhObCWvk6IyKnWLug+ECip1KBveYUHfp+8e9klMJ9c= -golang.org/x/net v0.2.0/go.mod h1:KqCZLdyyvdV855qA2rE3GC2aiw5xGR5TEjj8smXukLY= golang.org/x/net v0.6.0/go.mod h1:2Tu9+aMcznHK/AK1HMvgo6xiTLG5rD5rZLDS+rp2Bjs= golang.org/x/net v0.7.0/go.mod h1:2Tu9+aMcznHK/AK1HMvgo6xiTLG5rD5rZLDS+rp2Bjs= -golang.org/x/net v0.8.0/go.mod h1:QVkue5JL9kW//ek3r6jTKnTFis1tRmNAW2P1shuFdJc= golang.org/x/net v0.10.0/go.mod h1:0qNGK6F8kojg2nk9dLZ2mShWaEBan6FAoqfSigmmuDg= golang.org/x/net v0.21.0/go.mod h1:bIjVDfnllIU7BJ2DNgfnXvpSvtn8VRwhlsaeUTyUS44= golang.org/x/net v0.24.0/go.mod h1:2Q7sJY5mzlzWjKtYUEXSlBWCdyaioyXzRB2RtU8KVE8= -golang.org/x/net v0.30.0 h1:AcW1SDZMkb8IpzCdQUaIq2sP4sZ4zw+55h6ynffypl4= -golang.org/x/net v0.30.0/go.mod h1:2wGyMJ5iFasEhkwi13ChkO/t1ECNC4X4eBKkVFyYFlU= +golang.org/x/net v0.31.0 h1:68CPQngjLL0r2AlUKiSxtQFKvzRVbnzLwMUn5SzcLHo= +golang.org/x/net v0.31.0/go.mod h1:P4fl1q7dY2hnZFxEk4pPSkDHF+QqjitcnDjUQyMM+pM= golang.org/x/oauth2 v0.0.0-20190604053449-0f29369cfe45/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw= golang.org/x/oauth2 v0.20.0 h1:4mQdhULixXKP1rwYBW0vAijoXnkTG0BLCDRzfe1idMo= golang.org/x/oauth2 v0.20.0/go.mod h1:XYTD2NtWslqkgxebSiOHnXEap4TF09sJSc7H1sXbhtI= @@ -321,8 +315,8 @@ golang.org/x/sync v0.0.0-20190423024810-112230192c58/go.mod h1:RxMgew5VJxzue5/jJ golang.org/x/sync v0.0.0-20190911185100-cd5d95a43a6e/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sync v0.0.0-20220722155255-886fb9371eb4/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sync v0.1.0/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= -golang.org/x/sync v0.8.0 h1:3NFvSEYkUoMifnESzZl15y791HH1qU2xm6eCJU5ZPXQ= -golang.org/x/sync v0.8.0/go.mod h1:Czt+wKu1gCyEFDUtn0jG5QVvpJ6rzVqr5aXyt9drQfk= +golang.org/x/sync v0.9.0 h1:fEo0HyrW1GIgZdpbhCRO0PkJajUS5H9IFUztCgEo2jQ= +golang.org/x/sync v0.9.0/go.mod h1:Czt+wKu1gCyEFDUtn0jG5QVvpJ6rzVqr5aXyt9drQfk= golang.org/x/sys v0.0.0-20181122145206-62eef0e2fa9b/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= golang.org/x/sys v0.0.0-20190222072716-a9d3bda3a223/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= @@ -344,46 +338,40 @@ golang.org/x/sys v0.0.0-20220715151400-c0bba94af5f8/go.mod h1:oPkhp1MJrh7nUepCBc golang.org/x/sys v0.0.0-20220722155257-8c9f86f7a55f/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20220811171246-fbc7d0a398ab/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.1.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= -golang.org/x/sys v0.2.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= -golang.org/x/sys v0.3.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.5.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.6.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.8.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.17.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA= golang.org/x/sys v0.19.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA= -golang.org/x/sys v0.26.0 h1:KHjCJyddX0LoSTb3J+vWpupP9p0oznkqVk/IfjymZbo= -golang.org/x/sys v0.26.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA= +golang.org/x/sys v0.27.0 h1:wBqf8DvsY9Y/2P8gAfPDEYNuS30J4lPHJxXSb/nJZ+s= +golang.org/x/sys v0.27.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA= golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo= golang.org/x/term v0.0.0-20210927222741-03fcf44c2211/go.mod h1:jbD1KX2456YbFQfuXm/mYQcufACuNUgVhRMnK/tPxf8= -golang.org/x/term v0.2.0/go.mod h1:TVmDHMZPmdnySmBfhjOoOdhjzdE1h4u1VwSiw2l1Nuc= golang.org/x/term v0.5.0/go.mod h1:jMB1sMXY+tzblOD4FWmEbocvup2/aLOaQEp7JmGp78k= -golang.org/x/term v0.6.0/go.mod h1:m6U89DPEgQRMq3DNkDClhWw02AUbt2daBVO4cn4Hv9U= golang.org/x/term v0.8.0/go.mod h1:xPskH00ivmX89bAKVGSKKtLOWNx2+17Eiy94tnKShWo= golang.org/x/term v0.17.0/go.mod h1:lLRBjIVuehSbZlaOtGMbcMncT+aqLLLmKrsjNrUguwk= golang.org/x/term v0.19.0/go.mod h1:2CuTdWZ7KHSQwUzKva0cbMg6q2DMI3Mmxp+gKJbskEk= -golang.org/x/term v0.25.0 h1:WtHI/ltw4NvSUig5KARz9h521QvRC8RmF/cuYqifU24= -golang.org/x/term v0.25.0/go.mod h1:RPyXicDX+6vLxogjjRxjgD2TKtmAO6NZBsBRfrOLu7M= +golang.org/x/term v0.26.0 h1:WEQa6V3Gja/BhNxg540hBip/kkaYtRg3cxg4oXSw4AU= +golang.org/x/term v0.26.0/go.mod h1:Si5m1o57C5nBNQo5z1iq+XDijt21BDBDp2bK0QI8e3E= golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= golang.org/x/text v0.3.2/go.mod h1:bEr9sfX3Q8Zfm5fL9x+3itogRgK3+ptLWKqgva+5dAk= golang.org/x/text v0.3.3/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ= golang.org/x/text v0.3.5/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ= golang.org/x/text v0.3.6/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ= golang.org/x/text v0.3.7/go.mod h1:u+2+/6zg+i71rQMx5EYifcz6MCKuco9NR6JIITiCfzQ= -golang.org/x/text v0.4.0/go.mod h1:mrYo+phRRbMaCq/xk9113O4dZlRixOauAjOtrjsXDZ8= golang.org/x/text v0.7.0/go.mod h1:mrYo+phRRbMaCq/xk9113O4dZlRixOauAjOtrjsXDZ8= -golang.org/x/text v0.8.0/go.mod h1:e1OnstbJyHTd6l/uOt8jFFHp6TRDWZR/bV3emEE/zU8= golang.org/x/text v0.9.0/go.mod h1:e1OnstbJyHTd6l/uOt8jFFHp6TRDWZR/bV3emEE/zU8= golang.org/x/text v0.14.0/go.mod h1:18ZOQIKpY8NJVqYksKHtTdi31H5itFRjB5/qKTNYzSU= -golang.org/x/text v0.19.0 h1:kTxAhCbGbxhK0IwgSKiMO5awPoDQ0RpfiVYBfK860YM= -golang.org/x/text v0.19.0/go.mod h1:BuEKDfySbSR4drPmRPG/7iBdf8hvFMuRexcpahXilzY= +golang.org/x/text v0.20.0 h1:gK/Kv2otX8gz+wn7Rmb3vT96ZwuoxnQlY+HlJVj7Qug= +golang.org/x/text v0.20.0/go.mod h1:D4IsuqiFMhST5bX19pQ9ikHC2GsaKyk/oF+pn3ducp4= golang.org/x/time v0.5.0 h1:o7cqy6amK/52YcAKIPlM3a+Fpj35zvRj2TP+e1xFSfk= golang.org/x/time v0.5.0/go.mod h1:3BpzKBy/shNhVucY/MWOyx10tF3SFh9QdLuxbVysPQM= golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= golang.org/x/tools v0.0.0-20191119224855-298f0cb1881e/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= golang.org/x/tools v0.1.12/go.mod h1:hNGJHUnrk76NpqgfD5Aqm5Crs+Hm0VOH/i9J2+nxYbc= golang.org/x/tools v0.6.0/go.mod h1:Xwgl3UAJ/d3gWutnCtw505GrjyAbvKui8lOU390QaIU= -golang.org/x/tools v0.26.0 h1:v/60pFQmzmT9ExmjDv2gGIfi3OqfKoEP6I5+umXlbnQ= -golang.org/x/tools v0.26.0/go.mod h1:TPVVj70c7JJ3WCazhD8OdXcZg/og+b9+tH/KxylGwH0= +golang.org/x/tools v0.27.0 h1:qEKojBykQkQ4EynWy4S8Weg69NumxKdn40Fce3uc/8o= +golang.org/x/tools v0.27.0/go.mod h1:sUi0ZgbwW9ZPAq26Ekut+weQPR5eIM6GQLQ1Yjm1H0Q= golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= google.golang.org/appengine v1.4.0/go.mod h1:xpcJRLb0r/rnEns0DIKYYv+WjYCduHsrkT7/EB5XEv4= diff --git a/tests/utils/integration/test_integrationutils.go b/tests/utils/integration/test_integrationutils.go index ac20c728..e5a1e5bc 100644 --- a/tests/utils/integration/test_integrationutils.go +++ b/tests/utils/integration/test_integrationutils.go @@ -61,14 +61,16 @@ func GetTestServerDetails() *config.ServerDetails { return configTests.RtDetails } -func InitXscTest(t *testing.T, validations ...func()) func() { +func InitXscTest(t *testing.T, validations ...func()) (string, string, func()) { if !*configTests.TestXsc { t.Skip(getSkipTestMsg("XSC integration", "--test.xsc")) } + xrayVersion, err := testUtils.GetTestsXrayVersion() + assert.NoError(t, err) // validate XSC is enabled at the given server - xscManager, err := xsc.CreateXscServiceManager(configTests.XscDetails) + xscService, err := xsc.CreateXscService(xrayVersion.GetVersion(), configTests.XscDetails) assert.NoError(t, err) - _, err = xscManager.GetVersion() + xscVersion, err := xscService.GetVersion() if err != nil { t.Skip("Skipping XSC integration tests. XSC is not enabled at the given server.") } @@ -77,7 +79,7 @@ func InitXscTest(t *testing.T, validations ...func()) func() { } // Make sure the audit request will work with xsc and not xray assert.NoError(t, os.Setenv(coreutils.ReportUsage, "true")) - return func() { + return xrayVersion.GetVersion(), xscVersion, func() { assert.NoError(t, os.Setenv(coreutils.ReportUsage, "false")) } } diff --git a/tests/utils/test_utils.go b/tests/utils/test_utils.go index 6d4f88b1..e747bed7 100644 --- a/tests/utils/test_utils.go +++ b/tests/utils/test_utils.go @@ -49,7 +49,7 @@ func UnmarshalXML(t *testing.T, output string) formats.Bom { } func ValidateXrayVersion(t *testing.T, minVersion string) { - xrayVersion, err := getTestsXrayVersion() + xrayVersion, err := GetTestsXrayVersion() if err != nil { assert.NoError(t, err) return @@ -95,7 +95,7 @@ func removeDirs(dirs ...string) { } } -func getTestsXrayVersion() (version.Version, error) { +func GetTestsXrayVersion() (version.Version, error) { xrayVersion, err := configTests.XrAuth.GetVersion() return *version.NewVersion(xrayVersion), err } diff --git a/utils/auditbasicparams.go b/utils/auditbasicparams.go index 049cbcbe..5e2b7727 100644 --- a/utils/auditbasicparams.go +++ b/utils/auditbasicparams.go @@ -67,6 +67,8 @@ type AuditBasicParams struct { isRecursiveScan bool skipAutoInstall bool allowPartialResults bool + xrayVersion string + xscVersion string } func (abp *AuditBasicParams) DirectDependencies() *[]string { @@ -275,3 +277,21 @@ func (abp *AuditBasicParams) SkipAutoInstall() bool { func (abp *AuditBasicParams) AllowPartialResults() bool { return abp.allowPartialResults } + +func (abp *AuditBasicParams) SetXrayVersion(xrayVersion string) *AuditBasicParams { + abp.xrayVersion = xrayVersion + return abp +} + +func (abp *AuditBasicParams) GetXrayVersion() string { + return abp.xrayVersion +} + +func (abp *AuditBasicParams) SetXscVersion(xscVersion string) *AuditBasicParams { + abp.xscVersion = xscVersion + return abp +} + +func (abp *AuditBasicParams) GetXscVersion() string { + return abp.xscVersion +} diff --git a/utils/results/results.go b/utils/results/results.go index c01d1856..e954ff4f 100644 --- a/utils/results/results.go +++ b/utils/results/results.go @@ -5,6 +5,7 @@ import ( "fmt" "strings" "sync" + "time" "github.com/jfrog/gofrog/datastructures" "github.com/jfrog/jfrog-cli-security/utils" @@ -19,9 +20,11 @@ import ( type SecurityCommandResults struct { // General fields describing the command metadata XrayVersion string `json:"xray_version"` + XscVersion string `json:"xsc_version,omitempty"` EntitledForJas bool `json:"jas_entitled"` SecretValidation bool `json:"secret_validation,omitempty"` CmdType utils.CommandType `json:"command_type"` + StartTime time.Time `json:"start_time"` // MultiScanId is a unique identifier that is used to group multiple scans together. MultiScanId string `json:"multi_scan_id,omitempty"` // Results for each target in the command @@ -91,11 +94,21 @@ func NewCommandResults(cmdType utils.CommandType) *SecurityCommandResults { return &SecurityCommandResults{CmdType: cmdType, targetsMutex: sync.Mutex{}, errorsMutex: sync.Mutex{}} } +func (r *SecurityCommandResults) SetStartTime(startTime time.Time) *SecurityCommandResults { + r.StartTime = startTime + return r +} + func (r *SecurityCommandResults) SetXrayVersion(xrayVersion string) *SecurityCommandResults { r.XrayVersion = xrayVersion return r } +func (r *SecurityCommandResults) SetXscVersion(xscVersion string) *SecurityCommandResults { + r.XscVersion = xscVersion + return r +} + func (r *SecurityCommandResults) SetEntitledForJas(entitledForJas bool) *SecurityCommandResults { r.EntitledForJas = entitledForJas return r diff --git a/utils/validations/test_mocks.go b/utils/validations/test_mocks.go index 3b835a36..a1123b77 100644 --- a/utils/validations/test_mocks.go +++ b/utils/validations/test_mocks.go @@ -2,15 +2,17 @@ package validations import ( "fmt" - "github.com/jfrog/jfrog-cli-core/v2/artifactory/utils" - "github.com/jfrog/jfrog-cli-core/v2/utils/config" - "github.com/jfrog/jfrog-client-go/artifactory" - "github.com/stretchr/testify/assert" "net/http" "net/http/httptest" "os" "strings" "testing" + + "github.com/jfrog/jfrog-cli-core/v2/artifactory/utils" + "github.com/jfrog/jfrog-cli-core/v2/utils/config" + "github.com/jfrog/jfrog-client-go/artifactory" + xscutils "github.com/jfrog/jfrog-client-go/xsc/services/utils" + "github.com/stretchr/testify/assert" ) const ( @@ -25,7 +27,7 @@ const ( ) var ( - versionApiUrl = "/%s/api/v1/system/version" + versionApiUrl = "/%s/%ssystem/version" ) type restsTestHandler func(w http.ResponseWriter, r *http.Request) @@ -50,24 +52,53 @@ func CreateXrayRestsMockServer(testHandler restsTestHandler) (*httptest.Server, return testServer, serverDetails } -func XscServer(t *testing.T, xscVersion string) (*httptest.Server, *config.ServerDetails) { - serverMock, serverDetails, _ := CreateXscRestsMockServer(t, func(w http.ResponseWriter, r *http.Request) { - if r.RequestURI == fmt.Sprintf(versionApiUrl, "xsc") { - _, err := w.Write([]byte(fmt.Sprintf(`{"xsc_version": "%s"}`, xscVersion))) +type MockServerParams struct { + XrayVersion string + XscVersion string + XscNotExists bool + ReturnMsi string +} + +func XscServer(t *testing.T, params MockServerParams) (*httptest.Server, *config.ServerDetails) { + if !xscutils.IsXscXrayInnerService(params.XrayVersion) { + serverMock, serverDetails, _ := CreateXscRestsMockServer(t, getXscServerApiHandler(t, params)) + return serverMock, serverDetails + } + return XrayServer(t, params) +} + +func getXscServerApiHandler(t *testing.T, params MockServerParams) func(w http.ResponseWriter, r *http.Request) { + apiUrlPart := "api/v1/" + if xscutils.IsXscXrayInnerService(params.XrayVersion) { + apiUrlPart = "" + } + return func(w http.ResponseWriter, r *http.Request) { + if params.XscNotExists { + w.WriteHeader(http.StatusNotFound) + _, err := w.Write([]byte("Xsc service is not enabled")) + assert.NoError(t, err) + return + } + + if r.RequestURI == fmt.Sprintf(versionApiUrl, apiUrlPart, "xsc") { + _, err := w.Write([]byte(fmt.Sprintf(`{"xsc_version": "%s"}`, params.XscVersion))) if !assert.NoError(t, err) { return } } - if r.RequestURI == "/xsc/api/v1/event" { + if strings.Contains(r.RequestURI, "/xsc/"+apiUrlPart+"event") { if r.Method == http.MethodPost { w.WriteHeader(http.StatusCreated) - _, err := w.Write([]byte(fmt.Sprintf(`{"multi_scan_id": "%s"}`, TestMsi))) + if params.ReturnMsi == "" { + params.ReturnMsi = TestMsi + } + _, err := w.Write([]byte(fmt.Sprintf(`{"multi_scan_id": "%s"}`, params.ReturnMsi))) if !assert.NoError(t, err) { return } } } - if r.RequestURI == "/xsc/api/v1/profile/"+TestConfigProfileName { + if strings.Contains(r.RequestURI, "/xsc/"+apiUrlPart+"profile/"+TestConfigProfileName) { if r.Method == http.MethodGet { w.WriteHeader(http.StatusOK) content, err := os.ReadFile("../../tests/testdata/other/configProfile/configProfileExample.json") @@ -80,14 +111,14 @@ func XscServer(t *testing.T, xscVersion string) (*httptest.Server, *config.Serve } } } - }) - return serverMock, serverDetails + w.WriteHeader(http.StatusNotFound) + } } -func XrayServer(t *testing.T, xrayVersion string) (*httptest.Server, *config.ServerDetails) { +func XrayServer(t *testing.T, params MockServerParams) (*httptest.Server, *config.ServerDetails) { serverMock, serverDetails := CreateXrayRestsMockServer(func(w http.ResponseWriter, r *http.Request) { - if r.RequestURI == fmt.Sprintf(versionApiUrl, "xray") { - _, err := w.Write([]byte(fmt.Sprintf(`{"xray_version": "%s", "xray_revision": "xxx"}`, xrayVersion))) + if r.RequestURI == fmt.Sprintf(versionApiUrl, "api/v1/", "xray") { + _, err := w.Write([]byte(fmt.Sprintf(`{"xray_version": "%s", "xray_revision": "xxx"}`, params.XrayVersion))) if !assert.NoError(t, err) { return } @@ -122,6 +153,10 @@ func XrayServer(t *testing.T, xrayVersion string) (*httptest.Server, *config.Ser } } } + if !xscutils.IsXscXrayInnerService(params.XrayVersion) { + return + } + getXscServerApiHandler(t, params)(w, r) }) return serverMock, serverDetails } diff --git a/utils/xray/scangraph/params.go b/utils/xray/scangraph/params.go index e6547cd9..eb51f2ee 100644 --- a/utils/xray/scangraph/params.go +++ b/utils/xray/scangraph/params.go @@ -20,8 +20,6 @@ type CommonGraphScanParams struct { ScanType services.ScanType IncludeVulnerabilities bool IncludeLicenses bool - XscVersion string - MultiScanId string } func NewScanGraphParams() *ScanGraphParams { diff --git a/utils/xray/scangraph/scangraph.go b/utils/xray/scangraph/scangraph.go index d5342113..bca54ed7 100644 --- a/utils/xray/scangraph/scangraph.go +++ b/utils/xray/scangraph/scangraph.go @@ -26,7 +26,7 @@ func RunScanGraphAndGetResults(params *ScanGraphParams, xrayManager *xray.XraySe } xscEnabled := params.xrayGraphScanParams.XscVersion != "" - scanResult, err := xrayManager.GetScanGraphResults(scanId, params.XrayGraphScanParams().IncludeVulnerabilities, params.XrayGraphScanParams().IncludeLicenses, xscEnabled) + scanResult, err := xrayManager.GetScanGraphResults(scanId, params.xrayVersion, params.XrayGraphScanParams().IncludeVulnerabilities, params.XrayGraphScanParams().IncludeLicenses, xscEnabled) if err != nil { return nil, err } diff --git a/utils/xsc/analyticsmetrics.go b/utils/xsc/analyticsmetrics.go index 3fb4d795..a72ffa55 100644 --- a/utils/xsc/analyticsmetrics.go +++ b/utils/xsc/analyticsmetrics.go @@ -13,205 +13,155 @@ import ( "github.com/jfrog/jfrog-cli-security/utils/results/conversion" clientutils "github.com/jfrog/jfrog-client-go/utils" "github.com/jfrog/jfrog-client-go/utils/log" - "github.com/jfrog/jfrog-client-go/xsc" xscservices "github.com/jfrog/jfrog-client-go/xsc/services" ) -type AnalyticsMetricsService struct { - xscManager *xsc.XscServicesManager - // Should the CLI reports analytics metrics to XSC. - shouldReportEvents bool - msi string - startTime time.Time - // In the case of multiple scanning, aggregate all audit results into one finalize event. - finalizeEvent *xscservices.XscAnalyticsGeneralEventFinalize -} - -func NewAnalyticsMetricsService(serviceDetails *config.ServerDetails) *AnalyticsMetricsService { - ams := AnalyticsMetricsService{} - xscManager, err := CreateXscServiceManager(serviceDetails) - if err != nil { - // When an error occurs, shouldReportEvents will be false and no XscServiceManager commands will be executed. - log.Debug(fmt.Sprintf("Failed to create xsc manager for analytics metrics service. %s", err.Error())) - return &ams - } - ams.xscManager = xscManager - ams.shouldReportEvents = ams.calcShouldReportEvents() - return &ams -} - -func (ams *AnalyticsMetricsService) calcShouldReportEvents() bool { - // A user who explicitly requests not to send reports will not receive XSC analytics metrics. - if !usage.ShouldReportUsage() { - return false - } - // Verify xsc version. - xscVersion, err := ams.xscManager.GetVersion() - if err != nil { - return false - } - if err = clientutils.ValidateMinimumVersion(clientutils.Xsc, xscVersion, xscservices.AnalyticsMetricsMinXscVersion); err != nil { - return false - } - return true -} - -func (ams *AnalyticsMetricsService) XscManager() *xsc.XscServicesManager { - return ams.xscManager -} - -func (ams *AnalyticsMetricsService) SetMsi(msi string) { - ams.msi = msi -} - -func (ams *AnalyticsMetricsService) GetMsi() string { - return ams.msi -} - -func (ams *AnalyticsMetricsService) SetStartTime() { - ams.startTime = time.Now() -} - -func (ams *AnalyticsMetricsService) GetStartTime() time.Time { - return ams.startTime -} - -func (ams *AnalyticsMetricsService) ShouldReportEvents() bool { - return ams.shouldReportEvents -} - -func (ams *AnalyticsMetricsService) FinalizeEvent() *xscservices.XscAnalyticsGeneralEventFinalize { - return ams.finalizeEvent -} - -func (ams *AnalyticsMetricsService) SetFinalizeEvent(finalizeEvent *xscservices.XscAnalyticsGeneralEventFinalize) { - ams.finalizeEvent = finalizeEvent -} - -func (ams *AnalyticsMetricsService) CreateGeneralEvent(product xscservices.ProductName, eventType xscservices.EventType) *xscservices.XscAnalyticsGeneralEvent { - osAndArc, err := coreutils.GetOSAndArc() - curOs, curArch := "", "" - if err != nil { - log.Debug(fmt.Errorf("failed to get os and arcitucture for general event request to XSC service, error: %s ", err.Error())) - } else { - splitOsAndArch := strings.Split(osAndArc, "-") - curOs = splitOsAndArch[0] - curArch = splitOsAndArch[1] - } - +func CreateAnalyticsEvent(product xscservices.ProductName, eventType xscservices.EventType, serviceDetails *config.ServerDetails) *xscservices.XscAnalyticsGeneralEvent { + curOs, curArch := getOsAndArch() event := xscservices.XscAnalyticsGeneralEvent{ XscAnalyticsBasicGeneralEvent: xscservices.XscAnalyticsBasicGeneralEvent{ EventType: eventType, EventStatus: xscservices.Started, Product: product, - JfrogUser: ams.xscManager.Config().GetServiceDetails().GetUser(), + JfrogUser: serviceDetails.GetUser(), OsPlatform: curOs, OsArchitecture: curArch, + JpdVersion: serviceDetails.ServerId, AnalyzerManagerVersion: jas.GetAnalyzerManagerVersion(), }, } return &event } -func (ams *AnalyticsMetricsService) AddGeneralEvent(event *xscservices.XscAnalyticsGeneralEvent) { - if !ams.ShouldReportEvents() { - log.Debug("Analytics metrics are disabled, skipping sending event request to XSC") +func SendNewScanEvent(xrayVersion, xscVersion string, serviceDetails *config.ServerDetails, event *xscservices.XscAnalyticsGeneralEvent) (multiScanId string, startTime time.Time) { + if !shouldReportEvents(xscVersion) { + log.Debug("Analytics metrics are disabled, skip sending event request to XSC") return } - msi, err := ams.xscManager.AddAnalyticsGeneralEvent(*event) + xscService, err := CreateXscService(xrayVersion, serviceDetails) if err != nil { - log.Debug(fmt.Errorf("failed sending general event request to XSC service, error: %s ", err.Error())) + log.Debug(fmt.Sprintf("failed to create xsc manager for analytics metrics service, error: %s ", err.Error())) return } - log.Debug(fmt.Sprintf("New General event added successfully. multi_scan_id %s", msi)) - // Set event's analytics data. - ams.SetMsi(msi) - ams.SetStartTime() + if multiScanId, err = xscService.AddAnalyticsGeneralEvent(*event); err != nil { + log.Debug(fmt.Sprintf("failed sending general event request to XSC service, error: %s ", err.Error())) + return + } + startTime = time.Now() + return } -func (ams *AnalyticsMetricsService) UpdateGeneralEvent(event *xscservices.XscAnalyticsGeneralEventFinalize) { - if !ams.ShouldReportEvents() { - log.Debug("Analytics metrics are disabled, skipping sending update event request to XSC") +func SendScanEndedEvent(xrayVersion, xscVersion string, serviceDetails *config.ServerDetails, multiScanId string, startTime time.Time, totalFindings int, scanError error) { + if !shouldReportEvents(xscVersion) { return } - if ams.msi == "" { - log.Debug("MultiScanId is empty, skipping update general event.") + if multiScanId == "" { + log.Debug("MultiScanId is empty, skip sending command finalize event.") return } - err := ams.xscManager.UpdateAnalyticsGeneralEvent(*event) + // Generate the finalize event. + xscService, err := CreateXscService(xrayVersion, serviceDetails) if err != nil { - log.Debug(fmt.Sprintf("failed updading general event request in XSC service for multi_scan_id %s, error: %s \"", ams.GetMsi(), err.Error())) - } else { - log.Debug(fmt.Sprintf("General event updated\n%v", *event)) + log.Debug(fmt.Sprintf("failed to create xsc manager for analytics metrics service, skip sending command finalize event, error: %s ", err.Error())) + return } -} -func (ams *AnalyticsMetricsService) GetGeneralEvent(msi string) (*xscservices.XscAnalyticsGeneralEvent, error) { - if !ams.ShouldReportEvents() { - log.Debug("Can't get general event from XSC - analytics metrics are disabled.") - return nil, nil + event := CreateFinalizedEvent(multiScanId, startTime, totalFindings, scanError) + + if err = xscService.UpdateAnalyticsGeneralEvent(event); err != nil { + log.Debug(fmt.Sprintf("failed updating general event in XSC service for multi_scan_id %s, error: %s \"", multiScanId, err.Error())) + return } - event, err := ams.xscManager.GetAnalyticsGeneralEvent(msi) - if err != nil { - log.Debug(fmt.Sprintf("failed getting general event from XSC service for multi_scan_id %s, error: %s \"", msi, err.Error())) + log.Debug(fmt.Sprintf("Command event:\n%v", event)) +} + +func SendScanEndedWithResults(serviceDetails *config.ServerDetails, cmdResults *results.SecurityCommandResults) { + if cmdResults == nil || serviceDetails == nil { + return } - return event, err + SendScanEndedEvent( + cmdResults.XrayVersion, + cmdResults.XscVersion, + serviceDetails, + cmdResults.MultiScanId, + cmdResults.StartTime, + getTotalFindings(cmdResults), + cmdResults.GetErrors(), + ) } -func (ams *AnalyticsMetricsService) CreateXscAnalyticsGeneralEventFinalizeFromAuditResults(auditResults *results.SecurityCommandResults) *xscservices.XscAnalyticsGeneralEventFinalize { - totalDuration := time.Since(ams.GetStartTime()) +func CreateFinalizedEvent(multiScanId string, startTime time.Time, totalFindings int, err error) xscservices.XscAnalyticsGeneralEventFinalize { + totalDuration := time.Since(startTime) eventStatus := xscservices.Completed - if auditResults.GetErrors() != nil { - eventStatus = xscservices.Failed - } - summary, err := conversion.NewCommandResultsConvertor(conversion.ResultConvertParams{IncludeVulnerabilities: true, HasViolationContext: true}).ConvertToSummary(auditResults) if err != nil { - log.Warn(fmt.Sprintf("Failed to convert audit results to summary. %s", err.Error())) - } - var totalFindings int - if summary.HasViolations() { - totalFindings = summary.GetTotalViolations() - } else { - totalFindings = summary.GetTotalVulnerabilities() - } - // return summary.GetTotalVulnerabilities() - basicEvent := xscservices.XscAnalyticsBasicGeneralEvent{ - EventStatus: eventStatus, - TotalFindings: totalFindings, - TotalScanDuration: totalDuration.String(), + eventStatus = xscservices.Failed } - return &xscservices.XscAnalyticsGeneralEventFinalize{ - MultiScanId: ams.msi, - XscAnalyticsBasicGeneralEvent: basicEvent, + return xscservices.XscAnalyticsGeneralEventFinalize{ + MultiScanId: multiScanId, + XscAnalyticsBasicGeneralEvent: xscservices.XscAnalyticsBasicGeneralEvent{ + EventStatus: eventStatus, + TotalFindings: totalFindings, + TotalScanDuration: totalDuration.String(), + }, } } -func (ams *AnalyticsMetricsService) UpdateAndSendXscAnalyticsGeneralEventFinalize(err error) { - if !ams.ShouldReportEvents() { - return +func createFinalizedEvent(cmdResults *results.SecurityCommandResults) xscservices.XscAnalyticsGeneralEventFinalize { + return CreateFinalizedEvent(cmdResults.MultiScanId, cmdResults.StartTime, getTotalFindings(cmdResults), cmdResults.GetErrors()) +} + +func GetScanEvent(xrayVersion, xscVersion, multiScanId string, serviceDetails *config.ServerDetails) (*xscservices.XscAnalyticsGeneralEvent, error) { + if !shouldReportEvents(xscVersion) { + log.Debug("Can't get general event from XSC - analytics metrics are disabled.") + return nil, nil } + xscService, err := CreateXscService(xrayVersion, serviceDetails) if err != nil { - ams.UpdateXscAnalyticsGeneralEventFinalizeStatus(xscservices.Failed) - } else { - ams.UpdateXscAnalyticsGeneralEventFinalizeWithTotalScanDuration() - ams.UpdateXscAnalyticsGeneralEventFinalizeStatus(xscservices.Completed) + log.Debug(fmt.Sprintf("failed to create xsc manager for analytics metrics service, skip getting general event, error: %s ", err.Error())) + return nil, err } - ams.UpdateGeneralEvent(ams.FinalizeEvent()) -} - -func (ams *AnalyticsMetricsService) UpdateXscAnalyticsGeneralEventFinalizeWithTotalScanDuration() { - totalDuration := time.Since(ams.GetStartTime()) - ams.finalizeEvent.TotalScanDuration = totalDuration.String() + event, err := xscService.GetAnalyticsGeneralEvent(multiScanId) + if err != nil { + log.Debug(fmt.Sprintf("failed getting general event from XSC service for multi_scan_id %s, error: %s \"", multiScanId, err.Error())) + } + return event, err } -func (ams *AnalyticsMetricsService) UpdateXscAnalyticsGeneralEventFinalizeStatus(status xscservices.EventStatus) { - ams.finalizeEvent.EventStatus = status +func shouldReportEvents(xscVersion string) bool { + // A user who explicitly requests not to send reports will not receive XSC analytics metrics. + if !usage.ShouldReportUsage() { + return false + } + // Verify xsc version. + if err := clientutils.ValidateMinimumVersion(clientutils.Xsc, xscVersion, xscservices.AnalyticsMetricsMinXscVersion); err != nil { + return false + } + return true } -func (ams *AnalyticsMetricsService) AddScanFindingsToXscAnalyticsGeneralEventFinalize(findingsAmount int) { - ams.finalizeEvent.TotalFindings += findingsAmount +func getOsAndArch() (os, arch string) { + osAndArch, err := coreutils.GetOSAndArc() + if err != nil { + log.Debug(fmt.Errorf("failed to get os and architecture for general event request to XSC service, error: %s ", err.Error())) + return + } + splitOsAndArch := strings.Split(osAndArch, "-") + return splitOsAndArch[0], splitOsAndArch[1] } -func (ams *AnalyticsMetricsService) SetShouldReportEvents(shouldReportEvents bool) { - ams.shouldReportEvents = shouldReportEvents +func getTotalFindings(cmdResults *results.SecurityCommandResults) (totalFindings int) { + if cmdResults == nil { + return + } + summary, err := conversion.NewCommandResultsConvertor(conversion.ResultConvertParams{IncludeVulnerabilities: true, HasViolationContext: true}).ConvertToSummary(cmdResults) + if err != nil { + log.Warn(fmt.Sprintf("Failed to convert command results to summary. %s", err.Error())) + return + } + if summary.HasViolations() { + totalFindings = summary.GetTotalViolations() + } else { + totalFindings = summary.GetTotalVulnerabilities() + } + return } diff --git a/utils/xsc/analyticsmetrics_test.go b/utils/xsc/analyticsmetrics_test.go index 71d5b19a..a783eb42 100644 --- a/utils/xsc/analyticsmetrics_test.go +++ b/utils/xsc/analyticsmetrics_test.go @@ -2,6 +2,7 @@ package xsc import ( "errors" + "fmt" "os" "testing" "time" @@ -14,6 +15,7 @@ import ( "github.com/jfrog/jfrog-client-go/utils/tests" "github.com/jfrog/jfrog-client-go/xray/services" xscservices "github.com/jfrog/jfrog-client-go/xsc/services" + xscutils "github.com/jfrog/jfrog-client-go/xsc/services/utils" "github.com/owenrumney/go-sarif/v2/sarif" "github.com/stretchr/testify/assert" ) @@ -30,78 +32,151 @@ func TestCalcShouldReportEvents(t *testing.T) { reportUsageCallback := tests.SetEnvWithCallbackAndAssert(t, coreutils.ReportUsage, "") defer reportUsageCallback() - // Minimum Xsc version. - mockServer, serverDetails := validations.XscServer(t, xscservices.AnalyticsMetricsMinXscVersion) - defer mockServer.Close() - am := NewAnalyticsMetricsService(serverDetails) - assert.True(t, am.calcShouldReportEvents()) - - // Lower Xsc version. - mockServerLowerVersion, serverDetails := validations.XscServer(t, lowerAnalyticsMetricsMinXscVersion) - defer mockServerLowerVersion.Close() - am = NewAnalyticsMetricsService(serverDetails) - assert.False(t, am.calcShouldReportEvents()) - - // Higher Xsc version. - mockServerHigherVersion, serverDetails := validations.XscServer(t, higherAnalyticsMetricsMinXscVersion) - defer mockServerHigherVersion.Close() - am = NewAnalyticsMetricsService(serverDetails) - assert.True(t, am.calcShouldReportEvents()) - - // JFROG_CLI_REPORT_USAGE is false. - err := os.Setenv(utils.JfMsiEnvVariable, "") - assert.NoError(t, err) - err = os.Setenv(coreutils.ReportUsage, "false") - assert.NoError(t, err) - assert.False(t, am.calcShouldReportEvents()) + testCases := []struct { + name string + mockParams validations.MockServerParams + setEnvVarReportFalse bool + expectedShouldReport bool + }{ + { + name: "Minimum Xsc version", + mockParams: validations.MockServerParams{XrayVersion: xscutils.MinXrayVersionXscTransitionToXray, XscVersion: xscservices.AnalyticsMetricsMinXscVersion}, + expectedShouldReport: true, + }, + { + name: "Lower Xsc version", + mockParams: validations.MockServerParams{XrayVersion: xscutils.MinXrayVersionXscTransitionToXray, XscVersion: lowerAnalyticsMetricsMinXscVersion}, + expectedShouldReport: false, + }, + { + name: "Higher Xsc version", + mockParams: validations.MockServerParams{XrayVersion: xscutils.MinXrayVersionXscTransitionToXray, XscVersion: higherAnalyticsMetricsMinXscVersion}, + expectedShouldReport: true, + }, + { + name: "JFROG_CLI_REPORT_USAGE is false", + mockParams: validations.MockServerParams{XrayVersion: xscutils.MinXrayVersionXscTransitionToXray, XscVersion: higherAnalyticsMetricsMinXscVersion}, + setEnvVarReportFalse: true, + expectedShouldReport: false, + }, + } + + for _, testcase := range testCases { + t.Run(testcase.name, func(t *testing.T) { + mockServer, _ := validations.XscServer(t, testcase.mockParams) + defer mockServer.Close() + + if testcase.setEnvVarReportFalse { + err := os.Setenv(utils.JfMsiEnvVariable, "") + assert.NoError(t, err) + err = os.Setenv(coreutils.ReportUsage, "false") + assert.NoError(t, err) + } + + if testcase.expectedShouldReport { + assert.True(t, shouldReportEvents(testcase.mockParams.XscVersion)) + } else { + assert.False(t, shouldReportEvents(testcase.mockParams.XscVersion)) + } + }) + } } -func TestAddGeneralEvent(t *testing.T) { - msiCallback := tests.SetEnvWithCallbackAndAssert(t, utils.JfMsiEnvVariable, "") - defer msiCallback() - usageCallback := tests.SetEnvWithCallbackAndAssert(t, coreutils.ReportUsage, "true") - defer usageCallback() - // Successful flow. - mockServer, serverDetails := validations.XscServer(t, xscservices.AnalyticsMetricsMinXscVersion) - defer mockServer.Close() - am := NewAnalyticsMetricsService(serverDetails) - am.AddGeneralEvent(am.CreateGeneralEvent(xscservices.CliProduct, xscservices.CliEventType)) - assert.Equal(t, validations.TestMsi, am.GetMsi()) - - // In case cli should not report analytics, verify that request won't be sent. - am.shouldReportEvents = false - am.SetMsi("test-msi") - am.AddGeneralEvent(am.CreateGeneralEvent(xscservices.CliProduct, xscservices.CliEventType)) - assert.Equal(t, "test-msi", am.GetMsi()) +func TestSendStartScanEvent(t *testing.T) { + testCases := []struct { + name string + mockParams validations.MockServerParams + reportUsage bool + expectedMsi string + }{ + { + name: "Don't report events - user disabled feature", + mockParams: validations.MockServerParams{ + XrayVersion: xscutils.MinXrayVersionXscTransitionToXray, + XscVersion: xscservices.AnalyticsMetricsMinXscVersion, + ReturnMsi: "test-msi", + }, + expectedMsi: "", + }, + { + name: "Xsc service in xray", + mockParams: validations.MockServerParams{ + XrayVersion: xscutils.MinXrayVersionXscTransitionToXray, + XscVersion: xscservices.AnalyticsMetricsMinXscVersion, + ReturnMsi: "other-msi", + }, + reportUsage: true, + expectedMsi: "other-msi", + }, + { + name: "Deprecated Xsc version", + mockParams: validations.MockServerParams{ + XrayVersion: "3.0.0", + XscVersion: xscservices.AnalyticsMetricsMinXscVersion, + ReturnMsi: "diff-msi", + }, + reportUsage: true, + expectedMsi: "diff-msi", + }, + } + + for _, testCase := range testCases { + t.Run(testCase.name, func(t *testing.T) { + msiCallback := tests.SetEnvWithCallbackAndAssert(t, utils.JfMsiEnvVariable, "") + defer msiCallback() + usageCallback := tests.SetEnvWithCallbackAndAssert(t, coreutils.ReportUsage, fmt.Sprintf("%t", testCase.reportUsage)) + defer usageCallback() + + mockServer, serverDetails := validations.XscServer(t, testCase.mockParams) + defer mockServer.Close() + + msi, startTime := SendNewScanEvent(testCase.mockParams.XrayVersion, testCase.mockParams.XscVersion, serverDetails, CreateAnalyticsEvent(xscservices.CliProduct, xscservices.CliEventType, serverDetails)) + if testCase.reportUsage { + assert.NotEmpty(t, startTime) + } + assert.Equal(t, testCase.expectedMsi, msi) + }) + } } -func TestAnalyticsMetricsService_createAuditResultsFromXscAnalyticsBasicGeneralEvent(t *testing.T) { - usageCallback := tests.SetEnvWithCallbackAndAssert(t, coreutils.ReportUsage, "true") - defer usageCallback() +func TestCreateFinalizedEvent(t *testing.T) { + + time := time.Now() - testStruct := []struct { + testCases := []struct { name string auditResults *results.SecurityCommandResults - want xscservices.XscAnalyticsBasicGeneralEvent + expected xscservices.XscAnalyticsBasicGeneralEvent }{ - {name: "No audit results", auditResults: &results.SecurityCommandResults{}, want: xscservices.XscAnalyticsBasicGeneralEvent{EventStatus: xscservices.Completed}}, - {name: "Valid audit result", auditResults: getDummyContentForGeneralEvent(true, false), want: xscservices.XscAnalyticsBasicGeneralEvent{TotalFindings: 7, EventStatus: xscservices.Completed}}, - {name: "Scan failed with findings.", auditResults: getDummyContentForGeneralEvent(false, true), want: xscservices.XscAnalyticsBasicGeneralEvent{TotalFindings: 1, EventStatus: xscservices.Failed}}, - {name: "Scan failed no findings.", auditResults: &results.SecurityCommandResults{Targets: []*results.TargetResults{{Errors: []error{errors.New("an error")}}}}, want: xscservices.XscAnalyticsBasicGeneralEvent{TotalFindings: 0, EventStatus: xscservices.Failed}}, + { + name: "No audit results", + auditResults: &results.SecurityCommandResults{MultiScanId: "msi", StartTime: time}, + expected: xscservices.XscAnalyticsBasicGeneralEvent{EventStatus: xscservices.Completed}, + }, + { + name: "Valid audit result", + auditResults: getDummyContentForGeneralEvent(true, false), + expected: xscservices.XscAnalyticsBasicGeneralEvent{TotalFindings: 7, EventStatus: xscservices.Completed}, + }, + { + name: "Scan failed with findings.", + auditResults: getDummyContentForGeneralEvent(false, true), + expected: xscservices.XscAnalyticsBasicGeneralEvent{TotalFindings: 1, EventStatus: xscservices.Failed}, + }, + { + name: "Scan failed no findings.", + auditResults: &results.SecurityCommandResults{MultiScanId: "msi", StartTime: time, Targets: []*results.TargetResults{{Errors: []error{errors.New("an error")}}}}, + expected: xscservices.XscAnalyticsBasicGeneralEvent{TotalFindings: 0, EventStatus: xscservices.Failed}, + }, } - mockServer, serverDetails := validations.XscServer(t, xscservices.AnalyticsMetricsMinXscVersion) - defer mockServer.Close() - am := NewAnalyticsMetricsService(serverDetails) - am.SetStartTime() - time.Sleep(time.Millisecond) - for _, tt := range testStruct { - t.Run(tt.name, func(t *testing.T) { - event := am.CreateXscAnalyticsGeneralEventFinalizeFromAuditResults(tt.auditResults) - assert.Equal(t, tt.want.TotalFindings, event.TotalFindings) - assert.Equal(t, tt.want.EventStatus, event.EventStatus) - totalDuration, err := time.ParseDuration(event.TotalScanDuration) - assert.NoError(t, err) - assert.True(t, totalDuration > 0) + + for _, testCase := range testCases { + t.Run(testCase.name, func(t *testing.T) { + event := createFinalizedEvent(testCase.auditResults) + assert.Equal(t, testCase.expected.TotalFindings, event.TotalFindings) + assert.Equal(t, testCase.expected.EventStatus, event.EventStatus) + assert.Equal(t, testCase.auditResults.MultiScanId, event.MultiScanId) + assert.NotEmpty(t, event.TotalScanDuration) }) } } @@ -113,6 +188,8 @@ func getDummyContentForGeneralEvent(withJas, withErr bool) *results.SecurityComm vulnerabilities := []services.Vulnerability{{IssueId: "XRAY-ID", Severity: "medium", Cves: []services.Cve{{Id: "CVE-123"}}, Components: map[string]services.Component{"issueId_2_direct_dependency": {}}}} cmdResults := results.NewCommandResults(utils.SourceCode).SetEntitledForJas(true).SetSecretValidation(true) + cmdResults.StartTime = time.Now() + cmdResults.MultiScanId = "msi" scanResults := cmdResults.NewScanResults(results.ScanTarget{Target: "target"}) scanResults.NewScaScanResults(services.ScanResponse{Vulnerabilities: vulnerabilities}) diff --git a/utils/xsc/configprofile.go b/utils/xsc/configprofile.go index c9a9b58b..d1b7693b 100644 --- a/utils/xsc/configprofile.go +++ b/utils/xsc/configprofile.go @@ -8,23 +8,16 @@ import ( "github.com/jfrog/jfrog-client-go/xsc/services" ) -func GetConfigProfile(serverDetails *config.ServerDetails, profileName string) (*services.ConfigProfile, error) { - xscManager, err := CreateXscServiceManager(serverDetails) - if err != nil { +func GetConfigProfile(xrayVersion, xscVersion string, serverDetails *config.ServerDetails, profileName string) (*services.ConfigProfile, error) { + if err := clientutils.ValidateMinimumVersion(clientutils.Xsc, xscVersion, services.ConfigProfileMinXscVersion); err != nil { + log.Info("Minimal Xsc version required to utilize config profile is '%s'. All configurations will be induced from provided Env vars and files") return nil, err } - - xscVersion, err := xscManager.GetVersion() + xscService, err := CreateXscService(xrayVersion, serverDetails) if err != nil { - return nil, fmt.Errorf("failed to get XSC service version: %q", err) - } - - if err = clientutils.ValidateMinimumVersion(clientutils.Xsc, xscVersion, services.ConfigProfileMinXscVersion); err != nil { - log.Info("Minimal Xsc version required to utilize config profile is '%s'. All configurations will be induced from provided Env vars and files") return nil, err } - - configProfile, err := xscManager.GetConfigProfile(profileName) + configProfile, err := xscService.GetConfigProfile(profileName) if err != nil { err = fmt.Errorf("failed to get config profile '%s': %q", profileName, err) } diff --git a/utils/xsc/configprofile_test.go b/utils/xsc/configprofile_test.go index 7d8c8962..0e52a560 100644 --- a/utils/xsc/configprofile_test.go +++ b/utils/xsc/configprofile_test.go @@ -7,31 +7,53 @@ import ( "github.com/jfrog/jfrog-cli-security/utils/validations" "github.com/jfrog/jfrog-client-go/xsc/services" + xscutils "github.com/jfrog/jfrog-client-go/xsc/services/utils" "github.com/stretchr/testify/assert" ) -func TestGetConfigProfile_ValidRequest_SuccessExpected(t *testing.T) { - mockServer, serverDetails := validations.XscServer(t, services.ConfigProfileMinXscVersion) - defer mockServer.Close() - - configProfile, err := GetConfigProfile(serverDetails, validations.TestConfigProfileName) - assert.NoError(t, err) - - profileFileContent, err := os.ReadFile("../../tests/testdata/other/configProfile/configProfileExample.json") - assert.NoError(t, err) - - var configProfileForComparison services.ConfigProfile - err = json.Unmarshal(profileFileContent, &configProfileForComparison) - assert.NoError(t, err) - - assert.Equal(t, &configProfileForComparison, configProfile) -} - -func TestGetConfigProfile_TooLowXscVersion_FailureExpected(t *testing.T) { - mockServer, serverDetails := validations.XscServer(t, "1.0.0") - defer mockServer.Close() - - configProfile, err := GetConfigProfile(serverDetails, validations.TestConfigProfileName) - assert.Error(t, err) - assert.Nil(t, configProfile) +func TestGetConfigProfile(t *testing.T) { + testCases := []struct { + name string + mockParams validations.MockServerParams + expectError bool + }{ + { + name: "Deprecated XSC service", + mockParams: validations.MockServerParams{XrayVersion: "3.0.0", XscVersion: services.ConfigProfileMinXscVersion}, + }, + { + name: "Xsc as inner service in Xray", + mockParams: validations.MockServerParams{XrayVersion: xscutils.MinXrayVersionXscTransitionToXray, XscVersion: services.ConfigProfileMinXscVersion}, + }, + { + name: "Expected error - Xsc version too low", + mockParams: validations.MockServerParams{XrayVersion: xscutils.MinXrayVersionXscTransitionToXray, XscVersion: "1.0.0"}, + expectError: true, + }, + } + + for _, testcase := range testCases { + t.Run(testcase.name, func(t *testing.T) { + mockServer, serverDetails := validations.XscServer(t, testcase.mockParams) + defer mockServer.Close() + + configProfile, err := GetConfigProfile(testcase.mockParams.XrayVersion, testcase.mockParams.XscVersion, serverDetails, validations.TestConfigProfileName) + if testcase.expectError { + assert.Error(t, err) + assert.Nil(t, configProfile) + return + } + // Validate results + assert.NoError(t, err) + + profileFileContent, err := os.ReadFile("../../tests/testdata/other/configProfile/configProfileExample.json") + assert.NoError(t, err) + + var configProfileForComparison services.ConfigProfile + err = json.Unmarshal(profileFileContent, &configProfileForComparison) + assert.NoError(t, err) + + assert.Equal(t, &configProfileForComparison, configProfile) + }) + } } diff --git a/utils/xsc/errorreport.go b/utils/xsc/errorreport.go index fec947f2..83762b4f 100644 --- a/utils/xsc/errorreport.go +++ b/utils/xsc/errorreport.go @@ -12,40 +12,34 @@ import ( // Sends an error report when the Xsc service is enabled. // Errors returned by this function typically do not disrupt the flow, as reporting errors is optional. -func ReportError(serverDetails *config.ServerDetails, errorToReport error, source string) error { +func ReportError(xrayVersion, xscVersion string, serverDetails *config.ServerDetails, errorToReport error, source string) error { + if xscVersion == "" { + log.Debug("Xsc service is not available. Reporting to JFrog analytics is skipped...") + return nil + } log.Debug("Sending an error report to JFrog analytics...") - xscManager, err := CreateXscServiceManager(serverDetails) + xscService, err := CreateXscService(xrayVersion, serverDetails) if err != nil { - return fmt.Errorf("failed to create an HTTP client: %s.\nReporting to JFrog analytics is skipped...", err.Error()) + return fmt.Errorf("failed to create an HTTP client: %s.\nReporting to JFrog analytics is skipped", err.Error()) } - errorLog := &services.ExternalErrorLog{ Log_level: "error", Source: source, Message: errorToReport.Error(), } - return sendXscLogMessageIfEnabled(errorLog, xscManager) + return sendXscLogMessageIfEnabled(xscVersion, errorLog, xscService) } -func sendXscLogMessageIfEnabled(errorLog *services.ExternalErrorLog, xscManager *xsc.XscServicesManager) error { - if !IsReportLogErrorEventPossible(xscManager) { +func sendXscLogMessageIfEnabled(xscVersion string, errorLog *services.ExternalErrorLog, xscService xsc.XscService) error { + if !IsReportLogErrorEventPossible(xscVersion) { return nil } - return xscManager.SendXscLogErrorRequest(errorLog) + return xscService.SendXscLogErrorRequest(errorLog) } // Determines if reporting the error is feasible. -func IsReportLogErrorEventPossible(xscManager *xsc.XscServicesManager) bool { - xscVersion, err := xscManager.GetVersion() - if err != nil { - log.Debug(fmt.Sprintf("failed to check availability of Xsc service:%s\nReporting to JFrog analytics is skipped...", err.Error())) - return false - } - if xscVersion == "" { - log.Debug("Xsc service is not available. Reporting to JFrog analytics is skipped...") - return false - } - if err = clientutils.ValidateMinimumVersion(clientutils.Xsc, xscVersion, MinXscVersionForErrorReport); err != nil { +func IsReportLogErrorEventPossible(xscVersion string) bool { + if err := clientutils.ValidateMinimumVersion(clientutils.Xsc, xscVersion, MinXscVersionForErrorReport); err != nil { log.Debug(err.Error()) return false } diff --git a/utils/xsc/errorreport_test.go b/utils/xsc/errorreport_test.go index 42c7f6af..9afbf20e 100644 --- a/utils/xsc/errorreport_test.go +++ b/utils/xsc/errorreport_test.go @@ -1,13 +1,9 @@ package xsc import ( - "net/http" - "net/http/httptest" "testing" - "github.com/jfrog/jfrog-cli-core/v2/utils/config" "github.com/jfrog/jfrog-cli-core/v2/utils/coreutils" - "github.com/jfrog/jfrog-cli-security/utils/validations" clienttestutils "github.com/jfrog/jfrog-client-go/utils/tests" "github.com/stretchr/testify/assert" ) @@ -22,52 +18,34 @@ func TestReportLogErrorEventPossible(t *testing.T) { defer restoreEnvVarFunc() testCases := []struct { - serverCreationFunc func() (*httptest.Server, *config.ServerDetails) - expectedResponse bool + name string + xscVersion string + expectedResponse bool }{ { - serverCreationFunc: func() (*httptest.Server, *config.ServerDetails) { - serverMock, serverDetails, _ := validations.CreateXscRestsMockServer(t, func(w http.ResponseWriter, r *http.Request) { - if r.RequestURI == "/xsc/api/v1/system/version" { - w.WriteHeader(http.StatusNotFound) - _, innerError := w.Write([]byte("Xsc service is not enabled")) - if innerError != nil { - return - } - } - }) - return serverMock, serverDetails - }, + name: "Send Error fail - Xsc service is not enabled", + xscVersion: "", expectedResponse: false, }, { - serverCreationFunc: func() (*httptest.Server, *config.ServerDetails) { return validations.XscServer(t, "") }, - expectedResponse: false, - }, - { - serverCreationFunc: func() (*httptest.Server, *config.ServerDetails) { - return validations.XscServer(t, unsupportedXscVersionForErrorLogs) - }, + name: "Send Error fail - Xsc version too low", + xscVersion: unsupportedXscVersionForErrorLogs, expectedResponse: false, }, { - serverCreationFunc: func() (*httptest.Server, *config.ServerDetails) { - return validations.XscServer(t, supportedXscVersionForErrorLogs) - }, + name: "Send Error success", + xscVersion: supportedXscVersionForErrorLogs, expectedResponse: true, }, } - for _, testcase := range testCases { - mockServer, serverDetails := testcase.serverCreationFunc() - xscManager, err := CreateXscServiceManager(serverDetails) - assert.NoError(t, err) - reportPossible := IsReportLogErrorEventPossible(xscManager) - if testcase.expectedResponse { - assert.True(t, reportPossible) - } else { - assert.False(t, reportPossible) - } - mockServer.Close() + t.Run(testcase.name, func(t *testing.T) { + reportPossible := IsReportLogErrorEventPossible(testcase.xscVersion) + if testcase.expectedResponse { + assert.True(t, reportPossible) + } else { + assert.False(t, reportPossible) + } + }) } } diff --git a/utils/xsc/xscmanager.go b/utils/xsc/xscmanager.go index 45dff324..a9d5a762 100644 --- a/utils/xsc/xscmanager.go +++ b/utils/xsc/xscmanager.go @@ -1,18 +1,34 @@ package xsc import ( - "fmt" - "github.com/jfrog/jfrog-cli-core/v2/utils/config" "github.com/jfrog/jfrog-cli-core/v2/utils/coreutils" clientconfig "github.com/jfrog/jfrog-client-go/config" - "github.com/jfrog/jfrog-client-go/utils/log" + xscservices "github.com/jfrog/jfrog-client-go/xray/services/xsc" "github.com/jfrog/jfrog-client-go/xsc" + xscservicesutils "github.com/jfrog/jfrog-client-go/xsc/services/utils" + + "github.com/jfrog/jfrog-cli-security/utils/xray" ) const MinXscVersionForErrorReport = "1.7.7" -func CreateXscServiceManager(serviceDetails *config.ServerDetails) (*xsc.XscServicesManager, error) { +func CreateXscService(xrayVersion string, serviceDetails *config.ServerDetails) (xsc.XscService, error) { + if xscservicesutils.IsXscXrayInnerService(xrayVersion) { + return createXscService(serviceDetails) + } + return createDeprecatedXscServiceManager(serviceDetails) +} + +func createXscService(serviceDetails *config.ServerDetails) (*xscservices.XscInnerService, error) { + xrayManager, err := xray.CreateXrayServiceManager(serviceDetails) + if err != nil { + return nil, err + } + return xrayManager.Xsc(), nil +} + +func createDeprecatedXscServiceManager(serviceDetails *config.ServerDetails) (*xsc.XscServicesManager, error) { certsPath, err := coreutils.GetJfrogCertsDir() if err != nil { return nil, err @@ -31,20 +47,3 @@ func CreateXscServiceManager(serviceDetails *config.ServerDetails) (*xsc.XscServ } return xsc.New(serviceConfig) } - -func GetXscMsiAndVersion(analyticsMetricsService *AnalyticsMetricsService) (multiScanId, xscVersion string) { - var err error - if analyticsMetricsService != nil { - multiScanId = analyticsMetricsService.GetMsi() - } - if multiScanId != "" { - xscManager := analyticsMetricsService.XscManager() - if xscManager != nil { - xscVersion, err = xscManager.GetVersion() - if err != nil { - log.Debug(fmt.Sprintf("Can't get XSC version for xray graph scan params. Cause: %s", err.Error())) - } - } - } - return -} diff --git a/xsc_test.go b/xsc_test.go index 7a593c4a..2f1265f6 100644 --- a/xsc_test.go +++ b/xsc_test.go @@ -21,15 +21,15 @@ import ( ) func TestReportError(t *testing.T) { - cleanUp := integration.InitXscTest(t, func() { securityTestUtils.ValidateXscVersion(t, xsc.MinXscVersionForErrorReport) }) + xrayVersion, xscVersion, cleanUp := integration.InitXscTest(t, func() { securityTestUtils.ValidateXscVersion(t, xsc.MinXscVersionForErrorReport) }) defer cleanUp() errorToReport := errors.New("THIS IS NOT A REAL ERROR! This Error is posted as part of TestReportError test") - assert.NoError(t, xsc.ReportError(tests.XscDetails, errorToReport, "cli")) + assert.NoError(t, xsc.ReportError(xrayVersion, xscVersion, tests.XscDetails, errorToReport, "cli")) } // In the npm tests we use a watch flag, so we would get only violations func TestXscAuditNpmJsonWithWatch(t *testing.T) { - cleanUp := integration.InitXscTest(t) + _, _, cleanUp := integration.InitXscTest(t) defer cleanUp() output := testAuditNpm(t, string(format.Json), false) validations.VerifyJsonResults(t, output, validations.ValidationParams{ @@ -39,7 +39,7 @@ func TestXscAuditNpmJsonWithWatch(t *testing.T) { } func TestXscAuditNpmSimpleJsonWithWatch(t *testing.T) { - cleanUp := integration.InitXscTest(t) + _, _, cleanUp := integration.InitXscTest(t) defer cleanUp() output := testAuditNpm(t, string(format.SimpleJson), true) validations.VerifySimpleJsonResults(t, output, validations.ValidationParams{ @@ -50,7 +50,7 @@ func TestXscAuditNpmSimpleJsonWithWatch(t *testing.T) { } func TestXscAuditMavenJson(t *testing.T) { - cleanUp := integration.InitXscTest(t) + _, _, cleanUp := integration.InitXscTest(t) defer cleanUp() output := testXscAuditMaven(t, string(format.Json)) validations.VerifyJsonResults(t, output, validations.ValidationParams{ @@ -60,7 +60,7 @@ func TestXscAuditMavenJson(t *testing.T) { } func TestXscAuditMavenSimpleJson(t *testing.T) { - cleanUp := integration.InitXscTest(t) + _, _, cleanUp := integration.InitXscTest(t) defer cleanUp() output := testXscAuditMaven(t, string(format.SimpleJson)) validations.VerifySimpleJsonResults(t, output, validations.ValidationParams{ @@ -70,25 +70,24 @@ func TestXscAuditMavenSimpleJson(t *testing.T) { } func TestXscAnalyticsForAudit(t *testing.T) { - cleanUp := integration.InitXscTest(t) + xrayVersion, xscVersion, cleanUp := integration.InitXscTest(t) defer cleanUp() // Scan npm project and verify that analytics general event were sent to XSC. output := testAuditNpm(t, string(format.SimpleJson), false) - validateAnalyticsBasicEvent(t, output) + validateAnalyticsBasicEvent(t, xrayVersion, xscVersion, output) } -func validateAnalyticsBasicEvent(t *testing.T, output string) { +func validateAnalyticsBasicEvent(t *testing.T, xrayVersion, xscVersion, output string) { // Get MSI. var results formats.SimpleJsonResults err := json.Unmarshal([]byte(output), &results) assert.NoError(t, err) // Verify analytics metrics. - am := xsc.NewAnalyticsMetricsService(tests.XscDetails) - assert.NotNil(t, am) - assert.NotEmpty(t, results.MultiScanId) - event, err := am.GetGeneralEvent(results.MultiScanId) + event, err := xsc.GetScanEvent(xrayVersion, xscVersion, results.MultiScanId, tests.XscDetails) assert.NoError(t, err) + assert.NotNil(t, event) + assert.NotEmpty(t, results.MultiScanId) // Event creation and addition information. assert.Equal(t, xscservices.CliProduct, event.Product) @@ -101,7 +100,7 @@ func validateAnalyticsBasicEvent(t *testing.T, output string) { } func TestAdvancedSecurityDockerScanWithXsc(t *testing.T) { - cleanUpXsc := integration.InitXscTest(t) + _, _, cleanUpXsc := integration.InitXscTest(t) defer cleanUpXsc() testCli, cleanupDocker := integration.InitNativeDockerTest(t) defer cleanupDocker()