Skip to content

Commit

Permalink
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Merge branch 'dev' into releaseBundleV2
Browse files Browse the repository at this point in the history
oshratZairi authored Jan 6, 2025
2 parents aab504a + e342ed5 commit 6b6e8d6
Showing 16 changed files with 347 additions and 103 deletions.
18 changes: 10 additions & 8 deletions artifactory/services/utils/tests/xray/consts.go
Original file line number Diff line number Diff line change
@@ -1,6 +1,8 @@
package xray

import xrayServices "github.com/jfrog/jfrog-client-go/xray/services"
import (
xscServices "github.com/jfrog/jfrog-client-go/xsc/services"
)

const ScanResponse = `
{
@@ -1437,15 +1439,15 @@ const XscGitInfoResponse = `{"multi_scan_id": "3472b4e2-bddc-11ee-a9c9-acde48001

const XscGitInfoBadResponse = `"failed create git info request: git_repo_url field must contain value"`

var GitInfoContextWithMinimalRequiredFields = xrayServices.XscGitInfoContext{
GitRepoUrl: "https://git.jfrog.info/projects/XSC/repos/xsc-service",
BranchName: "feature/XRAY-123-cool-feature",
CommitHash: "acc5e24e69a-d3c1-4022-62eb-69e4a1e5",
var GitInfoContextWithMinimalRequiredFields = xscServices.XscGitInfoContext{
GitRepoHttpsCloneUrl: "https://git.jfrog.info/projects/XSC/repos/xsc-service",
BranchName: "feature/XRAY-123-cool-feature",
LastCommitHash: "acc5e24e69a-d3c1-4022-62eb-69e4a1e5",
}

var GitInfoContextWithMissingFields = xrayServices.XscGitInfoContext{
GitRepoUrl: "https://git.jfrog.info/projects/XSC/repos/xsc-service",
BranchName: "feature/XRAY-123-cool-feature",
var GitInfoContextWithMissingFields = xscServices.XscGitInfoContext{
GitRepoHttpsCloneUrl: "https://git.jfrog.info/projects/XSC/repos/xsc-service",
BranchName: "feature/XRAY-123-cool-feature",
}

const TestMultiScanId = "3472b4e2-bddc-11ee-a9c9-acde48001122"
21 changes: 5 additions & 16 deletions artifactory/usage/call_home.go
Original file line number Diff line number Diff line change
@@ -11,8 +11,6 @@ import (
"github.com/jfrog/jfrog-client-go/utils/io/httputils"
)

const minArtifactoryVersion = "6.9.0"

type ReportUsageAttribute struct {
AttributeName string
AttributeValue string
@@ -28,7 +26,7 @@ func (rua *ReportUsageAttribute) isEmpty() bool {
return rua.AttributeName == ""
}

func (ach *ArtifactoryCallHome) validateAndGetUsageServerInfo(serviceManager artifactory.ArtifactoryServicesManager) (url string, clientDetails httputils.HttpClientDetails, err error) {
func (ach *ArtifactoryCallHome) getUsageServerInfo(serviceManager artifactory.ArtifactoryServicesManager) (url string, clientDetails httputils.HttpClientDetails, err error) {
config := serviceManager.GetConfig()
if config == nil {
err = errorutils.CheckErrorf("expected full config, but no configuration exists.")
@@ -39,15 +37,6 @@ func (ach *ArtifactoryCallHome) validateAndGetUsageServerInfo(serviceManager art
err = errorutils.CheckErrorf("Artifactory details not configured.")
return
}
// Check Artifactory version
artifactoryVersion, err := rtDetails.GetVersion()
if err != nil {
err = errors.New("Couldn't get Artifactory version. Error: " + err.Error())
return
}
if err = clientutils.ValidateMinimumVersion(clientutils.Artifactory, artifactoryVersion, minArtifactoryVersion); err != nil {
return
}
url, err = clientutils.BuildUrl(rtDetails.GetUrl(), "api/system/usage", make(map[string]string))
if err != nil {
return
@@ -69,8 +58,8 @@ func (ach *ArtifactoryCallHome) sendReport(url string, serviceManager artifactor
return nil
}

func (ach *ArtifactoryCallHome) SendUsageToArtifactory(productId string, serviceManager artifactory.ArtifactoryServicesManager, features ...Feature) error {
url, clientDetails, err := ach.validateAndGetUsageServerInfo(serviceManager)
func (ach *ArtifactoryCallHome) SendToArtifactory(productId string, serviceManager artifactory.ArtifactoryServicesManager, features ...Feature) error {
url, clientDetails, err := ach.getUsageServerInfo(serviceManager)
if err != nil || url == "" {
return err
}
@@ -81,8 +70,8 @@ func (ach *ArtifactoryCallHome) SendUsageToArtifactory(productId string, service
return ach.sendReport(url, serviceManager, clientDetails, bodyContent)
}

func (ach *ArtifactoryCallHome) SendUsage(productId, commandName string, serviceManager artifactory.ArtifactoryServicesManager, attributes ...ReportUsageAttribute) error {
url, clientDetails, err := ach.validateAndGetUsageServerInfo(serviceManager)
func (ach *ArtifactoryCallHome) Send(productId, commandName string, serviceManager artifactory.ArtifactoryServicesManager, attributes ...ReportUsageAttribute) error {
url, clientDetails, err := ach.getUsageServerInfo(serviceManager)
if err != nil || url == "" {
return err
}
1 change: 1 addition & 0 deletions jfconnect/services/metrics.go
Original file line number Diff line number Diff line change
@@ -45,6 +45,7 @@ func (jcs *JfConnectService) PostVisibilityMetric(metric VisibilityMetric) error

type Labels struct {
ProductID string `json:"product_id"`
ProductVersion string `json:"product_version"`
FeatureID string `json:"feature_id"`
OIDCUsed string `json:"oidc_used"`
JobID string `json:"job_id"`
7 changes: 3 additions & 4 deletions xray/services/enrich.go
Original file line number Diff line number Diff line change
@@ -91,8 +91,7 @@ func (es *EnrichService) GetImportGraphResults(scanId string) (*ScanResponse, er
type XrayGraphImportParams struct {
// A path in Artifactory that this Artifact is intended to be deployed to.
// This will provide a way to extract the watches that should be applied on this graph
ScanType ScanType
SBOMInput []byte
XscGitInfoContext *XscGitInfoContext
XscVersion string
ScanType ScanType
SBOMInput []byte
XscVersion string
}
23 changes: 22 additions & 1 deletion xray/services/ignorerule.go
Original file line number Diff line number Diff line change
@@ -64,9 +64,12 @@ func (xirs *IgnoreRuleService) Delete(ignoreRuleId string) error {
}

// Create will create a new Xray ignore rule
// The function creates the ignore rule and returns its id which is recieved after post
// The function creates the ignore rule and returns its id which is received after post
func (xirs *IgnoreRuleService) Create(params utils.IgnoreRuleParams) (ignoreRuleId string, err error) {
ignoreRuleBody := utils.CreateIgnoreRuleBody(params)
if err = validateIgnoreFilters(ignoreRuleBody.IgnoreFilters); err != nil {
return "", err
}
content, err := json.Marshal(ignoreRuleBody)
if err != nil {
return "", errorutils.CheckError(err)
@@ -98,6 +101,24 @@ func (xirs *IgnoreRuleService) Create(params utils.IgnoreRuleParams) (ignoreRule
return ignoreRuleId, nil
}

func validateIgnoreFilters(ignoreFilters utils.IgnoreFilters) error {
filters := []string{}
if len(ignoreFilters.CVEs) > 0 {
filters = append(filters, "CVEs")
}
if ignoreFilters.Exposures != nil {
filters = append(filters, "Exposures")
}
if ignoreFilters.Sast != nil {
filters = append(filters, "Sast")
}
// if more than one filter is set, notify the user
if len(filters) > 1 {
return errorutils.CheckErrorf("more than one ignore filter is set, split them to multiple ignore rules: %v", filters)
}
return nil
}

func getIgnoreRuleIdFromBody(body []byte) (string, error) {
str := string(body)

59 changes: 27 additions & 32 deletions xray/services/scan.go
Original file line number Diff line number Diff line change
@@ -6,9 +6,10 @@ import (
"strings"
"time"

clientUtils "github.com/jfrog/jfrog-client-go/utils"
"github.com/jfrog/jfrog-client-go/utils/log"
xrayUtils "github.com/jfrog/jfrog-client-go/xray/services/utils"
"github.com/jfrog/jfrog-client-go/xsc/services/utils"
xscUtils "github.com/jfrog/jfrog-client-go/xsc/services/utils"

"github.com/jfrog/jfrog-client-go/auth"
"github.com/jfrog/jfrog-client-go/http/jfroghttpclient"
@@ -46,7 +47,8 @@ const (

scanTechQueryParam = "tech="

XscVersionAPI = "system/version"
gitRepoKeyQueryParam = "git_repo="
MinXrayVersionGitRepoKey = "3.111.0"
)

type ScanType string
@@ -75,28 +77,33 @@ func createScanGraphQueryParams(scanParams XrayGraphScanParams) string {
}
}
}

// Xsc params are used only when XSC is enabled and MultiScanId is provided
if scanParams.XscVersion != "" && scanParams.MultiScanId != "" {
params = append(params, multiScanIdParam+scanParams.MultiScanId)
gitInfoContext := scanParams.XscGitInfoContext
if gitInfoContext != nil {
if len(gitInfoContext.Technologies) > 0 {
// Append the tech type, each graph can contain only one tech type
params = append(params, scanTechQueryParam+gitInfoContext.Technologies[0])
}
if scanParams.Technology != "" {
params = append(params, scanTechQueryParam+scanParams.Technology)
}
}

if scanParams.ScanType != "" {
params = append(params, scanTypeQueryParam+string(scanParams.ScanType))
}

if isGitRepoUrlSupported(scanParams.XrayVersion) && scanParams.GitRepoHttpsCloneUrl != "" {
// Add git repo key to the query params to produce violations defined in the git repo policy
params = append(params, gitRepoKeyQueryParam+xscUtils.GetGitRepoUrlKey(scanParams.GitRepoHttpsCloneUrl))
}

if len(params) == 0 {
return ""
}
return "?" + strings.Join(params, "&")
}

func isGitRepoUrlSupported(xrayVersion string) bool {
return clientUtils.ValidateMinimumVersion(clientUtils.Xray, xrayVersion, MinXrayVersionGitRepoKey) == nil
}

func (ss *ScanService) ScanGraph(scanParams XrayGraphScanParams) (string, error) {
httpClientsDetails := ss.XrayDetails.CreateHttpClientDetails()
httpClientsDetails.SetContentTypeApplicationJson()
@@ -114,7 +121,7 @@ func (ss *ScanService) ScanGraph(scanParams XrayGraphScanParams) (string, error)

// When XSC is enabled and MultiScanId is provided, modify the URL to use XSC scan graph (analytics enabled)
if scanParams.XrayVersion != "" && scanParams.XscVersion != "" && scanParams.MultiScanId != "" {
url = utils.XrayUrlToXscUrl(ss.XrayDetails.GetUrl(), scanParams.XrayVersion) + XscGraphAPI
url = xscUtils.XrayUrlToXscUrl(ss.XrayDetails.GetUrl(), scanParams.XrayVersion) + XscGraphAPI
}
url += createScanGraphQueryParams(scanParams)
resp, body, err := ss.client.SendPost(url, requestBody, &httpClientsDetails)
@@ -144,7 +151,7 @@ func (ss *ScanService) GetScanGraphResults(scanId, xrayVersion string, includeVu
endPoint := ss.XrayDetails.GetUrl() + scanGraphAPI
// Modify endpoint if XSC is enabled
if xscEnabled {
endPoint = utils.XrayUrlToXscUrl(ss.XrayDetails.GetUrl(), xrayVersion) + XscGraphAPI
endPoint = xscUtils.XrayUrlToXscUrl(ss.XrayDetails.GetUrl(), xrayVersion) + XscGraphAPI
}
endPoint += "/" + scanId

@@ -183,7 +190,10 @@ func (ss *ScanService) GetScanGraphResults(scanId, xrayVersion string, includeVu
type XrayGraphScanParams struct {
// A path in Artifactory that this Artifact is intended to be deployed to.
// This will provide a way to extract the watches that should be applied on this graph
RepoPath string
RepoPath string
// This will provide a way to extract the watches that should be applied on this graph
GitRepoHttpsCloneUrl string
// This will provide a way to extract the watches that should be applied on this graph
ProjectKey string
Watches []string
ScanType ScanType
@@ -193,9 +203,9 @@ type XrayGraphScanParams struct {
BinaryGraph *xrayUtils.BinaryGraphNode
IncludeVulnerabilities bool
IncludeLicenses bool
XscGitInfoContext *XscGitInfoContext
XscVersion string
XrayVersion string
Technology string
MultiScanId string
}

@@ -231,6 +241,7 @@ type Violation struct {
LicenseKey string `json:"license_key,omitempty"`
LicenseName string `json:"license_name,omitempty"`
IgnoreUrl string `json:"ignore_url,omitempty"`
Policies []Policy `json:"policies,omitempty"`
RiskReason string `json:"risk_reason,omitempty"`
IsEol *bool `json:"is_eol,omitempty"`
EolMessage string `json:"eol_message,omitempty"`
@@ -308,25 +319,9 @@ type JfrogResearchSeverityReason struct {
IsPositive bool `json:"is_positive,omitempty"`
}

type XscPostContextResponse struct {
MultiScanId string `json:"multi_scan_id,omitempty"`
}

type XscVersionResponse struct {
Version string `json:"xsc_version"`
}

type XscGitInfoContext struct {
GitRepoUrl string `json:"git_repo_url"`
GitRepoName string `json:"git_repo_name,omitempty"`
GitProject string `json:"git_project,omitempty"`
GitProvider string `json:"git_provider,omitempty"`
Technologies []string `json:"technologies,omitempty"`
BranchName string `json:"branch_name"`
LastCommit string `json:"last_commit,omitempty"`
CommitHash string `json:"commit_hash"`
CommitMessage string `json:"commit_message,omitempty"`
CommitAuthor string `json:"commit_author,omitempty"`
type Policy struct {
Policy string `json:"policy,omitempty"`
Rule string `json:"rule,omitempty"`
}

func (gp *XrayGraphScanParams) GetProjectKey() string {
65 changes: 37 additions & 28 deletions xray/services/scan_test.go
Original file line number Diff line number Diff line change
@@ -8,39 +8,48 @@ import (
func TestCreateScanGraphQueryParams(t *testing.T) {
tests := []struct {
testName string
projectKey string
repoPath string
watches []string
scanType ScanType
params XrayGraphScanParams
expectedQuery string
}{
{"with_project_key", "p1", "", nil, Binary,
fmt.Sprintf("?%s%s&%s%s", projectQueryParam, "p1", scanTypeQueryParam, Binary)},

{"with_repo_path", "", "r1", nil, Binary,
fmt.Sprintf("?%s%s&%s%s", repoPathQueryParam, "r1", scanTypeQueryParam, Binary)},

{"with_watches", "", "", []string{"w1", "w2"}, Binary,
fmt.Sprintf("?%s%s&%s%s&%s%s", watchesQueryParam, "w1", watchesQueryParam, "w2", scanTypeQueryParam, Binary)},

{"with_empty_watch_string", "", "", []string{""}, "",
""},

{"without_context", "", "", nil, Dependency,
fmt.Sprintf("?%s%s", scanTypeQueryParam, Dependency)},

{"without_scan_type", "", "", []string{"w1", "w2"}, "",
fmt.Sprintf("?%s%s&%s%s", watchesQueryParam, "w1", watchesQueryParam, "w2")},
{
testName: "with_project_key",
params: XrayGraphScanParams{ProjectKey: "p1", ScanType: Binary},
expectedQuery: fmt.Sprintf("?%s%s&%s%s", projectQueryParam, "p1", scanTypeQueryParam, Binary),
},
{
testName: "with_repo_path",
params: XrayGraphScanParams{RepoPath: "r1", ScanType: Binary},
expectedQuery: fmt.Sprintf("?%s%s&%s%s", repoPathQueryParam, "r1", scanTypeQueryParam, Binary),
},
{
testName: "with_watches",
params: XrayGraphScanParams{Watches: []string{"w1", "w2"}, ScanType: Binary},
expectedQuery: fmt.Sprintf("?%s%s&%s%s&%s%s", watchesQueryParam, "w1", watchesQueryParam, "w2", scanTypeQueryParam, Binary),
},
{
testName: "with_empty_watch_string",
params: XrayGraphScanParams{Watches: []string{""}},
expectedQuery: "",
},
{
testName: "without_context",
params: XrayGraphScanParams{ScanType: Dependency, XrayVersion: MinXrayVersionGitRepoKey},
expectedQuery: fmt.Sprintf("?%s%s", scanTypeQueryParam, Dependency),
},
{
testName: "without_scan_type",
params: XrayGraphScanParams{Watches: []string{"w1", "w2"}},
expectedQuery: fmt.Sprintf("?%s%s&%s%s", watchesQueryParam, "w1", watchesQueryParam, "w2"),
},
{
testName: "with_git_repo_url",
params: XrayGraphScanParams{GitRepoHttpsCloneUrl: "http://some-url", ScanType: Dependency, XrayVersion: MinXrayVersionGitRepoKey},
expectedQuery: fmt.Sprintf("?%s%s&%s%s", scanTypeQueryParam, Dependency, gitRepoKeyQueryParam, "some-url.git"),
},
}
for _, test := range tests {
t.Run(test.testName, func(t *testing.T) {
params := XrayGraphScanParams{
RepoPath: test.repoPath,
Watches: test.watches,
ProjectKey: test.projectKey,
ScanType: test.scanType,
}
actualQuery := createScanGraphQueryParams(params)
actualQuery := createScanGraphQueryParams(test.params)
if actualQuery != test.expectedQuery {
t.Error(test.testName, "Expecting:", test.expectedQuery, "Got:", actualQuery)
}
25 changes: 20 additions & 5 deletions xray/services/utils/ignorerulebody.go
Original file line number Diff line number Diff line change
@@ -2,6 +2,13 @@ package utils

import "time"

const (
SecretExposureType ExposureType = "secrets"
IacExposureType ExposureType = "iac"
)

type ExposureType string

type IgnoreRuleParams struct {
Notes string `json:"notes"`
ExpiresAt time.Time `json:"expires_at,omitempty"`
@@ -20,17 +27,25 @@ type IgnoreFilters struct {
Vulnerabilities []string `json:"vulnerabilities,omitempty"`
Licenses []string `json:"licenses,omitempty"`
CVEs []string `json:"cves,omitempty"`
GitRepositories []string `json:"git_repositories,omitempty"`
Policies []string `json:"policies,omitempty"`
Watches []string `json:"watches,omitempty"`
DockerLayers []string `json:"docker-layers,omitempty"`
OperationalRisks []string `json:"operational_risk,omitempty"`
Exposures []ExposuresFilterName `json:"exposures,omitempty"`
Exposures *ExposuresFilterName `json:"exposures,omitempty"`
Sast *SastFilterName `json:"sast,omitempty"`
ReleaseBundles []IgnoreFilterNameVersion `json:"release-bundles,omitempty"`
Builds []IgnoreFilterNameVersion `json:"builds,omitempty"`
Components []IgnoreFilterNameVersion `json:"components,omitempty"`
Artifacts []IgnoreFilterNameVersionPath `json:"artifacts,omitempty"`
}

type SastFilterName struct {
Fingerprint []string `json:"fingerprint,omitempty"`
Rule []string `json:"rule,omitempty"`
FilePath []string `json:"file_path,omitempty"`
}

type IgnoreFilterNameVersion struct {
Name string `json:"name"`
Version string `json:"version,omitempty"`
@@ -42,12 +57,12 @@ type IgnoreFilterNameVersionPath struct {
}

type ExposuresFilterName struct {
Catagories []ExposuresCatagories `json:"catagories,omitempty"`
Scanners []string `json:"scanners,omitempty"`
FilePath []string `json:"file_path,omitempty"`
Categories []ExposureType `json:"categories,omitempty"`
Scanners []string `json:"scanners,omitempty"`
FilePath []string `json:"file_path,omitempty"`
}

type ExposuresCatagories struct {
type ExposuresCategories struct {
Secrets bool `json:"secrets,omitempty"`
Services bool `json:"services,omitempty"`
Applications bool `json:"applications,omitempty"`
44 changes: 42 additions & 2 deletions xray/services/utils/policybody.go
Original file line number Diff line number Diff line change
@@ -56,8 +56,10 @@ type PolicyRule struct {

type PolicyCriteria struct {
// Security
MinSeverity Severity `json:"min_severity,omitempty"`
CvssRange *PolicyCvssRange `json:"cvss_range,omitempty"`
MinSeverity Severity `json:"min_severity,omitempty"`
CvssRange *PolicyCvssRange `json:"cvss_range,omitempty"`
Exposures *PolicyExposureCriteria `json:"exposures,omitempty"`
Sast *PolicySastCriteria `json:"sast,omitempty"`

// License
AllowedLicenses []string `json:"allowed_licenses,omitempty"`
@@ -66,6 +68,19 @@ type PolicyCriteria struct {
MultiLicensePermissive *bool `json:"multi_license_permissive,omitempty"`
}

type PolicyExposureCriteria struct {
MinSeverity Severity `json:"min_severity,omitempty"`
Secrets *bool `json:"secrets,omitempty"`
Applications *bool `json:"applications,omitempty"`
Services *bool `json:"services,omitempty"`
IaC *bool `json:"iac,omitempty"`
MaliciousCode *bool `json:"malicious_code,omitempty"`
}

type PolicySastCriteria struct {
MinSeverity Severity `json:"min_severity,omitempty"`
}

type PolicyCvssRange struct {
From float64 `json:"from,omitempty"`
To float64 `json:"to,omitempty"`
@@ -93,6 +108,31 @@ func CreateSeverityPolicyCriteria(minSeverity Severity) *PolicyCriteria {
}
}

func CreateExposuresPolicyCriteria(minSeverity Severity, secrets, applications, services, iac bool) *PolicyCriteria {
criteria := &PolicyCriteria{Exposures: &PolicyExposureCriteria{MinSeverity: minSeverity}}
if secrets {
criteria.Exposures.Secrets = &secrets
}
if applications {
criteria.Exposures.Applications = &applications
}
if services {
criteria.Exposures.Services = &services
}
if iac {
criteria.Exposures.IaC = &iac
}
return criteria
}

func CreateSastPolicyCriteria(minSeverity Severity) *PolicyCriteria {
return &PolicyCriteria{
Sast: &PolicySastCriteria{
MinSeverity: minSeverity,
},
}
}

// Create security policy criteria with range.
// from - CVSS range from 0.0 to 10.0
// to - CVSS range from 0.0 to 10.0
31 changes: 29 additions & 2 deletions xray/services/utils/watchbody.go
Original file line number Diff line number Diff line change
@@ -22,6 +22,8 @@ const (
WatchRepositoriesAll WatchRepositoriesType = "all"
// WatchRepositoriesByName is the option where repositories are selected by name to be watched
WatchRepositoriesByName WatchRepositoriesType = "byname"

WatchGitRepository = "gitRepository"
)

// WatchBuildType defines the type of filter for a builds on a watch
@@ -57,10 +59,13 @@ type WatchParams struct {
Description string
Active bool

Repositories WatchRepositoriesParams
Repositories WatchRepositoriesParams
GitRepositories WatchGitRepositoryParams
Builds WatchBuildsParams

Builds WatchBuildsParams
Policies []AssignedPolicy

ProjectKey string
}

// WatchRepositoriesParams is a struct that stores the repository configuration for watch
@@ -76,6 +81,10 @@ type WatchRepositoryAll struct {
Filters watchFilters
}

type WatchGitRepositoryParams struct {
Resources []string
}

// WatchRepository is used to define a specific repository in a watch
type WatchRepository struct {
Name string
@@ -161,6 +170,11 @@ type watchFilterPropertyValue struct {
Value string `json:"value"`
}

type ResourcesWatchesBody struct {
GitRepositoryWatches []string `json:"git_repository_watches,omitempty"`
ProjectWatches []string `json:"project_watches,omitempty"`
}

// CreateBody creates a payload to configure a Watch in Xray
// This can configure repositories and builds
// However, bundles are not supported.
@@ -187,9 +201,22 @@ func CreateBody(params WatchParams) (*WatchBody, error) {
return nil, err
}

configureGitRepositories(&payloadBody, params)

return &payloadBody, nil
}

func configureGitRepositories(payloadBody *WatchBody, params WatchParams) {
for _, gitRepoResource := range params.GitRepositories.Resources {
gitRepo := watchProjectResourcesElement{
Type: WatchGitRepository,
BinMgrID: "default",
Name: gitRepoResource,
}
payloadBody.ProjectResources.Resources = append(payloadBody.ProjectResources.Resources, gitRepo)
}
}

func configureRepositories(payloadBody *WatchBody, params WatchParams) error {
// Filters needs to be an empty array for Xray to accept the payload.

10 changes: 9 additions & 1 deletion xray/services/watch.go
Original file line number Diff line number Diff line change
@@ -54,6 +54,14 @@ func (xws *WatchService) getWatchURL() string {
return clientutils.AddTrailingSlashIfNeeded(xws.XrayDetails.GetUrl()) + watchAPIURL
}

func (xws *WatchService) getWatchUrlWithProjectKey(projectKey string) string {
baseUrl := xws.getWatchURL()
if projectKey == "" {
return baseUrl
}
return baseUrl + "?projectKey=" + projectKey
}

// Delete will delete an existing watch by name
// It will error if no watch can be found by that name.
func (xws *WatchService) Delete(watchName string) error {
@@ -86,7 +94,7 @@ func (xws *WatchService) Create(params utils.WatchParams) error {

httpClientsDetails := xws.XrayDetails.CreateHttpClientDetails()
httpClientsDetails.SetContentTypeApplicationJson()
var url = xws.getWatchURL()
var url = xws.getWatchUrlWithProjectKey(params.ProjectKey)

log.Info(fmt.Sprintf("Creating a new Watch named %s on JFrog Xray....", params.Name))
resp, body, err := xws.client.SendPost(url, content, &httpClientsDetails)
7 changes: 7 additions & 0 deletions xray/services/xsc/xsc.go
Original file line number Diff line number Diff line change
@@ -3,6 +3,7 @@ package xsc
import (
"github.com/jfrog/jfrog-client-go/auth"
"github.com/jfrog/jfrog-client-go/http/jfroghttpclient"
"github.com/jfrog/jfrog-client-go/xray/services/utils"
"github.com/jfrog/jfrog-client-go/xsc/services"
)

@@ -58,3 +59,9 @@ func (xs *XscInnerService) GetConfigProfileByUrl(repoUrl string) (*services.Conf
configProfileService.XrayDetails = xs.XrayDetails
return configProfileService.GetConfigurationProfileByUrl(repoUrl)
}

func (xs *XscInnerService) GetResourceWatches(gitRepo, project string) (watches *utils.ResourcesWatchesBody, err error) {
watchService := services.NewWatchService(xs.client)
watchService.XrayDetails = xs.XrayDetails
return watchService.GetResourceWatches(gitRepo, project)
}
18 changes: 15 additions & 3 deletions xsc/services/analytics.go
Original file line number Diff line number Diff line change
@@ -9,7 +9,6 @@ import (
"github.com/jfrog/jfrog-client-go/http/jfroghttpclient"
"github.com/jfrog/jfrog-client-go/utils"
"github.com/jfrog/jfrog-client-go/utils/errorutils"
"github.com/jfrog/jfrog-client-go/xray/services"
xscutils "github.com/jfrog/jfrog-client-go/xsc/services/utils"
)

@@ -117,8 +116,21 @@ func (vs *AnalyticsEventService) GetGeneralEvent(msi string) (*XscAnalyticsGener
// XscAnalyticsGeneralEvent extend the basic struct with Frogbot related info.
type XscAnalyticsGeneralEvent struct {
XscAnalyticsBasicGeneralEvent
GitInfo *services.XscGitInfoContext `json:"gitinfo,omitempty"`
IsGitInfoFlow bool `json:"is_gitinfo_flow,omitempty"`
GitInfo *XscGitInfoContext `json:"gitinfo,omitempty"`
IsGitInfoFlow bool `json:"is_gitinfo_flow,omitempty"`
}

type XscGitInfoContext struct {
GitRepoHttpsCloneUrl string `json:"git_repo_url"`
GitRepoName string `json:"git_repo_name,omitempty"`
GitProject string `json:"git_project,omitempty"`
GitProvider string `json:"git_provider,omitempty"`
Technologies []string `json:"technologies,omitempty"`
BranchName string `json:"branch_name"`
LastCommitUrl string `json:"last_commit,omitempty"`
LastCommitHash string `json:"commit_hash"`
LastCommitMessage string `json:"commit_message,omitempty"`
LastCommitAuthor string `json:"commit_author,omitempty"`
}

type XscAnalyticsGeneralEventFinalize struct {
17 changes: 17 additions & 0 deletions xsc/services/utils/utils.go
Original file line number Diff line number Diff line change
@@ -32,3 +32,20 @@ func IsXscXrayInnerService(xrayVersion string) bool {
}
return true
}

// The platform expects the git repo key to be in the format of the https/http clone Git URL without the protocol.
func GetGitRepoUrlKey(gitRepoHttpUrl string) string {
if len(gitRepoHttpUrl) == 0 {
// No git context was provided
return ""
}
if !strings.HasSuffix(gitRepoHttpUrl, ".git") {
// Append .git to the URL if not included
gitRepoHttpUrl += ".git"
}
// Remove the Http/s protocol from the URL
if strings.HasPrefix(gitRepoHttpUrl, "http") {
return strings.TrimPrefix(strings.TrimPrefix(gitRepoHttpUrl, "https://"), "http://")
}
return gitRepoHttpUrl
}
22 changes: 21 additions & 1 deletion xsc/services/utils/utils_test.go
Original file line number Diff line number Diff line change
@@ -1,6 +1,9 @@
package utils

import "testing"
import (
"github.com/stretchr/testify/assert"
"testing"
)

func TestXrayUrlToXscUrl(t *testing.T) {
tests := []struct {
@@ -21,3 +24,20 @@ func TestXrayUrlToXscUrl(t *testing.T) {
})
}
}

func TestGetGitRepoUrlKey(t *testing.T) {
expected := "git.com/jfrog/jfrog-client-go.git"
tests := []struct {
testName string
gitRepoUrl string
}{
{"with_http", "http://git.com/jfrog/jfrog-client-go.git"},
{"with_https", "https://git.com/jfrog/jfrog-client-go.git"},
{"without_protocol", "git.com/jfrog/jfrog-client-go"},
}
for _, test := range tests {
t.Run(test.testName, func(t *testing.T) {
assert.Equal(t, expected, GetGitRepoUrlKey(test.gitRepoUrl))
})
}
}
82 changes: 82 additions & 0 deletions xsc/services/watch.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,82 @@
package services

import (
"encoding/json"
"errors"
"fmt"
"net/http"
"strings"

"github.com/jfrog/jfrog-client-go/auth"
"github.com/jfrog/jfrog-client-go/http/jfroghttpclient"
"github.com/jfrog/jfrog-client-go/utils"
"github.com/jfrog/jfrog-client-go/utils/errorutils"
"github.com/jfrog/jfrog-client-go/utils/log"

api "github.com/jfrog/jfrog-client-go/xray/services/utils"
xscutils "github.com/jfrog/jfrog-client-go/xsc/services/utils"
)

const (
watchResourceAPIUrl = "watches/resource"
gitRepoResourceUrlKey = "git_repository"
projectResourceUrlKey = "project"
)

// WatchService defines the http client and Xray details
type WatchService struct {
client *jfroghttpclient.JfrogHttpClient
XrayDetails auth.ServiceDetails
}

// NewWatchService creates a new Xray Watch Service
func NewWatchService(client *jfroghttpclient.JfrogHttpClient) *WatchService {
return &WatchService{client: client}
}

// GetResourceWatches retrieves the active watches that are associated with a specific git repository and project
func (xws *WatchService) GetResourceWatches(gitRepo, project string) (watches *api.ResourcesWatchesBody, err error) {
if gitRepo == "" && project == "" {
return nil, errors.New("no resources provided")
}
httpClientsDetails := xws.XrayDetails.CreateHttpClientDetails()
log.Info(fmt.Sprintf("Getting resources (%s) active watches...", getResourcesString(gitRepo, project)))
resp, body, _, err := xws.client.SendGet(xws.getWatchURL(gitRepo, project), true, &httpClientsDetails)
if err != nil {
return
}
if err = errorutils.CheckResponseStatusWithBody(resp, body, http.StatusOK); err != nil {
return
}
log.Debug(fmt.Sprintf("Xray response (status %s): %s", resp.Status, body))
watches = &api.ResourcesWatchesBody{}
err = json.Unmarshal(body, &watches)
if err != nil {
return nil, errors.New("failed un-marshalling resources watches body")
}
log.Info(fmt.Sprintf("Found %d active watches", len(watches.GitRepositoryWatches)+len(watches.ProjectWatches)))
return
}

func getResourcesString(gitRepo, project string) string {
providedResources := []string{}
if gitRepo != "" {
providedResources = append(providedResources, fmt.Sprintf("git repository: %s", gitRepo))
}
if project != "" {
providedResources = append(providedResources, fmt.Sprintf("project: %s", project))
}
return strings.Join(providedResources, ", ")
}

func (xws *WatchService) getWatchURL(gitRepo, project string) string {
url := utils.AddTrailingSlashIfNeeded(xws.XrayDetails.GetUrl()) + xscutils.XscInXraySuffix + watchResourceAPIUrl
params := []string{}
if gitRepo != "" {
params = append(params, fmt.Sprintf("%s=%s", gitRepoResourceUrlKey, gitRepo))
}
if project != "" {
params = append(params, fmt.Sprintf("%s=%s", projectResourceUrlKey, project))
}
return url + "?" + strings.Join(params, "&")
}

0 comments on commit 6b6e8d6

Please sign in to comment.