Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Adding analytics reports to Frogbot flows #676

Merged
merged 8 commits into from
Apr 10, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion go.mod
Original file line number Diff line number Diff line change
Expand Up @@ -123,7 +123,7 @@ require (

replace github.com/jfrog/jfrog-cli-core/v2 => github.com/jfrog/jfrog-cli-core/v2 v2.31.1-0.20240408074156-13680c04f22e

replace github.com/jfrog/jfrog-cli-security => github.com/jfrog/jfrog-cli-security v1.0.6-0.20240410071835-35f62009cd4e
replace github.com/jfrog/jfrog-cli-security => github.com/jfrog/jfrog-cli-security v1.0.6-0.20240410125927-aed7f83026cb

replace github.com/jfrog/jfrog-client-go => github.com/jfrog/jfrog-client-go v1.28.1-0.20240409191434-4e96d77edd64

Expand Down
4 changes: 2 additions & 2 deletions go.sum
Original file line number Diff line number Diff line change
Expand Up @@ -900,8 +900,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.31.1-0.20240408074156-13680c04f22e h1:PjCzGWHyJqK4j1MP3osPDDAW6KBXMJlBypOxKtp/ZKo=
github.com/jfrog/jfrog-cli-core/v2 v2.31.1-0.20240408074156-13680c04f22e/go.mod h1:qXAP68g+DlyX2wk5znNbQdK2CcEHfOLOfYXPzdlnkxI=
github.com/jfrog/jfrog-cli-security v1.0.6-0.20240410071835-35f62009cd4e h1:yCLVnw5XW1o293wOu4RJlawSkf5HrsiT1DTLO3W/ceg=
github.com/jfrog/jfrog-cli-security v1.0.6-0.20240410071835-35f62009cd4e/go.mod h1:6eJBg54tzN7bBRe+vbM9QWxBAdZACvcWVgt1aQRpewo=
github.com/jfrog/jfrog-cli-security v1.0.6-0.20240410125927-aed7f83026cb h1:oB8m42t9WEFttiL6OyVYLAMX08vfzAKllyL0PF6Nqo4=
github.com/jfrog/jfrog-cli-security v1.0.6-0.20240410125927-aed7f83026cb/go.mod h1:6eJBg54tzN7bBRe+vbM9QWxBAdZACvcWVgt1aQRpewo=
github.com/jfrog/jfrog-client-go v1.28.1-0.20240409191434-4e96d77edd64 h1:q0GV0IdhYdTqEkNykRwNZP0qNEE8j9dWfY9uKovDPzM=
github.com/jfrog/jfrog-client-go v1.28.1-0.20240409191434-4e96d77edd64/go.mod h1:tUyEmxznphh0nwAGo6xz9Sps7RRW/TBMxIJZteo+j2k=
github.com/jordan-wright/email v4.0.1-0.20210109023952-943e75fe5223+incompatible h1:jdpOPRN1zP63Td1hDQbZW73xKmzDvZHzVdNYxhnTMDA=
Expand Down
72 changes: 44 additions & 28 deletions scanpullrequest/scanpullrequest.go
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,7 @@ import (
"github.com/jfrog/froggit-go/vcsutils"
"github.com/jfrog/gofrog/datastructures"
"github.com/jfrog/jfrog-cli-security/formats"
xrayutils "github.com/jfrog/jfrog-cli-security/utils"
securityutils "github.com/jfrog/jfrog-cli-security/utils"
"github.com/jfrog/jfrog-client-go/utils/log"
"github.com/jfrog/jfrog-client-go/xray/services"
)
Expand All @@ -20,6 +20,7 @@ const (
SecurityIssueFoundErr = "issues were detected by Frogbot\n You can avoid marking the Frogbot scan as failed by setting failOnSecurityIssues to false in the " + utils.FrogbotConfigFile + " file"
noGitHubEnvErr = "frogbot did not scan this PR, because a GitHub Environment named 'frogbot' does not exist. Please refer to the Frogbot documentation for instructions on how to create the Environment"
noGitHubEnvReviewersErr = "frogbot did not scan this PR, because the existing GitHub Environment named 'frogbot' doesn't have reviewers selected. Please refer to the Frogbot documentation for instructions on how to create the Environment"
analyticsScanPrScanType = "PR"
)

type ScanPullRequestCmd struct{}
Expand Down Expand Up @@ -87,8 +88,13 @@ func scanPullRequest(repo *utils.Repository, client vcsclient.VcsClient) (err er
pullRequestDetails.Target.Owner, pullRequestDetails.Target.Repository, pullRequestDetails.Target.Name))
log.Info("-----------------------------------------------------------")

