Skip to content

Commit

Permalink
Add Git Repository Resource in Xray API's (#1039)
Browse files Browse the repository at this point in the history
  • Loading branch information
attiasas authored Dec 30, 2024
1 parent 80a5e1b commit e342ed5
Show file tree
Hide file tree
Showing 14 changed files with 341 additions and 87 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 = `
{
Expand Down Expand Up @@ -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"
Expand Down
7 changes: 3 additions & 4 deletions xray/services/enrich.go
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Up @@ -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)
Expand Down Expand Up @@ -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)

Expand Down
59 changes: 27 additions & 32 deletions xray/services/scan.go
Original file line number Diff line number Diff line change
Expand Up @@ -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"
Expand Down Expand Up @@ -46,7 +47,8 @@ const (

scanTechQueryParam = "tech="

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

type ScanType string
Expand Down Expand Up @@ -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()
Expand All @@ -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)
Expand Down Expand Up @@ -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

Expand Down Expand Up @@ -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
Expand All @@ -193,9 +203,9 @@ type XrayGraphScanParams struct {
BinaryGraph *xrayUtils.BinaryGraphNode
IncludeVulnerabilities bool
IncludeLicenses bool
XscGitInfoContext *XscGitInfoContext
XscVersion string
XrayVersion string
Technology string
MultiScanId string
}

Expand Down Expand Up @@ -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"`
Expand Down Expand Up @@ -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 {
Expand Down
65 changes: 37 additions & 28 deletions xray/services/scan_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -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)
}
Expand Down
25 changes: 20 additions & 5 deletions xray/services/utils/ignorerulebody.go
Original file line number Diff line number Diff line change
Expand Up @@ -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"`
Expand All @@ -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"`
Expand All @@ -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"`
Expand Down
Loading

0 comments on commit e342ed5

Please sign in to comment.