From 56b291b051926b013b1a983d3117dd2c7f3afc8c Mon Sep 17 00:00:00 2001 From: Assaf Attias <49212512+attiasas@users.noreply.github.com> Date: Thu, 18 Jan 2024 13:52:27 +0200 Subject: [PATCH] Move security content and use Jfrog-CLI-Security embedded plugin (#2413) --- .github/workflows/xrayTests.yml | 71 -- buildtools/cli.go | 24 +- buildtools/help.go | 10 - docs/buildtools/dockerscan/help.go | 12 - docs/xray/auditgo/help.go | 7 - docs/xray/auditgradle/help.go | 7 - docs/xray/auditmvn/help.go | 7 - docs/xray/auditnpm/help.go | 7 - docs/xray/auditpip/help.go | 7 - docs/xray/curl/help.go | 12 - docs/xray/offlineupdate/help.go | 7 - docs/xray/scan/help.go | 15 - go.mod | 19 +- go.sum | 35 +- gradle_test.go | 34 +- main.go | 61 +- main_test.go | 21 +- maven_test.go | 49 +- npm_test.go | 27 +- pip_test.go | 2 +- scan/cli.go | 478 ------------ utils/cliutils/commandsflags.go | 48 -- xray/cli.go | 222 ------ xray/cli_test.go | 39 - xray_test.go | 1162 ---------------------------- 25 files changed, 206 insertions(+), 2177 deletions(-) delete mode 100644 .github/workflows/xrayTests.yml delete mode 100644 docs/buildtools/dockerscan/help.go delete mode 100644 docs/xray/auditgo/help.go delete mode 100644 docs/xray/auditgradle/help.go delete mode 100644 docs/xray/auditmvn/help.go delete mode 100644 docs/xray/auditnpm/help.go delete mode 100644 docs/xray/auditpip/help.go delete mode 100644 docs/xray/curl/help.go delete mode 100644 docs/xray/offlineupdate/help.go delete mode 100644 docs/xray/scan/help.go delete mode 100644 scan/cli.go delete mode 100644 xray/cli.go delete mode 100644 xray/cli_test.go delete mode 100644 xray_test.go diff --git a/.github/workflows/xrayTests.yml b/.github/workflows/xrayTests.yml deleted file mode 100644 index fdc8a691b..000000000 --- a/.github/workflows/xrayTests.yml +++ /dev/null @@ -1,71 +0,0 @@ -name: Xray Tests -on: - push: - branches: - - '**' - tags-ignore: - - '**' - # Triggers the workflow on labeled PRs only. - pull_request_target: - types: [ labeled ] -# Ensures that only the latest commit is running for each PR at a time. -concurrency: - group: ${{ github.workflow }}-${{ github.event.pull_request.number || github.sha }}-${{ github.ref }} - cancel-in-progress: true -jobs: - CLI-Tests: - if: contains(github.event.pull_request.labels.*.name, 'safe to test') || github.event_name == 'push' - name: Xray tests (${{ matrix.os }}) - strategy: - fail-fast: false - matrix: - os: [ ubuntu, windows, macos ] - runs-on: ${{ matrix.os }}-latest - env: - GRADLE_OPTS: -Dorg.gradle.daemon=false - steps: - - name: Install Go - uses: actions/setup-go@v3 - with: - go-version: 1.20.x - - name: Install npm - uses: actions/setup-node@v3 - with: - node-version: "16" - - name: Install Java - uses: actions/setup-java@v3 - with: - java-version: "11" - distribution: "adopt" - - name: Install NuGet - uses: nuget/setup-nuget@v1 - with: - nuget-version: 6.x - - name: Install dotnet - uses: actions/setup-dotnet@v3 - with: - dotnet-version: "6.x" - - name: Setup Python3 - uses: actions/setup-python@v4 - with: - python-version: "3.x" - - name: Setup Pipenv - run: python -m pip install pipenv - - name: Setup Poetry - run: python -m pip install poetry - - name: Setup Gradle - uses: gradle/gradle-build-action@v2 - with: - gradle-version: 7.6 - - name: Checkout code - uses: actions/checkout@v3 - with: - ref: ${{ github.event.pull_request.head.sha }} - - - name: Run Xray tests (without Docker Scan) - run: go test -v github.com/jfrog/jfrog-cli --timeout 0 --test.xray --jfrog.url=${{ secrets.PLATFORM_URL }} --jfrog.adminToken=${{ secrets.PLATFORM_ADMIN_TOKEN }} --jfrog.user=${{ secrets.PLATFORM_USER }} --test.containerRegistry=${{ secrets.CONTAINER_REGISTRY }} --ci.runId=${{ runner.os }}-xray - if: ${{ matrix.os != 'ubuntu' }} - - - name: Run Xray tests (with Docker Scan, only on Ubuntu) - run: go test -v github.com/jfrog/jfrog-cli --timeout 0 --test.xray --test.dockerScan --jfrog.url=${{ secrets.PLATFORM_URL }} --jfrog.adminToken=${{ secrets.PLATFORM_ADMIN_TOKEN }} --test.containerRegistry=${{ secrets.CONTAINER_REGISTRY }} --ci.runId=${{ runner.os }}-xray - if: ${{ matrix.os == 'ubuntu' }} diff --git a/buildtools/cli.go b/buildtools/cli.go index 92c09bd69..019b5425c 100644 --- a/buildtools/cli.go +++ b/buildtools/cli.go @@ -24,8 +24,12 @@ import ( outputFormat "github.com/jfrog/jfrog-cli-core/v2/common/format" "github.com/jfrog/jfrog-cli-core/v2/common/project" corecommon "github.com/jfrog/jfrog-cli-core/v2/docs/common" + "github.com/jfrog/jfrog-cli-core/v2/plugins/components" coreConfig "github.com/jfrog/jfrog-cli-core/v2/utils/config" "github.com/jfrog/jfrog-cli-core/v2/utils/coreutils" + securityCLI "github.com/jfrog/jfrog-cli-security/cli" + securityDocs "github.com/jfrog/jfrog-cli-security/cli/docs" + "github.com/jfrog/jfrog-cli-security/commands/scan" terraformdocs "github.com/jfrog/jfrog-cli/docs/artifactory/terraform" "github.com/jfrog/jfrog-cli/docs/artifactory/terraformconfig" "github.com/jfrog/jfrog-cli/docs/buildtools/docker" @@ -51,7 +55,6 @@ import ( yarndocs "github.com/jfrog/jfrog-cli/docs/buildtools/yarn" "github.com/jfrog/jfrog-cli/docs/buildtools/yarnconfig" "github.com/jfrog/jfrog-cli/docs/common" - "github.com/jfrog/jfrog-cli/scan" "github.com/jfrog/jfrog-cli/utils/cliutils" "github.com/jfrog/jfrog-client-go/utils/errorutils" "github.com/jfrog/jfrog-client-go/utils/log" @@ -411,6 +414,9 @@ func MvnCmd(c *cli.Context) (err error) { if err != nil { return err } + if xrayScan { + commandsUtils.ConditionalUploadScanFunc = scan.ConditionalUploadDefaultScanFunc + } filteredMavenArgs, format, err := coreutils.ExtractXrayOutputFormatFromArgs(filteredMavenArgs) if err != nil { return err @@ -464,6 +470,9 @@ func GradleCmd(c *cli.Context) (err error) { if err != nil { return err } + if xrayScan { + commandsUtils.ConditionalUploadScanFunc = scan.ConditionalUploadDefaultScanFunc + } filteredGradleArgs, format, err := coreutils.ExtractXrayOutputFormatFromArgs(filteredGradleArgs) if err != nil { return err @@ -687,7 +696,7 @@ func dockerCmd(c *cli.Context) error { case "push": err = pushCmd(c, image) case "scan": - return scan.DockerScan(c, image) + return dockerScanCmd(c, image) default: err = dockerNativeCmd(c) } @@ -745,6 +754,14 @@ func pushCmd(c *cli.Context, image string) (err error) { return } +func dockerScanCmd(c *cli.Context, imageTag string) error { + convertedCtx, err := components.ConvertContext(c, securityDocs.GetCommandFlags(securityDocs.DockerScan)...) + if err != nil { + return err + } + return securityCLI.DockerScan(convertedCtx, imageTag) +} + func dockerNativeCmd(c *cli.Context) error { if show, err := cliutils.ShowCmdHelpIfNeeded(c, c.Args()); show || err != nil { return err @@ -827,6 +844,9 @@ func NpmPublishCmd(c *cli.Context) (err error) { if err = npmCmd.Init(); err != nil { return err } + if npmCmd.GetXrayScan() { + commandsUtils.ConditionalUploadScanFunc = scan.ConditionalUploadDefaultScanFunc + } printDeploymentView, detailedSummary := log.IsStdErrTerminal(), npmCmd.IsDetailedSummary() if !detailedSummary { npmCmd.SetDetailedSummary(printDeploymentView) diff --git a/buildtools/help.go b/buildtools/help.go index 472dce3b0..2b2f3b092 100644 --- a/buildtools/help.go +++ b/buildtools/help.go @@ -4,7 +4,6 @@ import ( corecommon "github.com/jfrog/jfrog-cli-core/v2/docs/common" "github.com/jfrog/jfrog-cli/docs/buildtools/dockerpull" "github.com/jfrog/jfrog-cli/docs/buildtools/dockerpush" - "github.com/jfrog/jfrog-cli/docs/buildtools/dockerscan" "github.com/jfrog/jfrog-cli/docs/buildtools/npmci" "github.com/jfrog/jfrog-cli/docs/buildtools/npminstall" "github.com/jfrog/jfrog-cli/docs/buildtools/npmpublish" @@ -35,15 +34,6 @@ func GetBuildToolsHelpCommands() []cli.Command { ArgsUsage: common.CreateEnvVars(), Hidden: true, }, - { - Name: "dockerscanhelp", - Flags: cliutils.GetCommandFlags(cliutils.DockerScan), - Usage: dockerscan.GetDescription(), - HelpName: corecommon.CreateUsage("docker scan", dockerscan.GetDescription(), dockerscan.Usage), - UsageText: dockerscan.GetArguments(), - ArgsUsage: common.CreateEnvVars(), - Hidden: true, - }, { Name: "npminstallhelp", Flags: cliutils.GetCommandFlags(cliutils.NpmInstallCi), diff --git a/docs/buildtools/dockerscan/help.go b/docs/buildtools/dockerscan/help.go deleted file mode 100644 index 52baa8422..000000000 --- a/docs/buildtools/dockerscan/help.go +++ /dev/null @@ -1,12 +0,0 @@ -package dockerscan - -var Usage = []string{"docker scan "} - -func GetDescription() string { - return "Scan local docker image using the docker client and Xray." -} - -func GetArguments() string { - return ` docker scan args - The docker scan args to run docker scan.` -} diff --git a/docs/xray/auditgo/help.go b/docs/xray/auditgo/help.go deleted file mode 100644 index e03d10d4a..000000000 --- a/docs/xray/auditgo/help.go +++ /dev/null @@ -1,7 +0,0 @@ -package auditgo - -var Usage = []string{"xr audit-go [command options]"} - -func GetDescription() string { - return "Execute an audit Go command, using the configured Xray details." -} diff --git a/docs/xray/auditgradle/help.go b/docs/xray/auditgradle/help.go deleted file mode 100644 index df90fe765..000000000 --- a/docs/xray/auditgradle/help.go +++ /dev/null @@ -1,7 +0,0 @@ -package auditgradle - -var Usage = []string{"xr audit-gradle [command options]"} - -func GetDescription() string { - return "Execute an audit Gradle command, using the configured Xray details." -} diff --git a/docs/xray/auditmvn/help.go b/docs/xray/auditmvn/help.go deleted file mode 100644 index 3b489af4e..000000000 --- a/docs/xray/auditmvn/help.go +++ /dev/null @@ -1,7 +0,0 @@ -package auditmvn - -var Usage = []string{"xr audit-mvn [command options]"} - -func GetDescription() string { - return "Execute an audit Maven command, using the configured Xray details." -} diff --git a/docs/xray/auditnpm/help.go b/docs/xray/auditnpm/help.go deleted file mode 100644 index 3ad981fb4..000000000 --- a/docs/xray/auditnpm/help.go +++ /dev/null @@ -1,7 +0,0 @@ -package auditnpm - -var Usage = []string{"xr audit-npm [command options]"} - -func GetDescription() string { - return "Execute an audit Npm command, using the configured Xray details." -} diff --git a/docs/xray/auditpip/help.go b/docs/xray/auditpip/help.go deleted file mode 100644 index 64892bbb4..000000000 --- a/docs/xray/auditpip/help.go +++ /dev/null @@ -1,7 +0,0 @@ -package auditpip - -var Usage = []string{"xr audit-pip [command options]"} - -func GetDescription() string { - return "Execute an audit Pip command, using the configured Xray details." -} diff --git a/docs/xray/curl/help.go b/docs/xray/curl/help.go deleted file mode 100644 index 2b81957e3..000000000 --- a/docs/xray/curl/help.go +++ /dev/null @@ -1,12 +0,0 @@ -package curl - -var Usage = []string{"xr curl [command options] "} - -func GetDescription() string { - return "Execute a cUrl command, using the configured Xray details." -} - -func GetArguments() string { - return ` curl command - cUrl command to run.` -} diff --git a/docs/xray/offlineupdate/help.go b/docs/xray/offlineupdate/help.go deleted file mode 100644 index cdd5acb63..000000000 --- a/docs/xray/offlineupdate/help.go +++ /dev/null @@ -1,7 +0,0 @@ -package offlineupdate - -var Usage = []string{"xr offline-update --license-id= [command options]"} - -func GetDescription() string { - return "Download Xray offline updates." -} diff --git a/docs/xray/scan/help.go b/docs/xray/scan/help.go deleted file mode 100644 index b141eef7a..000000000 --- a/docs/xray/scan/help.go +++ /dev/null @@ -1,15 +0,0 @@ -package scan - -var Usage = []string{"xr scan [command options] ", - "xr scan [command options] --spec= "} - -func GetDescription() string { - return "Scan files located on the local file-system with Xray." -} - -func GetArguments() string { - return ` source pattern - Specifies the local file system path of the files to be scanned. - You can specify multiple files by using wildcards, Ant pattern or a regular expression. - If you have specified that you are using regular expressions, then the first one used in the argument must be enclosed in parenthesis.` -} diff --git a/go.mod b/go.mod index a3b9105ad..0e63fd9b7 100644 --- a/go.mod +++ b/go.mod @@ -10,6 +10,7 @@ require ( github.com/jfrog/build-info-go v1.9.20 github.com/jfrog/gofrog v1.5.0 github.com/jfrog/jfrog-cli-core/v2 v2.47.10 + github.com/jfrog/jfrog-cli-security v1.0.0 github.com/jfrog/jfrog-client-go v1.35.6 github.com/jszwec/csvutil v1.9.0 github.com/mholt/archiver/v3 v3.5.1 @@ -17,7 +18,7 @@ require ( github.com/testcontainers/testcontainers-go v0.23.0 github.com/urfave/cli v1.22.14 github.com/xeipuuv/gojsonschema v1.2.0 - golang.org/x/exp v0.0.0-20240103183307-be819d1f06fc + golang.org/x/exp v0.0.0-20240112132812-db7319d0e0e3 golang.org/x/term v0.16.0 gopkg.in/yaml.v2 v2.4.0 ) @@ -60,7 +61,7 @@ require ( github.com/gookit/color v1.5.4 // indirect github.com/hashicorp/hcl v1.0.0 // indirect github.com/jbenet/go-context v0.0.0-20150711004518-d14ea06fba99 // indirect - github.com/jedib0t/go-pretty/v6 v6.5.0 // indirect + github.com/jedib0t/go-pretty/v6 v6.5.3 // indirect github.com/jfrog/archiver/v3 v3.5.3 // indirect github.com/jfrog/jfrog-apps-config v1.0.1 // indirect github.com/kevinburke/ssh_config v1.2.0 // indirect @@ -71,7 +72,7 @@ require ( github.com/manifoldco/promptui v0.9.0 // indirect github.com/mattn/go-colorable v0.1.13 // indirect github.com/mattn/go-isatty v0.0.17 // indirect - github.com/mattn/go-runewidth v0.0.13 // indirect + github.com/mattn/go-runewidth v0.0.15 // indirect github.com/mattn/go-tty v0.0.3 // indirect github.com/minio/sha256-simd v1.0.1 // indirect github.com/mitchellh/mapstructure v1.5.0 // indirect @@ -113,13 +114,13 @@ require ( github.com/xo/terminfo v0.0.0-20210125001918-ca9a967f8778 // indirect go.uber.org/atomic v1.9.0 // indirect go.uber.org/multierr v1.9.0 // indirect - golang.org/x/crypto v0.17.0 // indirect + golang.org/x/crypto v0.18.0 // indirect golang.org/x/mod v0.14.0 // indirect - golang.org/x/net v0.19.0 // indirect - golang.org/x/sync v0.5.0 // indirect + golang.org/x/net v0.20.0 // indirect + golang.org/x/sync v0.6.0 // indirect golang.org/x/sys v0.16.0 // indirect golang.org/x/text v0.14.0 // indirect - golang.org/x/tools v0.16.0 // indirect + golang.org/x/tools v0.17.0 // indirect google.golang.org/genproto/googleapis/rpc v0.0.0-20231120223509-83a465c0220f // indirect google.golang.org/grpc v1.59.0 // indirect google.golang.org/protobuf v1.31.0 // indirect @@ -128,7 +129,9 @@ require ( gopkg.in/yaml.v3 v3.0.1 // indirect ) -replace github.com/jfrog/jfrog-cli-core/v2 => github.com/jfrog/jfrog-cli-core/v2 v2.31.1-0.20240116074500-2653d8805fcc +replace github.com/jfrog/jfrog-cli-core/v2 => github.com/jfrog/jfrog-cli-core/v2 v2.31.1-0.20240118100957-b4e1537e91dd + +replace github.com/jfrog/jfrog-cli-security => github.com/jfrog/jfrog-cli-security v0.0.0-20240118110600-d44be676f906 // replace github.com/jfrog/build-info-go => github.com/jfrog/build-info-go v1.8.9-0.20231220102935-c8776c613ad8 diff --git a/go.sum b/go.sum index 49226870e..a39a2659e 100644 --- a/go.sum +++ b/go.sum @@ -125,8 +125,8 @@ github.com/hashicorp/hcl v1.0.0 h1:0Anlzjpi4vEasTeNFn2mLJgTSwt0+6sfsiTG8qcWGx4= github.com/hashicorp/hcl v1.0.0/go.mod h1:E5yfLk+7swimpb2L/Alb/PJmXilQ/rhwaUYs4T20WEQ= github.com/jbenet/go-context v0.0.0-20150711004518-d14ea06fba99 h1:BQSFePA1RWJOlocH6Fxy8MmwDt+yVQYULKfN0RoTN8A= github.com/jbenet/go-context v0.0.0-20150711004518-d14ea06fba99/go.mod h1:1lJo3i6rXxKeerYnT8Nvf0QmHCRC1n8sfWVwXF2Frvo= -github.com/jedib0t/go-pretty/v6 v6.5.0 h1:FI0L5PktzbafnZKuPae/D3150x3XfYbFe2hxMT+TbpA= -github.com/jedib0t/go-pretty/v6 v6.5.0/go.mod h1:Ndk3ase2CkQbXLLNf5QDHoYb6J9WtVfmHZu9n8rk2xs= +github.com/jedib0t/go-pretty/v6 v6.5.3 h1:GIXn6Er/anHTkVUoufs7ptEvxdD6KIhR7Axa2wYCPF0= +github.com/jedib0t/go-pretty/v6 v6.5.3/go.mod h1:5LQIxa52oJ/DlDSLv0HEkWOFMDGoWkJb9ss5KqPpJBg= github.com/jfrog/archiver/v3 v3.5.3 h1:Udz6+z/YIhTFmcEp1TeW2DEwNyo7JSAnrGUsrbL2FZI= github.com/jfrog/archiver/v3 v3.5.3/go.mod h1:/MbmBhPzkliu9PtweAg9lCYHGcKdapwMMZS/QS09T5c= github.com/jfrog/build-info-go v1.9.20 h1:tQF6EMjt/EEX8syTrgpL/c7FjhlBSjtv848jNvxpMp8= @@ -135,8 +135,10 @@ github.com/jfrog/gofrog v1.5.0 h1:OLaXpNaEniliE4Kq8lJ5evVYzzt3zdYtpMIBu6TO++c= github.com/jfrog/gofrog v1.5.0/go.mod h1:wQqagqq2VpuCWRPlq/65GbH9gsRz+7Bgc1Q+PKD4Y+k= github.com/jfrog/jfrog-apps-config v1.0.1 h1:mtv6k7g8A8BVhlHGlSveapqf4mJfonwvXYLipdsOFMY= github.com/jfrog/jfrog-apps-config v1.0.1/go.mod h1:8AIIr1oY9JuH5dylz2S6f8Ym2MaadPLR6noCBO4C22w= -github.com/jfrog/jfrog-cli-core/v2 v2.31.1-0.20240116074500-2653d8805fcc h1:YAW8UfyS3lKchgXgPMSYIIrUu0q4FM3ovpNc3Mc0/1A= -github.com/jfrog/jfrog-cli-core/v2 v2.31.1-0.20240116074500-2653d8805fcc/go.mod h1:dFpRoGR5/Qe+bvszvRPYGqMEdwmjNhjFLXlovGs9sII= +github.com/jfrog/jfrog-cli-core/v2 v2.31.1-0.20240118100957-b4e1537e91dd h1:7JOQANVaULKq0b2X10ERsEAZOGccfooOvstr3UZcGTc= +github.com/jfrog/jfrog-cli-core/v2 v2.31.1-0.20240118100957-b4e1537e91dd/go.mod h1:tbplJYWXBgQNLMWadfZYh2uaajZjG1tLgBb1txLNAQw= +github.com/jfrog/jfrog-cli-security v0.0.0-20240118110600-d44be676f906 h1:QW+LBf069sSLQMxcHlt45MOP5Q9UVeM/iByKsR+wRdw= +github.com/jfrog/jfrog-cli-security v0.0.0-20240118110600-d44be676f906/go.mod h1:ryzQ8KeRxslgZ6jcbtlZzCKn8KyslsZ62AF3NlDhB+I= github.com/jfrog/jfrog-client-go v1.35.6 h1:nVS94x6cwSRkhtj8OM3elbUcGgQhqsK8YMPvC/gf5sk= github.com/jfrog/jfrog-client-go v1.35.6/go.mod h1:V+XKC27k6GA5OcWIAItpnxZAZnCigg8xCkpXKP905Fk= github.com/jszwec/csvutil v1.9.0 h1:iTmq9G1P0e+AUq/MkFg6tetJ+1BH3fOX8Xi0RAcwiGc= @@ -176,8 +178,9 @@ github.com/mattn/go-isatty v0.0.17 h1:BTarxUcIeDqL27Mc+vyvdWYSL28zpIhv3RoTdsLMPn github.com/mattn/go-isatty v0.0.17/go.mod h1:kYGgaQfpe5nmfYZH+SKPsOc2e4SrIfOl2e/yFXSvRLM= github.com/mattn/go-runewidth v0.0.6/go.mod h1:H031xJmbD/WCDINGzjvQ9THkh0rPKHF+m2gUSrubnMI= github.com/mattn/go-runewidth v0.0.9/go.mod h1:H031xJmbD/WCDINGzjvQ9THkh0rPKHF+m2gUSrubnMI= -github.com/mattn/go-runewidth v0.0.13 h1:lTGmDsbAYt5DmK6OnoV7EuIF1wEIFAcxld6ypU4OSgU= github.com/mattn/go-runewidth v0.0.13/go.mod h1:Jdepj2loyihRzMpdS35Xk/zdY8IAYHsh153qUoGf23w= +github.com/mattn/go-runewidth v0.0.15 h1:UNAjwbU9l54TA3KzvqLGxwWjHmMgBUVhBiTjelZgg3U= +github.com/mattn/go-runewidth v0.0.15/go.mod h1:Jdepj2loyihRzMpdS35Xk/zdY8IAYHsh153qUoGf23w= github.com/mattn/go-tty v0.0.3 h1:5OfyWorkyO7xP52Mq7tB36ajHDG5OHrmBGIS/DtakQI= github.com/mattn/go-tty v0.0.3/go.mod h1:ihxohKRERHTVzN+aSVRwACLCeqIoZAWpoICkkvrWyR0= github.com/mholt/archiver/v3 v3.5.1 h1:rDjOBX9JSF5BvoJGvjqK479aL70qh9DIpZCl+k7Clwo= @@ -220,7 +223,6 @@ github.com/pkg/browser v0.0.0-20240102092130-5ac0b6a4141c h1:+mdjkGKdHQG3305AYmd github.com/pkg/browser v0.0.0-20240102092130-5ac0b6a4141c/go.mod h1:7rwL4CYBLnjLxUqIJNnCWiEdr3bn6IUYi15bNlnbCCU= github.com/pkg/errors v0.9.1 h1:FEBLx1zS214owpjy7qsBeixbURkuhQAwrK5UwLGTwt4= github.com/pkg/errors v0.9.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0= -github.com/pkg/profile v1.6.0/go.mod h1:qBsxPvzyUincmltOk6iyRVxHYg4adc0OFOv72ZdLa18= github.com/pkg/term v1.1.0 h1:xIAAdCMh3QIAy+5FrE8Ad8XoDhEU4ufwbaSozViP9kk= github.com/pkg/term v1.1.0/go.mod h1:E25nymQcrSllhX42Ok8MRm1+hyBdHY0dCeiKZ9jpNGw= github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4= @@ -265,7 +267,6 @@ github.com/stretchr/testify v1.3.0/go.mod h1:M5WIy9Dh21IEIfnGCwXGc5bZfKNJtfHm1UV github.com/stretchr/testify v1.4.0/go.mod h1:j7eGeouHqKxXV5pUuKE4zz7dFj8WfuZ+81PSLYec5m4= github.com/stretchr/testify v1.7.0/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg= github.com/stretchr/testify v1.7.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg= -github.com/stretchr/testify v1.7.4/go.mod h1:yNjHg4UonilssWZ8iaSj1OCr/vHnekPRkoO+kdMU+MU= github.com/stretchr/testify v1.8.0/go.mod h1:yNjHg4UonilssWZ8iaSj1OCr/vHnekPRkoO+kdMU+MU= github.com/stretchr/testify v1.8.4 h1:CcVxjf3Q8PM0mHUKJCdn+eZZtm5yQwehR5yeSVQQcUk= github.com/stretchr/testify v1.8.4/go.mod h1:sz/lmYIOXD/1dqDmKjjqLyZ2RngseejIcXlSw2iwfAo= @@ -315,10 +316,10 @@ golang.org/x/crypto v0.0.0-20210921155107-089bfa567519/go.mod h1:GvvjBRRGRdwPK5y golang.org/x/crypto v0.0.0-20220622213112-05595931fe9d/go.mod h1:IxCIyHEi3zRg3s0A5j5BB6A9Jmi73HwBIUl50j+osU4= golang.org/x/crypto v0.3.1-0.20221117191849-2c476679df9a/go.mod h1:hebNnKkNXi2UzZN1eVRvBB7co0a+JxK6XbPiWVs/3J4= golang.org/x/crypto v0.7.0/go.mod h1:pYwdfH91IfpZVANVyUOhSIPZaFoJGxTFbZhFTx+dXZU= -golang.org/x/crypto v0.17.0 h1:r8bRNjWL3GshPW3gkd+RpvzWrZAwPS49OmTGZ/uhM4k= -golang.org/x/crypto v0.17.0/go.mod h1:gCAAfMLgwOJRpTjQ2zCCt2OcSfYMTeZVSRtQlPC7Nq4= -golang.org/x/exp v0.0.0-20240103183307-be819d1f06fc h1:ao2WRsKSzW6KuUY9IWPwWahcHCgR0s52IfwutMfEbdM= -golang.org/x/exp v0.0.0-20240103183307-be819d1f06fc/go.mod h1:iRJReGqOEeBhDZGkGbynYwcHlctCvnjTYIamk7uXpHI= +golang.org/x/crypto v0.18.0 h1:PGVlW0xEltQnzFZ55hkuX5+KLyrMYhHld1YHO4AKcdc= +golang.org/x/crypto v0.18.0/go.mod h1:R0j02AL6hcrfOiy9T4ZYp/rcWeMxM3L6QYxlOuEG1mg= +golang.org/x/exp v0.0.0-20240112132812-db7319d0e0e3 h1:hNQpMuAJe5CtcUqCXaWga3FHu+kQvCqcsoVaQgSV60o= +golang.org/x/exp v0.0.0-20240112132812-db7319d0e0e3/go.mod h1:idGWGoKP1toJGkd5/ig9ZLuPcZBC3ewk7SzmH0uou08= golang.org/x/mod v0.2.0/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA= golang.org/x/mod v0.3.0/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA= golang.org/x/mod v0.6.0-dev.0.20220419223038-86c51ed26bb4/go.mod h1:jJ57K6gSWd91VN4djpZkiMVwK6gcyfeH4XE8wZrZaV4= @@ -338,15 +339,15 @@ golang.org/x/net v0.0.0-20220722155237-a158d28d115b/go.mod h1:XRhObCWvk6IyKnWLug golang.org/x/net v0.2.0/go.mod h1:KqCZLdyyvdV855qA2rE3GC2aiw5xGR5TEjj8smXukLY= golang.org/x/net v0.6.0/go.mod h1:2Tu9+aMcznHK/AK1HMvgo6xiTLG5rD5rZLDS+rp2Bjs= golang.org/x/net v0.8.0/go.mod h1:QVkue5JL9kW//ek3r6jTKnTFis1tRmNAW2P1shuFdJc= -golang.org/x/net v0.19.0 h1:zTwKpTd2XuCqf8huc7Fo2iSy+4RHPd10s4KzeTnVr1c= -golang.org/x/net v0.19.0/go.mod h1:CfAk/cbD4CthTvqiEl8NpboMuiuOYsAr/7NOjZJtv1U= +golang.org/x/net v0.20.0 h1:aCL9BSgETF1k+blQaYUBx9hJ9LOGP3gAVemcZlf1Kpo= +golang.org/x/net v0.20.0/go.mod h1:z8BVo6PvndSri0LbOE3hAn0apkU+1YvI6E70E9jsnvY= golang.org/x/sync v0.0.0-20190423024810-112230192c58/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sync v0.0.0-20190911185100-cd5d95a43a6e/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sync v0.0.0-20201020160332-67f06af15bc9/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sync v0.0.0-20220722155255-886fb9371eb4/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sync v0.1.0/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= -golang.org/x/sync v0.5.0 h1:60k92dhOjHxJkrqnwsfl8KuaHbn/5dl0lUPUklKo3qE= -golang.org/x/sync v0.5.0/go.mod h1:Czt+wKu1gCyEFDUtn0jG5QVvpJ6rzVqr5aXyt9drQfk= +golang.org/x/sync v0.6.0 h1:5BMeUDZ7vkXGfEr1x9B4bRcTH4lpkTkpdh0T/J+qjbQ= +golang.org/x/sync v0.6.0/go.mod h1:Czt+wKu1gCyEFDUtn0jG5QVvpJ6rzVqr5aXyt9drQfk= golang.org/x/sys v0.0.0-20181122145206-62eef0e2fa9b/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= golang.org/x/sys v0.0.0-20190222072716-a9d3bda3a223/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= @@ -408,8 +409,8 @@ golang.org/x/tools v0.0.0-20200619180055-7c47624df98f/go.mod h1:EkVYQZoAsY45+roY golang.org/x/tools v0.0.0-20210106214847-113979e3529a/go.mod h1:emZCQorbCU4vsT4fOWvOPXz4eW1wZW4PmDk9uLelYpA= golang.org/x/tools v0.1.12/go.mod h1:hNGJHUnrk76NpqgfD5Aqm5Crs+Hm0VOH/i9J2+nxYbc= golang.org/x/tools v0.6.0/go.mod h1:Xwgl3UAJ/d3gWutnCtw505GrjyAbvKui8lOU390QaIU= -golang.org/x/tools v0.16.0 h1:GO788SKMRunPIBCXiQyo2AaexLstOrVhuAL5YwsckQM= -golang.org/x/tools v0.16.0/go.mod h1:kYVVN6I1mBNoB1OX+noeBjbRk4IUEPa7JJ+TJMEooJ0= +golang.org/x/tools v0.17.0 h1:FvmRgNOcs3kOa+T20R1uhfP9F6HgG2mfxDv1vrx1Htc= +golang.org/x/tools v0.17.0/go.mod h1:xsh6VxdV005rRVaS6SSAf9oiAqljS7UZUacMZ8Bnsps= golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= golang.org/x/xerrors v0.0.0-20191011141410-1b5146add898/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= diff --git a/gradle_test.go b/gradle_test.go index 37cd28e42..a1ffed56d 100644 --- a/gradle_test.go +++ b/gradle_test.go @@ -1,17 +1,24 @@ package main import ( + "errors" "os" "path/filepath" "strings" "testing" + "github.com/jfrog/jfrog-cli/utils/cliutils" + "github.com/jfrog/jfrog-client-go/utils/log" + clientTestUtils "github.com/jfrog/jfrog-client-go/utils/tests" buildinfo "github.com/jfrog/build-info-go/entities" "github.com/jfrog/jfrog-cli-core/v2/artifactory/commands/gradle" "github.com/jfrog/jfrog-cli-core/v2/common/build" + commonCliUtils "github.com/jfrog/jfrog-cli-core/v2/common/cliutils" "github.com/jfrog/jfrog-cli-core/v2/common/commands" + outputFormat "github.com/jfrog/jfrog-cli-core/v2/common/format" + "github.com/jfrog/jfrog-cli-core/v2/common/project" "github.com/jfrog/jfrog-cli-core/v2/common/spec" "github.com/jfrog/jfrog-cli-core/v2/utils/coreutils" @@ -45,12 +52,37 @@ func TestGradleBuildConditionalUpload(t *testing.T) { oldHomeDir := changeWD(t, filepath.Dir(buildGradlePath)) defer clientTestUtils.ChangeDirAndAssert(t, oldHomeDir) execFunc := func() error { - return runJfrogCliWithoutAssertion("gradle", "clean", "artifactoryPublish", "-b"+buildGradlePath, "--scan") + return runGradleConditionalUploadTest(buildGradlePath) } testConditionalUpload(t, execFunc, searchSpec, tests.GetGradleDeployedArtifacts()...) cleanGradleTest(t) } +func runGradleConditionalUploadTest(buildGradlePath string) (err error) { + configFilePath, exists, err := project.GetProjectConfFilePath(project.Gradle) + if err != nil { + return err + } + if !exists { + return errors.New("no config file was found!") + } + buildConfig := build.NewBuildConfiguration("", "", "", "") + if err = buildConfig.ValidateBuildAndModuleParams(); err != nil { + return + } + printDeploymentView := log.IsStdErrTerminal() + gradleCmd := gradle.NewGradleCommand(). + SetTasks([]string{"clean", "artifactoryPublish", "-b" + buildGradlePath}). + SetConfiguration(buildConfig). + SetXrayScan(true).SetScanOutputFormat(outputFormat.Table). + SetDetailedSummary(printDeploymentView).SetConfigPath(configFilePath).SetThreads(commonCliUtils.Threads) + err = commands.Exec(gradleCmd) + result := gradleCmd.Result() + defer cliutils.CleanupResult(result, &err) + err = cliutils.PrintCommandSummary(gradleCmd.Result(), false, printDeploymentView, false, err) + return +} + func TestGradleWithDeploymentView(t *testing.T) { initGradleTest(t) buildGradlePath := createGradleProject(t, "gradleproject") diff --git a/main.go b/main.go index 8cf9155c0..78aa9d6ce 100644 --- a/main.go +++ b/main.go @@ -2,12 +2,19 @@ package main import ( "fmt" + "os" + "runtime" + "sort" + "strings" + "github.com/agnivade/levenshtein" corecommon "github.com/jfrog/jfrog-cli-core/v2/docs/common" setupcore "github.com/jfrog/jfrog-cli-core/v2/general/envsetup" + "github.com/jfrog/jfrog-cli-core/v2/plugins/components" coreconfig "github.com/jfrog/jfrog-cli-core/v2/utils/config" "github.com/jfrog/jfrog-cli-core/v2/utils/coreutils" "github.com/jfrog/jfrog-cli-core/v2/utils/log" + securityCLI "github.com/jfrog/jfrog-cli-security/cli" "github.com/jfrog/jfrog-cli/artifactory" "github.com/jfrog/jfrog-cli/buildtools" "github.com/jfrog/jfrog-cli/completion" @@ -27,18 +34,12 @@ import ( "github.com/jfrog/jfrog-cli/pipelines" "github.com/jfrog/jfrog-cli/plugins" "github.com/jfrog/jfrog-cli/plugins/utils" - "github.com/jfrog/jfrog-cli/scan" "github.com/jfrog/jfrog-cli/utils/cliutils" - "github.com/jfrog/jfrog-cli/xray" clientutils "github.com/jfrog/jfrog-client-go/utils" "github.com/jfrog/jfrog-client-go/utils/io/fileutils" clientlog "github.com/jfrog/jfrog-client-go/utils/log" "github.com/urfave/cli" "golang.org/x/exp/slices" - "os" - "runtime" - "sort" - "strings" ) const commandHelpTemplate string = `{{.HelpName}}{{if .UsageText}} @@ -70,6 +71,8 @@ OPTIONS: {{end}} ` +const jfrogAppName = "jf" + func main() { log.SetDefaultLogger() err := execMain() @@ -84,13 +87,19 @@ func execMain() error { clientutils.SetUserAgent(coreutils.GetCliUserAgent()) app := cli.NewApp() - app.Name = "jf" + app.Name = jfrogAppName app.Usage = "See https://github.com/jfrog/jfrog-cli for usage instructions." app.Version = cliutils.GetVersion() args := os.Args cliutils.SetCliExecutableName(args[0]) app.EnableBashCompletion = true - app.Commands = getCommands() + commands, err := getCommands() + if err != nil { + clientlog.Error(err) + os.Exit(1) + } + sort.Slice(commands, func(i, j int) bool { return commands[i].Name < commands[j].Name }) + app.Commands = commands cli.CommandHelpTemplate = commandHelpTemplate cli.AppHelpTemplate = getAppHelpTemplate() cli.SubcommandHelpTemplate = subcommandHelpTemplate @@ -127,7 +136,7 @@ func execMain() error { } return nil } - err := app.Run(args) + err = app.Run(args) return err } @@ -168,7 +177,7 @@ func searchSimilarCmds(cmds []cli.Command, toCompare string) (bestSimilarity []s const otherCategory = "Other" -func getCommands() []cli.Command { +func getCommands() ([]cli.Command, error) { cliNameSpaces := []cli.Command{ { Name: cliutils.CmdArtifactory, @@ -182,12 +191,6 @@ func getCommands() []cli.Command { Subcommands: missioncontrol.GetCommands(), Category: otherCategory, }, - { - Name: cliutils.CmdXray, - Usage: "Xray commands.", - Subcommands: xray.GetCommands(), - Category: otherCategory, - }, { Name: cliutils.CmdDistribution, Usage: "Distribution commands.", @@ -289,11 +292,31 @@ func getCommands() []cli.Command { Action: token.AccessTokenCreateCmd, }, } - allCommands := append(slices.Clone(cliNameSpaces), utils.GetPlugins()...) - allCommands = append(allCommands, scan.GetCommands()...) + + securityCmds, err := ConvertEmbeddedPlugin(securityCLI.GetJfrogCliSecurityApp()) + if err != nil { + return nil, err + } + allCommands := append(slices.Clone(cliNameSpaces), securityCmds...) + allCommands = append(allCommands, utils.GetPlugins()...) allCommands = append(allCommands, buildtools.GetCommands()...) allCommands = append(allCommands, lifecycle.GetCommands()...) - return append(allCommands, buildtools.GetBuildToolsHelpCommands()...) + return append(allCommands, buildtools.GetBuildToolsHelpCommands()...), nil +} + +// Embedded plugins are CLI plugins that are embedded in the JFrog CLI and not require any installation. +// This function converts an embedded plugin to a cli.Command slice to be registered as commands of the cli. +func ConvertEmbeddedPlugin(jfrogPlugin components.App) (converted []cli.Command, err error) { + for i := range jfrogPlugin.Subcommands { + // commands name-space without category are considered as 'other' category + if jfrogPlugin.Subcommands[i].Category == "" { + jfrogPlugin.Subcommands[i].Category = otherCategory + } + } + if converted, err = components.ConvertAppCommands(jfrogPlugin); err != nil { + err = fmt.Errorf("failed adding '%s' embedded plugin commands. Last error: %s", jfrogPlugin.Name, err.Error()) + } + return } func getAppHelpTemplate() string { diff --git a/main_test.go b/main_test.go index e4b6fd3b5..f0f8859a8 100644 --- a/main_test.go +++ b/main_test.go @@ -68,9 +68,6 @@ func setupIntegrationTests() { if *tests.TestPlugins { InitPluginsTests() } - if *tests.TestXray { - InitXrayTests() - } if *tests.TestAccess { InitAccessTests() } @@ -89,9 +86,6 @@ func tearDownIntegrationTests() { if *tests.TestNpm || *tests.TestGradle || *tests.TestMaven || *tests.TestGo || *tests.TestNuget || *tests.TestPip || *tests.TestPipenv || *tests.TestDocker || *tests.TestPodman || *tests.TestDockerScan { CleanBuildToolsTests() } - if *tests.TestXray { - CleanXrayTests() - } if *tests.TestDistribution { CleanDistributionTests() } @@ -261,7 +255,12 @@ func createConfigFile(inDir, configFilePath string, t *testing.T) { // Validate that all CLI commands' aliases are unique, and that two commands don't use the same alias. func validateCmdAliasesUniqueness() { - for _, command := range getCommands() { + cmds, err := getCommands() + if err != nil { + clientlog.Error(err) + os.Exit(1) + } + for _, command := range cmds { subcommands := command.Subcommands aliasesMap := map[string]bool{} for _, subcommand := range subcommands { @@ -295,14 +294,16 @@ func testConditionalUpload(t *testing.T, execFunc func() error, searchSpec strin } func TestSearchSimilarCmds(t *testing.T) { + cmds, err := getCommands() + assert.NoError(t, err) testData := []struct { badCmdSyntax string searchIn []cli.Command expectedRes []string }{ - {"rtt", getCommands(), []string{"rt"}}, - {"bp", getCommands(), []string{"rt bp"}}, - {"asdfewrwqfaxf", getCommands(), []string{}}, + {"rtt", cmds, []string{"rt"}}, + {"bp", cmds, []string{"rt bp"}}, + {"asdfewrwqfaxf", cmds, []string{}}, {"bpp", artifactory.GetCommands(), []string{"bpr", "bp", "pp"}}, {"uplid", artifactory.GetCommands(), []string{"upload"}}, {"downlo", artifactory.GetCommands(), []string{"download"}}, diff --git a/maven_test.go b/maven_test.go index bd6a46421..e993ac8e7 100644 --- a/maven_test.go +++ b/maven_test.go @@ -1,7 +1,12 @@ package main import ( + "errors" "fmt" + commonCliUtils "github.com/jfrog/jfrog-cli-core/v2/common/cliutils" + outputFormat "github.com/jfrog/jfrog-cli-core/v2/common/format" + "github.com/jfrog/jfrog-cli-core/v2/common/project" + "github.com/jfrog/jfrog-cli/utils/cliutils" "os" "path/filepath" "strings" @@ -83,7 +88,9 @@ func TestMavenBuildWithConditionalUpload(t *testing.T) { buildNumber := "505" execFunc := func() error { - return runMaven(t, createSimpleMavenProject, tests.MavenConfig, "install", "--scan", "--build-name="+buildName, "--build-number="+buildNumber) + oldHomeDir := changeWD(t, beforeRunMaven(t, createSimpleMavenProject, tests.MavenConfig)) + defer clientTestUtils.ChangeDirAndAssert(t, oldHomeDir) + return runMvnConditionalUploadTest(buildName, buildNumber) } searchSpec, err := tests.CreateSpec(tests.SearchAllMaven) assert.NoError(t, err) @@ -91,6 +98,30 @@ func TestMavenBuildWithConditionalUpload(t *testing.T) { cleanMavenTest(t) } +func runMvnConditionalUploadTest(buildName, buildNumber string) (err error) { + configFilePath, exists, err := project.GetProjectConfFilePath(project.Maven) + if err != nil { + return + } + if !exists { + return errors.New("no config file was found!") + } + buildConfig := buildUtils.NewBuildConfiguration(buildName, buildNumber, "", "") + if err = buildConfig.ValidateBuildAndModuleParams(); err != nil { + return + } + printDeploymentView := log.IsStdErrTerminal() + mvnCmd := mvn.NewMvnCommand(). + SetGoals([]string{"clean", "install", "-B", localRepoSystemProperty + localRepoDir}). + SetConfiguration(buildConfig). + SetXrayScan(true).SetScanOutputFormat(outputFormat.Table). + SetConfigPath(configFilePath).SetDetailedSummary(printDeploymentView).SetThreads(commonCliUtils.Threads) + err = commands.Exec(mvnCmd) + result := mvnCmd.Result() + defer cliutils.CleanupResult(result, &err) + return cliutils.PrintCommandSummary(mvnCmd.Result(), false, printDeploymentView, false, err) +} + func TestMavenBuildWithServerIDAndDetailedSummary(t *testing.T) { initMavenTest(t, false) pomDir := createSimpleMavenProject(t) @@ -311,12 +342,7 @@ func deleteDeployedArtifacts(t *testing.T) { } func runMaven(t *testing.T, createProjectFunction func(*testing.T) string, configFileName string, args ...string) error { - projDir := createProjectFunction(t) - configFilePath := filepath.Join(filepath.FromSlash(tests.GetTestResourcesPath()), "buildspecs", configFileName) - destPath := filepath.Join(projDir, ".jfrog", "projects") - createConfigFile(destPath, configFilePath, t) - assert.NoError(t, os.Rename(filepath.Join(destPath, configFileName), filepath.Join(destPath, "maven.yaml"))) - oldHomeDir := changeWD(t, projDir) + oldHomeDir := changeWD(t, beforeRunMaven(t, createProjectFunction, configFileName)) defer clientTestUtils.ChangeDirAndAssert(t, oldHomeDir) repoLocalSystemProp := localRepoSystemProperty + localRepoDir @@ -324,3 +350,12 @@ func runMaven(t *testing.T, createProjectFunction func(*testing.T) string, confi args = append(args, "-B", repoLocalSystemProp) return runJfrogCliWithoutAssertion(args...) } + +func beforeRunMaven(t *testing.T, createProjectFunction func(*testing.T) string, configFileName string) string { + projDir := createProjectFunction(t) + configFilePath := filepath.Join(filepath.FromSlash(tests.GetTestResourcesPath()), "buildspecs", configFileName) + destPath := filepath.Join(projDir, ".jfrog", "projects") + createConfigFile(destPath, configFilePath, t) + assert.NoError(t, os.Rename(filepath.Join(destPath, configFileName), filepath.Join(destPath, "maven.yaml"))) + return projDir +} diff --git a/npm_test.go b/npm_test.go index 564cfbe96..3c0cf1474 100644 --- a/npm_test.go +++ b/npm_test.go @@ -15,6 +15,8 @@ import ( biutils "github.com/jfrog/build-info-go/utils" "github.com/jfrog/gofrog/version" coretests "github.com/jfrog/jfrog-cli-core/v2/utils/tests" + "github.com/jfrog/jfrog-cli/utils/cliutils" + "github.com/jfrog/jfrog-client-go/utils/errorutils" "github.com/jfrog/jfrog-client-go/utils/log" clientTestUtils "github.com/jfrog/jfrog-client-go/utils/tests" @@ -204,11 +206,34 @@ func TestNpmConditionalUpload(t *testing.T) { buildNumber := "505" runJfrogCli(t, []string{"npm", "install", "--build-name=" + buildName, "--build-number=" + buildNumber}...) execFunc := func() error { - return runJfrogCliWithoutAssertion([]string{"npm", "publish", "--scan", "--build-name=" + buildName, "--build-number=" + buildNumber}...) + return runNpmConditionalUploadTest(buildName, buildNumber) } testConditionalUpload(t, execFunc, searchSpec, tests.GetNpmDeployedArtifacts(isNpm7(npmVersion))...) } +func runNpmConditionalUploadTest(buildName, buildNumber string) (err error) { + configFilePath, exists, err := project.GetProjectConfFilePath(project.Npm) + if err != nil { + return + } else if !exists { + return errorutils.CheckErrorf("no config file was found!") + } + npmCmd := npm.NewNpmPublishCommand() + npmCmd.SetConfigFilePath(configFilePath).SetArgs([]string{"--scan", "--build-name=" + buildName, "--build-number=" + buildNumber}) + if err = npmCmd.Init(); err != nil { + return err + } + printDeploymentView, detailedSummary := log.IsStdErrTerminal(), npmCmd.IsDetailedSummary() + if !detailedSummary { + npmCmd.SetDetailedSummary(printDeploymentView) + } + err = commands.Exec(npmCmd) + result := npmCmd.Result() + defer cliutils.CleanupResult(result, &err) + err = cliutils.PrintCommandSummary(npmCmd.Result(), detailedSummary, printDeploymentView, false, err) + return +} + func validateNpmrcFileInfo(t *testing.T, npmTest npmTestParams, npmrcFileInfo, postTestNpmrcFileInfo os.FileInfo, err, postTestFileInfoErr error) { if postTestFileInfoErr != nil && !os.IsNotExist(postTestFileInfoErr) { assert.Fail(t, postTestFileInfoErr.Error()) diff --git a/pip_test.go b/pip_test.go index fac79e04b..34267df70 100644 --- a/pip_test.go +++ b/pip_test.go @@ -4,7 +4,7 @@ import ( biutils "github.com/jfrog/build-info-go/utils" "github.com/jfrog/jfrog-cli-core/v2/utils/coreutils" coretests "github.com/jfrog/jfrog-cli-core/v2/utils/tests" - "github.com/jfrog/jfrog-cli-core/v2/xray/commands/audit/sca/python" + "github.com/jfrog/jfrog-cli-security/commands/audit/sca/python" clientTestUtils "github.com/jfrog/jfrog-client-go/utils/tests" "os" "path/filepath" diff --git a/scan/cli.go b/scan/cli.go deleted file mode 100644 index e9efabc25..000000000 --- a/scan/cli.go +++ /dev/null @@ -1,478 +0,0 @@ -package scan - -import ( - "github.com/jfrog/jfrog-cli-core/v2/xray/commands/curation" - xrutils "github.com/jfrog/jfrog-cli-core/v2/xray/utils" - curationdocs "github.com/jfrog/jfrog-cli/docs/scan/curation" - "os" - "strings" - - "github.com/jfrog/jfrog-cli-core/v2/common/progressbar" - - commonCliUtils "github.com/jfrog/jfrog-cli-core/v2/common/cliutils" - "github.com/jfrog/jfrog-cli-core/v2/common/commands" - outputFormat "github.com/jfrog/jfrog-cli-core/v2/common/format" - "github.com/jfrog/jfrog-cli-core/v2/common/spec" - corecommondocs "github.com/jfrog/jfrog-cli-core/v2/docs/common" - coreconfig "github.com/jfrog/jfrog-cli-core/v2/utils/config" - "github.com/jfrog/jfrog-cli-core/v2/utils/coreutils" - "github.com/jfrog/jfrog-cli-core/v2/xray/commands/audit" - "github.com/jfrog/jfrog-cli-core/v2/xray/commands/scan" - "github.com/jfrog/jfrog-cli/docs/common" - auditdocs "github.com/jfrog/jfrog-cli/docs/scan/audit" - auditgodocs "github.com/jfrog/jfrog-cli/docs/scan/auditgo" - auditgradledocs "github.com/jfrog/jfrog-cli/docs/scan/auditgradle" - "github.com/jfrog/jfrog-cli/docs/scan/auditmvn" - auditnpmdocs "github.com/jfrog/jfrog-cli/docs/scan/auditnpm" - auditpipdocs "github.com/jfrog/jfrog-cli/docs/scan/auditpip" - auditpipenvdocs "github.com/jfrog/jfrog-cli/docs/scan/auditpipenv" - buildscandocs "github.com/jfrog/jfrog-cli/docs/scan/buildscan" - scandocs "github.com/jfrog/jfrog-cli/docs/scan/scan" - "github.com/jfrog/jfrog-cli/utils/cliutils" - "github.com/urfave/cli" - - "github.com/jfrog/jfrog-client-go/utils/errorutils" -) - -const auditScanCategory = "Audit & Scan" - -func GetCommands() []cli.Command { - return cliutils.GetSortedCommands(cli.CommandsByName{ - { - Name: "curation-audit", - Category: auditScanCategory, - Flags: cliutils.GetCommandFlags(cliutils.CurationAudit), - Aliases: []string{"ca"}, - Usage: curationdocs.GetDescription(), - HelpName: corecommondocs.CreateUsage("curation-audit", curationdocs.GetDescription(), curationdocs.Usage), - ArgsUsage: common.CreateEnvVars(), - BashComplete: corecommondocs.CreateBashCompletionFunc(), - Action: CurationCmd, - }, - { - Name: "audit", - Category: auditScanCategory, - Flags: cliutils.GetCommandFlags(cliutils.Audit), - Aliases: []string{"aud"}, - Usage: auditdocs.GetDescription(), - HelpName: corecommondocs.CreateUsage("audit", auditdocs.GetDescription(), auditdocs.Usage), - ArgsUsage: common.CreateEnvVars(), - BashComplete: corecommondocs.CreateBashCompletionFunc(), - Action: AuditCmd, - }, - { - Name: "audit-mvn", - Category: auditScanCategory, - Flags: cliutils.GetCommandFlags(cliutils.AuditMvn), - Aliases: []string{"am"}, - Usage: auditmvn.GetDescription(), - HelpName: corecommondocs.CreateUsage("audit-mvn", auditmvn.GetDescription(), auditmvn.Usage), - ArgsUsage: common.CreateEnvVars(), - BashComplete: corecommondocs.CreateBashCompletionFunc(), - Action: func(c *cli.Context) error { - return AuditSpecificCmd(c, coreutils.Maven) - }, - Hidden: true, - }, - { - Name: "audit-gradle", - Category: auditScanCategory, - Flags: cliutils.GetCommandFlags(cliutils.AuditGradle), - Aliases: []string{"ag"}, - Usage: auditgradledocs.GetDescription(), - HelpName: corecommondocs.CreateUsage("audit-gradle", auditgradledocs.GetDescription(), auditgradledocs.Usage), - ArgsUsage: common.CreateEnvVars(), - BashComplete: corecommondocs.CreateBashCompletionFunc(), - Action: func(c *cli.Context) error { - return AuditSpecificCmd(c, coreutils.Gradle) - }, - Hidden: true, - }, - { - Name: "audit-npm", - Category: auditScanCategory, - Flags: cliutils.GetCommandFlags(cliutils.AuditNpm), - Aliases: []string{"an"}, - Usage: auditnpmdocs.GetDescription(), - HelpName: corecommondocs.CreateUsage("audit-npm", auditnpmdocs.GetDescription(), auditnpmdocs.Usage), - ArgsUsage: common.CreateEnvVars(), - BashComplete: corecommondocs.CreateBashCompletionFunc(), - Action: func(c *cli.Context) error { - return AuditSpecificCmd(c, coreutils.Npm) - }, - Hidden: true, - }, - { - Name: "audit-go", - Category: auditScanCategory, - Flags: cliutils.GetCommandFlags(cliutils.AuditGo), - Aliases: []string{"ago"}, - Usage: auditgodocs.GetDescription(), - HelpName: corecommondocs.CreateUsage("audit-go", auditgodocs.GetDescription(), auditgodocs.Usage), - ArgsUsage: common.CreateEnvVars(), - BashComplete: corecommondocs.CreateBashCompletionFunc(), - Action: func(c *cli.Context) error { - return AuditSpecificCmd(c, coreutils.Go) - }, - Hidden: true, - }, - { - Name: "audit-pip", - Category: auditScanCategory, - Flags: cliutils.GetCommandFlags(cliutils.AuditPip), - Aliases: []string{"ap"}, - Usage: auditpipdocs.GetDescription(), - HelpName: corecommondocs.CreateUsage("audit-pip", auditpipdocs.GetDescription(), auditpipdocs.Usage), - ArgsUsage: common.CreateEnvVars(), - BashComplete: corecommondocs.CreateBashCompletionFunc(), - Action: func(c *cli.Context) error { - return AuditSpecificCmd(c, coreutils.Pip) - }, - Hidden: true, - }, - { - Name: "audit-pipenv", - Category: auditScanCategory, - Flags: cliutils.GetCommandFlags(cliutils.AuditPipenv), - Aliases: []string{"ape"}, - Usage: auditpipenvdocs.GetDescription(), - HelpName: corecommondocs.CreateUsage("audit-pipenv", auditpipenvdocs.GetDescription(), auditpipenvdocs.Usage), - ArgsUsage: common.CreateEnvVars(), - BashComplete: corecommondocs.CreateBashCompletionFunc(), - Action: func(c *cli.Context) error { - return AuditSpecificCmd(c, coreutils.Pipenv) - }, - Hidden: true, - }, - { - Name: "scan", - Category: auditScanCategory, - Flags: cliutils.GetCommandFlags(cliutils.XrScan), - Aliases: []string{"s"}, - Usage: scandocs.GetDescription(), - HelpName: corecommondocs.CreateUsage("scan", scandocs.GetDescription(), scandocs.Usage), - UsageText: scandocs.GetArguments(), - ArgsUsage: common.CreateEnvVars(), - BashComplete: corecommondocs.CreateBashCompletionFunc(), - Action: ScanCmd, - }, - { - Name: "build-scan", - Category: auditScanCategory, - Flags: cliutils.GetCommandFlags(cliutils.BuildScan), - Aliases: []string{"bs"}, - Usage: buildscandocs.GetDescription(), - UsageText: buildscandocs.GetArguments(), - HelpName: corecommondocs.CreateUsage("build-scan", buildscandocs.GetDescription(), buildscandocs.Usage), - ArgsUsage: common.CreateEnvVars(), - BashComplete: corecommondocs.CreateBashCompletionFunc(), - Action: BuildScan, - }, - }) -} - -func AuditCmd(c *cli.Context) error { - auditCmd, err := createAuditCmd(c) - if err != nil { - return err - } - - // Check if user used specific technologies flags - allTechnologies := coreutils.GetAllTechnologiesList() - technologies := []string{} - for _, tech := range allTechnologies { - var techExists bool - if tech == coreutils.Maven { - // On Maven we use '--mvn' flag - techExists = c.Bool("mvn") - } else { - techExists = c.Bool(tech.String()) - } - if techExists { - technologies = append(technologies, tech.String()) - } - } - auditCmd.SetTechnologies(technologies) - return progressbar.ExecWithProgress(auditCmd) -} - -func AuditSpecificCmd(c *cli.Context, technology coreutils.Technology) error { - commonCliUtils.LogNonGenericAuditCommandDeprecation(c.Command.Name) - auditCmd, err := createAuditCmd(c) - if err != nil { - return err - } - technologies := []string{string(technology)} - auditCmd.SetTechnologies(technologies) - return progressbar.ExecWithProgress(auditCmd) -} - -func CurationCmd(c *cli.Context) error { - threads, err := curation.DetectNumOfThreads(c.Int("threads")) - if err != nil { - return err - } - curationAuditCommand := curation.NewCurationAuditCommand(). - SetWorkingDirs(splitByCommaAndTrim(c.String("working-dirs"))). - SetParallelRequests(threads) - - serverDetails, err := cliutils.CreateServerDetailsWithConfigOffer(c, true, "rt") - if err != nil { - return err - } - format, err := curation.GetCurationOutputFormat(c.String("format")) - if err != nil { - return err - } - curationAuditCommand.SetServerDetails(serverDetails). - SetExcludeTestDependencies(c.Bool(cliutils.ExcludeTestDeps)). - SetOutputFormat(format). - SetUseWrapper(c.BoolT(cliutils.UseWrapper)). - SetInsecureTls(c.Bool(cliutils.InsecureTls)). - SetNpmScope(c.String(cliutils.DepType)). - SetPipRequirementsFile(c.String(cliutils.RequirementsFile)) - return progressbar.ExecWithProgress(curationAuditCommand) -} - -func createAuditCmd(c *cli.Context) (*audit.AuditCommand, error) { - auditCmd := audit.NewGenericAuditCommand() - serverDetails, err := createServerDetailsWithConfigOffer(c) - if err != nil { - return nil, err - } - err = validateXrayContext(c, serverDetails) - if err != nil { - return nil, err - } - format, err := outputFormat.GetOutputFormat(c.String("format")) - if err != nil { - return nil, err - } - minSeverity, err := xrutils.GetSeveritiesFormat(c.String(cliutils.MinSeverity)) - if err != nil { - return nil, err - } - auditCmd.SetTargetRepoPath(addTrailingSlashToRepoPathIfNeeded(c)). - SetProject(c.String("project")). - SetIncludeVulnerabilities(shouldIncludeVulnerabilities(c)). - SetIncludeLicenses(c.Bool("licenses")). - SetFail(c.BoolT("fail")). - SetPrintExtendedTable(c.Bool(cliutils.ExtendedTable)). - SetMinSeverityFilter(minSeverity). - SetFixableOnly(c.Bool(cliutils.FixableOnly)). - SetThirdPartyApplicabilityScan(c.Bool(cliutils.ThirdPartyContextualAnalysis)). - SetExclusions(cliutils.GetStringsArrFlagValue(c, "exclusions")) - - if c.String("watches") != "" { - auditCmd.SetWatches(splitByCommaAndTrim(c.String("watches"))) - } - - if c.String("working-dirs") != "" { - auditCmd.SetWorkingDirs(splitByCommaAndTrim(c.String("working-dirs"))) - } - auditCmd.SetServerDetails(serverDetails). - SetExcludeTestDependencies(c.Bool(cliutils.ExcludeTestDeps)). - SetOutputFormat(format). - SetUseWrapper(c.BoolT(cliutils.UseWrapper)). - SetInsecureTls(c.Bool(cliutils.InsecureTls)). - SetNpmScope(c.String(cliutils.DepType)). - SetPipRequirementsFile(c.String(cliutils.RequirementsFile)) - return auditCmd, err -} - -func ScanCmd(c *cli.Context) error { - if c.NArg() == 0 && !c.IsSet("spec") { - return cliutils.PrintHelpAndReturnError("providing either a argument or the 'spec' option is mandatory", c) - } - serverDetails, err := createServerDetailsWithConfigOffer(c) - if err != nil { - return err - } - err = validateXrayContext(c, serverDetails) - if err != nil { - return err - } - var specFile *spec.SpecFiles - if c.IsSet("spec") { - specFile, err = cliutils.GetFileSystemSpec(c) - if err != nil { - return err - } - } else { - specFile = createDefaultScanSpec(c, addTrailingSlashToRepoPathIfNeeded(c)) - } - err = spec.ValidateSpec(specFile.Files, false, false) - if err != nil { - return err - } - threads, err := cliutils.GetThreadsCount(c) - if err != nil { - return err - } - format, err := outputFormat.GetOutputFormat(c.String("format")) - if err != nil { - return err - } - cliutils.FixWinPathsForFileSystemSourcedCmds(specFile, c) - minSeverity, err := xrutils.GetSeveritiesFormat(c.String(cliutils.MinSeverity)) - if err != nil { - return err - } - scanCmd := scan.NewScanCommand(). - SetServerDetails(serverDetails). - SetThreads(threads). - SetSpec(specFile). - SetOutputFormat(format). - SetProject(c.String("project")). - SetIncludeVulnerabilities(shouldIncludeVulnerabilities(c)). - SetIncludeLicenses(c.Bool("licenses")). - SetFail(c.BoolT("fail")). - SetPrintExtendedTable(c.Bool(cliutils.ExtendedTable)). - SetBypassArchiveLimits(c.Bool(cliutils.BypassArchiveLimits)). - SetFixableOnly(c.Bool(cliutils.FixableOnly)). - SetMinSeverityFilter(minSeverity) - if c.String("watches") != "" { - scanCmd.SetWatches(splitByCommaAndTrim(c.String("watches"))) - } - return commands.Exec(scanCmd) -} - -// Scan published builds with Xray -func BuildScan(c *cli.Context) error { - if c.NArg() > 2 { - return cliutils.WrongNumberOfArgumentsHandler(c) - } - buildConfiguration := cliutils.CreateBuildConfiguration(c) - if err := buildConfiguration.ValidateBuildParams(); err != nil { - return err - } - serverDetails, err := createServerDetailsWithConfigOffer(c) - if err != nil { - return err - } - err = validateXrayContext(c, serverDetails) - if err != nil { - return err - } - format, err := outputFormat.GetOutputFormat(c.String("format")) - if err != nil { - return err - } - buildScanCmd := scan.NewBuildScanCommand(). - SetServerDetails(serverDetails). - SetFailBuild(c.BoolT("fail")). - SetBuildConfiguration(buildConfiguration). - SetOutputFormat(format). - SetPrintExtendedTable(c.Bool(cliutils.ExtendedTable)). - SetRescan(c.Bool("rescan")) - if format != outputFormat.Sarif { - // Sarif shouldn't include the additional all-vulnerabilities info that received by adding the vuln flag - buildScanCmd.SetIncludeVulnerabilities(c.Bool("vuln")) - } - return commands.Exec(buildScanCmd) -} - -func DockerScan(c *cli.Context, image string) error { - if show, err := cliutils.ShowGenericCmdHelpIfNeeded(c, c.Args(), "dockerscanhelp"); show || err != nil { - return err - } - if image == "" { - return cli.ShowCommandHelp(c, "dockerscanhelp") - } - serverDetails, err := createServerDetailsWithConfigOffer(c) - if err != nil { - return err - } - err = validateXrayContext(c, serverDetails) - if err != nil { - return err - } - containerScanCommand := scan.NewDockerScanCommand() - format, err := outputFormat.GetOutputFormat(c.String("format")) - if err != nil { - return err - } - minSeverity, err := xrutils.GetSeveritiesFormat(c.String(cliutils.MinSeverity)) - if err != nil { - return err - } - containerScanCommand.SetImageTag(image). - SetTargetRepoPath(addTrailingSlashToRepoPathIfNeeded(c)). - SetServerDetails(serverDetails). - SetOutputFormat(format). - SetProject(c.String("project")). - SetIncludeVulnerabilities(shouldIncludeVulnerabilities(c)). - SetIncludeLicenses(c.Bool("licenses")). - SetFail(c.BoolT("fail")). - SetPrintExtendedTable(c.Bool(cliutils.ExtendedTable)). - SetBypassArchiveLimits(c.Bool(cliutils.BypassArchiveLimits)). - SetFixableOnly(c.Bool(cliutils.FixableOnly)). - SetMinSeverityFilter(minSeverity) - if c.String("watches") != "" { - containerScanCommand.SetWatches(splitByCommaAndTrim(c.String("watches"))) - } - return progressbar.ExecWithProgress(containerScanCommand) -} - -func addTrailingSlashToRepoPathIfNeeded(c *cli.Context) string { - repoPath := c.String("repo-path") - if repoPath != "" && !strings.Contains(repoPath, "/") { - // In case only repo name was provided (no path) we are adding a trailing slash. - repoPath += "/" - } - return repoPath -} - -func createDefaultScanSpec(c *cli.Context, defaultTarget string) *spec.SpecFiles { - return spec.NewBuilder(). - Pattern(c.Args().Get(0)). - Target(defaultTarget). - Recursive(c.BoolT("recursive")). - Exclusions(cliutils.GetStringsArrFlagValue(c, "exclusions")). - Regexp(c.Bool("regexp")). - Ant(c.Bool("ant")). - IncludeDirs(c.Bool("include-dirs")). - BuildSpec() -} - -func createServerDetailsWithConfigOffer(c *cli.Context) (*coreconfig.ServerDetails, error) { - return cliutils.CreateServerDetailsWithConfigOffer(c, true, "xr") -} - -func shouldIncludeVulnerabilities(c *cli.Context) bool { - // If no context was provided by the user, no Violations will be triggered by Xray, so include general vulnerabilities in the command output - return c.String("watches") == "" && !isProjectProvided(c) && c.String("repo-path") == "" -} - -func validateXrayContext(c *cli.Context, serverDetails *coreconfig.ServerDetails) error { - if serverDetails.XrayUrl == "" { - return errorutils.CheckErrorf("JFrog Xray URL must be provided in order run this command. Use the 'jf c add' command to set the Xray server details.") - } - contextFlag := 0 - if c.String("watches") != "" { - contextFlag++ - } - if isProjectProvided(c) { - contextFlag++ - } - if c.String("repo-path") != "" { - contextFlag++ - } - if contextFlag > 1 { - return errorutils.CheckErrorf("only one of the following flags can be supplied: --watches, --project or --repo-path") - } - return nil -} - -func isProjectProvided(c *cli.Context) bool { - return c.String("project") != "" || os.Getenv(coreutils.Project) != "" -} - -func splitByCommaAndTrim(paramValue string) (res []string) { - args := strings.Split(paramValue, ",") - res = make([]string, len(args)) - for i, arg := range args { - res[i] = strings.TrimSpace(arg) - } - return -} diff --git a/utils/cliutils/commandsflags.go b/utils/cliutils/commandsflags.go index 6a410f23d..30a9b2b5f 100644 --- a/utils/cliutils/commandsflags.go +++ b/utils/cliutils/commandsflags.go @@ -7,7 +7,6 @@ import ( commonCliUtils "github.com/jfrog/jfrog-cli-core/v2/common/cliutils" "github.com/jfrog/jfrog-cli-core/v2/utils/coreutils" - "github.com/jfrog/jfrog-cli-core/v2/xray/commands/offlineupdate" "github.com/jfrog/jfrog-client-go/utils/log" "github.com/urfave/cli" @@ -1326,10 +1325,6 @@ var flagsMap = map[string]cli.Flag{ Name: licenseId, Usage: "[Mandatory] Xray license ID.` `", }, - Stream: cli.StringFlag{ - Name: Stream, - Usage: fmt.Sprintf("[Optional] Xray DBSync V3 stream, Possible values are: %s.` `", offlineupdate.NewValidStreams().GetValidStreamsString()), - }, from: cli.StringFlag{ Name: from, Usage: "[Optional] From update date in YYYY-MM-DD format.` `", @@ -2010,49 +2005,6 @@ var commandFlags = map[string][]string{ lcUrl, user, password, accessToken, serverId, lcDryRun, DistRules, site, city, countryCodes, InsecureTls, CreateRepo, lcPathMappingPattern, lcPathMappingTarget, }, - // Xray's commands - OfflineUpdate: { - licenseId, from, to, Version, target, Stream, Periodic, - }, - XrCurl: { - serverId, - }, - CurationAudit: { - curationOutput, workingDirs, curationThreads, - }, - Audit: { - xrUrl, user, password, accessToken, serverId, InsecureTls, Project, watches, repoPath, licenses, xrOutput, ExcludeTestDeps, - useWrapperAudit, DepType, RequirementsFile, fail, ExtendedTable, workingDirs, ExclusionsAudit, Mvn, Gradle, Npm, Yarn, Go, Nuget, Pip, Pipenv, Poetry, MinSeverity, FixableOnly, ThirdPartyContextualAnalysis, - }, - AuditMvn: { - xrUrl, user, password, accessToken, serverId, InsecureTls, Project, ExclusionsAudit, watches, repoPath, licenses, xrOutput, fail, ExtendedTable, useWrapperAudit, - }, - AuditGradle: { - xrUrl, user, password, accessToken, serverId, ExcludeTestDeps, ExclusionsAudit, useWrapperAudit, Project, watches, repoPath, licenses, xrOutput, fail, ExtendedTable, - }, - AuditNpm: { - xrUrl, user, password, accessToken, serverId, DepType, Project, ExclusionsAudit, watches, repoPath, licenses, xrOutput, fail, ExtendedTable, - }, - AuditGo: { - xrUrl, user, password, accessToken, serverId, Project, ExclusionsAudit, watches, repoPath, licenses, xrOutput, fail, ExtendedTable, - }, - AuditPip: { - xrUrl, user, password, accessToken, serverId, RequirementsFile, Project, ExclusionsAudit, watches, repoPath, licenses, xrOutput, fail, ExtendedTable, - }, - AuditPipenv: { - xrUrl, user, password, accessToken, serverId, Project, ExclusionsAudit, watches, repoPath, licenses, xrOutput, ExtendedTable, - }, - XrScan: { - xrUrl, user, password, accessToken, serverId, specFlag, threads, scanRecursive, scanRegexp, scanAnt, - Project, watches, repoPath, licenses, xrOutput, fail, ExtendedTable, BypassArchiveLimits, MinSeverity, FixableOnly, - }, - DockerScan: { - // Flags added here should be also added to Docker command - serverId, Project, watches, repoPath, licenses, xrOutput, fail, ExtendedTable, BypassArchiveLimits, MinSeverity, FixableOnly, - }, - BuildScan: { - xrUrl, user, password, accessToken, serverId, Project, vuln, xrOutput, fail, ExtendedTable, rescan, - }, // Mission Control's commands McConfig: { mcUrl, mcAccessToken, mcInteractive, diff --git a/xray/cli.go b/xray/cli.go deleted file mode 100644 index cb766bd7c..000000000 --- a/xray/cli.go +++ /dev/null @@ -1,222 +0,0 @@ -package xray - -import ( - "time" - - "github.com/jfrog/jfrog-cli-core/v2/utils/coreutils" - - corecommon "github.com/jfrog/jfrog-cli-core/v2/common/commands" - corecommondocs "github.com/jfrog/jfrog-cli-core/v2/docs/common" - "github.com/jfrog/jfrog-cli-core/v2/xray/commands/curl" - "github.com/jfrog/jfrog-cli-core/v2/xray/commands/offlineupdate" - "github.com/jfrog/jfrog-cli/docs/common" - auditgodocs "github.com/jfrog/jfrog-cli/docs/xray/auditgo" - "github.com/jfrog/jfrog-cli/docs/xray/auditgradle" - "github.com/jfrog/jfrog-cli/docs/xray/auditmvn" - auditnpmdocs "github.com/jfrog/jfrog-cli/docs/xray/auditnpm" - auditpipdocs "github.com/jfrog/jfrog-cli/docs/xray/auditpip" - curldocs "github.com/jfrog/jfrog-cli/docs/xray/curl" - offlineupdatedocs "github.com/jfrog/jfrog-cli/docs/xray/offlineupdate" - scandocs "github.com/jfrog/jfrog-cli/docs/xray/scan" - "github.com/jfrog/jfrog-cli/scan" - "github.com/jfrog/jfrog-cli/utils/cliutils" - "github.com/jfrog/jfrog-client-go/utils/errorutils" - "github.com/urfave/cli" -) - -const DateFormat = "2006-01-02" - -func GetCommands() []cli.Command { - return cliutils.GetSortedCommands(cli.CommandsByName{ - { - Name: "curl", - Flags: cliutils.GetCommandFlags(cliutils.XrCurl), - Aliases: []string{"cl"}, - Usage: curldocs.GetDescription(), - HelpName: corecommondocs.CreateUsage("xr curl", curldocs.GetDescription(), curldocs.Usage), - UsageText: curldocs.GetArguments(), - ArgsUsage: common.CreateEnvVars(), - BashComplete: corecommondocs.CreateBashCompletionFunc(), - SkipFlagParsing: true, - Action: curlCmd, - }, - { - Name: "audit-mvn", - Flags: cliutils.GetCommandFlags(cliutils.AuditMvn), - Aliases: []string{"am"}, - Usage: auditmvn.GetDescription(), - HelpName: corecommondocs.CreateUsage("xr audit-mvn", auditmvn.GetDescription(), auditmvn.Usage), - ArgsUsage: common.CreateEnvVars(), - BashComplete: corecommondocs.CreateBashCompletionFunc(), - Action: func(c *cli.Context) error { - return scan.AuditSpecificCmd(c, coreutils.Maven) - }, - }, - { - Name: "audit-gradle", - Flags: cliutils.GetCommandFlags(cliutils.AuditGradle), - Aliases: []string{"ag"}, - Usage: auditgradle.GetDescription(), - HelpName: corecommondocs.CreateUsage("xr audit-gradle", auditgradle.GetDescription(), auditgradle.Usage), - ArgsUsage: common.CreateEnvVars(), - BashComplete: corecommondocs.CreateBashCompletionFunc(), - Action: func(c *cli.Context) error { - return scan.AuditSpecificCmd(c, coreutils.Gradle) - }, - }, - { - Name: "audit-npm", - Flags: cliutils.GetCommandFlags(cliutils.AuditNpm), - Aliases: []string{"an"}, - Usage: auditnpmdocs.GetDescription(), - HelpName: corecommondocs.CreateUsage("xr audit-npm", auditnpmdocs.GetDescription(), auditnpmdocs.Usage), - ArgsUsage: common.CreateEnvVars(), - BashComplete: corecommondocs.CreateBashCompletionFunc(), - Action: func(c *cli.Context) error { - return scan.AuditSpecificCmd(c, coreutils.Npm) - }, - }, - { - Name: "audit-go", - Flags: cliutils.GetCommandFlags(cliutils.AuditGo), - Aliases: []string{"ago"}, - Usage: auditgodocs.GetDescription(), - HelpName: corecommondocs.CreateUsage("xr audit-go", auditgodocs.GetDescription(), auditgodocs.Usage), - ArgsUsage: common.CreateEnvVars(), - BashComplete: corecommondocs.CreateBashCompletionFunc(), - Action: func(c *cli.Context) error { - return scan.AuditSpecificCmd(c, coreutils.Go) - }, - }, - { - Name: "audit-pip", - Flags: cliutils.GetCommandFlags(cliutils.AuditPip), - Aliases: []string{"ap"}, - Usage: auditpipdocs.GetDescription(), - HelpName: corecommondocs.CreateUsage("xr audit-pip", auditpipdocs.GetDescription(), auditpipdocs.Usage), - ArgsUsage: common.CreateEnvVars(), - BashComplete: corecommondocs.CreateBashCompletionFunc(), - Action: func(c *cli.Context) error { - return scan.AuditSpecificCmd(c, coreutils.Pip) - }, - }, - { - Name: "scan", - Flags: cliutils.GetCommandFlags(cliutils.XrScan), - Aliases: []string{"s"}, - Usage: scandocs.GetDescription(), - UsageText: scandocs.GetArguments(), - HelpName: corecommondocs.CreateUsage("xr scan", scandocs.GetDescription(), scandocs.Usage), - ArgsUsage: common.CreateEnvVars(), - BashComplete: corecommondocs.CreateBashCompletionFunc(), - Action: func(c *cli.Context) error { - return cliutils.RunCmdWithDeprecationWarning("scan", "xr", c, scan.ScanCmd) - }, - }, - { - Name: "offline-update", - Usage: offlineupdatedocs.GetDescription(), - HelpName: corecommondocs.CreateUsage("xr offline-update", offlineupdatedocs.GetDescription(), offlineupdatedocs.Usage), - ArgsUsage: common.CreateEnvVars(), - Flags: cliutils.GetCommandFlags(cliutils.OfflineUpdate), - Aliases: []string{"ou"}, - BashComplete: corecommondocs.CreateBashCompletionFunc(), - Action: offlineUpdates, - }, - }) -} - -func getOfflineUpdatesFlag(c *cli.Context) (flags *offlineupdate.OfflineUpdatesFlags, err error) { - flags = new(offlineupdate.OfflineUpdatesFlags) - flags.Version = c.String("version") - flags.License = c.String("license-id") - flags.Target = c.String("target") - if len(flags.License) < 1 { - return nil, errorutils.CheckErrorf("the --license-id option is mandatory") - } - // Handle V3 flags - stream := c.String(cliutils.Stream) - flags.IsPeriodicUpdate = c.Bool(cliutils.Periodic) - // If a 'stream' flag was provided - validate its value and return. - if stream != "" { - flags.Stream, err = validateStream(stream) - return - } - if flags.IsPeriodicUpdate { - return nil, errorutils.CheckErrorf("the %s option is only valid with %s", cliutils.Periodic, cliutils.Stream) - } - // Handle V1 flags - from := c.String("from") - to := c.String("to") - if len(to) > 0 && len(from) < 1 { - return nil, errorutils.CheckErrorf("the --from option is mandatory, when the --to option is sent") - } - if len(from) > 0 && len(to) < 1 { - return nil, errorutils.CheckErrorf("the --to option is mandatory, when the --from option is sent") - } - if len(from) > 0 && len(to) > 0 { - flags.From, err = dateToMilliseconds(from) - err = errorutils.CheckError(err) - if err != nil { - return - } - flags.To, err = dateToMilliseconds(to) - err = errorutils.CheckError(err) - } - return -} - -// Verify that the given string is a valid optional stream. -func validateStream(stream string) (string, error) { - streams := offlineupdate.NewValidStreams() - if streams.StreamsMap[stream] { - return stream, nil - } - return "", errorutils.CheckErrorf("Invalid stream type: %s, Possible values are: %v", stream, streams.GetValidStreamsString()) -} - -func dateToMilliseconds(date string) (dateInMillisecond int64, err error) { - t, err := time.Parse(DateFormat, date) - if errorutils.CheckError(err) != nil { - return - } - dateInMillisecond = t.UnixNano() / (int64(time.Millisecond) / int64(time.Nanosecond)) - return -} - -func offlineUpdates(c *cli.Context) error { - offlineUpdateFlags, err := getOfflineUpdatesFlag(c) - if err != nil { - return err - } - return offlineupdate.OfflineUpdate(offlineUpdateFlags) -} - -func curlCmd(c *cli.Context) error { - if show, err := cliutils.ShowCmdHelpIfNeeded(c, c.Args()); show || err != nil { - return err - } - if c.NArg() < 1 { - return cliutils.WrongNumberOfArgumentsHandler(c) - } - xrCurlCmd, err := newXrCurlCommand(c) - if err != nil { - return err - } - return corecommon.Exec(xrCurlCmd) -} - -func newXrCurlCommand(c *cli.Context) (*curl.XrCurlCommand, error) { - curlCommand := corecommon.NewCurlCommand().SetArguments(cliutils.ExtractCommand(c)) - xrCurlCommand := curl.NewXrCurlCommand(*curlCommand) - xrDetails, err := xrCurlCommand.GetServerDetails() - if err != nil { - return nil, err - } - if xrDetails.XrayUrl == "" { - return nil, errorutils.CheckErrorf("No Xray servers configured. Use the 'jf c add' command to set the Xray server details.") - } - xrCurlCommand.SetServerDetails(xrDetails) - xrCurlCommand.SetUrl(xrDetails.XrayUrl) - return xrCurlCommand, err -} diff --git a/xray/cli_test.go b/xray/cli_test.go deleted file mode 100644 index c88f2e21b..000000000 --- a/xray/cli_test.go +++ /dev/null @@ -1,39 +0,0 @@ -package xray - -import ( - "github.com/jfrog/jfrog-cli-core/v2/xray/commands/offlineupdate" - "reflect" - "testing" -) - -func TestValidateStream(t *testing.T) { - streams := offlineupdate.NewValidStreams() - type args struct { - streams string - } - tests := []struct { - name string - args args - want string - wantErr bool - }{ - {"empty array", args{streams: ""}, "", true}, - {"PublicData", args{streams: streams.GetPublicDataStream()}, streams.GetPublicDataStream(), false}, - {"ContextualAnalysis", args{streams: streams.GetContextualAnalysisStream()}, streams.GetContextualAnalysisStream(), false}, - {"Exposures", args{streams: streams.GetExposuresStream()}, streams.GetExposuresStream(), false}, - {"invalid elements", args{streams: "bad_stream"}, "", true}, - {"array", args{streams: streams.GetPublicDataStream() + ";" + streams.GetContextualAnalysisStream()}, "", true}, - } - for _, tt := range tests { - t.Run(tt.name, func(t *testing.T) { - got, err := validateStream(tt.args.streams) - if (err != nil) != tt.wantErr { - t.Errorf("validateStream() error = %v, wantErr %v", err, tt.wantErr) - return - } - if !reflect.DeepEqual(got, tt.want) { - t.Errorf("validateStream() got = %v, want %v", got, tt.want) - } - }) - } -} diff --git a/xray_test.go b/xray_test.go deleted file mode 100644 index 1c8baabba..000000000 --- a/xray_test.go +++ /dev/null @@ -1,1162 +0,0 @@ -package main - -import ( - "encoding/json" - "errors" - "flag" - "fmt" - "net/http" - "net/http/httptest" - "os" - "os/exec" - "path" - "path/filepath" - "strconv" - "strings" - "sync" - "testing" - "time" - - biutils "github.com/jfrog/build-info-go/utils" - "github.com/jfrog/jfrog-cli-core/v2/xray/scangraph" - - "github.com/jfrog/gofrog/version" - coreContainer "github.com/jfrog/jfrog-cli-core/v2/artifactory/commands/container" - "github.com/jfrog/jfrog-cli-core/v2/artifactory/utils/container" - "github.com/jfrog/jfrog-cli-core/v2/common/build" - coreCmd "github.com/jfrog/jfrog-cli-core/v2/common/commands" - "github.com/jfrog/jfrog-cli-core/v2/common/format" - "github.com/jfrog/jfrog-cli-core/v2/common/project" - commontests "github.com/jfrog/jfrog-cli-core/v2/common/tests" - "github.com/jfrog/jfrog-cli-core/v2/utils/config" - "github.com/jfrog/jfrog-cli-core/v2/utils/coreutils" - "github.com/jfrog/jfrog-cli-core/v2/utils/dependencies" - coretests "github.com/jfrog/jfrog-cli-core/v2/utils/tests" - coreCuration "github.com/jfrog/jfrog-cli-core/v2/xray/commands/curation" - "github.com/jfrog/jfrog-cli-core/v2/xray/commands/scan" - "github.com/jfrog/jfrog-cli-core/v2/xray/formats" - "github.com/jfrog/jfrog-cli-core/v2/xray/utils" - "github.com/jfrog/jfrog-cli/utils/cliutils" - "github.com/jfrog/jfrog-cli/utils/tests" - "github.com/jfrog/jfrog-client-go/auth" - clientUtils "github.com/jfrog/jfrog-client-go/utils" - "github.com/jfrog/jfrog-client-go/utils/io/fileutils" - clientTestUtils "github.com/jfrog/jfrog-client-go/utils/tests" - "github.com/jfrog/jfrog-client-go/xray/services" - xrayUtils "github.com/jfrog/jfrog-client-go/xray/services/utils" - "github.com/stretchr/testify/assert" - "github.com/stretchr/testify/require" - "github.com/urfave/cli" -) - -const ( - jvmLaunchEnvVar = "MAVEN_OPTS" - mavenCacheRedirectionVal = "-Dmaven.repo.local=" - goCacheEnvVar = "GOMODCACHE" - pipCacheEnvVar = "PIP_CACHE_DIR" -) - -var ( - xrayDetails *config.ServerDetails - xrayAuth auth.ServiceDetails - // JFrog CLI for Xray commands - xrayCli *coretests.JfrogCli -) - -func InitXrayTests() { - initXrayCli() - initArtifactoryCli() - cleanUpOldRepositories() - tests.AddTimestampToGlobalVars() - createRequiredRepos() -} - -func CleanXrayTests() { - deleteCreatedRepos() -} - -func authenticateXray() string { - *tests.JfrogUrl = clientUtils.AddTrailingSlashIfNeeded(*tests.JfrogUrl) - xrayDetails = &config.ServerDetails{XrayUrl: *tests.JfrogUrl + tests.XrayEndpoint} - cred := fmt.Sprintf("--url=%s", xrayDetails.XrayUrl) - if *tests.JfrogAccessToken != "" { - xrayDetails.AccessToken = *tests.JfrogAccessToken - cred += fmt.Sprintf(" --access-token=%s", xrayDetails.AccessToken) - } else { - xrayDetails.User = *tests.JfrogUser - xrayDetails.Password = *tests.JfrogPassword - cred += fmt.Sprintf(" --user=%s --password=%s", xrayDetails.User, xrayDetails.Password) - } - - var err error - if xrayAuth, err = xrayDetails.CreateXrayAuthConfig(); err != nil { - coreutils.ExitOnErr(errors.New("Failed while attempting to authenticate with Xray: " + err.Error())) - } - xrayDetails.XrayUrl = xrayAuth.GetUrl() - return cred -} - -func initXrayCli() { - if xrayCli != nil { - return - } - cred := authenticateXray() - xrayCli = coretests.NewJfrogCli(execMain, "jfrog", cred) -} - -// Tests basic binary scan by providing pattern (path to testdata binaries) and --licenses flag -// and asserts any error. -func TestXrayBinaryScanJson(t *testing.T) { - output := testXrayBinaryScan(t, string(format.Json)) - verifyJsonScanResults(t, output, 0, 1, 1) -} - -func TestXrayBinaryScanSimpleJson(t *testing.T) { - output := testXrayBinaryScan(t, string(format.SimpleJson)) - verifySimpleJsonScanResults(t, output, 1, 1) -} - -func TestXrayBinaryScanJsonWithProgress(t *testing.T) { - callback := commontests.MockProgressInitialization() - defer callback() - output := testXrayBinaryScan(t, string(format.Json)) - verifyJsonScanResults(t, output, 0, 1, 1) -} - -func TestXrayBinaryScanSimpleJsonWithProgress(t *testing.T) { - callback := commontests.MockProgressInitialization() - defer callback() - output := testXrayBinaryScan(t, string(format.SimpleJson)) - verifySimpleJsonScanResults(t, output, 1, 1) -} - -func testXrayBinaryScan(t *testing.T, format string) string { - initXrayTest(t, scangraph.GraphScanMinXrayVersion) - binariesPath := filepath.Join(filepath.FromSlash(tests.GetTestResourcesPath()), "xray", "binaries", "*") - return xrayCli.RunCliCmdWithOutput(t, "scan", binariesPath, "--licenses", "--format="+format) -} - -func TestXrayBinaryScanWithBypassArchiveLimits(t *testing.T) { - initXrayTest(t, scan.BypassArchiveLimitsMinXrayVersion) - unsetEnv := clientTestUtils.SetEnvWithCallbackAndAssert(t, "JF_INDEXER_COMPRESS_MAXENTITIES", "10") - defer unsetEnv() - binariesPath := filepath.Join(filepath.FromSlash(tests.GetTestResourcesPath()), "xray", "binaries", "*") - scanArgs := []string{"scan", binariesPath, "--format=json", "--licenses"} - // Run without bypass flag and expect scan to fail - err := xrayCli.Exec(scanArgs...) - // Expect error - assert.Error(t, err) - - // Run with bypass flag and expect it to find vulnerabilities - scanArgs = append(scanArgs, "--bypass-archive-limits") - output := xrayCli.RunCliCmdWithOutput(t, scanArgs...) - verifyJsonScanResults(t, output, 0, 1, 1) -} - -// Tests npm audit by providing simple npm project and asserts any error. -func TestXrayAuditNpmJson(t *testing.T) { - output := testXrayAuditNpm(t, string(format.Json)) - verifyJsonScanResults(t, output, 0, 1, 1) -} - -func TestXrayAuditNpmSimpleJson(t *testing.T) { - output := testXrayAuditNpm(t, string(format.SimpleJson)) - verifySimpleJsonScanResults(t, output, 1, 1) -} - -func testXrayAuditNpm(t *testing.T, format string) string { - initXrayTest(t, scangraph.GraphScanMinXrayVersion) - tempDirPath, createTempDirCallback := coretests.CreateTempDirWithCallbackAndAssert(t) - defer createTempDirCallback() - npmProjectPath := filepath.Join(filepath.FromSlash(tests.GetTestResourcesPath()), "xray", "npm") - // Copy the npm project from the testdata to a temp dir - assert.NoError(t, biutils.CopyDir(npmProjectPath, tempDirPath, true, nil)) - prevWd := changeWD(t, tempDirPath) - defer clientTestUtils.ChangeDirAndAssert(t, prevWd) - // Run npm install before executing jfrog xr npm-audit - assert.NoError(t, exec.Command("npm", "install").Run()) - // Add dummy descriptor file to check that we run only specific audit - addDummyPackageDescriptor(t, true) - return xrayCli.RunCliCmdWithOutput(t, "audit", "--npm", "--licenses", "--format="+format) -} - -func TestXrayAuditYarnV2Json(t *testing.T) { - testXrayAuditYarn(t, "yarn-v2", func() { - output := runXrayAuditYarnWithOutput(t, string(format.Json)) - verifyJsonScanResults(t, output, 0, 1, 1) - }) -} - -func TestXrayAuditYarnV2SimpleJson(t *testing.T) { - testXrayAuditYarn(t, "yarn-v2", func() { - output := runXrayAuditYarnWithOutput(t, string(format.SimpleJson)) - verifySimpleJsonScanResults(t, output, 1, 1) - }) -} - -func TestXrayAuditYarnV1Json(t *testing.T) { - testXrayAuditYarn(t, "yarn-v1", func() { - output := runXrayAuditYarnWithOutput(t, string(format.Json)) - verifyJsonScanResults(t, output, 0, 1, 1) - }) -} - -func TestXrayAuditYarnV1JsonWithoutDevDependencies(t *testing.T) { - unsetEnv := clientTestUtils.SetEnvWithCallbackAndAssert(t, "NODE_ENV", "production") - defer unsetEnv() - testXrayAuditYarn(t, "yarn-v1", func() { - output := runXrayAuditYarnWithOutput(t, string(format.Json)) - var results []services.ScanResponse - err := json.Unmarshal([]byte(output), &results) - assert.NoError(t, err) - assert.Len(t, results[0].Vulnerabilities, 0) - }) -} - -func TestXrayAuditYarnV1SimpleJson(t *testing.T) { - testXrayAuditYarn(t, "yarn-v1", func() { - output := runXrayAuditYarnWithOutput(t, string(format.SimpleJson)) - verifySimpleJsonScanResults(t, output, 1, 1) - }) -} - -func testXrayAuditYarn(t *testing.T, projectDirName string, yarnCmd func()) { - initXrayTest(t, scangraph.GraphScanMinXrayVersion) - tempDirPath, createTempDirCallback := coretests.CreateTempDirWithCallbackAndAssert(t) - defer createTempDirCallback() - yarnProjectPath := filepath.Join(filepath.FromSlash(tests.GetTestResourcesPath()), "xray", projectDirName) - // Copy the Yarn project from the testdata to a temp directory - assert.NoError(t, biutils.CopyDir(yarnProjectPath, tempDirPath, true, nil)) - prevWd := changeWD(t, tempDirPath) - defer clientTestUtils.ChangeDirAndAssert(t, prevWd) - // Run yarn install before executing jf audit --yarn. Return error to assert according to test. - assert.NoError(t, exec.Command("yarn").Run()) - // Add dummy descriptor file to check that we run only specific audit - addDummyPackageDescriptor(t, true) - yarnCmd() -} - -func runXrayAuditYarnWithOutput(t *testing.T, format string) string { - return xrayCli.RunCliCmdWithOutput(t, "audit", "--yarn", "--licenses", "--format="+format) -} - -// Tests NuGet audit by providing simple NuGet project + multi-project NuGet project and asserts any error. -func TestXrayAuditNugetJson(t *testing.T) { - var testdata = []struct { - projectName string - format string - restoreTech string - minVulnerabilities int - minLicences int - }{ - { - projectName: "single4.0", - format: string(format.Json), - restoreTech: "nuget", - minVulnerabilities: 2, - minLicences: 0, - }, - { - projectName: "single5.0", - format: string(format.Json), - restoreTech: "dotnet", - minVulnerabilities: 3, - minLicences: 2, - }, - { - projectName: "single5.0", - format: string(format.Json), - restoreTech: "", - minVulnerabilities: 3, - minLicences: 2, - }, - { - projectName: "multi", - format: string(format.Json), - restoreTech: "dotnet", - minVulnerabilities: 4, - minLicences: 3, - }, - { - projectName: "multi", - format: string(format.Json), - restoreTech: "", - minVulnerabilities: 4, - minLicences: 3, - }, - } - for _, test := range testdata { - runInstallCommand := test.restoreTech != "" - t.Run(fmt.Sprintf("projectName:%s,runInstallCommand:%t", test.projectName, runInstallCommand), - func(t *testing.T) { - output := testXrayAuditNuget(t, test.projectName, test.format, test.restoreTech) - verifyJsonScanResults(t, output, 0, test.minVulnerabilities, test.minLicences) - }) - } -} - -func TestXrayAuditNugetSimpleJson(t *testing.T) { - var testdata = []struct { - projectName string - format string - restoreTech string - minVulnerabilities int - minLicences int - }{ - { - projectName: "single4.0", - format: string(format.SimpleJson), - restoreTech: "nuget", - minVulnerabilities: 2, - minLicences: 0, - }, - { - projectName: "single5.0", - format: string(format.SimpleJson), - restoreTech: "dotnet", - minVulnerabilities: 3, - minLicences: 2, - }, - { - projectName: "single5.0", - format: string(format.SimpleJson), - restoreTech: "", - minVulnerabilities: 3, - minLicences: 2, - }, - } - for _, test := range testdata { - runInstallCommand := test.restoreTech != "" - t.Run(fmt.Sprintf("projectName:%s,runInstallCommand:%t", test.projectName, runInstallCommand), - func(t *testing.T) { - output := testXrayAuditNuget(t, test.projectName, test.format, test.restoreTech) - verifySimpleJsonScanResults(t, output, test.minVulnerabilities, test.minLicences) - }) - } -} - -func testXrayAuditNuget(t *testing.T, projectName, format string, restoreTech string) string { - initXrayTest(t, scangraph.GraphScanMinXrayVersion) - tempDirPath, createTempDirCallback := coretests.CreateTempDirWithCallbackAndAssert(t) - defer createTempDirCallback() - projectPath := filepath.Join(filepath.FromSlash(tests.GetTestResourcesPath()), "xray", "nuget", projectName) - - assert.NoError(t, biutils.CopyDir(projectPath, tempDirPath, true, nil)) - prevWd := changeWD(t, tempDirPath) - defer clientTestUtils.ChangeDirAndAssert(t, prevWd) - // Add dummy descriptor file to check that we run only specific audit - addDummyPackageDescriptor(t, false) - // Run NuGet/Dotnet restore before executing jfrog xr audit (NuGet) - if restoreTech != "" { - _, err := exec.Command(restoreTech, "restore").CombinedOutput() - assert.NoError(t, err) - } - return xrayCli.RunCliCmdWithOutput(t, "audit", "--nuget", "--format="+format, "--licenses") -} - -func TestXrayAuditGradleJson(t *testing.T) { - output := testXrayAuditGradle(t, string(format.Json)) - verifyJsonScanResults(t, output, 0, 3, 3) -} - -func TestXrayAuditGradleSimpleJson(t *testing.T) { - output := testXrayAuditGradle(t, string(format.SimpleJson)) - verifySimpleJsonScanResults(t, output, 3, 3) -} - -func testXrayAuditGradle(t *testing.T, format string) string { - initXrayTest(t, scangraph.GraphScanMinXrayVersion) - tempDirPath, createTempDirCallback := coretests.CreateTempDirWithCallbackAndAssert(t) - defer createTempDirCallback() - gradleProjectPath := filepath.Join(filepath.FromSlash(tests.GetTestResourcesPath()), "xray", "gradle") - // Copy the gradle project from the testdata to a temp dir - assert.NoError(t, biutils.CopyDir(gradleProjectPath, tempDirPath, true, nil)) - prevWd := changeWD(t, tempDirPath) - defer clientTestUtils.ChangeDirAndAssert(t, prevWd) - // Add dummy descriptor file to check that we run only specific audit - addDummyPackageDescriptor(t, false) - return xrayCli.RunCliCmdWithOutput(t, "audit", "--gradle", "--licenses", "--format="+format) -} - -func TestXrayAuditMavenJson(t *testing.T) { - output := testXrayAuditMaven(t, string(format.Json)) - verifyJsonScanResults(t, output, 0, 1, 1) -} - -func TestXrayAuditMavenSimpleJson(t *testing.T) { - output := testXrayAuditMaven(t, string(format.SimpleJson)) - verifySimpleJsonScanResults(t, output, 1, 1) -} - -func testXrayAuditMaven(t *testing.T, format string) string { - initXrayTest(t, scangraph.GraphScanMinXrayVersion) - tempDirPath, createTempDirCallback := coretests.CreateTempDirWithCallbackAndAssert(t) - defer createTempDirCallback() - mvnProjectPath := filepath.Join(filepath.FromSlash(tests.GetTestResourcesPath()), "xray", "maven") - // Copy the maven project from the testdata to a temp dir - assert.NoError(t, biutils.CopyDir(mvnProjectPath, tempDirPath, true, nil)) - prevWd := changeWD(t, tempDirPath) - defer clientTestUtils.ChangeDirAndAssert(t, prevWd) - // Add dummy descriptor file to check that we run only specific audit - addDummyPackageDescriptor(t, false) - return xrayCli.RunCliCmdWithOutput(t, "audit", "--mvn", "--licenses", "--format="+format) -} - -func TestXrayAuditNoTech(t *testing.T) { - initXrayTest(t, scangraph.GraphScanMinXrayVersion) - tempDirPath, createTempDirCallback := coretests.CreateTempDirWithCallbackAndAssert(t) - defer createTempDirCallback() - prevWd := changeWD(t, tempDirPath) - defer clientTestUtils.ChangeDirAndAssert(t, prevWd) - // Run audit on empty folder, expect an error - err := xrayCli.Exec("audit") - assert.NoError(t, err) -} - -func TestXrayAuditDetectTech(t *testing.T) { - initXrayTest(t, scangraph.GraphScanMinXrayVersion) - tempDirPath, createTempDirCallback := coretests.CreateTempDirWithCallbackAndAssert(t) - defer createTempDirCallback() - mvnProjectPath := filepath.Join(filepath.FromSlash(tests.GetTestResourcesPath()), "xray", "maven") - // Copy the maven project from the testdata to a temp dir - assert.NoError(t, biutils.CopyDir(mvnProjectPath, tempDirPath, true, nil)) - prevWd := changeWD(t, tempDirPath) - defer clientTestUtils.ChangeDirAndAssert(t, prevWd) - // Run generic audit on mvn project with a vulnerable dependency - output := xrayCli.RunCliCmdWithOutput(t, "audit", "--licenses", "--format="+string(format.SimpleJson)) - var results formats.SimpleJsonResults - err := json.Unmarshal([]byte(output), &results) - assert.NoError(t, err) - // Expects the ImpactedPackageType of the known vulnerability to be maven - assert.Equal(t, strings.ToLower(results.Vulnerabilities[0].ImpactedDependencyType), "maven") -} - -func TestXrayAuditMultiProjects(t *testing.T) { - initXrayTest(t, scangraph.GraphScanMinXrayVersion) - tempDirPath, createTempDirCallback := coretests.CreateTempDirWithCallbackAndAssert(t) - defer createTempDirCallback() - multiProject := filepath.Join(filepath.FromSlash(tests.GetTestResourcesPath()), "xray") - // Copy the multi project from the testdata to a temp dir - assert.NoError(t, biutils.CopyDir(multiProject, tempDirPath, true, nil)) - prevWd := changeWD(t, tempDirPath) - defer clientTestUtils.ChangeDirAndAssert(t, prevWd) - workingDirsFlag := fmt.Sprintf("--working-dirs=%s, %s ,%s, %s", - filepath.Join(tempDirPath, "maven"), filepath.Join(tempDirPath, "nuget", "single4.0"), - filepath.Join(tempDirPath, "python", "pip"), filepath.Join(tempDirPath, "jas-test")) - // Configure a new server named "default" - createJfrogHomeConfig(t, true) - defer cleanTestsHomeEnv() - output := xrayCli.WithoutCredentials().RunCliCmdWithOutput(t, "audit", "--format="+string(format.SimpleJson), workingDirsFlag) - verifySimpleJsonScanResults(t, output, 35, 0) - verifySimpleJsonJasResults(t, output, 1, 9, 7, 3) -} - -func TestXrayAuditPipJson(t *testing.T) { - output := testXrayAuditPip(t, string(format.Json), "") - verifyJsonScanResults(t, output, 0, 3, 1) -} - -func TestXrayAuditPipSimpleJson(t *testing.T) { - output := testXrayAuditPip(t, string(format.SimpleJson), "") - verifySimpleJsonScanResults(t, output, 3, 1) -} - -func TestXrayAuditPipJsonWithRequirementsFile(t *testing.T) { - output := testXrayAuditPip(t, string(format.Json), "requirements.txt") - verifyJsonScanResults(t, output, 0, 2, 0) -} - -func TestXrayAuditPipSimpleJsonWithRequirementsFile(t *testing.T) { - output := testXrayAuditPip(t, string(format.SimpleJson), "requirements.txt") - verifySimpleJsonScanResults(t, output, 2, 0) -} - -func testXrayAuditPip(t *testing.T, format, requirementsFile string) string { - initXrayTest(t, scangraph.GraphScanMinXrayVersion) - tempDirPath, createTempDirCallback := coretests.CreateTempDirWithCallbackAndAssert(t) - defer createTempDirCallback() - pipProjectPath := filepath.Join(filepath.FromSlash(tests.GetTestResourcesPath()), "xray", "python", "pip") - // Copy the pip project from the testdata to a temp dir - assert.NoError(t, biutils.CopyDir(pipProjectPath, tempDirPath, true, nil)) - prevWd := changeWD(t, tempDirPath) - defer clientTestUtils.ChangeDirAndAssert(t, prevWd) - // Add dummy descriptor file to check that we run only specific audit - addDummyPackageDescriptor(t, false) - args := []string{"audit", "--pip", "--licenses", "--format=" + format} - if requirementsFile != "" { - args = append(args, "--requirements-file="+requirementsFile) - - } - return xrayCli.RunCliCmdWithOutput(t, args...) -} - -func TestXrayAuditPipenvJson(t *testing.T) { - output := testXrayAuditPipenv(t, string(format.Json)) - verifyJsonScanResults(t, output, 0, 3, 1) -} - -func TestXrayAuditPipenvSimpleJson(t *testing.T) { - output := testXrayAuditPipenv(t, string(format.SimpleJson)) - verifySimpleJsonScanResults(t, output, 3, 1) -} - -func testXrayAuditPipenv(t *testing.T, format string) string { - initXrayTest(t, scangraph.GraphScanMinXrayVersion) - tempDirPath, createTempDirCallback := coretests.CreateTempDirWithCallbackAndAssert(t) - defer createTempDirCallback() - pipenvProjectPath := filepath.Join(filepath.FromSlash(tests.GetTestResourcesPath()), "xray", "python", "pipenv") - // Copy the pipenv project from the testdata to a temp dir - assert.NoError(t, biutils.CopyDir(pipenvProjectPath, tempDirPath, true, nil)) - prevWd := changeWD(t, tempDirPath) - defer clientTestUtils.ChangeDirAndAssert(t, prevWd) - // Add dummy descriptor file to check that we run only specific audit - addDummyPackageDescriptor(t, false) - return xrayCli.RunCliCmdWithOutput(t, "audit", "--pipenv", "--licenses", "--format="+format) -} - -func TestDownloadAnalyzerManagerIfNeeded(t *testing.T) { - initXrayTest(t, "") - - // Configure a new JFrog CLI home dir. - tempDirPath, createTempDirCallback := coretests.CreateTempDirWithCallbackAndAssert(t) - defer createTempDirCallback() - setEnvCallBack := clientTestUtils.SetEnvWithCallbackAndAssert(t, coreutils.HomeDir, tempDirPath) - defer setEnvCallBack() - - // Download - err := dependencies.DownloadAnalyzerManagerIfNeeded() - assert.NoError(t, err) - - // Validate Analyzer manager app & checksum.sh2 file exist - path, err := utils.GetAnalyzerManagerDirAbsolutePath() - assert.NoError(t, err) - amPath := filepath.Join(path, utils.GetAnalyzerManagerExecutableName()) - exists, err := fileutils.IsFileExists(amPath, false) - assert.NoError(t, err) - assert.True(t, exists) - checksumPath := filepath.Join(path, dependencies.ChecksumFileName) - exists, err = fileutils.IsFileExists(checksumPath, false) - assert.NoError(t, err) - assert.True(t, exists) - checksumFileStat, err := os.Stat(checksumPath) - assert.NoError(t, err) - assert.True(t, checksumFileStat.Size() > 0) - - // Validate no second download occurred - firstFileStat, err := os.Stat(amPath) - assert.NoError(t, err) - err = dependencies.DownloadAnalyzerManagerIfNeeded() - assert.NoError(t, err) - secondFileStat, err := os.Stat(amPath) - assert.NoError(t, err) - assert.Equal(t, firstFileStat.ModTime(), secondFileStat.ModTime()) -} - -func TestXrayAuditPoetryJson(t *testing.T) { - output := testXrayAuditPoetry(t, string(format.Json)) - verifyJsonScanResults(t, output, 0, 3, 1) -} - -func TestXrayAuditPoetrySimpleJson(t *testing.T) { - output := testXrayAuditPoetry(t, string(format.SimpleJson)) - verifySimpleJsonScanResults(t, output, 3, 1) -} - -func testXrayAuditPoetry(t *testing.T, format string) string { - initXrayTest(t, scangraph.GraphScanMinXrayVersion) - tempDirPath, createTempDirCallback := coretests.CreateTempDirWithCallbackAndAssert(t) - defer createTempDirCallback() - poetryProjectPath := filepath.Join(filepath.FromSlash(tests.GetTestResourcesPath()), "xray", "python", "poetry") - // Copy the poetry project from the testdata to a temp dir - assert.NoError(t, biutils.CopyDir(poetryProjectPath, tempDirPath, true, nil)) - prevWd := changeWD(t, tempDirPath) - defer clientTestUtils.ChangeDirAndAssert(t, prevWd) - // Add dummy descriptor file to check that we run only specific audit - addDummyPackageDescriptor(t, false) - return xrayCli.RunCliCmdWithOutput(t, "audit", "--poetry", "--licenses", "--format="+format) -} - -func addDummyPackageDescriptor(t *testing.T, hasPackageJson bool) { - descriptor := "package.json" - if hasPackageJson { - descriptor = "pom.xml" - } - dummyFile, err := os.Create(descriptor) - assert.NoError(t, err) - assert.NoError(t, dummyFile.Close()) -} - -func initXrayTest(t *testing.T, minVersion string) { - if !*tests.TestXray { - t.Skip("Skipping Xray test. To run Xray test add the '-test.xray=true' option.") - } - validateXrayVersion(t, minVersion) -} - -func validateXrayVersion(t *testing.T, minVersion string) { - xrayVersion, err := getXrayVersion() - if err != nil { - assert.NoError(t, err) - return - } - err = clientUtils.ValidateMinimumVersion(clientUtils.Xray, xrayVersion.GetVersion(), minVersion) - if err != nil { - t.Skip(err) - } -} - -func getXrayVersion() (version.Version, error) { - xrayVersion, err := xrayAuth.GetVersion() - return *version.NewVersion(xrayVersion), err -} - -func verifyJsonScanResults(t *testing.T, content string, minViolations, minVulnerabilities, minLicenses int) { - var results []services.ScanResponse - err := json.Unmarshal([]byte(content), &results) - if assert.NoError(t, err) { - var violations []services.Violation - var vulnerabilities []services.Vulnerability - var licenses []services.License - for _, result := range results { - violations = append(violations, result.Violations...) - vulnerabilities = append(vulnerabilities, result.Vulnerabilities...) - licenses = append(licenses, result.Licenses...) - } - assert.True(t, len(violations) >= minViolations, fmt.Sprintf("Expected at least %d violations in scan results, but got %d violations.", minViolations, len(violations))) - assert.True(t, len(vulnerabilities) >= minVulnerabilities, fmt.Sprintf("Expected at least %d vulnerabilities in scan results, but got %d vulnerabilities.", minVulnerabilities, len(vulnerabilities))) - assert.True(t, len(licenses) >= minLicenses, fmt.Sprintf("Expected at least %d Licenses in scan results, but got %d Licenses.", minLicenses, len(licenses))) - } -} - -func verifySimpleJsonScanResults(t *testing.T, content string, minVulnerabilities, minLicenses int) { - var results formats.SimpleJsonResults - err := json.Unmarshal([]byte(content), &results) - if assert.NoError(t, err) { - assert.GreaterOrEqual(t, len(results.Vulnerabilities), minVulnerabilities) - assert.GreaterOrEqual(t, len(results.Licenses), minLicenses) - } -} - -func TestXrayCurl(t *testing.T) { - initXrayTest(t, "") - // Configure a new server named "default". - createJfrogHomeConfig(t, true) - defer cleanTestsHomeEnv() - // Check curl command with the default configured server. - err := xrayCli.WithoutCredentials().Exec("xr", "curl", "-XGET", "/api/v1/system/version") - assert.NoError(t, err) - // Check curl command with '--server-id' flag - err = xrayCli.WithoutCredentials().Exec("xr", "curl", "-XGET", "/api/system/version", "--server-id=default") - assert.NoError(t, err) - // Check curl command with invalid server id - should get an error. - err = xrayCli.WithoutCredentials().Exec("xr", "curl", "-XGET", "/api/system/version", "--server-id=not_configured_name") - assert.EqualError(t, err, "Server ID 'not_configured_name' does not exist.") -} - -func initNativeDockerWithXrayTest(t *testing.T) func() { - if !*tests.TestDockerScan || !*tests.TestXray { - t.Skip("Skipping Docker scan test. To run Xray Docker test add the '-test.dockerScan=true' and '-test.xray=true' options.") - } - oldHomeDir := os.Getenv(coreutils.HomeDir) - initXrayCli() - validateXrayVersion(t, scan.DockerScanMinXrayVersion) - // Create server config to use with the command. - createJfrogHomeConfig(t, true) - return func() { - clientTestUtils.SetEnvAndAssert(t, coreutils.HomeDir, oldHomeDir) - } -} - -func TestDockerScan(t *testing.T) { - cleanup := initNativeDockerWithXrayTest(t) - defer cleanup() - - watchName, deleteWatch := createTestWatch(t) - defer deleteWatch() - - imagesToScan := []string{ - // Simple image with vulnerabilities - "bitnami/minio:2022", - - // Image with RPM with vulnerabilities - "redhat/ubi8-micro:8.4", - } - for _, imageName := range imagesToScan { - runDockerScan(t, imageName, watchName, 3, 3, 3) - } - - // On Xray 3.40.3 there is a bug whereby xray fails to scan docker image with 0 vulnerabilities, - // So we skip it for now till the next version will be released - validateXrayVersion(t, "3.41.0") - - // Image with 0 vulnerabilities - runDockerScan(t, "busybox:1.35", "", 0, 0, 0) -} - -func TestDockerScanWithProgressBar(t *testing.T) { - callback := commontests.MockProgressInitialization() - defer callback() - TestDockerScan(t) -} - -func runDockerScan(t *testing.T, imageName, watchName string, minViolations, minVulnerabilities, minLicenses int) { - // Pull image from docker repo - imageTag := path.Join(*tests.ContainerRegistry, tests.DockerVirtualRepo, imageName) - dockerPullCommand := coreContainer.NewPullCommand(container.DockerClient) - dockerPullCommand.SetCmdParams([]string{"pull", imageTag}).SetImageTag(imageTag).SetRepo(tests.DockerVirtualRepo).SetServerDetails(serverDetails).SetBuildConfiguration(new(build.BuildConfiguration)) - if assert.NoError(t, dockerPullCommand.Run()) { - defer commontests.DeleteTestImage(t, imageTag, container.DockerClient) - - args := []string{"docker", "scan", imageTag, "--server-id=default", "--licenses", "--format=json", "--fail=false", "--min-severity=low", "--fixable-only"} - - // Run docker scan on image - output := xrayCli.WithoutCredentials().RunCliCmdWithOutput(t, args...) - if assert.NotEmpty(t, output) { - verifyJsonScanResults(t, output, 0, minVulnerabilities, minLicenses) - } - - // Run docker scan on image with watch - if watchName != "" { - args = append(args, "--watches="+watchName) - output = xrayCli.WithoutCredentials().RunCliCmdWithOutput(t, args...) - if assert.NotEmpty(t, output) { - verifyJsonScanResults(t, output, minViolations, 0, 0) - } - } - } -} - -func createTestWatch(t *testing.T) (string, func()) { - xrayManager, err := utils.CreateXrayServiceManager(xrayDetails) - assert.NoError(t, err) - // Create new default policy. - policyParams := xrayUtils.PolicyParams{ - Name: fmt.Sprintf("%s-%s", "docker-policy", strconv.FormatInt(time.Now().Unix(), 10)), - Type: xrayUtils.Security, - Rules: []xrayUtils.PolicyRule{{ - Name: "sec_rule", - Criteria: *xrayUtils.CreateSeverityPolicyCriteria(xrayUtils.Low), - Priority: 1, - Actions: &xrayUtils.PolicyAction{ - FailBuild: clientUtils.Pointer(true), - }, - }}, - } - if !assert.NoError(t, xrayManager.CreatePolicy(policyParams)) { - return "", func() {} - } - // Create new default watch. - watchParams := xrayUtils.NewWatchParams() - watchParams.Name = fmt.Sprintf("%s-%s", "docker-watch", strconv.FormatInt(time.Now().Unix(), 10)) - watchParams.Active = true - watchParams.Builds.Type = xrayUtils.WatchBuildAll - watchParams.Policies = []xrayUtils.AssignedPolicy{ - { - Name: policyParams.Name, - Type: "security", - }, - } - assert.NoError(t, xrayManager.CreateWatch(watchParams)) - return watchParams.Name, func() { - assert.NoError(t, xrayManager.DeleteWatch(watchParams.Name)) - assert.NoError(t, xrayManager.DeletePolicy(policyParams.Name)) - } -} - -func TestXrayOfflineDBSyncV3(t *testing.T) { - initXrayTest(t, "") - - // Validate license-id - err := xrayCli.WithoutCredentials().Exec("xr", "ou") - assert.EqualError(t, err, "the --license-id option is mandatory") - // Periodic valid only with stream - err = xrayCli.WithoutCredentials().Exec("xr", "ou", "--license-id=123", "--periodic") - assert.EqualError(t, err, fmt.Sprintf("the %s option is only valid with %s", cliutils.Periodic, cliutils.Stream)) - err = xrayCli.WithoutCredentials().Exec("xr", "ou", "--license-id=123", "--stream=", "--periodic") - assert.EqualError(t, err, fmt.Sprintf("the %s option is only valid with %s", cliutils.Periodic, cliutils.Stream)) - // Invalid stream - err = xrayCli.WithoutCredentials().Exec("xr", "ou", "--license-id=123", "--stream=bad_name") - assert.ErrorContains(t, err, "Invalid stream type") -} - -func TestXrayAuditJasSimpleJson(t *testing.T) { - output := testXrayAuditJas(t, string(format.SimpleJson), "jas-test") - verifySimpleJsonJasResults(t, output, 1, 9, 7, 2) -} - -func TestXrayAuditJasSimpleJsonWithConfig(t *testing.T) { - output := testXrayAuditJas(t, string(format.SimpleJson), "jas-config") - verifySimpleJsonJasResults(t, output, 0, 0, 1, 2) -} - -func TestXrayAuditJasNoViolationsSimpleJson(t *testing.T) { - output := testXrayAuditJas(t, string(format.SimpleJson), "npm") - verifySimpleJsonScanResults(t, output, 1, 0) - verifySimpleJsonJasResults(t, output, 0, 0, 0, 0) -} - -func testXrayAuditJas(t *testing.T, format string, project string) string { - initXrayTest(t, scangraph.GraphScanMinXrayVersion) - tempDirPath, createTempDirCallback := coretests.CreateTempDirWithCallbackAndAssert(t) - defer createTempDirCallback() - projectDir := filepath.Join(filepath.FromSlash(tests.GetTestResourcesPath()), filepath.Join("xray", project)) - // Copy the multi project from the testdata to a temp dir - assert.NoError(t, biutils.CopyDir(projectDir, tempDirPath, true, nil)) - // Configure a new server named "default" - createJfrogHomeConfig(t, true) - defer cleanTestsHomeEnv() - baseWd, err := os.Getwd() - assert.NoError(t, err) - chdirCallback := clientTestUtils.ChangeDirWithCallback(t, baseWd, tempDirPath) - defer chdirCallback() - return xrayCli.WithoutCredentials().RunCliCmdWithOutput(t, "audit", "--format="+format) -} - -func verifySimpleJsonJasResults(t *testing.T, content string, minSastViolations, minIacViolations, minSecrets, minApplicable int) { - var results formats.SimpleJsonResults - err := json.Unmarshal([]byte(content), &results) - if assert.NoError(t, err) { - assert.GreaterOrEqual(t, len(results.Sast), minSastViolations, "Found less sast then expected") - assert.GreaterOrEqual(t, len(results.Secrets), minSecrets, "Found less secrets then expected") - assert.GreaterOrEqual(t, len(results.Iacs), minIacViolations, "Found less IaC then expected") - var applicableResults, notApplicableResults int - for _, vuln := range results.Vulnerabilities { - if vuln.Applicable == string(utils.NotApplicable) { - notApplicableResults++ - } else if vuln.Applicable == string(utils.Applicable) { - applicableResults++ - } - } - assert.GreaterOrEqual(t, applicableResults, minApplicable, "Found less applicableResults then expected") - assert.GreaterOrEqual(t, notApplicableResults, 1, "Found less notApplicableResults then expected") - } -} - -func TestCurationAudit(t *testing.T) { - initXrayTest(t, "") - tempDirPath, createTempDirCallback := coretests.CreateTempDirWithCallbackAndAssert(t) - defer createTempDirCallback() - multiProject := filepath.Join(filepath.FromSlash(tests.GetTestResourcesPath()), "xray") - assert.NoError(t, biutils.CopyDir(multiProject, tempDirPath, true, nil)) - rootDir, err := os.Getwd() - require.NoError(t, err) - defer func() { - assert.NoError(t, os.Chdir(rootDir)) - }() - require.NoError(t, os.Chdir(filepath.Join(tempDirPath, "npm"))) - expectedRequest := map[string]bool{ - "/api/npm/npms/json/-/json-9.0.6.tgz": false, - "/api/npm/npms/xml/-/xml-1.0.1.tgz": false, - } - requestToFail := map[string]bool{ - "/api/npm/npms/xml/-/xml-1.0.1.tgz": false, - } - serverMock, config := curationServer(t, expectedRequest, requestToFail) - - cleanUpJfrogHome, err := coretests.SetJfrogHome() - assert.NoError(t, err) - defer cleanUpJfrogHome() - - config.User = "admin" - config.Password = "password" - config.ServerId = "test" - configCmd := coreCmd.NewConfigCommand(coreCmd.AddOrEdit, "test").SetDetails(config).SetUseBasicAuthOnly(true).SetInteractive(false) - assert.NoError(t, configCmd.Run()) - - defer serverMock.Close() - // Create build config - resolutionServerId := "server-id-resolve" - deploymentServerId := "server-id-deploy" - resolutionRepo := "repo-resolve" - deploymentRepo := "repo-deploy" - context := createContext(t, resolutionServerId+"="+config.ServerId, resolutionRepo+"=npms", deploymentServerId+"="+config.ServerId, deploymentRepo+"=npm-local", "global=false") - err = coreCmd.CreateBuildConfig(context, project.Npm) - assert.NoError(t, err) - - localXrayCli := xrayCli.WithoutCredentials() - workingDirsFlag := fmt.Sprintf("--working-dirs=%s", filepath.Join(tempDirPath, "npm")) - output := localXrayCli.RunCliCmdWithOutput(t, "curation-audit", "--format="+string(format.Json), workingDirsFlag) - expectedResp := getCurationExpectedResponse(config) - var got []coreCuration.PackageStatus - bracketIndex := strings.Index(output, "[") - require.Less(t, 0, bracketIndex, "Unexpected Curation output with missing '['") - err = json.Unmarshal([]byte(output[bracketIndex:]), &got) - assert.NoError(t, err) - assert.Equal(t, expectedResp, got) - for k, v := range expectedRequest { - assert.Truef(t, v, "didn't receive expected GET request for package url %s", k) - } -} - -func getCurationExpectedResponse(config *config.ServerDetails) []coreCuration.PackageStatus { - expectedResp := []coreCuration.PackageStatus{ - { - Action: "blocked", - PackageName: "xml", - PackageVersion: "1.0.1", - BlockedPackageUrl: config.ArtifactoryUrl + "api/npm/npms/xml/-/xml-1.0.1.tgz", - BlockingReason: coreCuration.BlockingReasonPolicy, - ParentName: "xml", - ParentVersion: "1.0.1", - DepRelation: "direct", - PkgType: "npm", - Policy: []coreCuration.Policy{ - { - Policy: "pol1", - Condition: "cond1", - Explanation: "explanation", - Recommendation: "recommendation", - }, - { - Policy: "pol2", - Condition: "cond2", - Explanation: "explanation2", - Recommendation: "recommendation2", - }, - }, - }, - } - return expectedResp -} - -func curationServer(t *testing.T, expectedRequest map[string]bool, requestToFail map[string]bool) (*httptest.Server, *config.ServerDetails) { - mapLockReadWrite := sync.Mutex{} - serverMock, config, _ := commontests.CreateRtRestsMockServer(t, func(w http.ResponseWriter, r *http.Request) { - if r.Method == http.MethodHead { - mapLockReadWrite.Lock() - if _, exist := expectedRequest[r.RequestURI]; exist { - expectedRequest[r.RequestURI] = true - } - mapLockReadWrite.Unlock() - if _, exist := requestToFail[r.RequestURI]; exist { - w.WriteHeader(http.StatusForbidden) - } - } - if r.Method == http.MethodGet { - if r.RequestURI == "/api/system/version" { - _, err := w.Write([]byte(`{"version": "7.0.0"}`)) - require.NoError(t, err) - w.WriteHeader(http.StatusOK) - return - } - - if _, exist := requestToFail[r.RequestURI]; exist { - w.WriteHeader(http.StatusForbidden) - _, err := w.Write([]byte("{\n \"errors\": [\n {\n \"status\": 403,\n " + - "\"message\": \"Package download was blocked by JFrog Packages " + - "Curation service due to the following policies violated {pol1, cond1, explanation, recommendation}, {pol2, cond2, explanation2, recommendation2}\"\n }\n ]\n}")) - require.NoError(t, err) - } - } - }) - return serverMock, config -} - -func createContext(t *testing.T, stringFlags ...string) *cli.Context { - flagSet := flag.NewFlagSet("TestFlagSet", flag.ContinueOnError) - flags := setStringFlags(flagSet, stringFlags...) - assert.NoError(t, flagSet.Parse(flags)) - return cli.NewContext(nil, flagSet, nil) -} - -func setStringFlags(flagSet *flag.FlagSet, flags ...string) []string { - cmdFlags := []string{} - for _, stringFlag := range flags { - flagSet.String(strings.Split(stringFlag, "=")[0], "", "") - cmdFlags = append(cmdFlags, "--"+stringFlag) - } - return cmdFlags -} - -// We perform validation on dependency resolution from an Artifactory server during the construction of the dependency tree during 'audit' flow. -// This process involves resolving all dependencies required by the project. -func TestDependencyResolutionFromArtifactory(t *testing.T) { - initXrayTest(t, "") - - testCases := []struct { - testProjectPath []string - resolveRepoName string - cacheRepoName string - projectType project.ProjectType - }{ - { - testProjectPath: []string{"npm", "npmproject"}, - resolveRepoName: tests.NpmRemoteRepo, - cacheRepoName: tests.NpmRemoteRepo, - projectType: project.Npm, - }, - { - testProjectPath: []string{"nuget", "simple-dotnet"}, - resolveRepoName: tests.NugetRemoteRepo, - cacheRepoName: tests.NugetRemoteRepo, - projectType: project.Dotnet, - }, - { - testProjectPath: []string{"yarn", "yarnproject"}, - resolveRepoName: tests.YarnRemoteRepo, - cacheRepoName: tests.YarnRemoteRepo, - projectType: project.Yarn, - }, - { - testProjectPath: []string{"gradle", "gradleproject"}, - resolveRepoName: tests.GradleRemoteRepo, - cacheRepoName: tests.GradleRemoteRepo, - projectType: project.Gradle, - }, - { - testProjectPath: []string{"maven", "mavenproject"}, - resolveRepoName: tests.MvnRemoteRepo, - cacheRepoName: tests.MvnRemoteRepo, - projectType: project.Maven, - }, - { - testProjectPath: []string{"go", "simple-project"}, - resolveRepoName: tests.GoVirtualRepo, - cacheRepoName: tests.GoRemoteRepo, - projectType: project.Go, - }, - { - testProjectPath: []string{"pipenv", "pipenvproject"}, - resolveRepoName: tests.PypiRemoteRepo, - cacheRepoName: tests.PypiRemoteRepo, - projectType: project.Pipenv, - }, - { - testProjectPath: []string{"pip", "setuppyproject"}, - resolveRepoName: tests.PypiRemoteRepo, - cacheRepoName: tests.PypiRemoteRepo, - projectType: project.Pip, - }, - { - testProjectPath: []string{"poetry", "projecttomlproject"}, - resolveRepoName: tests.PypiRemoteRepo, - cacheRepoName: tests.PypiRemoteRepo, - projectType: project.Poetry, - }, - } - createJfrogHomeConfig(t, true) - defer cleanTestsHomeEnv() - - for _, testCase := range testCases { - t.Run(testCase.projectType.String(), func(t *testing.T) { - testSingleTechDependencyResolution(t, testCase.testProjectPath, testCase.resolveRepoName, testCase.cacheRepoName, testCase.projectType) - }) - } -} - -func testSingleTechDependencyResolution(t *testing.T, testProjectPartialPath []string, resolveRepoName string, cacheRepoName string, projectType project.ProjectType) { - tempDirPath, createTempDirCallback := coretests.CreateTempDirWithCallbackAndAssert(t) - defer createTempDirCallback() - testProjectPath := filepath.Join(append([]string{filepath.FromSlash(tests.GetTestResourcesPath())}, testProjectPartialPath...)...) - assert.NoError(t, biutils.CopyDir(testProjectPath, tempDirPath, true, nil)) - rootDir, err := os.Getwd() - assert.NoError(t, err) - assert.NoError(t, os.Chdir(tempDirPath)) - defer func() { - assert.NoError(t, os.Chdir(rootDir)) - }() - - server := &config.ServerDetails{ - Url: *tests.JfrogUrl, - ArtifactoryUrl: *tests.JfrogUrl + tests.ArtifactoryEndpoint, - XrayUrl: *tests.JfrogUrl + tests.XrayEndpoint, - AccessToken: *tests.JfrogAccessToken, - ServerId: tests.ServerId, - } - configCmd := coreCmd.NewConfigCommand(coreCmd.AddOrEdit, tests.ServerId).SetDetails(server).SetUseBasicAuthOnly(true).SetInteractive(false) - assert.NoError(t, configCmd.Run()) - - context := createContext(t, "repo-resolve="+resolveRepoName, "server-id-resolve="+server.ServerId) - err = coreCmd.CreateBuildConfig(context, projectType) - assert.NoError(t, err) - - artifactoryPathToSearch := cacheRepoName + "-cache/*" - // To ensure a clean state between test cases, we need to verify that the cache remains clear for remote directories shared across multiple test cases. - assert.NoError(t, artifactoryCli.Exec("del", artifactoryPathToSearch)) - - callbackFunc := clearOrRedirectLocalCacheIfNeeded(t, projectType) - if callbackFunc != nil { - defer func() { - callbackFunc() - }() - } - - // Executing the 'audit' command on an uninstalled project, we anticipate the resolution of dependencies from the configured Artifactory server and repository. - assert.NoError(t, xrayCli.WithoutCredentials().Exec("audit")) - - // Following resolution from Artifactory, we anticipate the repository's cache to contain data. - output := artifactoryCli.RunCliCmdWithOutput(t, "s", artifactoryPathToSearch, "--fail-no-op") - // After the resolution from Artifactory, we verify whether the repository's cache is filled with artifacts. - assert.NotEqual(t, "[]\n", output) -} - -// To guarantee that dependencies are resolved from Artifactory, certain package managers may need their local cache to be cleared. -func clearOrRedirectLocalCacheIfNeeded(t *testing.T, projectType project.ProjectType) (callbackFunc func()) { - switch projectType { - case project.Dotnet: - _, err := exec.Command("dotnet", "nuget", "locals", "all", "--clear").CombinedOutput() - assert.NoError(t, err) - case project.Maven: - mavenCacheTempPath, createTempDirCallback := coretests.CreateTempDirWithCallbackAndAssert(t) - envVarCallbackFunc := clientTestUtils.SetEnvWithCallbackAndAssert(t, jvmLaunchEnvVar, mavenCacheRedirectionVal+mavenCacheTempPath) - callbackFunc = func() { - envVarCallbackFunc() - createTempDirCallback() - } - case project.Go: - goTempCachePath, createTempDirCallback := coretests.CreateTempDirWithCallbackAndAssert(t) - envVarCallbackFunc := clientTestUtils.SetEnvWithCallbackAndAssert(t, goCacheEnvVar, goTempCachePath) - - callbackFunc = func() { - envVarCallbackFunc() - // To remove the temporary cache in Go and all its contents, appropriate deletion permissions are required. - assert.NoError(t, coreutils.SetPermissionsRecursively(goTempCachePath, 0755)) - createTempDirCallback() - } - case project.Pip: - pipTempCachePath, createTempDirCallback := coretests.CreateTempDirWithCallbackAndAssert(t) - envVarCallbackFunc := clientTestUtils.SetEnvWithCallbackAndAssert(t, pipCacheEnvVar, pipTempCachePath) - callbackFunc = func() { - envVarCallbackFunc() - createTempDirCallback() - } - } - return -} - -func TestXrayRecursiveScan(t *testing.T) { - initXrayTest(t, scangraph.GraphScanMinXrayVersion) - - tempDirPath, createTempDirCallback := coretests.CreateTempDirWithCallbackAndAssert(t) - defer createTempDirCallback() - - // Creating an inner NPM project - npmDirPath, err := os.MkdirTemp(tempDirPath, "npm-project") - assert.NoError(t, err) - npmProjectToCopyPath := filepath.Join("testdata", "npm", "npmproject") - assert.NoError(t, biutils.CopyDir(npmProjectToCopyPath, npmDirPath, true, nil)) - - // Creating an inner .NET project - dotnetDirPath, err := os.MkdirTemp(tempDirPath, "dotnet-project") - assert.NoError(t, err) - dotnetProjectToCopyPath := filepath.Join("testdata", "nuget", "simple-dotnet") - assert.NoError(t, biutils.CopyDir(dotnetProjectToCopyPath, dotnetDirPath, true, nil)) - - curWd, err := os.Getwd() - assert.NoError(t, err) - - chDirCallback := clientTestUtils.ChangeDirWithCallback(t, curWd, tempDirPath) - defer chDirCallback() - - // We anticipate the execution of a recursive scan to encompass both the inner NPM project and the inner .NET project. - output := xrayCli.RunCliCmdWithOutput(t, "audit", "--format=json") - - // We anticipate the identification of five vulnerabilities: four originating from the .NET project and one from the NPM project. - verifyJsonScanResults(t, output, 0, 5, 0) - - var results []services.ScanResponse - err = json.Unmarshal([]byte(output), &results) - assert.NoError(t, err) - // We anticipate receiving an array with a length of 2 to confirm that we have obtained results from two distinct inner projects. - assert.Len(t, results, 2) -}