Skip to content
Open
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
52 changes: 37 additions & 15 deletions commands/audit/audit.go
Original file line number Diff line number Diff line change
Expand Up @@ -31,6 +31,7 @@ import (
"github.com/jfrog/jfrog-cli-security/utils/results"
"github.com/jfrog/jfrog-cli-security/utils/results/output"
"github.com/jfrog/jfrog-cli-security/utils/techutils"
"github.com/jfrog/jfrog-cli-security/utils/xray/artifact"

"golang.org/x/exp/slices"

Expand Down Expand Up @@ -986,50 +987,71 @@ func processScanResults(params *AuditParams, cmdResults *results.SecurityCommand
uploadPath := ""
if params.uploadCdxResults {
log.Debug("Finished scanning. Uploading scan results to Artifactory")
if params.rtResultRepository == "" {
if params.GetRtResultRepositoryWithProjectKey() == "" {
return cmdResults.AddGeneralError(errors.New("results repository was not provided, can't upload scan results to Artifactory"), false)
}
rtResultRepository := params.rtResultRepository
if params.resultsContext.ProjectKey != "" {
rtResultRepository = fmt.Sprintf("%s-%s", params.resultsContext.ProjectKey, rtResultRepository)
}
if params.Progress() != nil {
params.Progress().SetHeadlineMsg("Uploading scan results to platform")
}
uploadPath, err = uploadCdxResults(params, cmdResults, rtResultRepository)
uploadPath, err = uploadCdxResults(params, cmdResults)
if err != nil {
return cmdResults.AddGeneralError(fmt.Errorf("failed to upload scan results to Artifactory: %s", err.Error()), false)
}
if uiRoute, err := getScanResultsUiRoute(params, uploadPath); err != nil {
log.Warn(fmt.Sprintf("failed to get scan results UI route: %s", err.Error()))
} else if uiRoute != "" {
cmdResults.SetResultsPlatformUrl(uiRoute)
}
}
// Violations fetching
if cmdResults.HasViolationContext() {
if params.Progress() != nil {
params.Progress().SetHeadlineMsg("Fetching violations")
}
rtResultRepository := params.rtResultRepository
if rtResultRepository != "" && params.resultsContext.ProjectKey != "" {
rtResultRepository = fmt.Sprintf("%s-%s", params.resultsContext.ProjectKey, rtResultRepository)
}
if err = fetchViolations(uploadPath, cmdResults, params, rtResultRepository); err != nil {
if err = fetchViolations(uploadPath, cmdResults, params); err != nil {
cmdResults.AddGeneralError(fmt.Errorf("failed to get violations: %s", err.Error()), cmdResults.AllowPartialResults)
}
}
return cmdResults
}

func uploadCdxResults(auditParams *AuditParams, cmdResults *results.SecurityCommandResults, rtResultRepository string) (uploadPath string, err error) {
func uploadCdxResults(auditParams *AuditParams, cmdResults *results.SecurityCommandResults) (uploadPath string, err error) {
serverDetails, err := auditParams.ServerDetails()
if err != nil {
err = fmt.Errorf("failed to get server details: %s", err.Error())
return
}
if uploadPath, err = output.UploadCommandResults(serverDetails, rtResultRepository, cmdResults); err != nil {
if uploadPath, err = output.UploadCommandResults(serverDetails, auditParams.GetRtResultRepositoryWithProjectKey(), cmdResults); err != nil {
err = fmt.Errorf("failed to upload scan results to Artifactory: %s", err.Error())
}
return
}

func fetchViolations(uploadPath string, cmdResults *results.SecurityCommandResults, auditParams *AuditParams, rtResultRepository string) (err error) {
func getScanResultsUiRoute(auditParams *AuditParams, uploadPath string) (string, error) {
if auditParams.GitContext() == nil {
return "", nil
}
serverDetails, err := auditParams.ServerDetails()
if err != nil {
return "", fmt.Errorf("failed to get server details: %s", err.Error())
}
xrayManager, err := xrayutils.CreateXrayServiceManager(serverDetails, xrayutils.WithScopedProjectKey(auditParams.resultsContext.ProjectKey))
if err != nil {
return "", fmt.Errorf("failed to create Xray service manager: %s", err.Error())
}
if err = artifact.WaitForArtifactScanStatus(xrayManager, auditParams.GetRtResultRepositoryWithProjectKey(), uploadPath, artifact.ScanStarted()); err != nil {
return "", fmt.Errorf("failed to wait for artifact scan status: %s", err.Error())
}
return xsc.GetScanResultsUiRoute(&xsc.ScanResultsUiRouteParams{
XrayVersion: auditParams.GetXrayVersion(),
ServerDetails: serverDetails,
ProjectKey: auditParams.resultsContext.ProjectKey,
GitContext: auditParams.GitContext(),
ScanResultArtifactPath: fmt.Sprintf("%s/%s", auditParams.GetRtResultRepositoryWithProjectKey(), uploadPath),
})
}

func fetchViolations(uploadPath string, cmdResults *results.SecurityCommandResults, auditParams *AuditParams) (err error) {
serverDetails, err := auditParams.ServerDetails()
if err != nil {
return fmt.Errorf("failed to get server details: %s", err.Error())
Expand All @@ -1038,7 +1060,7 @@ func fetchViolations(uploadPath string, cmdResults *results.SecurityCommandResul
local.WithAllowedLicenses(auditParams.allowedLicenses),
enforcer.WithServerDetails(serverDetails),
enforcer.WithProjectKey(auditParams.resultsContext.ProjectKey),
enforcer.WithArtifactParams(rtResultRepository, uploadPath),
enforcer.WithArtifactParams(auditParams.GetRtResultRepositoryWithProjectKey(), uploadPath),
enforcer.WithWatches(auditParams.resultsContext.Watches),
enforcer.WithResultsOutputDir(auditParams.scanResultsOutputDir),
)
Expand Down
8 changes: 8 additions & 0 deletions commands/audit/auditparams.go
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
package audit

import (
"fmt"
"time"

jfrogappsconfig "github.com/jfrog/jfrog-apps-config/go"
Expand Down Expand Up @@ -361,3 +362,10 @@ func (params *AuditParams) SetIncludeSbom(include bool) *AuditParams {
params.resultsContext.IncludeSbom = include
return params
}

func (params *AuditParams) GetRtResultRepositoryWithProjectKey() string {
if params.rtResultRepository != "" && params.resultsContext.ProjectKey != "" {
return fmt.Sprintf("%s-%s", params.resultsContext.ProjectKey, params.rtResultRepository)
}
return params.rtResultRepository
}
2 changes: 1 addition & 1 deletion go.mod
Original file line number Diff line number Diff line change
Expand Up @@ -19,7 +19,7 @@ require (
github.com/jfrog/jfrog-apps-config v1.0.1
github.com/jfrog/jfrog-cli-artifactory v0.8.1-0.20260728121041-2227ac7420a0
github.com/jfrog/jfrog-cli-core/v2 v2.60.1-0.20260728123939-34b27f070f2e
github.com/jfrog/jfrog-client-go v1.55.1-0.20260728135425-4a7f34efb305
github.com/jfrog/jfrog-client-go v1.55.1-0.20260729072925-e1104f6b9e00
github.com/magiconair/properties v1.18.11
github.com/owenrumney/go-sarif/v3 v3.2.3
github.com/package-url/packageurl-go v0.1.3
Expand Down
4 changes: 2 additions & 2 deletions go.sum
Original file line number Diff line number Diff line change
Expand Up @@ -173,8 +173,8 @@ github.com/jfrog/jfrog-cli-artifactory v0.8.1-0.20260728121041-2227ac7420a0 h1:D
github.com/jfrog/jfrog-cli-artifactory v0.8.1-0.20260728121041-2227ac7420a0/go.mod h1:1vxzqW7jHBSuTNqO2vxEnhbniwq4dj5wveDuCMJX7Yo=
github.com/jfrog/jfrog-cli-core/v2 v2.60.1-0.20260728123939-34b27f070f2e h1:K0IK3w5a5h6SIi9yoOJ6a7DL+kuFjs5acypOxKyT2OM=
github.com/jfrog/jfrog-cli-core/v2 v2.60.1-0.20260728123939-34b27f070f2e/go.mod h1:MygQx8pekgPCXyXnejIAVG9S4ImGcDFmcfRPUug/0d0=
github.com/jfrog/jfrog-client-go v1.55.1-0.20260728135425-4a7f34efb305 h1:D86yACKH94D9sJ0XcBK4ioOVSHDec667y22rXondYN0=
github.com/jfrog/jfrog-client-go v1.55.1-0.20260728135425-4a7f34efb305/go.mod h1:FHpjN1nTDoj96xd6obe27EOgGErqzU0rQgC96L3Ch9E=
github.com/jfrog/jfrog-client-go v1.55.1-0.20260729072925-e1104f6b9e00 h1:QekOqpZ4c34Xb/eX9UCM1JMGipGoW/ZpkbwsWrx/kAQ=
github.com/jfrog/jfrog-client-go v1.55.1-0.20260729072925-e1104f6b9e00/go.mod h1:FHpjN1nTDoj96xd6obe27EOgGErqzU0rQgC96L3Ch9E=
github.com/jhump/protoreflect v1.15.1 h1:HUMERORf3I3ZdX05WaQ6MIpd/NJ434hTp5YiKgfCL6c=
github.com/jhump/protoreflect v1.15.1/go.mod h1:jD/2GMKKE6OqX8qTjhADU1e6DShO+gavG9e0Q693nKo=
github.com/kevinburke/ssh_config v1.6.0 h1:J1FBfmuVosPHf5GRdltRLhPJtJpTlMdKTBjRgTaQBFY=
Expand Down
5 changes: 2 additions & 3 deletions utils/results/output/reportartifact.go
Original file line number Diff line number Diff line change
Expand Up @@ -49,12 +49,11 @@ func UploadCommandResults(serverDetails *config.ServerDetails, rtResultRepositor
}

func GetCommandResultsPlatformUrlMessage(cmdResults *results.SecurityCommandResults, pretty bool) string {
isGitContext := cmdResults.CmdType == utils.SourceCode && cmdResults.GitContext != nil
uploadMsg := upload.GetScanResultsPlatformUrlMessage(isGitContext)
uploadMsg := upload.GetScanResultsPlatformUrlMessage(cmdResults.CmdType == utils.SourceCode && cmdResults.GitContext != nil)
if pretty {
uploadMsg = coreutils.PrintTitle(uploadMsg)
}
if isGitContext {
if cmdResults.ResultsPlatformUrl == "" {
return uploadMsg
}
link := cmdResults.ResultsPlatformUrl
Expand Down
28 changes: 23 additions & 5 deletions utils/xray/artifact/artifactstatus.go
Original file line number Diff line number Diff line change
Expand Up @@ -33,8 +33,9 @@ func GetArtifactScanStatus(xrayManager *xray.XrayServicesManager, repo, path str
}

type ScanCompleteParams struct {
Overall bool
Steps []XrayScanStep
Overall bool
WaitForScanStarted bool
Steps []XrayScanStep
}

type ScanCompleteOption func(params *ScanCompleteParams)
Expand All @@ -45,6 +46,12 @@ func OverallCompletion() ScanCompleteOption {
}
}

func ScanStarted() ScanCompleteOption {
return func(params *ScanCompleteParams) {
params.WaitForScanStarted = true
}
}

func Steps(steps ...XrayScanStep) ScanCompleteOption {
return func(params *ScanCompleteParams) {
params.Steps = steps
Expand All @@ -63,9 +70,13 @@ func NewScanCompleteParams(options ...ScanCompleteOption) *ScanCompleteParams {
func WaitForArtifactScanStatus(xrayManager *xray.XrayServicesManager, repo, path string, options ...ScanCompleteOption) error {
params := NewScanCompleteParams(options...)
if !params.Overall && len(params.Steps) == 0 {
return fmt.Errorf("no scan completion criteria were provided")
if !params.WaitForScanStarted {
return fmt.Errorf("no scan completion criteria were provided")
}
log.Debug("Waiting for artifact scan to start.")
} else {
log.Debug(fmt.Sprintf("Waiting for artifact scan completion. Overall: %t, Steps: %v", params.Overall, params.Steps))
}
log.Debug(fmt.Sprintf("Waiting for artifact scan completion. Overall: %t, Steps: %v", params.Overall, params.Steps))
pollingExecutor := &httputils.PollingExecutor{
PollingInterval: ArtifactStatusFetchingIntervalNano,
Timeout: ArtifactStatusFetchTimeoutNano,
Expand Down Expand Up @@ -94,7 +105,14 @@ func WaitForArtifactScanStatus(xrayManager *xray.XrayServicesManager, repo, path
}
return
}
log.Debug(fmt.Sprintf("Artifact scan completed the requested steps. [%s]", strings.Join(statusMapToString(getStatusMap(status.Details, params.Steps, false)), ", ")))
switch {
case !params.Overall && len(params.Steps) == 0:
log.Debug(fmt.Sprintf("Artifact scan started. (%s)", status.Overall.Status))
case params.Overall:
log.Debug(fmt.Sprintf("Artifact scan completed. (%s)", status.Overall.Status))
default:
log.Debug(fmt.Sprintf("Artifact scan completed the requested steps. [%s]", strings.Join(statusMapToString(getStatusMap(status.Details, params.Steps, false)), ", ")))
}
// We don't need to return any response body, as we don't use it.
// We just need to stop the polling executor.
shouldStop = true
Expand Down
47 changes: 47 additions & 0 deletions utils/xsc/scanresultsuiroute.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,47 @@
package xsc

import (
"fmt"

"github.com/jfrog/jfrog-cli-core/v2/utils/config"

xrayutils "github.com/jfrog/jfrog-cli-security/utils/xray"
clientutils "github.com/jfrog/jfrog-client-go/utils"
"github.com/jfrog/jfrog-client-go/utils/log"
xscServices "github.com/jfrog/jfrog-client-go/xsc/services"
)

type ScanResultsUiRouteParams struct {
XrayVersion string
ServerDetails *config.ServerDetails
ProjectKey string
GitContext *xscServices.XscGitInfoContext
ScanResultArtifactPath string
}

func GetScanResultsUiRoute(params *ScanResultsUiRouteParams) (string, error) {
if err := clientutils.ValidateMinimumVersion(clientutils.Xray, params.XrayVersion, xscServices.GetUIRouteAPIMinXrayVersion); err != nil {
log.Debug(fmt.Sprintf("Getting scan results UI route is not supported on your current Xray version. %s", err.Error()))
return "", nil
}
if params.GitContext == nil || params.ServerDetails == nil {
log.Verbose("No git context or server details provided, skipping getting scan results UI route")
return "", nil
}

xscService, err := CreateXscService(params.ServerDetails, xrayutils.WithScopedProjectKey(params.ProjectKey))
if err != nil {
return "", fmt.Errorf("failed to create XSC service: %w", err)
}

resp, err := xscService.GetScanResultsUIRoute(params.GitContext)
if err != nil {
return "", fmt.Errorf("failed to get scan results UI route: %w", err)
}

if params.ScanResultArtifactPath != "" && resp.Path != params.ScanResultArtifactPath {
return "", fmt.Errorf("scan result artifact path '%s' does not match the expected path '%s'", params.ScanResultArtifactPath, resp.Path)
}

return resp.Url, nil
}
Loading