analyticsService := utils.AddAnalyticsGeneralEvent(nil, &repo.Server, analyticsScanPrScanType)
defer func() {
analyticsService.UpdateAndSendXscAnalyticsGeneralEventFinalize(err)
}()

// Audit PR code
issues, err := auditPullRequest(repo, client)
issues, err := auditPullRequest(repo, client, analyticsService)
if err != nil {
return
}
Expand All @@ -110,6 +116,7 @@ func scanPullRequest(repo *utils.Repository, client vcsclient.VcsClient) (err er
// Fail the Frogbot task if a security issue is found and Frogbot isn't configured to avoid the failure.
if toFailTaskStatus(repo, issues) {
err = errors.New(SecurityIssueFoundErr)
return
}
return
}
Expand All @@ -120,13 +127,19 @@ func toFailTaskStatus(repo *utils.Repository, issues *utils.IssuesCollection) bo
}

// Downloads Pull Requests branches code and audits them
func auditPullRequest(repoConfig *utils.Repository, client vcsclient.VcsClient) (issuesCollection *utils.IssuesCollection, err error) {
func auditPullRequest(repoConfig *utils.Repository, client vcsclient.VcsClient, analyticsService *securityutils.AnalyticsMetricsService) (issuesCollection *utils.IssuesCollection, err error) {
scanDetails := utils.NewScanDetails(client, &repoConfig.Server, &repoConfig.Git).
SetXrayGraphScanParams(repoConfig.Watches, repoConfig.JFrogProjectKey, len(repoConfig.AllowedLicenses) > 0).
SetMinSeverity(repoConfig.MinSeverity).
SetFixableOnly(repoConfig.FixableOnly).
SetFailOnInstallationErrors(*repoConfig.FailOnSecurityIssues)

// If MSI exists we always need to report events
if analyticsService.GetMsi() != "" {
// MSI is passed to XrayGraphScanParams, so it can be later used by other analytics events in the scan phase
scanDetails.XrayGraphScanParams.MultiScanId = analyticsService.GetMsi()
}

issuesCollection = &utils.IssuesCollection{}
for i := range repoConfig.Projects {
scanDetails.SetProject(&repoConfig.Projects[i])
Expand All @@ -136,6 +149,9 @@ func auditPullRequest(repoConfig *utils.Repository, client vcsclient.VcsClient)
}
issuesCollection.Append(projectIssues)
}
if analyticsService.ShouldReportEvents() {
analyticsService.AddScanFindingsToXscAnalyticsGeneralEventFinalize(issuesCollection.CountIssuesCollectionFindings())
}
return
}

Expand All @@ -151,7 +167,7 @@ func auditPullRequestInProject(repoConfig *utils.Repository, scanDetails *utils.
}()

// Audit source branch
var sourceResults *xrayutils.Results
var sourceResults *securityutils.Results
workingDirs := utils.GetFullPathWorkingDirs(scanDetails.Project.WorkingDirs, sourceBranchWd)
log.Info("Scanning source branch...")
sourceResults, err = scanDetails.RunInstallAndAudit(workingDirs...)
Expand Down Expand Up @@ -180,7 +196,7 @@ func auditPullRequestInProject(repoConfig *utils.Repository, scanDetails *utils.
return
}

