Skip to content

Commit 802ca5b

Browse files
authored
Config profile with repo API integration (#797)
1 parent 9ac9e3c commit 802ca5b

File tree

10 files changed

+194
-83
lines changed

10 files changed

+194
-83
lines changed

.github/workflows/test.yml

-1
Original file line numberDiff line numberDiff line change
@@ -139,7 +139,6 @@ jobs:
139139
# Generate mocks
140140
- name: Generate mocks
141141
run: go generate ./...
142-
if: ${{ matrix.suite.name != 'Unit' }}
143142

144143
- name: Run Tests
145144
if: ${{ matrix.suite.name != 'GitHub Integration' || matrix.os == 'ubuntu' }}

go.mod

+4-3
Original file line numberDiff line numberDiff line change
@@ -9,15 +9,16 @@ require (
99
github.com/jfrog/build-info-go v1.10.7
1010
github.com/jfrog/froggit-go v1.16.2
1111
github.com/jfrog/gofrog v1.7.6
12-
github.com/jfrog/jfrog-cli-core/v2 v2.57.0
13-
github.com/jfrog/jfrog-cli-security v1.13.5
14-
github.com/jfrog/jfrog-client-go v1.48.2
12+
github.com/jfrog/jfrog-cli-core/v2 v2.57.2
13+
github.com/jfrog/jfrog-cli-security v1.13.6
14+
github.com/jfrog/jfrog-client-go v1.48.4
1515
github.com/jordan-wright/email v4.0.1-0.20210109023952-943e75fe5223+incompatible
1616
github.com/owenrumney/go-sarif/v2 v2.3.1
1717
github.com/stretchr/testify v1.10.0
1818
github.com/urfave/cli/v2 v2.27.4
1919
github.com/xeipuuv/gojsonschema v1.2.0
2020
golang.org/x/exp v0.0.0-20241108190413-2d47ceb2692f
21+
gopkg.in/yaml.v2 v2.4.0
2122
gopkg.in/yaml.v3 v3.0.1
2223
)
2324

go.sum

+7-6
Original file line numberDiff line numberDiff line change
@@ -129,12 +129,12 @@ github.com/jfrog/gofrog v1.7.6 h1:QmfAiRzVyaI7JYGsB7cxfAJePAZTzFz0gRWZSE27c6s=
129129
github.com/jfrog/gofrog v1.7.6/go.mod h1:ntr1txqNOZtHplmaNd7rS4f8jpA5Apx8em70oYEe7+4=
130130
github.com/jfrog/jfrog-apps-config v1.0.1 h1:mtv6k7g8A8BVhlHGlSveapqf4mJfonwvXYLipdsOFMY=
131131
github.com/jfrog/jfrog-apps-config v1.0.1/go.mod h1:8AIIr1oY9JuH5dylz2S6f8Ym2MaadPLR6noCBO4C22w=
132-
github.com/jfrog/jfrog-cli-core/v2 v2.57.0 h1:3ON0J6Sjc2+4HZrzh4eSbdciXx3sJsJUIJ3TPQXh/5c=
133-
github.com/jfrog/jfrog-cli-core/v2 v2.57.0/go.mod h1:SThaC/fniC96oN8YgCsHjvOxp5rBM7IppuIybn1oxT0=
134-
github.com/jfrog/jfrog-cli-security v1.13.5 h1:CjWyCpURUtSCiu6xLqgvcwWFfy56hi+kFcMMTqEFhOo=
135-
github.com/jfrog/jfrog-cli-security v1.13.5/go.mod h1:gc14ZiaO0vXRrOWBoOlsPKrbRdKDgcl+jRFuCaAH0Vk=
136-
github.com/jfrog/jfrog-client-go v1.48.2 h1:YVAIiNWuBEa4NbWL54I+YzvXHaxoHDk532USDKTvgLU=
137-
github.com/jfrog/jfrog-client-go v1.48.2/go.mod h1:1a7bmQHkRmPEza9wva2+WVrYzrGbosrMymq57kyG5gU=
132+
github.com/jfrog/jfrog-cli-core/v2 v2.57.2 h1:2shy1CRWm/8yf6WWfVyAW3AdmryQiI73Tkhfb62vgPE=
133+
github.com/jfrog/jfrog-cli-core/v2 v2.57.2/go.mod h1:sgi0gw96J00Yzx2cKG5xTG/x9XD0YiJbglJOnXUeaD0=
134+
github.com/jfrog/jfrog-cli-security v1.13.6 h1:gm8TnGTlprMJbRga0cujeoN2xal7Pagd2kDkUfclvrw=
135+
github.com/jfrog/jfrog-cli-security v1.13.6/go.mod h1:NarJyhl8Kh0HL6br74oeStIosLmCjA7atLYxZIFHJc4=
136+
github.com/jfrog/jfrog-client-go v1.48.4 h1:uXvBr2ebFKpBRUhWgC9TSSJe32IbSYGlbDp9tDzBcaY=
137+
github.com/jfrog/jfrog-client-go v1.48.4/go.mod h1:2ySOMva54L3EYYIlCBYBTcTgqfrrQ19gtpA/MWfA/ec=
138138
github.com/jordan-wright/email v4.0.1-0.20210109023952-943e75fe5223+incompatible h1:jdpOPRN1zP63Td1hDQbZW73xKmzDvZHzVdNYxhnTMDA=
139139
github.com/jordan-wright/email v4.0.1-0.20210109023952-943e75fe5223+incompatible/go.mod h1:1c7szIrayyPPB/987hsnvNzLushdWf4o/79s3P08L8A=
140140
github.com/k0kubun/colorstring v0.0.0-20150214042306-9440f1994b88/go.mod h1:3w7q1U84EfirKl04SVQ/s7nPm1ZPhiXd34z40TNz36k=
@@ -413,6 +413,7 @@ gopkg.in/ini.v1 v1.67.0/go.mod h1:pNLf8WUiyNEtQjuu5G5vTm06TEv9tsIgeAvK8hOrP4k=
413413
gopkg.in/warnings.v0 v0.1.2 h1:wFXVbFY8DY5/xOe1ECiWdKCzZlxgshcYVNkBHstARME=
414414
gopkg.in/warnings.v0 v0.1.2/go.mod h1:jksf8JmL6Qr/oQM2OXTHunEvvTAsrWBLb6OOjuVWRNI=
415415
gopkg.in/yaml.v2 v2.2.2/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
416+
gopkg.in/yaml.v2 v2.4.0 h1:D8xgwECY7CYvx+Y2n4sBz93Jn9JRvxdiyyo8CTfuKaY=
416417
gopkg.in/yaml.v2 v2.4.0/go.mod h1:RDklbk79AGWmwhnvt/jBztapEOGDOx6ZbXqjP6csGnQ=
417418
gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
418419
gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA=

scanpullrequest/scanpullrequest.go

+15-10
Original file line numberDiff line numberDiff line change
@@ -210,7 +210,7 @@ func auditTargetBranch(repoConfig *utils.Repository, scanDetails *utils.ScanDeta
210210
// Download target branch (if needed)
211211
cleanupTarget := func() error { return nil }
212212
if !repoConfig.IncludeAllVulnerabilities {
213-
if targetBranchWd, cleanupTarget, err = prepareTargetForScan(repoConfig.PullRequestDetails, scanDetails); err != nil {
213+
if targetBranchWd, cleanupTarget, err = prepareTargetForScan(repoConfig.Git, scanDetails); err != nil {
214214
return
215215
}
216216
}
@@ -232,8 +232,8 @@ func auditTargetBranch(repoConfig *utils.Repository, scanDetails *utils.ScanDeta
232232
return
233233
}
234234

235-
func prepareTargetForScan(pullRequestDetails vcsclient.PullRequestInfo, scanDetails *utils.ScanDetails) (targetBranchWd string, cleanupTarget func() error, err error) {
236-
target := pullRequestDetails.Target
235+
func prepareTargetForScan(gitDetails utils.Git, scanDetails *utils.ScanDetails) (targetBranchWd string, cleanupTarget func() error, err error) {
236+
target := gitDetails.PullRequestDetails.Target
237237
// Download target branch
238238
if targetBranchWd, cleanupTarget, err = utils.DownloadRepoToTempDir(scanDetails.Client(), target.Owner, target.Repository, target.Name); err != nil {
239239
return
@@ -243,18 +243,23 @@ func prepareTargetForScan(pullRequestDetails vcsclient.PullRequestInfo, scanDeta
243243
}
244244
log.Debug("Using most common ancestor commit as target branch commit")
245245
// Get common parent commit between source and target and use it (checkout) to the target branch commit
246-
if e := tryCheckoutToMostCommonAncestor(scanDetails, pullRequestDetails.Source.Name, target.Name, targetBranchWd); e != nil {
247-
log.Warn(fmt.Sprintf("Failed to get best common ancestor commit between source branch: %s and target branch: %s, defaulting to target branch commit. Error: %s", pullRequestDetails.Source.Name, target.Name, e.Error()))
246+
if e := tryCheckoutToMostCommonAncestor(scanDetails, gitDetails.PullRequestDetails.Source.Name, target.Name, targetBranchWd, gitDetails.RepositoryCloneUrl); e != nil {
247+
log.Warn(fmt.Sprintf("Failed to get best common ancestor commit between source branch: %s and target branch: %s, defaulting to target branch commit. Error: %s", gitDetails.PullRequestDetails.Source.Name, target.Name, e.Error()))
248248
}
249249
return
250250
}
251251

252-
func tryCheckoutToMostCommonAncestor(scanDetails *utils.ScanDetails, baseBranch, headBranch, targetBranchWd string) (err error) {
253-
repositoryInfo, err := scanDetails.Client().GetRepositoryInfo(context.Background(), scanDetails.RepoOwner, scanDetails.RepoName)
254-
if err != nil {
255-
return
252+
func tryCheckoutToMostCommonAncestor(scanDetails *utils.ScanDetails, baseBranch, headBranch, targetBranchWd, cloneRepoUrl string) (err error) {
253+
if cloneRepoUrl != "" {
254+
scanDetails.Git.RepositoryCloneUrl = cloneRepoUrl
255+
} else {
256+
var repositoryInfo vcsclient.RepositoryInfo
257+
repositoryInfo, err = scanDetails.Client().GetRepositoryInfo(context.Background(), scanDetails.RepoOwner, scanDetails.RepoName)
258+
if err != nil {
259+
return
260+
}
261+
scanDetails.Git.RepositoryCloneUrl = repositoryInfo.CloneInfo.HTTP
256262
}
257-
scanDetails.Git.RepositoryCloneUrl = repositoryInfo.CloneInfo.HTTP
258263
// Change working directory to the temp target branch directory
259264
cwd, err := os.Getwd()
260265
if err != nil {

scanrepository/scanrepository.go

+11-4
Original file line numberDiff line numberDiff line change
@@ -127,6 +127,7 @@ func (cfp *ScanRepositoryCmd) setCommandPrerequisites(repository *utils.Reposito
127127
SetXrayGraphScanParams(repository.Watches, repository.JFrogProjectKey, len(repository.AllowedLicenses) > 0).
128128
SetFailOnInstallationErrors(*repository.FailOnSecurityIssues).
129129
SetFixableOnly(repository.FixableOnly).
130+
SetConfigProfile(repository.ConfigProfile).
130131
SetSkipAutoInstall(repository.SkipAutoInstall).
131132
SetAllowPartialResults(repository.AllowPartialResults).
132133
SetDisableJas(repository.DisableJas)
@@ -137,11 +138,17 @@ func (cfp *ScanRepositoryCmd) setCommandPrerequisites(repository *utils.Reposito
137138
if cfp.scanDetails, err = cfp.scanDetails.SetMinSeverity(repository.MinSeverity); err != nil {
138139
return
139140
}
140-
repositoryInfo, err := client.GetRepositoryInfo(context.Background(), cfp.scanDetails.RepoOwner, cfp.scanDetails.RepoName)
141-
if err != nil {
142-
return
141+
if repository.Git.RepositoryCloneUrl != "" {
142+
cfp.scanDetails.Git.RepositoryCloneUrl = repository.Git.RepositoryCloneUrl
143+
} else {
144+
var repositoryInfo vcsclient.RepositoryInfo
145+
repositoryInfo, err = client.GetRepositoryInfo(context.Background(), cfp.scanDetails.RepoOwner, cfp.scanDetails.RepoName)
146+
if err != nil {
147+
return
148+
}
149+
cfp.scanDetails.Git.RepositoryCloneUrl = repositoryInfo.CloneInfo.HTTP
143150
}
144-
cfp.scanDetails.Git.RepositoryCloneUrl = repositoryInfo.CloneInfo.HTTP
151+
145152
// Set the flag for aggregating fixes to generate a unified pull request for fixing vulnerabilities
146153
cfp.aggregateFixes = repository.Git.AggregateFixes
147154
// Set the outputwriter interface for the relevant vcs git provider

utils/consts.go

+9-8
Original file line numberDiff line numberDiff line change
@@ -24,14 +24,15 @@ const (
2424
azurePipelines ciProvider = "azure-pipelines"
2525

2626
// JFrog platform environment variables
27-
JFrogUserEnv = "JF_USER"
28-
JFrogUrlEnv = "JF_URL"
29-
jfrogXrayUrlEnv = "JF_XRAY_URL"
30-
jfrogArtifactoryUrlEnv = "JF_ARTIFACTORY_URL"
31-
jfrogReleasesRepoEnv = "JF_RELEASES_REPO"
32-
JFrogPasswordEnv = "JF_PASSWORD"
33-
JFrogTokenEnv = "JF_ACCESS_TOKEN"
34-
JfrogConfigProfileEnv = "JF_CONFIG_PROFILE"
27+
JFrogUserEnv = "JF_USER"
28+
JFrogUrlEnv = "JF_URL"
29+
jfrogXrayUrlEnv = "JF_XRAY_URL"
30+
jfrogArtifactoryUrlEnv = "JF_ARTIFACTORY_URL"
31+
jfrogReleasesRepoEnv = "JF_RELEASES_REPO"
32+
JFrogPasswordEnv = "JF_PASSWORD"
33+
JFrogTokenEnv = "JF_ACCESS_TOKEN"
34+
JfrogUseConfigProfileEnv = "JF_USE_CONFIG_PROFILE"
35+
JfrogConfigProfileEnv = "JF_CONFIG_PROFILE"
3536

3637
// Git environment variables
3738
GitProvider = "JF_GIT_PROVIDER"

utils/params.go

+44-15
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,7 @@ import (
44
"context"
55
"errors"
66
"fmt"
7+
"gopkg.in/yaml.v2"
78
"net/http"
89
"net/url"
910
"os"
@@ -25,7 +26,6 @@ import (
2526
"github.com/jfrog/froggit-go/vcsutils"
2627
coreconfig "github.com/jfrog/jfrog-cli-core/v2/utils/config"
2728
"github.com/jfrog/jfrog-client-go/utils/log"
28-
"gopkg.in/yaml.v3"
2929
)
3030

3131
const (
@@ -426,11 +426,6 @@ func GetFrogbotDetails(commandName string) (frogbotDetails *FrogbotDetails, err
426426
return
427427
}
428428

429-
configProfile, err := getConfigProfileIfExistsAndValid(xrayVersion, xscVersion, jfrogServer)
430-
if err != nil {
431-
return
432-
}
433-
434429
gitParamsFromEnv, err := extractGitParamsFromEnvs(commandName)
435430
if err != nil {
436431
return
@@ -458,9 +453,16 @@ func GetFrogbotDetails(commandName string) (frogbotDetails *FrogbotDetails, err
458453
return
459454
}
460455

461-
// We apply the configProfile to all received repositories. This loop must be deleted when we will no longer accept multiple repositories in a single scan
456+
configProfile, repoCloneUrl, err := getConfigProfileIfExistsAndValid(xrayVersion, xscVersion, jfrogServer, client, gitParamsFromEnv)
457+
if err != nil {
458+
return
459+
}
460+
461+
// We apply the configProfile to all received repositories. If no config profile was fetched, a nil value is passed
462+
// TODO This loop must be deleted when we will no longer accept multiple repositories in a single scan
462463
for i := range configAggregator {
463464
configAggregator[i].Scan.ConfigProfile = configProfile
465+
configAggregator[i].Git.RepositoryCloneUrl = repoCloneUrl
464466
}
465467

466468
frogbotDetails = &FrogbotDetails{XrayVersion: xrayVersion, XscVersion: xscVersion, Repositories: configAggregator, GitClient: client, ServerDetails: jfrogServer, ReleasesRepo: os.Getenv(jfrogReleasesRepoEnv)}
@@ -791,28 +793,55 @@ func readConfigFromTarget(client vcsclient.VcsClient, gitParamsFromEnv *Git) (co
791793
return
792794
}
793795

794-
// This function fetches a config profile if JF_CONFIG_PROFILE is provided.
795-
// If so - it verifies there is only a single module with a '.' path from root. If these conditions doesn't hold we return an error.
796-
func getConfigProfileIfExistsAndValid(xrayVersion, xscVersion string, jfrogServer *coreconfig.ServerDetails) (configProfile *services.ConfigProfile, err error) {
796+
// This function attempts to fetch a config profile if JF_USE_CONFIG_PROFILE is set to true.
797+
// If we need to use a profile, we first try to get the profile by name that can be provided through JF_CONFIG_PROFILE. If name is provided but profile doesn't exist we return an error.
798+
// If we need to use a profile, but name is not provided, we check if there is a config profile associated to the repo URL.
799+
// When a profile is found we verify several conditions on it.
800+
// If a profile was requested but not found by url nor by name we return an error.
801+
func getConfigProfileIfExistsAndValid(xrayVersion, xscVersion string, jfrogServer *coreconfig.ServerDetails, gitClient vcsclient.VcsClient, gitParams *Git) (configProfile *services.ConfigProfile, repoCloneUrl string, err error) {
802+
var useConfigProfile bool
803+
if useConfigProfile, err = getBoolEnv(JfrogUseConfigProfileEnv, false); err != nil || !useConfigProfile {
804+
log.Debug(fmt.Sprintf("Configuration Profile usage is disabled. All configurations will be derived from environment variables and files.\nTo enable a Configuration Profile, please set %s to TRUE", JfrogUseConfigProfileEnv))
805+
return
806+
}
807+
808+
// Attempt to get the config profile by profile's name
797809
profileName := getTrimmedEnv(JfrogConfigProfileEnv)
798-
if profileName == "" {
799-
log.Debug(fmt.Sprintf("No %s environment variable was provided. All configurations will be induced from Env vars and files", JfrogConfigProfileEnv))
810+
if profileName != "" {
811+
log.Debug(fmt.Sprintf("Configuration profile was requested. Searching profile by provided name '%s'", profileName))
812+
if configProfile, err = xsc.GetConfigProfileByName(xrayVersion, xscVersion, jfrogServer, profileName); err != nil || configProfile == nil {
813+
return
814+
}
815+
err = verifyConfigProfileValidity(configProfile)
800816
return
801817
}
802818

803-
if configProfile, err = xsc.GetConfigProfile(xrayVersion, xscVersion, jfrogServer, profileName); err != nil {
819+
// Getting repository's url in order to get repository HTTP url
820+
repositoryInfo, err := gitClient.GetRepositoryInfo(context.Background(), gitParams.RepoOwner, gitParams.RepoName)
821+
if err != nil {
822+
return nil, "", err
823+
}
824+
repoCloneUrl = repositoryInfo.CloneInfo.HTTP
825+
826+
// Attempt to get a config profile associated with the repo URL
827+
log.Debug(fmt.Sprintf("Configuration profile was requested. Searching profile associated to repository '%s'", jfrogServer.Url))
828+
if configProfile, err = xsc.GetConfigProfileByUrl(xrayVersion, jfrogServer, repoCloneUrl); err != nil || configProfile == nil {
804829
return
805830
}
831+
err = verifyConfigProfileValidity(configProfile)
832+
return
833+
}
806834

835+
func verifyConfigProfileValidity(configProfile *services.ConfigProfile) (err error) {
807836
// Currently, only a single Module that represents the entire project is supported
808837
if len(configProfile.Modules) != 1 {
809838
err = fmt.Errorf("more than one module was found '%s' profile. Frogbot currently supports only one module per config profile", configProfile.ProfileName)
810839
return
811840
}
812841
if configProfile.Modules[0].PathFromRoot != "." {
813-
err = fmt.Errorf("module '%s' in profile '%s' contains the following path from root: '%s'. Frogbot currently supports only a single module with a '.' path from root", configProfile.Modules[0].ModuleName, profileName, configProfile.Modules[0].PathFromRoot)
842+
err = fmt.Errorf("module '%s' in profile '%s' contains the following path from root: '%s'. Frogbot currently supports only a single module with a '.' path from root", configProfile.Modules[0].ModuleName, configProfile.ProfileName, configProfile.Modules[0].PathFromRoot)
814843
return
815844
}
816-
log.Info(fmt.Sprintf("Using Config profile '%s'. jfrog-apps-config will be ignored if exists", profileName))
845+
log.Info(fmt.Sprintf("Using Config profile '%s'. jfrog-apps-config will be ignored if exists", configProfile.ProfileName))
817846
return
818847
}

0 commit comments

Comments
 (0)