func auditTargetBranch(repoConfig *utils.Repository, scanDetails *utils.ScanDetails, sourceScanResults *xrayutils.Results) (newIssues *utils.IssuesCollection, targetBranchWd string, err error) {
func auditTargetBranch(repoConfig *utils.Repository, scanDetails *utils.ScanDetails, sourceScanResults *securityutils.Results) (newIssues *utils.IssuesCollection, targetBranchWd string, err error) {
// Download target branch (if needed)
cleanupTarget := func() error { return nil }
if !repoConfig.IncludeAllVulnerabilities {
Expand All @@ -194,7 +210,7 @@ func auditTargetBranch(repoConfig *utils.Repository, scanDetails *utils.ScanDeta
}()

// Set target branch scan details
var targetResults *xrayutils.Results
var targetResults *securityutils.Results
workingDirs := utils.GetFullPathWorkingDirs(scanDetails.Project.WorkingDirs, targetBranchWd)
log.Info("Scanning target branch...")
targetResults, err = scanDetails.RunInstallAndAudit(workingDirs...)
Expand All @@ -207,24 +223,24 @@ func auditTargetBranch(repoConfig *utils.Repository, scanDetails *utils.ScanDeta
return
}

func getAllIssues(results *xrayutils.Results, allowedLicenses []string) (*utils.IssuesCollection, error) {
func getAllIssues(results *securityutils.Results, allowedLicenses []string) (*utils.IssuesCollection, error) {
log.Info("Frogbot is configured to show all vulnerabilities")
scanResults := results.ExtendedScanResults
xraySimpleJson, err := xrayutils.ConvertXrayScanToSimpleJson(results, results.IsMultipleProject(), false, true, allowedLicenses)
xraySimpleJson, err := securityutils.ConvertXrayScanToSimpleJson(results, results.IsMultipleProject(), false, true, allowedLicenses)
if err != nil {
return nil, err
}
return &utils.IssuesCollection{
Vulnerabilities: append(xraySimpleJson.Vulnerabilities, xraySimpleJson.SecurityViolations...),
Iacs: xrayutils.PrepareIacs(scanResults.IacScanResults),
Secrets: xrayutils.PrepareSecrets(scanResults.SecretsScanResults),
Sast: xrayutils.PrepareSast(scanResults.SastScanResults),
Iacs: securityutils.PrepareIacs(scanResults.IacScanResults),
Secrets: securityutils.PrepareSecrets(scanResults.SecretsScanResults),
Sast: securityutils.PrepareSast(scanResults.SastScanResults),
Licenses: xraySimpleJson.LicensesViolations,
}, nil
}

// Returns all the issues found in the source branch that didn't exist in the target branch.
func getNewlyAddedIssues(targetResults, sourceResults *xrayutils.Results, allowedLicenses []string) (*utils.IssuesCollection, error) {
func getNewlyAddedIssues(targetResults, sourceResults *securityutils.Results, allowedLicenses []string) (*utils.IssuesCollection, error) {
var newVulnerabilitiesOrViolations []formats.VulnerabilityOrViolationRow
var newLicenses []formats.LicenseRow
var err error
Expand All @@ -236,22 +252,22 @@ func getNewlyAddedIssues(targetResults, sourceResults *xrayutils.Results, allowe

var newIacs []formats.SourceCodeRow
if len(sourceResults.ExtendedScanResults.IacScanResults) > 0 {
targetIacRows := xrayutils.PrepareIacs(targetResults.ExtendedScanResults.IacScanResults)
sourceIacRows := xrayutils.PrepareIacs(sourceResults.ExtendedScanResults.IacScanResults)
targetIacRows := securityutils.PrepareIacs(targetResults.ExtendedScanResults.IacScanResults)
sourceIacRows := securityutils.PrepareIacs(sourceResults.ExtendedScanResults.IacScanResults)
newIacs = createNewSourceCodeRows(targetIacRows, sourceIacRows)
}

var newSecrets []formats.SourceCodeRow
if len(sourceResults.ExtendedScanResults.SecretsScanResults) > 0 {
targetSecretsRows := xrayutils.PrepareIacs(targetResults.ExtendedScanResults.SecretsScanResults)
sourceSecretsRows := xrayutils.PrepareIacs(sourceResults.ExtendedScanResults.SecretsScanResults)
targetSecretsRows := securityutils.PrepareIacs(targetResults.ExtendedScanResults.SecretsScanResults)
sourceSecretsRows := securityutils.PrepareIacs(sourceResults.ExtendedScanResults.SecretsScanResults)
newSecrets = createNewSourceCodeRows(targetSecretsRows, sourceSecretsRows)
}

var newSast []formats.SourceCodeRow
if len(targetResults.ExtendedScanResults.SastScanResults) > 0 {
targetSastRows := xrayutils.PrepareSast(targetResults.ExtendedScanResults.SastScanResults)
sourceSastRows := xrayutils.PrepareSast(sourceResults.ExtendedScanResults.SastScanResults)
targetSastRows := securityutils.PrepareSast(targetResults.ExtendedScanResults.SastScanResults)
sourceSastRows := securityutils.PrepareSast(sourceResults.ExtendedScanResults.SastScanResults)
newSast = createNewSourceCodeRows(targetSastRows, sourceSastRows)
}

Expand Down Expand Up @@ -279,7 +295,7 @@ func createNewSourceCodeRows(targetResults, sourceResults []formats.SourceCodeRo
}

// Create vulnerabilities rows. The rows should contain only the new issues added by this PR
func createNewVulnerabilitiesRows(targetResults, sourceResults *xrayutils.Results, allowedLicenses []string) (vulnerabilityOrViolationRows []formats.VulnerabilityOrViolationRow, licenseRows []formats.LicenseRow, err error) {
func createNewVulnerabilitiesRows(targetResults, sourceResults *securityutils.Results, allowedLicenses []string) (vulnerabilityOrViolationRows []formats.VulnerabilityOrViolationRow, licenseRows []formats.LicenseRow, err error) {
targetScanAggregatedResults := aggregateScanResults(targetResults.GetScaScansXrayResults())
sourceScanAggregatedResults := aggregateScanResults(sourceResults.GetScaScansXrayResults())

Expand All @@ -295,16 +311,16 @@ func createNewVulnerabilitiesRows(targetResults, sourceResults *xrayutils.Result
if newLicenses, err = getNewLicenseRows(&targetScanAggregatedResults, &sourceScanAggregatedResults); err != nil {
return
}
licenseRows = xrayutils.GetViolatedLicenses(allowedLicenses, newLicenses)
licenseRows = securityutils.GetViolatedLicenses(allowedLicenses, newLicenses)
return
}

func getNewSecurityVulnerabilities(targetScan, sourceScan *services.ScanResponse, auditResults *xrayutils.Results) (newVulnerabilitiesRows []formats.VulnerabilityOrViolationRow, err error) {
targetVulnerabilitiesRows, err := xrayutils.PrepareVulnerabilities(targetScan.Vulnerabilities, auditResults, auditResults.IsMultipleProject(), true)
func getNewSecurityVulnerabilities(targetScan, sourceScan *services.ScanResponse, auditResults *securityutils.Results) (newVulnerabilitiesRows []formats.VulnerabilityOrViolationRow, err error) {
targetVulnerabilitiesRows, err := securityutils.PrepareVulnerabilities(targetScan.Vulnerabilities, auditResults, auditResults.IsMultipleProject(), true)
if err != nil {
return newVulnerabilitiesRows, err
}
sourceVulnerabilitiesRows, err := xrayutils.PrepareVulnerabilities(sourceScan.Vulnerabilities, auditResults, auditResults.IsMultipleProject(), true)
sourceVulnerabilitiesRows, err := securityutils.PrepareVulnerabilities(sourceScan.Vulnerabilities, auditResults, auditResults.IsMultipleProject(), true)
if err != nil {
return newVulnerabilitiesRows, err
}
Expand All @@ -326,12 +342,12 @@ func getUniqueVulnerabilityOrViolationRows(targetRows, sourceRows []formats.Vuln
return newRows
}

func getNewViolations(targetScan, sourceScan *services.ScanResponse, auditResults *xrayutils.Results) (newSecurityViolationsRows []formats.VulnerabilityOrViolationRow, newLicenseViolationsRows []formats.LicenseRow, err error) {
targetSecurityViolationsRows, targetLicenseViolationsRows, _, err := xrayutils.PrepareViolations(targetScan.Violations, auditResults, auditResults.IsMultipleProject(), true)
func getNewViolations(targetScan, sourceScan *services.ScanResponse, auditResults *securityutils.Results) (newSecurityViolationsRows []formats.VulnerabilityOrViolationRow, newLicenseViolationsRows []formats.LicenseRow, err error) {
targetSecurityViolationsRows, targetLicenseViolationsRows, _, err := securityutils.PrepareViolations(targetScan.Violations, auditResults, auditResults.IsMultipleProject(), true)
if err != nil {
return
}
sourceSecurityViolationsRows, sourceLicenseViolationsRows, _, err := xrayutils.PrepareViolations(sourceScan.Violations, auditResults, auditResults.IsMultipleProject(), true)
sourceSecurityViolationsRows, sourceLicenseViolationsRows, _, err := securityutils.PrepareViolations(sourceScan.Violations, auditResults, auditResults.IsMultipleProject(), true)
if err != nil {
return
}
Expand All @@ -343,11 +359,11 @@ func getNewViolations(targetScan, sourceScan *services.ScanResponse, auditResult
}

func getNewLicenseRows(targetScan, sourceScan *services.ScanResponse) (newLicenses []formats.LicenseRow, err error) {
targetLicenses, err := xrayutils.PrepareLicenses(targetScan.Licenses)
targetLicenses, err := securityutils.PrepareLicenses(targetScan.Licenses)
if err != nil {
return
}
sourceLicenses, err := xrayutils.PrepareLicenses(sourceScan.Licenses)
sourceLicenses, err := securityutils.PrepareLicenses(sourceScan.Licenses)
if err != nil {
return
}
Expand Down
30 changes: 24 additions & 6 deletions scanrepository/scanrepository.go
Original file line number Diff line number Diff line change
Expand Up @@ -16,13 +16,15 @@ import (
"github.com/jfrog/gofrog/version"
"github.com/jfrog/jfrog-cli-core/v2/utils/coreutils"
"github.com/jfrog/jfrog-cli-security/formats"
xrayutils "github.com/jfrog/jfrog-cli-security/utils"
securityutils "github.com/jfrog/jfrog-cli-security/utils"
"github.com/jfrog/jfrog-client-go/utils/io/fileutils"
"github.com/jfrog/jfrog-client-go/utils/log"
"golang.org/x/exp/maps"
"golang.org/x/exp/slices"
)

const analyticsScanRepositoryScanType = "monitor"

type ScanRepositoryCmd struct {
// The interface that Frogbot utilizes to format and style the displayed messages on the Git providers
outputwriter.OutputWriter
Expand All @@ -42,6 +44,8 @@ type ScanRepositoryCmd struct {
projectTech []coreutils.Technology
// Stores all package manager handlers for detected issues
handlers map[coreutils.Technology]packagehandlers.PackageHandler
// The AnalyticsMetricsService used for analytics event report
analyticsService *securityutils.AnalyticsMetricsService
}

func (cfp *ScanRepositoryCmd) Run(repoAggregator utils.RepoAggregator, client vcsclient.VcsClient, frogbotRepoConnection *utils.UrlAccessChecker) (err error) {
Expand All @@ -68,6 +72,11 @@ func (cfp *ScanRepositoryCmd) scanAndFixRepository(repository *utils.Repository,
}

func (cfp *ScanRepositoryCmd) scanAndFixBranch(repository *utils.Repository) (err error) {
cfp.analyticsService = utils.AddAnalyticsGeneralEvent(cfp.scanDetails.XscGitInfoContext, cfp.scanDetails.ServerDetails, analyticsScanRepositoryScanType)
defer func() {
cfp.analyticsService.UpdateAndSendXscAnalyticsGeneralEventFinalize(err)
}()

clonedRepoDir, restoreBaseDir, err := cfp.cloneRepositoryAndCheckoutToBranch()
if err != nil {
return
Expand All @@ -81,6 +90,12 @@ func (cfp *ScanRepositoryCmd) scanAndFixBranch(repository *utils.Repository) (er
err = errors.Join(err, restoreBaseDir(), fileutils.RemoveTempDir(clonedRepoDir))
}()

// If MSI exists we always need to report events
if cfp.analyticsService.GetMsi() != "" {
// MSI is passed to XrayGraphScanParams, so it can be later used by other analytics events in the scan phase
cfp.scanDetails.XrayGraphScanParams.MultiScanId = cfp.analyticsService.GetMsi()
}

for i := range repository.Projects {
cfp.scanDetails.Project = &repository.Projects[i]
cfp.projectTech = []coreutils.Technology{}
Expand Down Expand Up @@ -132,6 +147,9 @@ func (cfp *ScanRepositoryCmd) scanAndFixProject(repository *utils.Repository) er
if err != nil {
return err
}
if cfp.analyticsService.ShouldReportEvents() {
cfp.analyticsService.AddScanFindingsToXscAnalyticsGeneralEventFinalize(scanResults.CountScanResultsFindings())
}

if repository.GitProvider.String() == vcsutils.GitHub.String() {
// Uploads Sarif results to GitHub in order to view the scan in the code scanning UI
Expand All @@ -158,7 +176,7 @@ func (cfp *ScanRepositoryCmd) scanAndFixProject(repository *utils.Repository) er
}

// Audit the dependencies of the current commit.
func (cfp *ScanRepositoryCmd) scan(currentWorkingDir string) (*xrayutils.Results, error) {
func (cfp *ScanRepositoryCmd) scan(currentWorkingDir string) (*securityutils.Results, error) {
// Audit commit code
auditResults, err := cfp.scanDetails.RunInstallAndAudit(currentWorkingDir)
if err != nil {
Expand All @@ -172,7 +190,7 @@ func (cfp *ScanRepositoryCmd) scan(currentWorkingDir string) (*xrayutils.Results
return auditResults, nil
}

func (cfp *ScanRepositoryCmd) getVulnerabilitiesMap(scanResults *xrayutils.Results, isMultipleRoots bool) (map[string]*utils.VulnerabilityDetails, error) {
func (cfp *ScanRepositoryCmd) getVulnerabilitiesMap(scanResults *securityutils.Results, isMultipleRoots bool) (map[string]*utils.VulnerabilityDetails, error) {
vulnerabilitiesMap, err := cfp.createVulnerabilitiesMap(scanResults, isMultipleRoots)
if err != nil {
return nil, err
Expand Down Expand Up @@ -446,11 +464,11 @@ func (cfp *ScanRepositoryCmd) cloneRepositoryAndCheckoutToBranch() (tempWd strin
}

// Create a vulnerabilities map - a map with 'impacted package' as a key and all the necessary information of this vulnerability as value.
func (cfp *ScanRepositoryCmd) createVulnerabilitiesMap(scanResults *xrayutils.Results, isMultipleRoots bool) (map[string]*utils.VulnerabilityDetails, error) {
func (cfp *ScanRepositoryCmd) createVulnerabilitiesMap(scanResults *securityutils.Results, isMultipleRoots bool) (map[string]*utils.VulnerabilityDetails, error) {
vulnerabilitiesMap := map[string]*utils.VulnerabilityDetails{}
for _, scanResult := range scanResults.GetScaScansXrayResults() {
if len(scanResult.Vulnerabilities) > 0 {
vulnerabilities, err := xrayutils.PrepareVulnerabilities(scanResult.Vulnerabilities, scanResults, isMultipleRoots, true)
vulnerabilities, err := securityutils.PrepareVulnerabilities(scanResult.Vulnerabilities, scanResults, isMultipleRoots, true)
if err != nil {
return nil, err
}
Expand All @@ -460,7 +478,7 @@ func (cfp *ScanRepositoryCmd) createVulnerabilitiesMap(scanResults *xrayutils.Re
}
}
} else if len(scanResult.Violations) > 0 {
violations, _, _, err := xrayutils.PrepareViolations(scanResult.Violations, scanResults, isMultipleRoots, true)
violations, _, _, err := securityutils.PrepareViolations(scanResult.Violations, scanResults, isMultipleRoots, true)
if err != nil {
return nil, err
}
Expand Down
Loading
Loading