diff --git a/.github/RELEASE_LINK_TEMPLATE.md b/.github/RELEASE_LINK_TEMPLATE.md new file mode 100644 index 000000000..bcd83c827 --- /dev/null +++ b/.github/RELEASE_LINK_TEMPLATE.md @@ -0,0 +1,23 @@ +
+ Downloads JFrog CLI + +### Linux + +[386](https://releases.jfrog.io/artifactory/jfrog-cli/v2-jf/{version}/jfrog-cli-linux-386/jf) +[AMD-64](https://releases.jfrog.io/artifactory/jfrog-cli/v2-jf/{version}/jfrog-cli-linux-amd64/jf) +[ARM-32](https://releases.jfrog.io/artifactory/jfrog-cli/v2-jf/{version}/jfrog-cli-linux-arm/jf) +[ARM-64](https://releases.jfrog.io/artifactory/jfrog-cli/v2-jf/{version}/jfrog-cli-linux-arm64/jf) +[PPC-64](https://releases.jfrog.io/artifactory/jfrog-cli/v2-jf/{version}/jfrog-cli-linux-ppc64/jf) +[PPC-64-LE](https://releases.jfrog.io/artifactory/jfrog-cli/v2-jf/{version}/jfrog-cli-linux-ppc64le/jf) +[S390X](https://releases.jfrog.io/artifactory/jfrog-cli/v2-jf/{version}/jfrog-cli-linux-s390x/jf) + +### MacOS + +[AMD-64](https://releases.jfrog.io/artifactory/jfrog-cli/v2-jf/{version}/jfrog-cli-mac-386/jf) +[ARM-64](https://releases.jfrog.io/artifactory/jfrog-cli/v2-jf/{version}/jfrog-cli-mac-arm64/jf) + +### Windows + +[AMD-64](https://releases.jfrog.io/artifactory/jfrog-cli/v2-jf/{version}/jfrog-cli-windows-amd64/jf.exe) + +
diff --git a/.github/workflows/addReleaseLinks.yml b/.github/workflows/addReleaseLinks.yml new file mode 100644 index 000000000..75622164b --- /dev/null +++ b/.github/workflows/addReleaseLinks.yml @@ -0,0 +1,27 @@ +name: Add links on release +on: + release: + types: [created] + +jobs: + add-links-on-release: + name: Add links on release + runs-on: ubuntu-latest + steps: + - name: Check out repository + uses: actions/checkout@v2 + + - name: Create markdown download links + run: | + # Remove the prefix 'v' from version. + RELEASE_VERSION=$(echo "${{ github.event.release.tag_name }}" | sed 's/^v//') + + # Replace the place-holders '{version}' with the actual release version. + sed "s/{version}/$RELEASE_VERSION/g" ./.github/RELEASE_LINK_TEMPLATE.md > ./temp_releaseLinkTemplate.md + + - name: Add links to release notes + uses: softprops/action-gh-release@v1 + with: + token: ${{ secrets.GITHUB_TOKEN }} + body_path: "temp_releaseLinkTemplate.md" + append_body: true diff --git a/.github/workflows/analysis.yml b/.github/workflows/analysis.yml index e78785890..ccc237c65 100644 --- a/.github/workflows/analysis.yml +++ b/.github/workflows/analysis.yml @@ -31,7 +31,7 @@ jobs: with: path: ~/go/pkg/mod key: ${{ runner.os }}-go-${{ hashFiles('**/go.sum') }} - restore-keys: ${{ runner.os }}-go + restore-keys: ${{ runner.os }}-go- - name: Run Go vet run: go vet -v ./... @@ -65,3 +65,15 @@ jobs: uses: securego/gosec@master with: args: -exclude G204,G301,G302,G304,G306 -tests -exclude-dir \.*test\.* ./... + + ShellCheck: + name: Shellcheck + runs-on: ubuntu-latest + steps: + - name: Checkout Source + uses: actions/checkout@v3 + + - name: Run ShellCheck + uses: ludeeus/action-shellcheck@master + with: + ignore_paths: '*test*' diff --git a/.github/workflows/dockerTests.yml b/.github/workflows/dockerTests.yml index 0c8c02535..1a30ece44 100644 --- a/.github/workflows/dockerTests.yml +++ b/.github/workflows/dockerTests.yml @@ -21,8 +21,7 @@ jobs: - name: Install Go uses: actions/setup-go@v3 with: - # We are temporarily downgrading to Go 1.20.5 due to a bug in version 1.20.6 that causes Docker tests to fail. - go-version: 1.20.5 + go-version: 1.20.x - name: Checkout code uses: actions/checkout@v3 with: diff --git a/.github/workflows/frogbot-scan-pull-request.yml b/.github/workflows/frogbot-scan-pull-request.yml index 998c8c91f..740419027 100644 --- a/.github/workflows/frogbot-scan-pull-request.yml +++ b/.github/workflows/frogbot-scan-pull-request.yml @@ -15,6 +15,7 @@ jobs: - uses: jfrog/frogbot@v2 env: JFROG_CLI_LOG_LEVEL: "DEBUG" + # [Mandatory] # JFrog platform URL (This functionality requires version 3.29.0 or above of Xray) JF_URL: ${{ secrets.FROGBOT_URL }} @@ -116,4 +117,5 @@ jobs: # [Optional] # Set the minimum severity for vulnerabilities that should be fixed and commented on in pull requests # The following values are accepted: Low, Medium, High or Critical - # JF_MIN_SEVERITY: "" \ No newline at end of file + # JF_MIN_SEVERITY: "" + diff --git a/.github/workflows/frogbot-scan-repository.yml b/.github/workflows/frogbot-scan-repository.yml index 01b568f67..12e117be8 100644 --- a/.github/workflows/frogbot-scan-repository.yml +++ b/.github/workflows/frogbot-scan-repository.yml @@ -10,6 +10,7 @@ permissions: security-events: write jobs: scan-repository: + name: Scan Repository (${{ matrix.branch }} branch) runs-on: ubuntu-latest strategy: matrix: @@ -19,6 +20,7 @@ jobs: - uses: jfrog/frogbot@v2 env: JFROG_CLI_LOG_LEVEL: "DEBUG" + # [Mandatory] # JFrog platform URL (This functionality requires version 3.29.0 or above of Xray) JF_URL: ${{ secrets.FROGBOT_URL }} diff --git a/.github/workflows/mavenTests.yml b/.github/workflows/mavenTests.yml index 25d0f1f10..a7f55587c 100644 --- a/.github/workflows/mavenTests.yml +++ b/.github/workflows/mavenTests.yml @@ -35,7 +35,7 @@ jobs: with: path: ~/go/pkg/mod key: ${{ runner.os }}-go-${{ hashFiles('**/go.sum') }} - restore-keys: ${{ runner.os }}-go + restore-keys: ${{ runner.os }}-go- - name: Setup Maven v3.8.8 for macOS uses: stCarolas/setup-maven@v4.5 with: diff --git a/.github/workflows/nugetTests.yml b/.github/workflows/nugetTests.yml index c17fadd48..262dabb50 100644 --- a/.github/workflows/nugetTests.yml +++ b/.github/workflows/nugetTests.yml @@ -43,7 +43,7 @@ jobs: with: path: ~/go/pkg/mod key: ${{ runner.os }}-go-${{ hashFiles('**/go.sum') }} - restore-keys: ${{ runner.os }}-go + restore-keys: ${{ runner.os }}-go- - name: Setup Artifactory run: | go install github.com/jfrog/jfrog-testing-infra/local-rt-setup@latest diff --git a/.github/workflows/pythonTests.yml b/.github/workflows/pythonTests.yml index d9e7dd0a7..fd937028b 100644 --- a/.github/workflows/pythonTests.yml +++ b/.github/workflows/pythonTests.yml @@ -27,10 +27,11 @@ jobs: uses: actions/setup-go@v3 with: go-version: 1.20.x + # Due to a bug in Python 3.12.0 we temporary use version 3.11.5 - name: Setup Python3 uses: actions/setup-python@v4 with: - python-version: "3.x" + python-version: "3.11.5" - name: Setup Pipenv if: ${{ matrix.suite == 'pipenv' }} run: python -m pip install pipenv diff --git a/.github/workflows/scriptTests.yml b/.github/workflows/scriptTests.yml index 7201edeeb..27ce99e5e 100644 --- a/.github/workflows/scriptTests.yml +++ b/.github/workflows/scriptTests.yml @@ -12,25 +12,86 @@ concurrency: cancel-in-progress: true jobs: Scripts-tests: - name: Script tests (${{ matrix.os }}) + name: Script tests (${{ matrix.suite.os }}) defaults: run: shell: bash strategy: fail-fast: false matrix: - os: [ ubuntu, windows, macos ] - runs-on: ${{ matrix.os }}-latest + suite: + - os: "ubuntu" + + - os: "macos" + + - os: "windows" + osSuffix: ".exe" + runs-on: ${{ matrix.suite.os }}-latest steps: - name: Checkout code uses: actions/checkout@v3 with: ref: ${{ github.event.pull_request.head.sha }} + + - name: Go Cache + uses: actions/cache@v3 + with: + path: ~/go/pkg/mod + key: ${{ runner.os }}-go-${{ hashFiles('**/go.sum') }} + restore-keys: ${{ runner.os }}-go- + - name: Test install CLI - jf - run: sh build/installcli/jf.sh && jf --version + run: | + sh build/installcli/jf.sh + jf --version + - name: Test install CLI - jfrog - run: sh build/installcli/jfrog.sh && jfrog --version + run: | + sh build/installcli/jfrog.sh + jfrog --version + - name: Test get CLI - jf - run: sh build/getcli/jf.sh && ./jf --version + run: | + sh build/getcli/jf.sh + ./jf --version + - name: Test get CLI - jfrog - run: sh build/getcli/jfrog.sh && ./jfrog --version + run: | + sh build/getcli/jfrog.sh + ./jfrog --version + + - name: Test Build CLI - sh + run: | + sh build/build.sh + ./jf${{ matrix.suite.osSuffix }} --version + if: ${{ matrix.suite.os != 'windows' }} + + - name: Test Build CLI - bat + run: | + build/build.bat + ./jf${{ matrix.suite.osSuffix }} --version + if: ${{ matrix.suite.os == 'windows' }} + + # Prior to the release, we set the new version in the package.json files, introducing the prereleased version. + # This adjustment may result in an attempt to download a version that hasn't been published to releases.jfrog.io yet. + # To handle it, we fetch the most recent JFrog CLI release and store it in the LATEST_RELEASE step output. + - name: "Get latest release" + id: latest-release + run: | + export LATEST_RELEASE=`curl https://api.github.com/repos/jfrog/jfrog-cli/releases/latest -s | jq .name -r | cut -c 2-` + echo "LATEST_RELEASE=$LATEST_RELEASE" >> "$GITHUB_OUTPUT" + shell: bash + + - name: Test install npm - v2 + working-directory: build/npm/v2 + run: | + npm version ${{ steps.latest-release.outputs.LATEST_RELEASE }} --allow-same-version + npm install + ./bin/jfrog${{ matrix.suite.osSuffix }} --version + + - name: Test install npm - v2-jf + working-directory: build/npm/v2-jf + run: | + npm version ${{ steps.latest-release.outputs.LATEST_RELEASE }} --allow-same-version + npm install + ./bin/jf${{ matrix.suite.osSuffix }} --version diff --git a/.github/workflows/xrayTests.yml b/.github/workflows/xrayTests.yml index 83d8a6f64..afb659a97 100644 --- a/.github/workflows/xrayTests.yml +++ b/.github/workflows/xrayTests.yml @@ -61,9 +61,11 @@ jobs: uses: actions/checkout@v3 with: ref: ${{ github.event.pull_request.head.sha }} - - name: Run Xray tests + + - 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 }} if: ${{ matrix.os != 'ubuntu' }} - - name: Run Docker scan and Xray tests + + - 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 }} if: ${{ matrix.os == 'ubuntu' }} diff --git a/Jenkinsfile b/Jenkinsfile index 88627412b..aed8e48b1 100644 --- a/Jenkinsfile +++ b/Jenkinsfile @@ -25,7 +25,7 @@ node("docker") { repo = 'jfrog-cli' sh 'rm -rf temp' sh 'mkdir temp' - def goRoot = tool 'go-1.20.5' + def goRoot = tool 'go-1.20.10' env.GOROOT="$goRoot" env.PATH+=":${goRoot}/bin" env.GO111MODULE="on" @@ -395,18 +395,22 @@ def distributeToReleases(stage, version, rbcSpecName) { } def publishNpmPackage(jfrogCliRepoDir) { + dir('/tmp') { + sh '''#!/bin/bash + apt update + apt install wget -y + echo "Downloading npm..." + wget https://nodejs.org/dist/v8.17.0/node-v8.17.0-linux-x64.tar.xz + tar -xvf node-v8.17.0-linux-x64.tar.xz + ''' + } dir(jfrogCliRepoDir+"build/npm/$identifier") { withCredentials([string(credentialsId: 'npm-authorization', variable: 'NPM_AUTH_TOKEN')]) { sh '''#!/bin/bash - apt update - apt install wget -y - echo "Downloading npm..." - wget https://nodejs.org/dist/v8.11.1/node-v8.11.1-linux-x64.tar.xz - tar -xvf node-v8.11.1-linux-x64.tar.xz - export PATH=$PATH:$PWD/node-v8.11.1-linux-x64/bin/ + export PATH=/tmp/node-v8.17.0-linux-x64/bin:$PATH echo "//registry.npmjs.org/:_authToken=$NPM_AUTH_TOKEN" > .npmrc echo "registry=https://registry.npmjs.org" >> .npmrc - ./node-v8.11.1-linux-x64/bin/npm publish + npm publish ''' } } diff --git a/README.md b/README.md index b16f33d2a..022e4f894 100644 --- a/README.md +++ b/README.md @@ -6,7 +6,7 @@ [![Scanned by Frogbot](https://raw.github.com/jfrog/frogbot/master/images/frogbot-badge.svg)](https://github.com/jfrog/frogbot#readme) [![Go Report Card](https://goreportcard.com/badge/github.com/jfrog/jfrog-cli)](https://goreportcard.com/report/github.com/jfrog/jfrog-cli) -[![license](https://img.shields.io/badge/License-Apache_2.0-blue.svg?style=flat)](https://raw.githubusercontent.com/jfrog/jfrog-cli/v2/LICENSE) [![](https://img.shields.io/badge/Docs-%F0%9F%93%96-blue)](https://www.jfrog.com/confluence/display/CLI/JFrog+CLI) +[![license](https://img.shields.io/badge/License-Apache_2.0-blue.svg?style=flat)](https://raw.githubusercontent.com/jfrog/jfrog-cli/v2/LICENSE) [![](https://img.shields.io/badge/Docs-%F0%9F%93%96-blue)](https://docs.jfrog-applications.jfrog.io/jfrog-applications/jfrog-cli) [![Go version](https://img.shields.io/github/go-mod/go-version/jfrog/jfrog-cli)](https://tip.golang.org/doc/go1.20) diff --git a/access_test.go b/access_test.go index 919eba11c..ba092b1d1 100644 --- a/access_test.go +++ b/access_test.go @@ -3,33 +3,86 @@ package main import ( "encoding/base64" "encoding/json" + "fmt" "github.com/jfrog/jfrog-cli-core/v2/common/commands" - coreenvsetup "github.com/jfrog/jfrog-cli-core/v2/general/envsetup" + coreEnvSetup "github.com/jfrog/jfrog-cli-core/v2/general/envsetup" "github.com/jfrog/jfrog-cli-core/v2/utils/config" "github.com/jfrog/jfrog-cli-core/v2/utils/coreutils" - coretests "github.com/jfrog/jfrog-cli-core/v2/utils/tests" + coreTests "github.com/jfrog/jfrog-cli-core/v2/utils/tests" "github.com/jfrog/jfrog-cli/utils/tests" "github.com/jfrog/jfrog-client-go/auth" + "github.com/jfrog/jfrog-client-go/http/httpclient" + clientUtils "github.com/jfrog/jfrog-client-go/utils" + "github.com/jfrog/jfrog-client-go/utils/errorutils" + "github.com/jfrog/jfrog-client-go/utils/io/httputils" clientTestUtils "github.com/jfrog/jfrog-client-go/utils/tests" "github.com/stretchr/testify/assert" + "net/http" "testing" ) +var ( + accessDetails *config.ServerDetails + accessCli *tests.JfrogCli + accessHttpDetails httputils.HttpClientDetails +) + func initAccessTest(t *testing.T) { if !*tests.TestAccess { t.Skip("Skipping Access test. To run Access test add the '-test.access=true' option.") } } +func initAccessCli() { + if accessCli != nil { + return + } + accessCli = tests.NewJfrogCli(execMain, "jfrog", authenticateAccess()) +} + +func InitAccessTests() { + initArtifactoryCli() + initAccessCli() + cleanUpOldBuilds() + cleanUpOldRepositories() + cleanUpOldUsers() + tests.AddTimestampToGlobalVars() + createRequiredRepos() + cleanArtifactoryTest() +} + +func authenticateAccess() string { + *tests.JfrogUrl = clientUtils.AddTrailingSlashIfNeeded(*tests.JfrogUrl) + accessDetails = &config.ServerDetails{ + AccessUrl: *tests.JfrogUrl + tests.AccessEndpoint} + + cred := fmt.Sprintf("--url=%s", *tests.JfrogUrl) + if *tests.JfrogAccessToken != "" { + accessDetails.AccessToken = *tests.JfrogAccessToken + cred += fmt.Sprintf(" --access-token=%s", accessDetails.AccessToken) + } else { + accessDetails.User = *tests.JfrogUser + accessDetails.Password = *tests.JfrogPassword + cred += fmt.Sprintf(" --user=%s --password=%s", accessDetails.User, accessDetails.Password) + } + + accessAuth, err := accessDetails.CreateAccessAuthConfig() + if err != nil { + coreutils.ExitOnErr(err) + } + accessHttpDetails = accessAuth.CreateHttpClientDetails() + return cred +} + func TestSetupInvitedUser(t *testing.T) { initAccessTest(t) - tempDirPath, createTempDirCallback := coretests.CreateTempDirWithCallbackAndAssert(t) + tempDirPath, createTempDirCallback := coreTests.CreateTempDirWithCallbackAndAssert(t) defer createTempDirCallback() setEnvCallBack := clientTestUtils.SetEnvWithCallbackAndAssert(t, coreutils.HomeDir, tempDirPath) defer setEnvCallBack() - serverDetails := &config.ServerDetails{Url: *tests.JfrogUrl, AccessToken: *tests.JfrogAccessToken} - encodedCred := encodeConnectionDetails(serverDetails, t) - setupCmd := coreenvsetup.NewEnvSetupCommand().SetEncodedConnectionDetails(encodedCred) + setupServerDetails := &config.ServerDetails{Url: *tests.JfrogUrl, AccessToken: *tests.JfrogAccessToken} + encodedCred := encodeConnectionDetails(setupServerDetails, t) + setupCmd := coreEnvSetup.NewEnvSetupCommand().SetEncodedConnectionDetails(encodedCred) suffix := setupCmd.SetupAndConfigServer() assert.Empty(t, suffix) configs, err := config.GetAllServersConfigs() @@ -54,7 +107,7 @@ func TestRefreshableAccessTokens(t *testing.T) { initAccessTest(t) server := &config.ServerDetails{Url: *tests.JfrogUrl, AccessToken: *tests.JfrogAccessToken} - err := coreenvsetup.GenerateNewLongTermRefreshableAccessToken(server) + err := coreEnvSetup.GenerateNewLongTermRefreshableAccessToken(server) assert.NoError(t, err) assert.NotEmpty(t, server.RefreshToken) configCmd := commands.NewConfigCommand(commands.AddOrEdit, tests.ServerId).SetDetails(server).SetInteractive(false) @@ -76,7 +129,7 @@ func TestRefreshableAccessTokens(t *testing.T) { assert.NotEmpty(t, curRefreshToken) // Make the token always refresh. - auth.InviteRefreshBeforeExpiryMinutes = 365 * 24 * 60 + auth.RefreshPlatformTokenBeforeExpiryMinutes = 365 * 24 * 60 // Upload a file and assert tokens were refreshed. uploadedFiles++ @@ -90,7 +143,7 @@ func TestRefreshableAccessTokens(t *testing.T) { } // Make the token not refresh. Verify Tokens did not refresh. - auth.InviteRefreshBeforeExpiryMinutes = 0 + auth.RefreshPlatformTokenBeforeExpiryMinutes = 0 uploadedFiles++ err = uploadWithSpecificServerAndVerify(t, artifactoryCommandExecutor, "testdata/a/b/b2.in", uploadedFiles) if !assert.NoError(t, err) { @@ -115,3 +168,121 @@ func getAccessTokensFromConfig(t *testing.T, serverId string) (accessToken, refr } return details.AccessToken, details.RefreshToken, nil } + +const ( + userScope = "applied-permissions/user" + defaultExpiry = 31536000 +) + +var atcTestCases = []struct { + name string + args []string + shouldExpire bool + // The expected expiry or -1 if we use the default expiry value + expectedExpiry int + expectedScope string + expectedRefreshable bool + expectedReference bool +}{ + { + name: "default", + args: []string{"atc"}, + shouldExpire: true, + expectedExpiry: -1, + expectedScope: userScope, + expectedRefreshable: false, + expectedReference: false, + }, + { + name: "explicit user, no expiry", + args: []string{"atc", auth.ExtractUsernameFromAccessToken(*tests.JfrogAccessToken), "--expiry=0"}, + shouldExpire: false, + expectedExpiry: 0, + expectedScope: userScope, + expectedRefreshable: false, + expectedReference: false, + }, + { + name: "refreshable, admin", + args: []string{"atc", "--refreshable", "--grant-admin"}, + shouldExpire: true, + expectedExpiry: -1, + expectedScope: "applied-permissions/admin", + expectedRefreshable: true, + expectedReference: false, + }, + { + name: "reference, custom scope, custom expiry", + args: []string{"atc", "--reference", "--scope=system:metrics:r", "--expiry=123456"}, + shouldExpire: true, + expectedExpiry: 123456, + expectedScope: "system:metrics:r", + expectedRefreshable: false, + expectedReference: true, + }, + { + name: "groups, description", + args: []string{"atc", "--groups=group1,group2", "--description=description"}, + shouldExpire: true, + expectedExpiry: -1, + expectedScope: "applied-permissions/groups:group1,group2", + expectedRefreshable: false, + expectedReference: false, + }, +} + +func TestAccessTokenCreate(t *testing.T) { + initAccessTest(t) + if *tests.JfrogAccessToken == "" { + t.Skip("access token create command only supports authorization with access token, but a token is not provided. Skipping...") + } + + for _, test := range atcTestCases { + t.Run(test.name, func(t *testing.T) { + var token auth.CreateTokenResponseData + output := accessCli.RunCliCmdWithOutput(t, test.args...) + assert.NoError(t, json.Unmarshal([]byte(output), &token)) + defer revokeToken(t, token.TokenId) + + if test.shouldExpire { + if test.expectedExpiry == -1 { + // If expectedExpiry is -1, expect the default expiry + assert.Positive(t, *token.ExpiresIn) + } else { + assert.EqualValues(t, test.expectedExpiry, *token.ExpiresIn) + } + } else { + assert.Nil(t, token.ExpiresIn) + } + assert.NotEmpty(t, token.AccessToken) + assert.Equal(t, test.expectedScope, token.Scope) + assertNotEmptyIfExpected(t, test.expectedRefreshable, token.RefreshToken) + assertNotEmptyIfExpected(t, test.expectedReference, token.ReferenceToken) + + // Try pinging Artifactory with the new token. + assert.NoError(t, tests.NewJfrogCli(execMain, "jfrog rt", + "--url="+*tests.JfrogUrl+tests.ArtifactoryEndpoint+" --access-token="+token.AccessToken).Exec("ping")) + }) + } +} + +func assertNotEmptyIfExpected(t *testing.T, expected bool, output string) { + if expected { + assert.NotEmpty(t, output) + } else { + assert.Empty(t, output) + } +} + +func revokeToken(t *testing.T, tokenId string) { + if tokenId == "" { + return + } + + client, err := httpclient.ClientBuilder().Build() + assert.NoError(t, err) + + resp, _, err := client.SendDelete(*tests.JfrogUrl+"access/api/v1/tokens/"+tokenId, nil, accessHttpDetails, "") + assert.NoError(t, err) + assert.NoError(t, errorutils.CheckResponseStatus(resp, http.StatusOK)) +} diff --git a/artifactory/cli.go b/artifactory/cli.go index 1e02ab521..12a639464 100644 --- a/artifactory/cli.go +++ b/artifactory/cli.go @@ -3,6 +3,7 @@ package artifactory import ( "errors" "fmt" + "github.com/jfrog/jfrog-cli/utils/accesstoken" "os" "strconv" "strings" @@ -860,13 +861,13 @@ func GetCommands() []cli.Command { { Name: "access-token-create", Aliases: []string{"atc"}, - Flags: cliutils.GetCommandFlags(cliutils.AccessTokenCreate), + Flags: cliutils.GetCommandFlags(cliutils.ArtifactoryAccessTokenCreate), Usage: accesstokencreate.GetDescription(), HelpName: corecommon.CreateUsage("rt atc", accesstokencreate.GetDescription(), accesstokencreate.Usage), UsageText: accesstokencreate.GetArguments(), ArgsUsage: common.CreateEnvVars(), BashComplete: corecommon.CreateBashCompletionFunc(), - Action: accessTokenCreateCmd, + Action: artifactoryAccessTokenCreateCmd, }, { Name: "transfer-settings", @@ -2199,7 +2200,7 @@ func groupDeleteCmd(c *cli.Context) error { return commands.Exec(groupDeleteCmd) } -func accessTokenCreateCmd(c *cli.Context) error { +func artifactoryAccessTokenCreateCmd(c *cli.Context) error { if c.NArg() > 1 { return cliutils.WrongNumberOfArgumentsHandler(c) } @@ -2208,20 +2209,16 @@ func accessTokenCreateCmd(c *cli.Context) error { if err != nil { return err } - // If the username is provided as an argument, then it is used when creating the token. - // If not, then the configured username (or the value of the --user option) is used. - var userName string - if c.NArg() > 0 { - userName = c.Args().Get(0) - } else { - userName = serverDetails.GetUser() - } - expiry, err := cliutils.GetIntFlagValue(c, "expiry", cliutils.TokenExpiry) + + username := accesstoken.GetSubjectUsername(c, serverDetails) + expiry, err := cliutils.GetIntFlagValue(c, cliutils.Expiry, cliutils.ArtifactoryTokenExpiry) if err != nil { return err } accessTokenCreateCmd := generic.NewAccessTokenCreateCommand() - accessTokenCreateCmd.SetUserName(userName).SetServerDetails(serverDetails).SetRefreshable(c.Bool("refreshable")).SetExpiry(expiry).SetGroups(c.String("groups")).SetAudience(c.String("audience")).SetGrantAdmin(c.Bool("grant-admin")) + accessTokenCreateCmd.SetUserName(username).SetServerDetails(serverDetails). + SetRefreshable(c.Bool(cliutils.Refreshable)).SetExpiry(expiry).SetGroups(c.String(cliutils.Groups)). + SetAudience(c.String(cliutils.Audience)).SetGrantAdmin(c.Bool(cliutils.GrantAdmin)) err = commands.Exec(accessTokenCreateCmd) if err != nil { return err diff --git a/artifactory_test.go b/artifactory_test.go index 390961e66..4330130a7 100644 --- a/artifactory_test.go +++ b/artifactory_test.go @@ -1,6 +1,7 @@ package main import ( + "archive/zip" "bytes" "crypto/tls" "encoding/csv" @@ -4166,6 +4167,38 @@ func TestUploadDeploymentViewWithArchive(t *testing.T) { cleanArtifactoryTest() } +func TestUploadZipAndCheckDeploymentViewWithArchive(t *testing.T) { + initArtifactoryTest(t, "") + + // Create tmp dir + assert.NoError(t, os.Mkdir(tests.Out, 0755)) + wd, err := os.Getwd() + assert.NoError(t, err) + defer cleanArtifactoryTest() + chdirCallback := clientTestUtils.ChangeDirWithCallback(t, wd, tests.Out) + defer chdirCallback() + + // Create file and a zip + fileName := "dummy_file.txt" + zipName := "test.zip" + assert.NoError(t, os.WriteFile(fileName, nil, 0644)) + + // Upload & download zip file + assert.NoError(t, artifactoryCli.Exec("upload", fileName, path.Join(tests.RtRepo1, zipName), "--archive", "zip")) + assert.NoError(t, artifactoryCli.Exec("download", path.Join(tests.RtRepo1, zipName))) + + // Check for time-zone offset for each file in the zip + r, err := zip.OpenReader(zipName) + assert.NoError(t, err) + defer func() { assert.NoError(t, r.Close()) }() + _, sysTimezoneOffset := time.Now().Zone() + for _, file := range r.File { + _, fileTimezoneOffset := file.Modified.Zone() + assert.Equal(t, sysTimezoneOffset, fileTimezoneOffset) + } + +} + func TestUploadDetailedSummary(t *testing.T) { initArtifactoryTest(t, "") uploadCmd := generic.NewUploadCommand() @@ -5190,7 +5223,7 @@ func TestArtifactoryReplicationCreate(t *testing.T) { cleanArtifactoryTest() } -func TestAccessTokenCreate(t *testing.T) { +func TestArtifactoryAccessTokenCreate(t *testing.T) { initArtifactoryTest(t, "") buffer, _, previousLog := coretests.RedirectLogOutputToBuffer() @@ -5268,7 +5301,7 @@ func TestRefreshableArtifactoryTokens(t *testing.T) { assert.NotEmpty(t, curRefreshToken) // Make the token always refresh. - auth.RefreshBeforeExpiryMinutes = 60 + auth.RefreshArtifactoryTokenBeforeExpiryMinutes = 60 // Upload a file and assert tokens were refreshed. uploadedFiles++ @@ -5312,7 +5345,7 @@ func TestRefreshableArtifactoryTokens(t *testing.T) { } // Make the token not refresh. Verify Tokens did not refresh. - auth.RefreshBeforeExpiryMinutes = 0 + auth.RefreshArtifactoryTokenBeforeExpiryMinutes = 0 uploadedFiles++ err = uploadWithSpecificServerAndVerify(t, artifactoryCommandExecutor, "testdata/a/b/b2.in", uploadedFiles) if err != nil { @@ -5601,7 +5634,7 @@ func testProjectInit(t *testing.T, projectExampleName string, technology coreuti // Copy a simple project in a temp work dir tmpWorkDir, deleteWorkDir := coretests.CreateTempDirWithCallbackAndAssert(t) defer deleteWorkDir() - testdataSrc := filepath.Join(filepath.FromSlash(tests.GetTestResourcesPath()), technology.ToString(), projectExampleName) + testdataSrc := filepath.Join(filepath.FromSlash(tests.GetTestResourcesPath()), technology.String(), projectExampleName) err = biutils.CopyDir(testdataSrc, tmpWorkDir, true, nil) assert.NoError(t, err) if technology == coreutils.Go { @@ -5619,7 +5652,7 @@ func testProjectInit(t *testing.T, projectExampleName string, technology coreuti err = platformCli.WithoutCredentials().Exec("project", "init", "--path", tmpWorkDir, "--server-id="+tests.ServerId) assert.NoError(t, err) // Validate correctness of .jfrog/projects/$technology.yml - validateProjectYamlFile(t, tmpWorkDir, technology.ToString()) + validateProjectYamlFile(t, tmpWorkDir, technology.String()) // Validate correctness of .jfrog/projects/build.yml validateBuildYamlFile(t, tmpWorkDir) } diff --git a/build/build.sh b/build/build.sh index ea46362c1..0cc3e14eb 100755 --- a/build/build.sh +++ b/build/build.sh @@ -8,5 +8,5 @@ if [ $# -eq 0 ] exe_name="$1" fi -CGO_ENABLED=0 go build -o $exe_name -ldflags '-w -extldflags "-static"' main.go +CGO_ENABLED=0 go build -o "$exe_name" -ldflags '-w -extldflags "-static"' main.go echo "The $exe_name executable was successfully created." diff --git a/build/chocolatey/v2-jf/README.md b/build/chocolatey/v2-jf/README.md index c85de6a4c..bc20431eb 100644 --- a/build/chocolatey/v2-jf/README.md +++ b/build/chocolatey/v2-jf/README.md @@ -24,13 +24,13 @@ choco pack version= ``` This will create the file _build/chocolatey/jfrog-cli.\.nupkg which can be -installed with Chcolatey +installed with Chocolatey ```powershell choco install jfrog-cli..nupkg ``` -See Chocolatey's official documenattion [here](https://chocolatey.org/docs/create-packages) +See Chocolatey's official documentation [here](https://chocolatey.org/docs/create-packages) [choco-dockerfile-pr]: https://github.com/chocolatey/choco/pull/1153 [choco-dockerfile]: https://github.com/chocolatey/choco/tree/master/docker diff --git a/build/chocolatey/v2-jf/jfrog-cli-v2-jf.nuspec b/build/chocolatey/v2-jf/jfrog-cli-v2-jf.nuspec index 4810c3614..9fc7ad50e 100644 --- a/build/chocolatey/v2-jf/jfrog-cli-v2-jf.nuspec +++ b/build/chocolatey/v2-jf/jfrog-cli-v2-jf.nuspec @@ -8,7 +8,7 @@ JFrog CLI V2-jf JFrog Ltd JFrog Ltd - https://www.jfrog.com/confluence/display/CLI/JFrog+CLI + https://docs.jfrog-applications.jfrog.io/jfrog-applications/jfrog-cli https://github.com/jfrog/jfrog-cli/blob/v2/LICENSE false https://github.com/jfrog/jfrog-cli/issues diff --git a/build/chocolatey/v2/README.md b/build/chocolatey/v2/README.md index c85de6a4c..bc20431eb 100644 --- a/build/chocolatey/v2/README.md +++ b/build/chocolatey/v2/README.md @@ -24,13 +24,13 @@ choco pack version= ``` This will create the file _build/chocolatey/jfrog-cli.\.nupkg which can be -installed with Chcolatey +installed with Chocolatey ```powershell choco install jfrog-cli..nupkg ``` -See Chocolatey's official documenattion [here](https://chocolatey.org/docs/create-packages) +See Chocolatey's official documentation [here](https://chocolatey.org/docs/create-packages) [choco-dockerfile-pr]: https://github.com/chocolatey/choco/pull/1153 [choco-dockerfile]: https://github.com/chocolatey/choco/tree/master/docker diff --git a/build/chocolatey/v2/jfrog-cli.nuspec b/build/chocolatey/v2/jfrog-cli.nuspec index 022f60c36..d65e32bb4 100644 --- a/build/chocolatey/v2/jfrog-cli.nuspec +++ b/build/chocolatey/v2/jfrog-cli.nuspec @@ -8,7 +8,7 @@ JFrog CLI V2 JFrog Ltd JFrog Ltd - https://www.jfrog.com/confluence/display/CLI/JFrog+CLI + https://docs.jfrog-applications.jfrog.io/jfrog-applications/jfrog-cli https://github.com/jfrog/jfrog-cli/blob/v2/LICENSE false https://github.com/jfrog/jfrog-cli/issues diff --git a/build/deb_rpm/v2-jf/build-scripts/deb-install.sh b/build/deb_rpm/v2-jf/build-scripts/deb-install.sh index 6e4651be5..d3e1cd727 100644 --- a/build/deb_rpm/v2-jf/build-scripts/deb-install.sh +++ b/build/deb_rpm/v2-jf/build-scripts/deb-install.sh @@ -1,3 +1,5 @@ +#!/bin/bash + wget -qO - https://releases.jfrog.io/artifactory/api/gpg/key/public | apt-key add -; echo "deb https://releases.jfrog.io/artifactory/jfrog-debs xenial contrib" | sudo tee -a /etc/apt/sources.list; apt update; diff --git a/build/deb_rpm/v2-jf/build-scripts/pack.sh b/build/deb_rpm/v2-jf/build-scripts/pack.sh index 6d258a119..7a02590aa 100755 --- a/build/deb_rpm/v2-jf/build-scripts/pack.sh +++ b/build/deb_rpm/v2-jf/build-scripts/pack.sh @@ -46,12 +46,11 @@ errorExit() { } checkDockerAccess() { - log "Checking docker" "DEBUG" - docker -v > /dev/null 2>&1 && docker ps > /dev/null 2>&1 - if [ $? -ne 0 ]; then - errorExit "Must run as user that can execute docker commands" + if docker -v > /dev/null 2>&1 && docker ps > /dev/null 2>&1; then + log "Docker is available" "DEBUG" + else + errorExit "Must run as a user that can execute docker commands" fi - log "Docker is avaiable" "DEBUG" } exitWithUsage(){ @@ -64,7 +63,7 @@ createDEBPackage(){ local flavour="deb" # cleanup old files and containers - rm -f ${JFROG_CLI_PKG}/${JFROG_CLI_PREFIX}*${VERSION_FORMATTED}*.${flavour} + rm -f "${JFROG_CLI_PKG}/${JFROG_CLI_PREFIX}*${VERSION_FORMATTED}*.${flavour}" docker rm -f "${RPM_BUILDER_NAME}" 2>/dev/null log "Building ${JFROG_CLI_PREFIX} ${flavour} ${JFROG_CLI_VERSION} on ${DEB_BUILD_IMAGE} image" @@ -72,7 +71,7 @@ createDEBPackage(){ docker run -t --rm -v "${JFROG_CLI_HOME}/${flavour}":${DEB_IMAGE_ROOT_DIR}/src \ -v "${JFROG_CLI_PKG}":${DEB_IMAGE_ROOT_DIR}/pkg \ --name ${DEB_BUILDER_NAME} \ - ${DEB_BUILD_IMAGE} bash -c "\ + "${DEB_BUILD_IMAGE}" bash -c "\ \ echo '' && echo '' && \ apt-get update && \ @@ -107,7 +106,7 @@ createRPMPackage(){ local flavour="rpm" # cleanup old files and containers - rm -f ${JFROG_CLI_PKG}/${JFROG_CLI_PREFIX}*${VERSION_FORMATTED}*.${flavour} + rm -f "${JFROG_CLI_PKG}/${JFROG_CLI_PREFIX}*${VERSION_FORMATTED}*.${flavour}" docker rm -f "${RPM_BUILDER_NAME}" 2>/dev/null log "Building ${JFROG_CLI_PREFIX} ${flavour} ${JFROG_CLI_VERSION} on ${RPM_BUILD_IMAGE} image" @@ -115,7 +114,7 @@ createRPMPackage(){ docker run -t --rm -v "${JFROG_CLI_HOME}/${flavour}":${RPM_IMAGE_ROOT_DIR}/src \ -v "${JFROG_CLI_PKG}":${RPM_IMAGE_ROOT_DIR}/pkg \ --name ${RPM_BUILDER_NAME} \ - ${RPM_BUILD_IMAGE} bash -c "\ + "${RPM_BUILD_IMAGE}" bash -c "\ echo '' && echo '' && \ yum install -y ${RPM_DEPS} && \ echo '' && echo '' && \ @@ -159,10 +158,10 @@ rpmSign()( if [[ -f "${filePath}" && -f "${gpgFileInHost}" ]]; then log ""; log ""; log "Initiating rpm sign on ${filePath}..." - docker run --rm --name cli-rpm-sign -v "${filePath}":${filePathInImage} \ + docker run --rm --name cli-rpm-sign -v "${filePath}:${filePathInImage}" \ -v "${gpgFileInHost}":"${gpgFileInImage}" \ -v "${JFROG_CLI_HOME}/build-scripts":${RPM_IMAGE_ROOT_DIR}/src \ - ${RPM_SIGN_IMAGE} \ + "${RPM_SIGN_IMAGE}" \ bash -c "yum install -y expect rpm-sign pinentry && \ ${RPM_IMAGE_ROOT_DIR}/src/${rpmSignScript} \"${gpgFileInImage}\" \"${keYID}\" \"${passphrase}\" \"${filePathInImage}\" \ && exit 0 || exit 1" \ @@ -178,7 +177,7 @@ rpmSign()( runTests()( local flavour=$1 - [ ! -z "${flavour}" ] || { echo "Flavour is mandatory to run tests"; exit 1; } + [ -n "${flavour}" ] || { echo "Flavour is mandatory to run tests"; exit 1; } local fileName="${JFROG_CLI_PREFIX}-${VERSION_FORMATTED}.${flavour}" local filePath="${JFROG_CLI_PKG}"/${fileName} @@ -199,7 +198,7 @@ runTests()( if [ -f "${filePath}" ]; then log ""; log ""; log "Testing ${filePath} on ${testImage}..." - docker run --rm --name cli-test -v "${filePath}":${filePathInImage} ${testImage} \ + docker run --rm --name cli-test -v "${filePath}:${filePathInImage}" "${testImage}" \ bash -c "${installCommand} && jf -version | grep ${JFROG_CLI_VERSION} && \ ${signatureTestCommand} && exit 0 || exit 1" \ || { echo "ERROR: ############### Test failed! ###################"; exit 1; } @@ -214,28 +213,28 @@ runTests()( getArch(){ local image=$1 - [ ! -z "$image" ] || return 0; + [ -n "$image" ] || return 0; - docker run --rm ${image} bash -c "uname -m 2>/dev/null" 2>/dev/null + docker run --rm "${image}" bash -c "uname -m 2>/dev/null" 2>/dev/null } createPackage(){ local flavour=$1 - [ ! -z "${flavour}" ] || errorExit "Flavour is not passed to createPackage method" + [ -n "${flavour}" ] || errorExit "Flavour is not passed to createPackage method" - cp -f "${JFROG_CLI_BINARY}" "${JFROG_CLI_HOME}"/${flavour}/jf \ + cp -f "${JFROG_CLI_BINARY}" "${JFROG_CLI_HOME}"/"${flavour}"/jf \ || errorExit "Failed to copy ${JFROG_CLI_BINARY} to ${JFROG_CLI_HOME}/${flavour}/jf" case "$flavour" in rpm) - [ ! -z "${JFROG_CLI_RPM_ARCH}" ] || JFROG_CLI_RPM_ARCH=$(getArch "${RPM_BUILD_IMAGE}") + [ -n "${JFROG_CLI_RPM_ARCH}" ] || JFROG_CLI_RPM_ARCH=$(getArch "${RPM_BUILD_IMAGE}") VERSION_FORMATTED="${JFROG_CLI_VERSION}.${JFROG_CLI_RPM_ARCH}" createRPMPackage ;; deb) - [ ! -z "${JFROG_CLI_DEB_ARCH}" ] || JFROG_CLI_DEB_ARCH=$(getArch "${DEB_BUILD_IMAGE}") + [ -n "${JFROG_CLI_DEB_ARCH}" ] || JFROG_CLI_DEB_ARCH=$(getArch "${DEB_BUILD_IMAGE}") VERSION_FORMATTED="${JFROG_CLI_VERSION}.${JFROG_CLI_DEB_ARCH}" createDEBPackage ;; @@ -248,7 +247,7 @@ createPackage(){ setBuildImage(){ local arch="$1" - [ ! -z "${arch}" ] || errorExit "Architecture is not passed to setBuildImage method" + [ -n "${arch}" ] || errorExit "Architecture is not passed to setBuildImage method" case "$1" in x86_64) @@ -262,7 +261,7 @@ setBuildImage(){ } main(){ - while [[ $# > 0 ]]; do + while [[ $# -gt 0 ]]; do case "$1" in -f | --flavour) flavours="$2" @@ -323,21 +322,21 @@ main(){ esac done - : ${flavours:="rpm deb"} - : ${JFROG_CLI_RUN_TEST:="false"} - : ${RPM_BUILD_IMAGE:="centos:8"} - : ${RPM_SIGN_IMAGE:="centos:7"} - : ${DEB_BUILD_IMAGE:="ubuntu:16.04"} - : ${DEB_TEST_IMAGE:="${DEB_BUILD_IMAGE}"} - : ${RPM_TEST_IMAGE:="${RPM_BUILD_IMAGE}"} - : ${JFROG_CLI_RELEASE_VERSION:="1"} - : ${RPM_SIGN_PASSPHRASE:="$(cat $RPM_SIGN_PASSPHRASE_FILE)"} - : ${RPM_SIGN_KEY_ID:="JFrog Inc."} - : ${RPM_SIGN_KEY_NAME:="RPM-GPG-KEY-jfrog-cli"} - - [ ! -z "${JFROG_CLI_BINARY}" ] || exitWithUsage "jfrog cli binary is not passed" + : "${flavours:="rpm deb"}" + : "${JFROG_CLI_RUN_TEST:="false"}" + : "${RPM_BUILD_IMAGE:="centos:8"}" + : "${RPM_SIGN_IMAGE:="centos:7"}" + : "${DEB_BUILD_IMAGE:="ubuntu:16.04"}" + : "${DEB_TEST_IMAGE:="${DEB_BUILD_IMAGE}"}" + : "${RPM_TEST_IMAGE:="${RPM_BUILD_IMAGE}"}" + : "${JFROG_CLI_RELEASE_VERSION:="1"}" + : "${RPM_SIGN_PASSPHRASE:=$(cat "$RPM_SIGN_PASSPHRASE_FILE")}" + : "${RPM_SIGN_KEY_ID:="JFrog Inc."}" + : "${RPM_SIGN_KEY_NAME:="RPM-GPG-KEY-jfrog-cli"}" + + [ -n "${JFROG_CLI_BINARY}" ] || exitWithUsage "jfrog cli binary is not passed" [ -f "$JFROG_CLI_BINARY" ] || exitWithUsage "jfrog cli binary is not available at $JFROG_CLI_BINARY" - [ ! -z "${JFROG_CLI_VERSION}" ] || exitWithUsage "version is not passed, pass the version to be built" + [ -n "${JFROG_CLI_VERSION}" ] || exitWithUsage "version is not passed, pass the version to be built" if [[ "$flavours" == *"rpm"* ]] && [[ -z "${RPM_SIGN_PASSPHRASE}" || "${RPM_SIGN_PASSPHRASE}" == "" ]]; then echo "ERROR: RPM_SIGN_PASSPHRASE environment variable is not set" @@ -351,8 +350,13 @@ main(){ for flavour in $flavours; do createPackage "$flavour" - [[ "${flavour}" == "rpm" ]] && rpmSign || true - [[ "${JFROG_CLI_RUN_TEST}" == "true" ]] && runTests "${flavour}" || true + if [[ "${flavour}" == "rpm" ]]; then + rpmSign + fi + + if [[ "${JFROG_CLI_RUN_TEST}" == "true" ]]; then + runTests "${flavour}" + fi done log "...and Done!" diff --git a/build/deb_rpm/v2-jf/build-scripts/rpm-install.sh b/build/deb_rpm/v2-jf/build-scripts/rpm-install.sh index 4b42e5864..6eaceadb5 100644 --- a/build/deb_rpm/v2-jf/build-scripts/rpm-install.sh +++ b/build/deb_rpm/v2-jf/build-scripts/rpm-install.sh @@ -1,7 +1,8 @@ -echo "[jfrog-cli]" > jfrog-cli.repo; -echo "name=jfrog-cli" >> jfrog-cli.repo; -echo "baseurl=https://releases.jfrog.io/artifactory/jfrog-rpms" >> jfrog-cli.repo; -echo "enabled=1" >> jfrog-cli.repo; -echo "gpgcheck=0" >> jfrog-cli.repo; -sudo mv jfrog-cli.repo /etc/yum.repos.d/; +#!/bin/bash + +echo -e "[jfrog-cli] +name=jfrog-cli +baseurl=https://releases.jfrog.io/artifactory/jfrog-rpms +enabled=1 +gpgcheck=0" | sudo tee /etc/yum.repos.d/jfrog-cli.repo >/dev/null yum install -y jfrog-cli-v2-jf; diff --git a/build/deb_rpm/v2-jf/build-scripts/rpm-sign.sh b/build/deb_rpm/v2-jf/build-scripts/rpm-sign.sh index 5a94ce9fe..f76d1d0c7 100755 --- a/build/deb_rpm/v2-jf/build-scripts/rpm-sign.sh +++ b/build/deb_rpm/v2-jf/build-scripts/rpm-sign.sh @@ -23,11 +23,14 @@ rpmInitSigning(){ log "Initializing rpm sign..." - gpg --allow-secret-key-import --import ${gpgKeyFile} && \ + gpg --allow-secret-key-import --import "${gpgKeyFile}" && \ gpg --export -a "${keyID}" > /tmp/tmpFile && \ - rpm --import /tmp/tmpFile && \ - rpm -q gpg-pubkey --qf '%{name}-%{version}-%{release} --> %{summary}\n' | grep "${keyID}" || \ - { echo "ERROR: RPM signature initialization failed!" >&2; exit 1; } + if rpm --import /tmp/tmpFile && rpm -q gpg-pubkey --qf '%{name}-%{version}-%{release} --> %{summary}\n' | grep "${keyID}"; then + echo "RPM signature initialization succeeded." + else + echo "ERROR: RPM signature initialization failed!" >&2 + exit 1 + fi rpmEditRpmMacro "${keyID}" || \ { echo "ERROR: Configuring rpm macro failed!" >&2; exit 1; } diff --git a/build/deb_rpm/v2-jf/deb/debian/control b/build/deb_rpm/v2-jf/deb/debian/control index 06b29a49f..8e2d15591 100644 --- a/build/deb_rpm/v2-jf/deb/debian/control +++ b/build/deb_rpm/v2-jf/deb/debian/control @@ -4,7 +4,7 @@ Priority: optional Maintainer: Jfrog Build-Depends: debhelper (>=8~) Standards-Version: 3.9.7 -Homepage: https://www.jfrog.com/confluence/display/CLI/JFrog+CLI +Homepage: https://docs.jfrog-applications.jfrog.io/jfrog-applications/jfrog-cli Package: jfrog-cli-v2-jf Architecture: any diff --git a/build/deb_rpm/v2/build-scripts/deb-install.sh b/build/deb_rpm/v2/build-scripts/deb-install.sh index 3540fc6ca..20253e4ff 100644 --- a/build/deb_rpm/v2/build-scripts/deb-install.sh +++ b/build/deb_rpm/v2/build-scripts/deb-install.sh @@ -1,3 +1,5 @@ +#!/bin/bash + wget -qO - https://releases.jfrog.io/artifactory/api/gpg/key/public | apt-key add -; echo "deb https://releases.jfrog.io/artifactory/jfrog-debs xenial contrib" | sudo tee -a /etc/apt/sources.list; apt update; diff --git a/build/deb_rpm/v2/build-scripts/pack.sh b/build/deb_rpm/v2/build-scripts/pack.sh index b47269c7a..90e904962 100755 --- a/build/deb_rpm/v2/build-scripts/pack.sh +++ b/build/deb_rpm/v2/build-scripts/pack.sh @@ -46,12 +46,11 @@ errorExit() { } checkDockerAccess() { - log "Checking docker" "DEBUG" - docker -v > /dev/null 2>&1 && docker ps > /dev/null 2>&1 - if [ $? -ne 0 ]; then - errorExit "Must run as user that can execute docker commands" - fi - log "Docker is avaiable" "DEBUG" +if docker -v > /dev/null 2>&1 && docker ps > /dev/null 2>&1; then + log "Docker is available" "DEBUG" +else + errorExit "Must run as a user that can execute docker commands" +fi } exitWithUsage(){ @@ -64,7 +63,7 @@ createDEBPackage(){ local flavour="deb" # cleanup old files and containers - rm -f ${JFROG_CLI_PKG}/${JFROG_CLI_PREFIX}*${VERSION_FORMATTED}*.${flavour} + rm -f "${JFROG_CLI_PKG}/${JFROG_CLI_PREFIX}*${VERSION_FORMATTED}*.${flavour}" docker rm -f "${RPM_BUILDER_NAME}" 2>/dev/null log "Building ${JFROG_CLI_PREFIX} ${flavour} ${JFROG_CLI_VERSION} on ${DEB_BUILD_IMAGE} image" @@ -72,7 +71,7 @@ createDEBPackage(){ docker run -t --rm -v "${JFROG_CLI_HOME}/${flavour}":${DEB_IMAGE_ROOT_DIR}/src \ -v "${JFROG_CLI_PKG}":${DEB_IMAGE_ROOT_DIR}/pkg \ --name ${DEB_BUILDER_NAME} \ - ${DEB_BUILD_IMAGE} bash -c "\ + "${DEB_BUILD_IMAGE}" bash -c "\ \ echo '' && echo '' && \ apt-get update && \ @@ -107,7 +106,7 @@ createRPMPackage(){ local flavour="rpm" # cleanup old files and containers - rm -f ${JFROG_CLI_PKG}/${JFROG_CLI_PREFIX}*${VERSION_FORMATTED}*.${flavour} + rm -f "${JFROG_CLI_PKG}/${JFROG_CLI_PREFIX}*${VERSION_FORMATTED}*.${flavour}" docker rm -f "${RPM_BUILDER_NAME}" 2>/dev/null log "Building ${JFROG_CLI_PREFIX} ${flavour} ${JFROG_CLI_VERSION} on ${RPM_BUILD_IMAGE} image" @@ -115,7 +114,7 @@ createRPMPackage(){ docker run -t --rm -v "${JFROG_CLI_HOME}/${flavour}":${RPM_IMAGE_ROOT_DIR}/src \ -v "${JFROG_CLI_PKG}":${RPM_IMAGE_ROOT_DIR}/pkg \ --name ${RPM_BUILDER_NAME} \ - ${RPM_BUILD_IMAGE} bash -c "\ + "${RPM_BUILD_IMAGE}" bash -c "\ echo '' && echo '' && \ yum install -y ${RPM_DEPS} && \ echo '' && echo '' && \ @@ -159,10 +158,10 @@ rpmSign()( if [[ -f "${filePath}" && -f "${gpgFileInHost}" ]]; then log ""; log ""; log "Initiating rpm sign on ${filePath}..." - docker run --rm --name cli-rpm-sign -v "${filePath}":${filePathInImage} \ + docker run --rm --name cli-rpm-sign -v "${filePath}:${filePathInImage}" \ -v "${gpgFileInHost}":"${gpgFileInImage}" \ -v "${JFROG_CLI_HOME}/build-scripts":${RPM_IMAGE_ROOT_DIR}/src \ - ${RPM_SIGN_IMAGE} \ + "${RPM_SIGN_IMAGE}" \ bash -c "yum install -y expect rpm-sign pinentry && \ ${RPM_IMAGE_ROOT_DIR}/src/${rpmSignScript} \"${gpgFileInImage}\" \"${keYID}\" \"${passphrase}\" \"${filePathInImage}\" \ && exit 0 || exit 1" \ @@ -178,7 +177,7 @@ rpmSign()( runTests()( local flavour=$1 - [ ! -z "${flavour}" ] || { echo "Flavour is mandatory to run tests"; exit 1; } + [ -n "${flavour}" ] || { echo "Flavour is mandatory to run tests"; exit 1; } local fileName="${JFROG_CLI_PREFIX}-${VERSION_FORMATTED}.${flavour}" local filePath="${JFROG_CLI_PKG}"/${fileName} @@ -199,7 +198,7 @@ runTests()( if [ -f "${filePath}" ]; then log ""; log ""; log "Testing ${filePath} on ${testImage}..." - docker run --rm --name cli-test -v "${filePath}":${filePathInImage} ${testImage} \ + docker run --rm --name cli-test -v "${filePath}:${filePathInImage}" "${testImage}" \ bash -c "${installCommand} && jfrog -version | grep ${JFROG_CLI_VERSION} && \ ${signatureTestCommand} && exit 0 || exit 1" \ || { echo "ERROR: ############### Test failed! ###################"; exit 1; } @@ -214,28 +213,28 @@ runTests()( getArch(){ local image=$1 - [ ! -z "$image" ] || return 0; + [ -n "$image" ] || return 0; - docker run --rm ${image} bash -c "uname -m 2>/dev/null" 2>/dev/null + docker run --rm "${image}" bash -c "uname -m 2>/dev/null" 2>/dev/null } createPackage(){ local flavour=$1 - [ ! -z "${flavour}" ] || errorExit "Flavour is not passed to createPackage method" + [ -n "${flavour}" ] || errorExit "Flavour is not passed to createPackage method" - cp -f "${JFROG_CLI_BINARY}" "${JFROG_CLI_HOME}"/${flavour}/jfrog \ + cp -f "${JFROG_CLI_BINARY}" "${JFROG_CLI_HOME}"/"${flavour}"/jfrog \ || errorExit "Failed to copy ${JFROG_CLI_BINARY} to ${JFROG_CLI_HOME}/${flavour}/jfrog" case "$flavour" in rpm) - [ ! -z "${JFROG_CLI_RPM_ARCH}" ] || JFROG_CLI_RPM_ARCH=$(getArch "${RPM_BUILD_IMAGE}") + [ -n "${JFROG_CLI_RPM_ARCH}" ] || JFROG_CLI_RPM_ARCH=$(getArch "${RPM_BUILD_IMAGE}") VERSION_FORMATTED="${JFROG_CLI_VERSION}.${JFROG_CLI_RPM_ARCH}" createRPMPackage ;; deb) - [ ! -z "${JFROG_CLI_DEB_ARCH}" ] || JFROG_CLI_DEB_ARCH=$(getArch "${DEB_BUILD_IMAGE}") + [ -n "${JFROG_CLI_DEB_ARCH}" ] || JFROG_CLI_DEB_ARCH=$(getArch "${DEB_BUILD_IMAGE}") VERSION_FORMATTED="${JFROG_CLI_VERSION}.${JFROG_CLI_DEB_ARCH}" createDEBPackage ;; @@ -248,7 +247,7 @@ createPackage(){ setBuildImage(){ local arch="$1" - [ ! -z "${arch}" ] || errorExit "Architecture is not passed to setBuildImage method" + [ -n "${arch}" ] || errorExit "Architecture is not passed to setBuildImage method" case "$1" in x86_64) @@ -262,7 +261,7 @@ setBuildImage(){ } main(){ - while [[ $# > 0 ]]; do + while [[ $# -gt 0 ]]; do case "$1" in -f | --flavour) flavours="$2" @@ -323,21 +322,21 @@ main(){ esac done - : ${flavours:="rpm deb"} - : ${JFROG_CLI_RUN_TEST:="false"} - : ${RPM_BUILD_IMAGE:="centos:8"} - : ${RPM_SIGN_IMAGE:="centos:7"} - : ${DEB_BUILD_IMAGE:="ubuntu:16.04"} - : ${DEB_TEST_IMAGE:="${DEB_BUILD_IMAGE}"} - : ${RPM_TEST_IMAGE:="${RPM_BUILD_IMAGE}"} - : ${JFROG_CLI_RELEASE_VERSION:="1"} - : ${RPM_SIGN_PASSPHRASE:="$(cat $RPM_SIGN_PASSPHRASE_FILE)"} - : ${RPM_SIGN_KEY_ID:="JFrog Inc."} - : ${RPM_SIGN_KEY_NAME:="RPM-GPG-KEY-jfrog-cli"} - - [ ! -z "${JFROG_CLI_BINARY}" ] || exitWithUsage "jfrog cli binary is not passed" + : "${flavours:="rpm deb"}" + : "${JFROG_CLI_RUN_TEST:="false"}" + : "${RPM_BUILD_IMAGE:="centos:8"}" + : "${RPM_SIGN_IMAGE:="centos:7"}" + : "${DEB_BUILD_IMAGE:="ubuntu:16.04"}" + : "${DEB_TEST_IMAGE:="${DEB_BUILD_IMAGE}"}" + : "${RPM_TEST_IMAGE:="${RPM_BUILD_IMAGE}"}" + : "${JFROG_CLI_RELEASE_VERSION:="1"}" + : "${RPM_SIGN_PASSPHRASE:=$(cat "$RPM_SIGN_PASSPHRASE_FILE")}" + : "${RPM_SIGN_KEY_ID:="JFrog Inc."}" + : "${RPM_SIGN_KEY_NAME:="RPM-GPG-KEY-jfrog-cli"}" + + [ -n "${JFROG_CLI_BINARY}" ] || exitWithUsage "jfrog cli binary is not passed" [ -f "$JFROG_CLI_BINARY" ] || exitWithUsage "jfrog cli binary is not available at $JFROG_CLI_BINARY" - [ ! -z "${JFROG_CLI_VERSION}" ] || exitWithUsage "version is not passed, pass the version to be built" + [ -n "${JFROG_CLI_VERSION}" ] || exitWithUsage "version is not passed, pass the version to be built" if [[ "$flavours" == *"rpm"* ]] && [[ -z "${RPM_SIGN_PASSPHRASE}" || "${RPM_SIGN_PASSPHRASE}" == "" ]]; then echo "ERROR: RPM_SIGN_PASSPHRASE environment variable is not set" @@ -351,8 +350,13 @@ main(){ for flavour in $flavours; do createPackage "$flavour" - [[ "${flavour}" == "rpm" ]] && rpmSign || true - [[ "${JFROG_CLI_RUN_TEST}" == "true" ]] && runTests "${flavour}" || true + if [[ "${flavour}" == "rpm" ]]; then + rpmSign + fi + + if [[ "${JFROG_CLI_RUN_TEST}" == "true" ]]; then + runTests "${flavour}" + fi done log "...and Done!" diff --git a/build/deb_rpm/v2/build-scripts/rpm-install.sh b/build/deb_rpm/v2/build-scripts/rpm-install.sh index 98849e879..5ac712e88 100644 --- a/build/deb_rpm/v2/build-scripts/rpm-install.sh +++ b/build/deb_rpm/v2/build-scripts/rpm-install.sh @@ -1,8 +1,9 @@ -echo "[jfrog-cli]" > jfrog-cli.repo; -echo "name=jfrog-cli" >> jfrog-cli.repo; -echo "baseurl=https://releases.jfrog.io/artifactory/jfrog-rpms" >> jfrog-cli.repo; -echo "enabled=1" >> jfrog-cli.repo; -echo "gpgcheck=0" >> jfrog-cli.repo; -sudo mv jfrog-cli.repo /etc/yum.repos.d/; +#!/bin/bash + +echo -e "[jfrog-cli] +name=jfrog-cli +baseurl=https://releases.jfrog.io/artifactory/jfrog-rpms +enabled=1 +gpgcheck=0" | sudo tee /etc/yum.repos.d/jfrog-cli.repo >/dev/null yum install -y jfrog-cli-v2; jf intro; diff --git a/build/deb_rpm/v2/build-scripts/rpm-sign.sh b/build/deb_rpm/v2/build-scripts/rpm-sign.sh index 5a94ce9fe..f76d1d0c7 100755 --- a/build/deb_rpm/v2/build-scripts/rpm-sign.sh +++ b/build/deb_rpm/v2/build-scripts/rpm-sign.sh @@ -23,11 +23,14 @@ rpmInitSigning(){ log "Initializing rpm sign..." - gpg --allow-secret-key-import --import ${gpgKeyFile} && \ + gpg --allow-secret-key-import --import "${gpgKeyFile}" && \ gpg --export -a "${keyID}" > /tmp/tmpFile && \ - rpm --import /tmp/tmpFile && \ - rpm -q gpg-pubkey --qf '%{name}-%{version}-%{release} --> %{summary}\n' | grep "${keyID}" || \ - { echo "ERROR: RPM signature initialization failed!" >&2; exit 1; } + if rpm --import /tmp/tmpFile && rpm -q gpg-pubkey --qf '%{name}-%{version}-%{release} --> %{summary}\n' | grep "${keyID}"; then + echo "RPM signature initialization succeeded." + else + echo "ERROR: RPM signature initialization failed!" >&2 + exit 1 + fi rpmEditRpmMacro "${keyID}" || \ { echo "ERROR: Configuring rpm macro failed!" >&2; exit 1; } diff --git a/build/deb_rpm/v2/deb/debian/control b/build/deb_rpm/v2/deb/debian/control index d93140e44..4c9f43f08 100644 --- a/build/deb_rpm/v2/deb/debian/control +++ b/build/deb_rpm/v2/deb/debian/control @@ -4,7 +4,7 @@ Priority: optional Maintainer: Jfrog Build-Depends: debhelper (>=8~) Standards-Version: 3.9.7 -Homepage: https://www.jfrog.com/confluence/display/CLI/JFrog+CLI +Homepage: https://docs.jfrog-applications.jfrog.io/jfrog-applications/jfrog-cli Package: jfrog-cli-v2 Architecture: any diff --git a/build/docker/slim/Dockerfile b/build/docker/slim/Dockerfile index ae69bd4c7..43e2ae71b 100644 --- a/build/docker/slim/Dockerfile +++ b/build/docker/slim/Dockerfile @@ -11,6 +11,6 @@ FROM ${repo_name_21}/jfrog-docker/alpine:3 ARG image_name=jfrog-cli ARG cli_executable_name ENV CI true -RUN apk add --no-cache bash tzdata ca-certificates curl +RUN apk add --no-cache bash tzdata ca-certificates curl jq COPY --from=builder /${image_name}/${cli_executable_name} /usr/local/bin/${cli_executable_name} RUN chmod +x /usr/local/bin/${cli_executable_name} diff --git a/build/getcli/jf.sh b/build/getcli/jf.sh index 177e476c8..7b1f5eeff 100644 --- a/build/getcli/jf.sh +++ b/build/getcli/jf.sh @@ -1,7 +1,6 @@ #!/bin/bash CLI_OS="na" -CLI_UNAME="na" CLI_MAJOR_VER="v2-jf" VERSION="[RELEASE]" @@ -13,11 +12,11 @@ else echo "Downloading the latest version of JFrog CLI..." fi -if $(echo "${OSTYPE}" | grep -q msys); then +if echo "${OSTYPE}" | grep -q msys; then CLI_OS="windows" URL="https://releases.jfrog.io/artifactory/jfrog-cli/${CLI_MAJOR_VER}/${VERSION}/jfrog-cli-windows-amd64/jf.exe" FILE_NAME="jf.exe" -elif $(echo "${OSTYPE}" | grep -q darwin); then +elif echo "${OSTYPE}" | grep -q darwin; then CLI_OS="mac" if [[ $(uname -m) == 'arm64' ]]; then URL="https://releases.jfrog.io/artifactory/jfrog-cli/${CLI_MAJOR_VER}/${VERSION}/jfrog-cli-mac-arm64/jf" @@ -52,7 +51,7 @@ else ;; *) echo "Unknown machine type: $MACHINE_TYPE" - exit -1 + exit 1 ;; esac URL="https://releases.jfrog.io/artifactory/jfrog-cli/${CLI_MAJOR_VER}/${VERSION}/jfrog-cli-${CLI_OS}-${ARCH}/jf" diff --git a/build/getcli/jfrog.sh b/build/getcli/jfrog.sh index 6f09e17a7..1debb922e 100644 --- a/build/getcli/jfrog.sh +++ b/build/getcli/jfrog.sh @@ -1,7 +1,6 @@ #!/bin/bash CLI_OS="na" -CLI_UNAME="na" CLI_MAJOR_VER="v2" VERSION="[RELEASE]" @@ -13,11 +12,11 @@ else echo "Downloading the latest version of JFrog CLI..." fi -if $(echo "${OSTYPE}" | grep -q msys); then +if echo "${OSTYPE}" | grep -q msys; then CLI_OS="windows" URL="https://releases.jfrog.io/artifactory/jfrog-cli/${CLI_MAJOR_VER}/${VERSION}/jfrog-cli-windows-amd64/jfrog.exe" FILE_NAME="jfrog.exe" -elif $(echo "${OSTYPE}" | grep -q darwin); then +elif echo "${OSTYPE}" | grep -q darwin; then CLI_OS="mac" if [[ $(uname -m) == 'arm64' ]]; then URL="https://releases.jfrog.io/artifactory/jfrog-cli/${CLI_MAJOR_VER}/${VERSION}/jfrog-cli-mac-arm64/jfrog" @@ -52,7 +51,7 @@ else ;; *) echo "Unknown machine type: $MACHINE_TYPE" - exit -1 + exit 1 ;; esac URL="https://releases.jfrog.io/artifactory/jfrog-cli/${CLI_MAJOR_VER}/${VERSION}/jfrog-cli-${CLI_OS}-${ARCH}/jfrog" diff --git a/build/gitlab/v2/.setup-jfrog-unix.yml b/build/gitlab/v2/.setup-jfrog-unix.yml index cbef2bb81..4f8f78cfa 100644 --- a/build/gitlab/v2/.setup-jfrog-unix.yml +++ b/build/gitlab/v2/.setup-jfrog-unix.yml @@ -39,7 +39,7 @@ # Build the URL to the executable matching the OS. - | - if $(echo "${OSTYPE}" | grep -q darwin); then + if echo "${OSTYPE}" | grep -q darwin; then CLI_OS="mac" if [[ $(uname -m) == 'arm64' ]]; then URL="${BASE_URL}/jfrog-cli/v2-jf/${VERSION}/jfrog-cli-mac-arm64/jf" diff --git a/build/installcli/jf.sh b/build/installcli/jf.sh index 134359c9b..4619708d4 100755 --- a/build/installcli/jf.sh +++ b/build/installcli/jf.sh @@ -1,11 +1,8 @@ #!/bin/bash CLI_OS="na" -CLI_UNAME="na" CLI_MAJOR_VER="v2-jf" VERSION="[RELEASE]" -# Order is by destination priority. -DESTINATION_PATHS="/usr/local/bin /usr/bin /opt/bin" if [ $# -eq 1 ] then @@ -16,11 +13,11 @@ else fi echo "" -if $(echo "${OSTYPE}" | grep -q msys); then +if echo "${OSTYPE}" | grep -q msys; then CLI_OS="windows" URL="https://releases.jfrog.io/artifactory/jfrog-cli/${CLI_MAJOR_VER}/${VERSION}/jfrog-cli-windows-amd64/jf.exe" FILE_NAME="jf.exe" -elif $(echo "${OSTYPE}" | grep -q darwin); then +elif echo "${OSTYPE}" | grep -q darwin; then CLI_OS="mac" if [[ $(uname -m) == 'arm64' ]]; then URL="https://releases.jfrog.io/artifactory/jfrog-cli/${CLI_MAJOR_VER}/${VERSION}/jfrog-cli-mac-arm64/jf" @@ -55,7 +52,7 @@ else ;; *) echo "Unknown machine type: $MACHINE_TYPE" - exit -1 + exit 1 ;; esac URL="https://releases.jfrog.io/artifactory/jfrog-cli/${CLI_MAJOR_VER}/${VERSION}/jfrog-cli-${CLI_OS}-${ARCH}/jf" @@ -66,13 +63,12 @@ curl -XGET "$URL" -L -k -g > $FILE_NAME chmod u+x $FILE_NAME # Move executable to a destination in path. -set -- $DESTINATION_PATHS +# Order is by destination priority. +set -- "/usr/local/bin" "/usr/bin" "/opt/bin" while [ -n "$1" ]; do # Check if destination is in path. - if echo $PATH|grep "$1" -> /dev/null ; then - mv $FILE_NAME $1 - if [ "$?" -eq "0" ] - then + if echo "$PATH"|grep "$1" -> /dev/null ; then + if mv $FILE_NAME "$1" ; then echo "" echo "The $FILE_NAME executable was installed in $1" jf intro @@ -80,9 +76,7 @@ while [ -n "$1" ]; do else echo "" echo "We'd like to install the JFrog CLI executable in $1. Please approve this installation by entering your password." - sudo mv $FILE_NAME $1 - if [ "$?" -eq "0" ] - then + if sudo mv $FILE_NAME "$1" ; then echo "" echo "The $FILE_NAME executable was installed in $1" jf intro diff --git a/build/installcli/jfrog.sh b/build/installcli/jfrog.sh index a8359af1e..80ec7c3f3 100755 --- a/build/installcli/jfrog.sh +++ b/build/installcli/jfrog.sh @@ -1,11 +1,8 @@ #!/bin/bash CLI_OS="na" -CLI_UNAME="na" CLI_MAJOR_VER="v2" VERSION="[RELEASE]" -# Order is by destination priority. -DESTINATION_PATHS="/usr/local/bin /usr/bin /opt/bin" if [ $# -eq 1 ] then @@ -16,11 +13,11 @@ else fi echo "" -if $(echo "${OSTYPE}" | grep -q msys); then +if echo "${OSTYPE}" | grep -q msys; then CLI_OS="windows" URL="https://releases.jfrog.io/artifactory/jfrog-cli/${CLI_MAJOR_VER}/${VERSION}/jfrog-cli-windows-amd64/jfrog.exe" FILE_NAME="jfrog.exe" -elif $(echo "${OSTYPE}" | grep -q darwin); then +elif echo "${OSTYPE}" | grep -q darwin; then CLI_OS="mac" if [[ $(uname -m) == 'arm64' ]]; then URL="https://releases.jfrog.io/artifactory/jfrog-cli/${CLI_MAJOR_VER}/${VERSION}/jfrog-cli-mac-arm64/jfrog" @@ -55,7 +52,7 @@ else ;; *) echo "Unknown machine type: $MACHINE_TYPE" - exit -1 + exit 1 ;; esac URL="https://releases.jfrog.io/artifactory/jfrog-cli/${CLI_MAJOR_VER}/${VERSION}/jfrog-cli-${CLI_OS}-${ARCH}/jfrog" @@ -66,22 +63,19 @@ curl -XGET "$URL" -L -k -g > $FILE_NAME chmod u+x $FILE_NAME # Move executable to a destination in path. -set -- $DESTINATION_PATHS +# Order is by destination priority. +set -- "/usr/local/bin" "/usr/bin" "/opt/bin" while [ -n "$1" ]; do # Check if destination is in path. - if echo $PATH|grep "$1" -> /dev/null ; then - mv $FILE_NAME $1 - if [ "$?" -eq "0" ] - then + if echo "$PATH"|grep "$1" -> /dev/null ; then + if mv $FILE_NAME "$1" ; then echo "" echo "The $FILE_NAME executable was installed in $1" exit 0 else echo "" echo "We'd like to install the JFrog CLI executable in $1. Please approve this installation by entering your password." - sudo mv $FILE_NAME $1 - if [ "$?" -eq "0" ] - then + if sudo mv $FILE_NAME "$1" ; then echo "" echo "The $FILE_NAME executable was installed in $1" exit 0 diff --git a/build/npm/v2-jf/README.md b/build/npm/v2-jf/README.md index a53432f1d..4b3ca76d9 100644 --- a/build/npm/v2-jf/README.md +++ b/build/npm/v2-jf/README.md @@ -6,7 +6,7 @@ # JFrog CLI -[Website](http://www.jfrog.com) • [Docs](https://www.jfrog.com/confluence/display/CLI/JFrog+CLI) • [Issues](https://github.com/jfrog/jfrog-cli-go/issues) • [Blog](https://jfrog.com/blog/) • [We're Hiring](https://join.jfrog.com/) • [Artifactory Free Trial](https://jfrog.com/artifactory/free-trial/) +[Website](http://www.jfrog.com) • [Docs](https://docs.jfrog-applications.jfrog.io/jfrog-applications/jfrog-cli) • [Issues](https://github.com/jfrog/jfrog-cli/issues) • [Blog](https://jfrog.com/blog/) • [We're Hiring](https://join.jfrog.com/) • [Artifactory Free Trial](https://jfrog.com/artifactory/free-trial/) ## Overview diff --git a/build/npm/v2-jf/init.js b/build/npm/v2-jf/init.js index e3d82ebd6..8b5499b1e 100644 --- a/build/npm/v2-jf/init.js +++ b/build/npm/v2-jf/init.js @@ -1,65 +1,90 @@ validateNpmVersion(); -var https = require('https'); -var http = require('http'); -var url = require('url'); -var fs = require('fs'); -var packageJson = require('./package.json'); -var fileName = getFileName(); -var filePath = "bin/" + fileName; -var version = packageJson.version; -var pkgName = "jfrog-cli-" + getArchitecture(); +const {get} = require("https"); +const {request} = require("http"); +const {URL} = require("url"); +const {createWriteStream, chmodSync} = require("fs"); +const packageJson = require("./package.json"); +const fileName = getFileName(); +const filePath = "bin/" + fileName; +const version = packageJson.version; +const pkgName = "jfrog-cli-" + getArchitecture(); downloadCli(); function validateNpmVersion() { if (!isValidNpmVersion()) { - throw new Error("JFrog CLI can be installed using npm version 5.0.0 or above."); + throw new Error( + "JFrog CLI can be installed using npm version 5.0.0 or above." + ); } } function downloadWithProxy(myUrl) { - var proxyparts = url.parse(process.env.https_proxy); - var myUrlParts = url.parse(myUrl); + const proxyParts = new URL(process.env.https_proxy); + const myUrlParts = new URL(myUrl); - http.request({ - host: proxyparts.hostname, - port: proxyparts.port, - method: 'CONNECT', - path: myUrlParts.hostname + ':443' - }).on('connect', function(res, socket, head) { - https.get({ - host: myUrlParts.hostname, - socket: socket, - path: myUrlParts.path, - agent: false - }, function(res) { - if (res.statusCode == 301 || res.statusCode == 302) { - downloadWithProxy(res.headers.location); - } else if (res.statusCode == 200) { - writeToFile(res); - } else { - console.log('Unexpected status code ' + res.statusCode + ' during JFrog CLI download'); - } - }).on('error', function (err) {console.error(err);}); - }).end(); + request({ + host: proxyParts.hostname, + port: proxyParts.port, + method: "CONNECT", + path: myUrlParts.hostname + ":443", + }) + .on("connect", function (res, socket, _) { + get( + { + host: myUrlParts.hostname, + socket: socket, + path: myUrlParts.path, + agent: false, + }, + function (res) { + if (res.statusCode === 301 || res.statusCode === 302) { + downloadWithProxy(res.headers.location); + } else if (res.statusCode === 200) { + writeToFile(res); + } else { + console.log( + "Unexpected status code " + + res.statusCode + + " during JFrog CLI download" + ); + } + } + ).on("error", function (err) { + console.error(err); + }); + }) + .end(); } function download(url) { - https.get(url, function(res) { - if (res.statusCode == 301 || res.statusCode == 302) { + get(url, function (res) { + if (res.statusCode === 301 || res.statusCode === 302) { download(res.headers.location); - } else if (res.statusCode == 200) { + } else if (res.statusCode === 200) { writeToFile(res); } else { - console.log('Unexpected status code ' + res.statusCode + ' during JFrog CLI download'); + console.log( + "Unexpected status code " + + res.statusCode + + " during JFrog CLI download" + ); } - }).on('error', function (err) {console.error(err);}); + }).on("error", function (err) { + console.error(err); + }); } function downloadCli() { - console.log("Downloading JFrog CLI " + version ); - var startUrl = 'https://releases.jfrog.io/artifactory/jfrog-cli/v2-jf/' + version + '/' + pkgName + '/' + fileName; + console.log("Downloading JFrog CLI " + version); + const startUrl = + "https://releases.jfrog.io/artifactory/jfrog-cli/v2-jf/" + + version + + "/" + + pkgName + + "/" + + fileName; // We detect outbound proxy by looking at the environment variable if (process.env.https_proxy && process.env.https_proxy.length > 0) { downloadWithProxy(startUrl); @@ -69,58 +94,61 @@ function downloadCli() { } function isValidNpmVersion() { - var child_process = require('child_process'); - var npmVersionCmdOut = child_process.execSync("npm version -json"); - var npmVersion = JSON.parse(npmVersionCmdOut).npm; - // Supported since version 5.0.0 - return parseInt(npmVersion.charAt(0)) > 4; + const child_process = require("child_process"); + const npmVersionCmdOut = child_process.execSync("npm version -json"); + const npmVersion = JSON.parse(npmVersionCmdOut).npm; + // Supported since version 5.0.0 (also support npm v10+) + return parseInt(npmVersion.split('.')[0]) > 4; } function writeToFile(response) { - var file = fs.createWriteStream(filePath); - response.on('data', function (chunk) { - file.write(chunk); - }).on('end', function () { - file.end(); - if (!process.platform.startsWith("win")) { - fs.chmodSync(filePath, 0555); - } - }).on('error', function (err) { - console.error(err); - }); + const file = createWriteStream(filePath); + response + .on("data", function (chunk) { + file.write(chunk); + }) + .on("end", function () { + file.end(); + if (!process.platform.startsWith("win")) { + chmodSync(filePath, '755'); + } + }) + .on("error", function (err) { + console.error(err); + }); } function getArchitecture() { const platform = process.platform; - if (platform.startsWith('win')) { + if (platform.startsWith("win")) { // Windows architecture: - return 'windows-amd64'; + return "windows-amd64"; } const arch = process.arch; - if (platform.includes('darwin')) { + if (platform.includes("darwin")) { // macOS architecture: - return arch === 'arm64' ? 'mac-arm64' : 'mac-386'; + return arch === "arm64" ? "mac-arm64" : "mac-386"; } // linux architecture: switch (arch) { - case 'x64': - return 'linux-amd64'; - case 'arm64': - return 'linux-arm64'; - case 'arm': - return 'linux-arm'; - case 's390x': - return 'linux-s390x'; - case 'ppc64': - return 'linux-ppc64'; + case "x64": + return "linux-amd64"; + case "arm64": + return "linux-arm64"; + case "arm": + return "linux-arm"; + case "s390x": + return "linux-s390x"; + case "ppc64": + return "linux-ppc64"; default: - return 'linux-386'; + return "linux-386"; } } function getFileName() { - var executable = "jf"; + let executable = "jf"; if (process.platform.startsWith("win")) { executable += ".exe"; } diff --git a/build/npm/v2-jf/package-lock.json b/build/npm/v2-jf/package-lock.json index 4657dffc3..cbbd69036 100644 --- a/build/npm/v2-jf/package-lock.json +++ b/build/npm/v2-jf/package-lock.json @@ -1,5 +1,5 @@ { "name": "jfrog-cli-v2-jf", - "version": "2.46.2", + "version": "2.50.4", "lockfileVersion": 1 } diff --git a/build/npm/v2-jf/package.json b/build/npm/v2-jf/package.json index 8285fd5af..fcb104097 100644 --- a/build/npm/v2-jf/package.json +++ b/build/npm/v2-jf/package.json @@ -1,6 +1,6 @@ { "name": "jfrog-cli-v2-jf", - "version": "2.46.2", + "version": "2.50.4", "description": "🐸 Command-line interface for JFrog Artifactory, Xray, Distribution, Pipelines and Mission Control 🐸", "homepage": "https://github.com/jfrog/jfrog-cli", "preferGlobal": true, diff --git a/build/npm/v2/README.md b/build/npm/v2/README.md index 621441235..ebae7d87d 100644 --- a/build/npm/v2/README.md +++ b/build/npm/v2/README.md @@ -6,7 +6,7 @@ # JFrog CLI -[Website](http://www.jfrog.com) • [Docs](https://www.jfrog.com/confluence/display/CLI/JFrog+CLI) • [Issues](https://github.com/jfrog/jfrog-cli-go/issues) • [Blog](https://jfrog.com/blog/) • [We're Hiring](https://join.jfrog.com/) • [Artifactory Free Trial](https://jfrog.com/artifactory/free-trial/) +[Website](http://www.jfrog.com) • [Docs](https://docs.jfrog-applications.jfrog.io/jfrog-applications/jfrog-cli) • [Issues](https://github.com/jfrog/jfrog-cli/issues) • [Blog](https://jfrog.com/blog/) • [We're Hiring](https://join.jfrog.com/) • [Artifactory Free Trial](https://jfrog.com/artifactory/free-trial/) ## Overview diff --git a/build/npm/v2/init.js b/build/npm/v2/init.js index 87ba9989b..e37011c6b 100644 --- a/build/npm/v2/init.js +++ b/build/npm/v2/init.js @@ -1,66 +1,91 @@ validateNpmVersion(); -var https = require('https'); -var http = require('http'); -var url = require('url'); -var fs = require('fs'); -var packageJson = require('./package.json'); -var fileName = getFileName(); -var filePath = "bin/" + fileName; -var version = packageJson.version; -var pkgName = "jfrog-cli-" + getArchitecture(); +const {get} = require("https"); +const {request} = require("http"); +const {URL} = require("url"); +const {createWriteStream, chmodSync} = require("fs"); +const packageJson = require("./package.json"); +const fileName = getFileName(); +const filePath = "bin/" + fileName; +const version = packageJson.version; +const pkgName = "jfrog-cli-" + getArchitecture(); downloadCli(); function validateNpmVersion() { if (!isValidNpmVersion()) { - throw new Error("JFrog CLI can be installed using npm version 5.0.0 or above."); + throw new Error( + "JFrog CLI can be installed using npm version 5.0.0 or above." + ); } } function downloadWithProxy(myUrl) { - var proxyparts = url.parse(process.env.https_proxy); - var myUrlParts = url.parse(myUrl); + const proxyParts = new URL(process.env.https_proxy); + const myUrlParts = new URL(myUrl); - http.request({ - host: proxyparts.hostname, - port: proxyparts.port, - method: 'CONNECT', - path: myUrlParts.hostname + ':443' - }).on('connect', function(res, socket, head) { - https.get({ - host: myUrlParts.hostname, - socket: socket, - path: myUrlParts.path, - agent: false - }, function(res) { - if (res.statusCode == 301 || res.statusCode == 302) { - downloadWithProxy(res.headers.location); - } else if (res.statusCode == 200) { - writeToFile(res); - } else { - console.log('Unexpected status code ' + res.statusCode + ' during JFrog CLI download'); - } - }).on('error', function (err) {console.error(err);}); - }).end(); + request({ + host: proxyParts.hostname, + port: proxyParts.port, + method: "CONNECT", + path: myUrlParts.hostname + ":443", + }) + .on("connect", function (res, socket, _) { + get( + { + host: myUrlParts.hostname, + socket: socket, + path: myUrlParts.path, + agent: false, + }, + function (res) { + if (res.statusCode === 301 || res.statusCode === 302) { + downloadWithProxy(res.headers.location); + } else if (res.statusCode === 200) { + writeToFile(res); + } else { + console.log( + "Unexpected status code " + + res.statusCode + + " during JFrog CLI download" + ); + } + } + ).on("error", function (err) { + console.error(err); + }); + }) + .end(); } function download(url) { - https.get(url, function(res) { - if (res.statusCode == 301 || res.statusCode == 302) { + get(url, function (res) { + if (res.statusCode === 301 || res.statusCode === 302) { download(res.headers.location); - } else if (res.statusCode == 200) { + } else if (res.statusCode === 200) { writeToFile(res); } else { - console.log('Unexpected status code ' + res.statusCode + ' during JFrog CLI download'); + console.log( + "Unexpected status code " + + res.statusCode + + " during JFrog CLI download" + ); } - }).on('error', function (err) {console.error(err);}); + }).on("error", function (err) { + console.error(err); + }); } function downloadCli() { - console.log("Downloading JFrog CLI " + version ); - var startUrl = 'https://releases.jfrog.io/artifactory/jfrog-cli/v2/' + version + '/' + pkgName + '/' + fileName; - // We detect outbount proxy by looking at the environment variable + console.log("Downloading JFrog CLI " + version); + const startUrl = + "https://releases.jfrog.io/artifactory/jfrog-cli/v2/" + + version + + "/" + + pkgName + + "/" + + fileName; + // We detect outbound proxy by looking at the environment variable if (process.env.https_proxy && process.env.https_proxy.length > 0) { downloadWithProxy(startUrl); } else { @@ -69,58 +94,61 @@ function downloadCli() { } function isValidNpmVersion() { - var child_process = require('child_process'); - var npmVersionCmdOut = child_process.execSync("npm version -json"); - var npmVersion = JSON.parse(npmVersionCmdOut).npm; - // Supported since version 5.0.0 - return parseInt(npmVersion.charAt(0)) > 4; + const child_process = require("child_process"); + const npmVersionCmdOut = child_process.execSync("npm version -json"); + const npmVersion = JSON.parse(npmVersionCmdOut).npm; + // Supported since version 5.0.0 (also support npm v10+) + return parseInt(npmVersion.split('.')[0]) > 4; } function writeToFile(response) { - var file = fs.createWriteStream(filePath); - response.on('data', function (chunk) { - file.write(chunk); - }).on('end', function () { - file.end(); - if (!process.platform.startsWith("win")) { - fs.chmodSync(filePath, 0555); - } - }).on('error', function (err) { - console.error(err); - }); + const file = createWriteStream(filePath); + response + .on("data", function (chunk) { + file.write(chunk); + }) + .on("end", function () { + file.end(); + if (!process.platform.startsWith("win")) { + chmodSync(filePath, '755'); + } + }) + .on("error", function (err) { + console.error(err); + }); } function getArchitecture() { const platform = process.platform; - if (platform.startsWith('win')) { + if (platform.startsWith("win")) { // Windows architecture: - return 'windows-amd64'; + return "windows-amd64"; } const arch = process.arch; - if (platform.includes('darwin')) { + if (platform.includes("darwin")) { // macOS architecture: - return arch === 'arm64' ? 'mac-arm64' : 'mac-386'; + return arch === "arm64" ? "mac-arm64" : "mac-386"; } // linux architecture: switch (arch) { - case 'x64': - return 'linux-amd64'; - case 'arm64': - return 'linux-arm64'; - case 'arm': - return 'linux-arm'; - case 's390x': - return 'linux-s390x'; - case 'ppc64': - return 'linux-ppc64'; + case "x64": + return "linux-amd64"; + case "arm64": + return "linux-arm64"; + case "arm": + return "linux-arm"; + case "s390x": + return "linux-s390x"; + case "ppc64": + return "linux-ppc64"; default: - return 'linux-386'; + return "linux-386"; } } function getFileName() { - var executable = "jfrog"; + let executable = "jfrog"; if (process.platform.startsWith("win")) { executable += ".exe"; } diff --git a/build/npm/v2/package-lock.json b/build/npm/v2/package-lock.json index 894036f90..83fc68b50 100644 --- a/build/npm/v2/package-lock.json +++ b/build/npm/v2/package-lock.json @@ -1,5 +1,5 @@ { "name": "jfrog-cli-v2", - "version": "2.46.2", - "lockfileVersion": 1 + "version": "2.50.4", + "lockfileVersion": 2 } diff --git a/build/npm/v2/package.json b/build/npm/v2/package.json index 84ad3f5d9..8d8d82559 100644 --- a/build/npm/v2/package.json +++ b/build/npm/v2/package.json @@ -1,6 +1,6 @@ { "name": "jfrog-cli-v2", - "version": "2.46.2", + "version": "2.50.4", "description": "🐸 Command-line interface for JFrog Artifactory, Xray, Distribution, Pipelines and Mission Control 🐸", "homepage": "https://github.com/jfrog/jfrog-cli", "preferGlobal": true, diff --git a/build/setupcli/jf.sh b/build/setupcli/jf.sh index d8a7493a4..ecf68f0cc 100755 --- a/build/setupcli/jf.sh +++ b/build/setupcli/jf.sh @@ -1,11 +1,8 @@ #!/bin/bash CLI_OS="na" -CLI_UNAME="na" CLI_MAJOR_VER="v2-jf" VERSION="[RELEASE]" -# Order is by destination priority. -DESTINATION_PATHS="/usr/local/bin /usr/bin /opt/bin" SETUP_COMMAND="jf setup" GREEN_COLOR='\033[0;32m' REMOVE_COLOR='\033[0m' @@ -20,11 +17,11 @@ then fi echo "Downloading the latest version of JFrog CLI..." -if $(echo "${OSTYPE}" | grep -q msys); then +if echo "${OSTYPE}" | grep -q msys; then CLI_OS="windows" URL="https://releases.jfrog.io/artifactory/jfrog-cli/${CLI_MAJOR_VER}/${VERSION}/jfrog-cli-windows-amd64/jf.exe" FILE_NAME="jf.exe" -elif $(echo "${OSTYPE}" | grep -q darwin); then +elif echo "${OSTYPE}" | grep -q darwin; then CLI_OS="mac" if [[ $(uname -m) == 'arm64' ]]; then URL="https://releases.jfrog.io/artifactory/jfrog-cli/${CLI_MAJOR_VER}/${VERSION}/jfrog-cli-mac-arm64/jf" @@ -59,7 +56,7 @@ else ;; *) echo "Unknown machine type: $MACHINE_TYPE" - exit -1 + exit 1 ;; esac URL="https://releases.jfrog.io/artifactory/jfrog-cli/${CLI_MAJOR_VER}/${VERSION}/jfrog-cli-${CLI_OS}-${ARCH}/jf" @@ -70,28 +67,25 @@ curl -XGET "$URL" -L -k -g > $FILE_NAME chmod u+x $FILE_NAME # Move executable to a destination in path. -set -- $DESTINATION_PATHS +# Order is by destination priority. +set -- "/usr/local/bin" "/usr/bin" "/opt/bin" while [ -n "$1" ]; do # Check if destination is in path. - if echo $PATH|grep "$1" -> /dev/null ; then - mv $FILE_NAME $1 - if [ "$?" -eq "0" ] - then + if echo "$PATH"|grep "$1" -> /dev/null ; then + if mv $FILE_NAME "$1" ; then echo "" echo "The $FILE_NAME executable was installed in $1" print_installation_greeting - $SETUP_COMMAND $BASE64_CRED + $SETUP_COMMAND "$BASE64_CRED" exit 0 else echo "" echo "We'd like to install the JFrog CLI executable in $1. Please approve this installation by entering your password." - sudo mv $FILE_NAME $1 - if [ "$?" -eq "0" ] - then + if sudo mv $FILE_NAME "$1" ; then echo "" echo "The $FILE_NAME executable was installed in $1" print_installation_greeting - $SETUP_COMMAND $BASE64_CRED + $SETUP_COMMAND "$BASE64_CRED" exit 0 fi fi diff --git a/docs/artifactory/accesstokencreate/help.go b/docs/artifactory/accesstokencreate/help.go index 22f57c693..f51c8e6f0 100644 --- a/docs/artifactory/accesstokencreate/help.go +++ b/docs/artifactory/accesstokencreate/help.go @@ -1,12 +1,12 @@ package accesstokencreate -var Usage = []string{"rt atc", "rt atc "} +var Usage = []string{"rt atc", "rt atc "} func GetDescription() string { - return "Creates an access token. By default an user-scoped token will be created, unless the --groups and/or --grant-admin options are specified." + return "Creates an Artifactory access token. By default an user-scoped token will be created, unless the --groups and/or --grant-admin options are specified." } func GetArguments() string { - return ` user name - The user name for which this token is created. If not specified, the token will be created for the current user.` + return ` username + The username for which this token is created. If not specified, the token will be created for the current user.` } diff --git a/docs/general/token/help.go b/docs/general/token/help.go new file mode 100644 index 000000000..601a1665d --- /dev/null +++ b/docs/general/token/help.go @@ -0,0 +1,14 @@ +package token + +var Usage = []string{"atc", "atc "} + +func GetDescription() string { + return `Creates an access token. + By default, an user-scoped token will be created. + Administrator may provide the scope explicitly with '--scope', or implicitly with '--groups', '--grant-admin'.` +} + +func GetArguments() string { + return ` username + The username for which this token is created. If not specified, the token will be created for the current user.` +} diff --git a/documentation/CLI-for-JFrog-Artifactory.md b/documentation/CLI-for-JFrog-Artifactory.md index 375d47eaf..41b473330 100644 --- a/documentation/CLI-for-JFrog-Artifactory.md +++ b/documentation/CLI-for-JFrog-Artifactory.md @@ -227,6 +227,7 @@ This command is used to upload files to Artifactory. | --symlinks | \[Default: false\]

If true, the command will preserve the soft links structure in Artifactory. The `symlink` file representation will contain the symbolic link and checksum properties. | | --explode | \[Default: false\]

If true, the command will extract an archive containing multiple artifacts after it is deployed to Artifactory, while maintaining the archive's file structure. | | --include-dirs | \[Default: false\]

If true, the source path applies to bottom-chain directories and not only to files. Bottom-chain directories are either empty or do not include other directories that match the source path. | +| --bypass-archive-inspection | \[Default: false\]

Set to true to bypass the archive security inspection before it is unarchived. Used with the 'explode' option.| | --exclusions | \[Optional\]

A list of Semicolon-separated exclude patterns. Allows using wildcards, regular expressions or ANT patterns, according to the value of the **--regexp** and **--ant** options. Please read the **--regexp** and **--ant** options description for more information. | | --sync-deletes | \[Optional\]

Specific path in Artifactory, under which to sync artifacts after the upload. After the upload, this path will include only the artifacts uploaded during this upload operation. The other files under this path will be deleted. | | --quiet | \[Default: false\]

If true, the delete confirmation message is skipped. | diff --git a/documentation/CLI-for-JFrog-Lifecycle.md b/documentation/CLI-for-JFrog-Lifecycle.md index 6a998e4c3..eaccb5bd6 100644 --- a/documentation/CLI-for-JFrog-Lifecycle.md +++ b/documentation/CLI-for-JFrog-Lifecycle.md @@ -17,7 +17,7 @@ The following sections describe the commands available in JFrog CLI when perform ### Creating a Release Bundle v2 from builds or from existing Release Bundles -This command creates a Release Bundle v2 from a published build-info or from an existing Release Bundle. +This command creates a Release Bundle v2 from published build-infos or from existing Release Bundles. 1. To create a Release Bundle from published build-infos, provide the `--builds` option, which accepts a path to a file using the following JSON format: ```json { @@ -48,20 +48,20 @@ This command creates a Release Bundle v2 from a published build-info or from an ``` `project` is optional (if left empty, the default project will be used) -| | | -|------------------------|---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------| -| Command-name | release-bundle-create | -| Abbreviation | rbc | -| Command options | | -| --builds | \[Optional\]

Path to a JSON file containing information about the source builds from which to create a Release Bundle. | -| --project | \[Optional\]

JFrog Project key associated with the Release Bundle version. | -| --release-bundles | \[Optional\]

Path to a JSON file containing information about the source Release Bundles from which to create a Release Bundle. | -| --server-id | \[Optional\]

Platform server ID configured using the `jf c add` command. | -| --signing-key | \[Mandatory\]

The GPG/RSA key-pair name given in Artifactory. | -| --sync | \[Default: false\]

Set to true to run synchronously. | -| Command arguments | | -| release bundle name | Name of the newly created Release Bundle. | -| release bundle version | Version of the newly created Release Bundle. | +| | | +|------------------------|----------------------------------------------------------------------------------------------------------------------------------------| +| Command-name | release-bundle-create | +| Abbreviation | rbc | +| Command options | | +| --builds | \[Optional\]

Path to a JSON file containing information about the source builds from which to create a Release Bundle. | +| --project | \[Optional\]

JFrog Project key associated with the Release Bundle version. | +| --release-bundles | \[Optional\]

Path to a JSON file containing information about the source Release Bundles from which to create a Release Bundle. | +| --server-id | \[Optional\]

Platform server ID configured using the `jf c add` command. | +| --signing-key | \[Mandatory\]

The GPG/RSA key-pair name given in Artifactory. | +| --sync | \[Default: false\]

Set to true to run synchronously. | +| Command arguments | | +| release bundle name | Name of the newly created Release Bundle. | +| release bundle version | Version of the newly created Release Bundle. | #### Examples @@ -77,33 +77,33 @@ jf rbc --builds=/path/to/builds-spec.json --signing-key=myKeyPair myApp 1.0.0 Create a Release Bundle v2 with the name "myApp" and version "1.0.0", with signing key pair "myKeyPair". The Release Bundle will include the artifacts of the Release Bundles that were provided in the Release Bundles spec. ``` -jf rbc --spec=/path/to/release-bundles-spec.json --signing-key=myKeyPair myApp 1.0.0 +jf rbc --release-bundles=/path/to/release-bundles-spec.json --signing-key=myKeyPair myApp 1.0.0 ``` ##### Example 3 Create a Release Bundle v2 synchronously with the name "myApp" and version "1.0.0", in project "project0", with signing key pair "myKeyPair". The Release Bundle will include the artifacts of the Release Bundles that were provided in the Release Bundles spec. ``` -jf rbc --spec=/path/to/release-bundles-spec.json --signing-key=myKeyPair --sync=true --project=project0 myApp 1.0.0 +jf rbc --release-bundles=/path/to/release-bundles-spec.json --signing-key=myKeyPair --sync=true --project=project0 myApp 1.0.0 ``` ### Promoting a Release Bundle v2 This command promotes a Release Bundle v2 to a target environment. -| | | -|------------------------|------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------| -| Command-name | release-bundle-promote | -| Abbreviation | rbp | -| Command options | | -| --overwrite | \[Default: false\]

Set to true to replace artifacts with the same name but a different checksum, if such already exist at the promotion targets. By default, the promotion is stopped when a conflict occurs.| -| --project | \[Optional\]

Project key associated with the Release Bundle version. | -| --server-id | \[Optional\]

Platform server ID configured using the config command. | -| --signing-key | \[Mandatory\]

The GPG/RSA key-pair name given in Artifactory. | -| --sync | \[Default: false\]

Set to true to run synchronously. | -| Command arguments | | -| release bundle name | Name of the Release Bundle to promote. | -| release bundle version | Version of the Release Bundle to promote. | -| environment | Name of the target environment for the promotion. | +| | | +|------------------------|----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------| +| Command-name | release-bundle-promote | +| Abbreviation | rbp | +| Command options | | +| --overwrite | \[Default: false\]

Set to true to replace artifacts with the same name but a different checksum, if such already exist at the promotion targets. By default, the promotion is stopped when a conflict occurs. | +| --project | \[Optional\]

Project key associated with the Release Bundle version. | +| --server-id | \[Optional\]

Platform server ID configured using the config command. | +| --signing-key | \[Mandatory\]

The GPG/RSA key-pair name given in Artifactory. | +| --sync | \[Default: false\]

Set to true to run synchronously. | +| Command arguments | | +| release bundle name | Name of the Release Bundle to promote. | +| release bundle version | Version of the Release Bundle to promote. | +| environment | Name of the target environment for the promotion. | #### Examples ##### Example 1 @@ -121,3 +121,69 @@ Use signing key pair "myKeyPair" and overwrite in case of conflicts. ``` jf rbp --signing-key=myKeyPair --project=project0 --overwrite=true --sync=true myApp 1.0.0 PROD ``` + +### Distributing a release bundle + +This command distributes a Release Bundle v2 to an edge node. + +| | | +|------------------------|---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------| +| Command-name | release-bundle-distribute | +| Abbreviation | rbd | +| Command options | | +| --create-repo | \[Default: false\]

Set to true to create the repository on the edge if it does not exist. | +| --dry-run | \[Default: false\]

Set to true to disable communication with JFrog Distribution. | +| --dist-rules | \[Optional\]

Path to a file, which includes the Distribution Rules in a JSON format. See [structure](#distribution-rules-structure) bellow. | +| --site | \[Default: *\]

Wildcard filter for site name. | +| --city | \[Default: *\]

Wildcard filter for site city name. | +| --country-codes | \[Default: *\]

Semicolon-separated list of wildcard filters for site country codes. | +| --insecure-tls | \[Default: false\]

Set to true to skip TLS certificates verification. | +| --mapping-pattern | \[Optional\]

Specify along with 'mapping-target' to distribute artifacts to a different path on the edge node. You can use wildcards to specify multiple artifacts. | +| --mapping-target | \[Optional\]

The target path for distributed artifacts on the edge node. If not specified, the artifacts will have the same path and name on the edge node, as on the source Artifactory server. For flexibility in specifying the distribution path, you can include [placeholders](https://www.jfrog.com/confluence/display/CLI/CLI+for+JFrog+Artifactory#CLIforJFrogArtifactory-UsingPlaceholders) in the form of {1}, {2} which are replaced by corresponding tokens in the pattern path that are enclosed in parenthesis. | +| Command arguments | | +| release bundle name | Name of the Release Bundle to distribute. | +| release bundle version | Version of the Release Bundle to distribute. | + +#### Distribution Rules Structure: + ```json + { + "distribution_rules": [ + { + "site_name": "DC-1", + "city_name": "New-York", + "country_codes": ["1"] + }, + { + "site_name": "DC-2", + "city_name": "Tel-Aviv", + "country_codes": ["972"] + } + ] + } + ``` +The Distribution Rules format also supports wildcards. For example: + ```json + { + "distribution_rules": [ + { + "site_name": "", + "city_name": "", + "country_codes": ["*"] + } + ] + } + ``` + +#### Examples + +#### Example 1 +Distribute the Release Bundle v2 named myApp with version 1.0.0. Use the distribution rules defined in the specified file. + + jf rbd --dist-rules=/path/to/dist-rules.json myApp 1.0.0 + +#### Example 2 + +Distribute the Release Bundle v2 named myApp with version 1.0.0 using the default distribution rules. +Map files under the 'source' directory to be placed under the 'target' directory. + + jf rbd --dist-rules=/path/to/dist-rules.json --mapping-pattern="(*)/source/(*)" --mapping-target="{1}/target/{2}" myApp 1.0.0 \ No newline at end of file diff --git a/documentation/CLI-for-JFrog-Xray.md b/documentation/CLI-for-JFrog-Xray.md index 4bd355393..57b96f506 100644 --- a/documentation/CLI-for-JFrog-Xray.md +++ b/documentation/CLI-for-JFrog-Xray.md @@ -31,25 +31,22 @@ To authenticate yourself using an Xray Access Token, either configure your Acces ## Scanning Project Dependencies ### General -The _**jf audit**_ command allows scanning your source code dependencies to find security vulnerabilities and licenses violations, with the ability to scan against your Xray policies. The command builds a deep dependencies graph for your project, scans it with Xray, and displays the results. It uses the package manager used by the project to build the dependencies graph. Currently, the following package managers are supported. - -* Maven (mvn) - Version 3.1.0 or above of Maven is supported. -* Gradle (gradle) -* Npm (npm) -* Yarn (yarn) -* Pip (pip) -* Pipenv (pipenv) -* Poetry (poetry) -* Go Modules (go) -* NuGet (nuget) -* .NET Core CLI (dotnet) +The _**jf audit**_ command allows scanning your source code dependencies to find security vulnerabilities and license violations, with the ability to scan against your Xray policies. The command builds a deep dependencies graph for your project, scans it with Xray, and displays the results. +It uses the package manager used by the project to build the dependencies graph. + +Currently, the following package managers are supported: + +|Go Go|Gradle Gradle|Maven Maven (3.1+)|npm npm|Yarn Yarn| +|:----|:----|:----|:----|:----| +|.NET .NET|NuGet NuGet|Pip Pip|Pipenv Pipenv|Poetry Poetry| The command will detect the package manager used by the project automatically. It requires version 3.29.0 or above of Xray and also version 2.13.0 or above of JFrog CLI. ### Advanced Scans This command also supports the following Advanced Scans with the **Advanced Security Package** enabled on the JFrog Platform instance. To enable the Advanced Security Package, contact us using [this](https://jfrog.com/advanced-security-contact-us/) form. -* **Vulnerability Contextual Analysis**: This feature uses the code context to eliminate false positive reports on vulnerable dependencies that are not applicable to the code. Vulnerability Contextual Analysis is currently supported for Python and JavaScript code. +* **Static Application Security Testing (SAST)**: Provides fast and accurate security-focused engines that detect zero-day security vulnerabilities on your source code sensitive operations, while minimizing false positives. +* **Vulnerability Contextual Analysis**: This feature uses the code context to eliminate false positive reports on vulnerable dependencies that are not applicable to the code. For CVE vulnerabilities that are applicable to your code, Frogbot will create pull request comments on the relevant code lines with full descriptions regarding the security issues caused by the CVE. Vulnerability Contextual Analysis is currently supported for Python, JavaScript, and Java code. * **Secrets Detection**: Detect any secrets left exposed inside the code. to stop any accidental leak of internal tokens or credentials. * **Infrastructure as Code scans (IaC)**: Scan Infrastructure as Code (Terraform) files for early detection of cloud and infrastructure misconfigurations. diff --git a/general/token/cli.go b/general/token/cli.go new file mode 100644 index 000000000..748a0f0a1 --- /dev/null +++ b/general/token/cli.go @@ -0,0 +1,112 @@ +package token + +import ( + "errors" + "fmt" + "github.com/jfrog/jfrog-cli-core/v2/common/commands" + generic "github.com/jfrog/jfrog-cli-core/v2/general/token" + coreConfig "github.com/jfrog/jfrog-cli-core/v2/utils/config" + "github.com/jfrog/jfrog-cli/utils/accesstoken" + "github.com/jfrog/jfrog-cli/utils/cliutils" + clientUtils "github.com/jfrog/jfrog-client-go/utils" + "github.com/jfrog/jfrog-client-go/utils/errorutils" + "github.com/jfrog/jfrog-client-go/utils/log" + "github.com/urfave/cli" + "strconv" +) + +func AccessTokenCreateCmd(c *cli.Context) error { + if c.NArg() > 1 { + return cliutils.WrongNumberOfArgumentsHandler(c) + } + + serverDetails, err := createPlatformDetailsByFlags(c) + if err != nil { + return err + } + + if err = assertAccessTokenAvailable(serverDetails); err != nil { + return err + } + + if err = assertScopeOptions(c); err != nil { + return err + } + + username := accesstoken.GetSubjectUsername(c, serverDetails) + + expiry, err := getExpiry(c) + if err != nil { + return err + } + + accessTokenCreateCmd := generic.NewAccessTokenCreateCommand() + accessTokenCreateCmd. + SetServerDetails(serverDetails). + SetUsername(username). + SetProjectKey(c.String(cliutils.Project)). + SetGroups(c.String(cliutils.Groups)). + SetScope(c.String(cliutils.Scope)). + SetGrantAdmin(c.Bool(cliutils.GrantAdmin)). + SetExpiry(expiry). + SetRefreshable(c.Bool(cliutils.Refreshable)). + SetDescription(c.String(cliutils.Description)). + SetAudience(c.String(cliutils.Audience)). + SetIncludeReferenceToken(c.Bool(cliutils.Reference)) + err = commands.Exec(accessTokenCreateCmd) + if err != nil { + return err + } + resString, err := accessTokenCreateCmd.Response() + if err != nil { + return err + } + log.Output(clientUtils.IndentJson(resString)) + + return nil +} + +func createPlatformDetailsByFlags(c *cli.Context) (*coreConfig.ServerDetails, error) { + platformDetails, err := cliutils.CreateServerDetailsWithConfigOffer(c, true, cliutils.Platform) + if err != nil { + return nil, err + } + if platformDetails.Url == "" { + return nil, errors.New("platform URL is mandatory for access token creation") + } + return platformDetails, nil +} + +func getExpiry(c *cli.Context) (*uint, error) { + if !c.IsSet(cliutils.Expiry) { + return nil, nil + } + + expiryInt, err := strconv.Atoi(c.String(cliutils.Expiry)) + if err != nil { + return nil, cliutils.PrintHelpAndReturnError( + fmt.Sprintf("The '--%s' option must have a numeric value. ", cliutils.Expiry), c) + } + if expiryInt < 0 { + return nil, cliutils.PrintHelpAndReturnError( + fmt.Sprintf("The '--%s' option must be non-negative. ", cliutils.Expiry), c) + } + expiry := uint(expiryInt) + return &expiry, nil +} + +func assertScopeOptions(c *cli.Context) error { + if c.IsSet(cliutils.Scope) && (c.IsSet(cliutils.GrantAdmin) || c.IsSet(cliutils.Groups)) { + return cliutils.PrintHelpAndReturnError( + fmt.Sprintf("Scope can either be provided explicitly with '--%s', or implicitly with '--%s' and '--%s'. ", + cliutils.Scope, cliutils.GrantAdmin, cliutils.Groups), c) + } + return nil +} + +func assertAccessTokenAvailable(serverDetails *coreConfig.ServerDetails) error { + if serverDetails.AccessToken == "" { + return errorutils.CheckErrorf("authenticating with access token is currently mandatory for creating access tokens") + } + return nil +} diff --git a/go.mod b/go.mod index 24b86da5f..78d691128 100644 --- a/go.mod +++ b/go.mod @@ -5,12 +5,12 @@ go 1.20 require ( github.com/agnivade/levenshtein v1.1.1 github.com/buger/jsonparser v1.1.1 - github.com/go-git/go-git/v5 v5.8.1 + github.com/go-git/go-git/v5 v5.9.0 github.com/gocarina/gocsv v0.0.0-20230616125104-99d496ca653d - github.com/jfrog/build-info-go v1.9.9 - github.com/jfrog/gofrog v1.3.0 - github.com/jfrog/jfrog-cli-core/v2 v2.41.2 - github.com/jfrog/jfrog-client-go v1.31.6 + github.com/jfrog/build-info-go v1.9.14 + github.com/jfrog/gofrog v1.3.1 + github.com/jfrog/jfrog-cli-core/v2 v2.45.4 + github.com/jfrog/jfrog-client-go v1.34.3 github.com/jszwec/csvutil v1.8.0 github.com/mholt/archiver/v3 v3.5.1 github.com/stretchr/testify v1.8.4 @@ -18,10 +18,9 @@ require ( github.com/urfave/cli v1.22.14 github.com/vbauerster/mpb/v7 v7.5.3 github.com/xeipuuv/gojsonschema v1.2.0 - golang.org/x/exp v0.0.0-20230817173708-d852ddb80c63 - golang.org/x/term v0.11.0 + golang.org/x/exp v0.0.0-20231006140011-7918f672742d + golang.org/x/term v0.13.0 gopkg.in/yaml.v2 v2.4.0 - ) require ( @@ -30,7 +29,8 @@ require ( github.com/BurntSushi/toml v1.3.2 // indirect github.com/CycloneDX/cyclonedx-go v0.7.2 // indirect github.com/Microsoft/go-winio v0.6.1 // indirect - github.com/ProtonMail/go-crypto v0.0.0-20230828082145-3c4c8a2d2371 // indirect + github.com/Microsoft/hcsshim v0.11.0 // indirect + github.com/ProtonMail/go-crypto v0.0.0-20230923063757-afb1ddc0824c // indirect github.com/VividCortex/ewma v1.2.0 // indirect github.com/acarl005/stripansi v0.0.0-20180116102854-5a71ef0e047d // indirect github.com/acomagu/bufpipe v1.0.4 // indirect @@ -39,12 +39,13 @@ require ( github.com/cenkalti/backoff/v4 v4.2.1 // indirect github.com/chzyer/readline v1.5.1 // indirect github.com/cloudflare/circl v1.3.3 // indirect - github.com/containerd/containerd v1.7.3 // indirect + github.com/containerd/containerd v1.7.6 // indirect github.com/cpuguy83/dockercfg v0.3.1 // indirect github.com/cpuguy83/go-md2man/v2 v2.0.2 // indirect - github.com/davecgh/go-spew v1.1.1 // indirect + github.com/cyphar/filepath-securejoin v0.2.4 // indirect + github.com/davecgh/go-spew v1.1.2-0.20180830191138-d8f796af33cc // indirect github.com/docker/distribution v2.8.2+incompatible // indirect - github.com/docker/docker v24.0.5+incompatible // indirect + github.com/docker/docker v24.0.6+incompatible // indirect github.com/docker/go-connections v0.4.0 // indirect github.com/docker/go-units v0.5.0 // indirect github.com/dsnet/compress v0.0.2-0.20210315054119-f66993602bf5 // indirect @@ -52,7 +53,7 @@ require ( github.com/forPelevin/gomoji v1.1.8 // indirect github.com/fsnotify/fsnotify v1.6.0 // indirect github.com/go-git/gcfg v1.5.1-0.20230307220236-3a3c6141e376 // indirect - github.com/go-git/go-billy/v5 v5.4.1 // indirect + github.com/go-git/go-billy/v5 v5.5.0 // indirect github.com/gogo/protobuf v1.3.2 // indirect github.com/golang-jwt/jwt/v4 v4.5.0 // indirect github.com/golang/groupcache v0.0.0-20210331224755-41bb18bfe9da // indirect @@ -62,15 +63,16 @@ 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.4.7 // indirect + github.com/jedib0t/go-pretty/v6 v6.4.8 // indirect + github.com/jfrog/jfrog-apps-config v1.0.1 // indirect github.com/kevinburke/ssh_config v1.2.0 // indirect - github.com/klauspost/compress v1.16.0 // indirect + github.com/klauspost/compress v1.17.0 // indirect github.com/klauspost/cpuid/v2 v2.2.3 // indirect github.com/klauspost/pgzip v1.2.5 // indirect github.com/magiconair/properties v1.8.7 // indirect github.com/manifoldco/promptui v0.9.0 // indirect - github.com/mattn/go-colorable v0.1.12 // indirect - github.com/mattn/go-isatty v0.0.14 // 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-tty v0.0.3 // indirect github.com/minio/sha256-simd v1.0.1 // indirect @@ -83,47 +85,51 @@ require ( github.com/opencontainers/go-digest v1.0.0 // indirect github.com/opencontainers/image-spec v1.1.0-rc4 // indirect github.com/opencontainers/runc v1.1.5 // indirect - github.com/owenrumney/go-sarif/v2 v2.2.0 // indirect - github.com/pelletier/go-toml/v2 v2.0.8 // indirect + github.com/owenrumney/go-sarif/v2 v2.3.0 // indirect + github.com/pelletier/go-toml/v2 v2.1.0 // indirect github.com/pierrec/lz4/v4 v4.1.2 // indirect github.com/pjbgf/sha1cd v0.3.0 // indirect github.com/pkg/browser v0.0.0-20210911075715-681adbf594b8 // indirect github.com/pkg/errors v0.9.1 // indirect github.com/pkg/term v1.1.0 // indirect - github.com/pmezard/go-difflib v1.0.0 // indirect + github.com/pmezard/go-difflib v1.0.1-0.20181226105442-5d4384ee4fb2 // indirect github.com/rivo/uniseg v0.4.3 // indirect github.com/russross/blackfriday/v2 v2.1.0 // indirect + github.com/sagikazarmark/locafero v0.3.0 // indirect + github.com/sagikazarmark/slog-shim v0.1.0 // indirect github.com/sergi/go-diff v1.1.0 // indirect github.com/sirupsen/logrus v1.9.0 // indirect github.com/skeema/knownhosts v1.2.0 // indirect - github.com/spf13/afero v1.9.5 // indirect + github.com/sourcegraph/conc v0.3.0 // indirect + github.com/spf13/afero v1.10.0 // indirect github.com/spf13/cast v1.5.1 // indirect - github.com/spf13/jwalterweatherman v1.1.0 // indirect github.com/spf13/pflag v1.0.5 // indirect - github.com/spf13/viper v1.16.0 // indirect - github.com/subosito/gotenv v1.4.2 // indirect + github.com/spf13/viper v1.17.0 // indirect + github.com/subosito/gotenv v1.6.0 // indirect github.com/ulikunitz/xz v0.5.9 // indirect github.com/xanzy/ssh-agent v0.3.3 // indirect - github.com/xeipuuv/gojsonpointer v0.0.0-20180127040702-4e3ac2762d5f // indirect + github.com/xeipuuv/gojsonpointer v0.0.0-20190905194746-02993c407bfb // indirect github.com/xeipuuv/gojsonreference v0.0.0-20180127040603-bd5ef7bd5415 // indirect github.com/xi2/xz v0.0.0-20171230120015-48954b6210f8 // indirect github.com/xo/terminfo v0.0.0-20210125001918-ca9a967f8778 // indirect - golang.org/x/crypto v0.12.0 // indirect - golang.org/x/mod v0.12.0 // indirect - golang.org/x/net v0.14.0 // indirect - golang.org/x/sync v0.3.0 // indirect - golang.org/x/sys v0.11.0 // indirect - golang.org/x/text v0.12.0 // indirect - golang.org/x/tools v0.12.1-0.20230815132531-74c255bcf846 // indirect - google.golang.org/genproto/googleapis/rpc v0.0.0-20230525234030-28d5490b6b19 // indirect - google.golang.org/grpc v1.57.0 // indirect - google.golang.org/protobuf v1.30.0 // indirect + go.uber.org/atomic v1.9.0 // indirect + go.uber.org/multierr v1.9.0 // indirect + golang.org/x/crypto v0.14.0 // indirect + golang.org/x/mod v0.13.0 // indirect + golang.org/x/net v0.17.0 // indirect + golang.org/x/sync v0.4.0 // indirect + golang.org/x/sys v0.13.0 // indirect + golang.org/x/text v0.13.0 // indirect + golang.org/x/tools v0.14.0 // indirect + google.golang.org/genproto/googleapis/rpc v0.0.0-20230920204549-e6e6cdab5c13 // indirect + google.golang.org/grpc v1.58.3 // indirect + google.golang.org/protobuf v1.31.0 // indirect gopkg.in/ini.v1 v1.67.0 // indirect gopkg.in/warnings.v0 v0.1.2 // indirect gopkg.in/yaml.v3 v3.0.1 // indirect ) -// replace github.com/jfrog/build-info-go => github.com/jfrog/build-info-go v1.8.9-0.20230828134416-f0db33dd9344 +// replace github.com/jfrog/build-info-go => github.com/jfrog/build-info-go v1.8.9-0.20231019085746-e1b192457664 replace github.com/jfrog/jfrog-cli-core/v2 => github.com/bhanurp/jfrog-cli-core/v2 v2.24.4-0.20230904193216-07be942c9e9e diff --git a/go.sum b/go.sum index 7c12cf234..f127d47c6 100644 --- a/go.sum +++ b/go.sum @@ -38,7 +38,7 @@ cloud.google.com/go/storage v1.14.0/go.mod h1:GrKmX003DSIwi9o29oFT7YDnHYwZoctc3f dario.cat/mergo v1.0.0 h1:AGCNq9Evsj31mOgNPcLyXc+4PNABt905YmuqPYYpBWk= dario.cat/mergo v1.0.0/go.mod h1:uNxQE+84aUszobStD9th8a29P2fMDhsBdgRYvZOxGmk= dmitri.shuralyov.com/gpu/mtl v0.0.0-20190408044501-666a987793e9/go.mod h1:H6x//7gZCb22OMCxBHrMx7a5I7Hp++hsVxbQ4BYO7hU= -github.com/AdaLogics/go-fuzz-headers v0.0.0-20230106234847-43070de90fa1 h1:EKPd1INOIyr5hWOWhvpmQpY6tKjeG0hT1s3AMC/9fic= +github.com/AdaLogics/go-fuzz-headers v0.0.0-20230811130428-ced1acdcaa24 h1:bvDV9vkmnHYOMsOr4WLk+Vo07yKIzd94sVoIqshQ4bU= github.com/Azure/go-ansiterm v0.0.0-20210617225240-d185dfc1b5a1 h1:UQHMgLO+TxOElx5B5HZ4hJQsoJ/PvUvKRhJHDQXO8P8= github.com/Azure/go-ansiterm v0.0.0-20210617225240-d185dfc1b5a1/go.mod h1:xomTg63KZ2rFqZQzSB4Vz2SUXa1BpHTVz9L5PTmPC4E= github.com/BurntSushi/toml v0.3.1/go.mod h1:xHWCNGjB5oqiDr8zfno3MHue2Ht5sIBksp03qcyfWMU= @@ -50,9 +50,10 @@ github.com/CycloneDX/cyclonedx-go v0.7.2/go.mod h1:K2bA+324+Og0X84fA8HhN2X066K7B github.com/Microsoft/go-winio v0.5.2/go.mod h1:WpS1mjBmmwHBEWmogvA2mj8546UReBk4v8QkMxJ6pZY= github.com/Microsoft/go-winio v0.6.1 h1:9/kr64B9VUZrLm5YYwbGtUJnMgqWVOdUAXu6Migciow= github.com/Microsoft/go-winio v0.6.1/go.mod h1:LRdKpFKfdobln8UmuiYcKPot9D2v6svN5+sAH+4kjUM= -github.com/Microsoft/hcsshim v0.10.0-rc.8 h1:YSZVvlIIDD1UxQpJp0h+dnpLUw+TrY0cx8obKsp3bek= -github.com/ProtonMail/go-crypto v0.0.0-20230828082145-3c4c8a2d2371 h1:kkhsdkhsCvIsutKu5zLMgWtgh9YxGCNAw8Ad8hjwfYg= -github.com/ProtonMail/go-crypto v0.0.0-20230828082145-3c4c8a2d2371/go.mod h1:EjAoLdwvbIOoOQr3ihjnSoLZRtE8azugULFRteWMNc0= +github.com/Microsoft/hcsshim v0.11.0 h1:7EFNIY4igHEXUdj1zXgAyU3fLc7QfOKHbkldRVTBdiM= +github.com/Microsoft/hcsshim v0.11.0/go.mod h1:OEthFdQv/AD2RAdzR6Mm1N1KPCztGKDurW1Z8b8VGMM= +github.com/ProtonMail/go-crypto v0.0.0-20230923063757-afb1ddc0824c h1:kMFnB0vCcX7IL/m9Y5LO+KQYv+t1CQOiFe6+SV2J7bE= +github.com/ProtonMail/go-crypto v0.0.0-20230923063757-afb1ddc0824c/go.mod h1:EjAoLdwvbIOoOQr3ihjnSoLZRtE8azugULFRteWMNc0= github.com/VividCortex/ewma v1.2.0 h1:f58SaIzcDXrSy3kWaHNvuJgJ3Nmz59Zji6XoJR/q1ow= github.com/VividCortex/ewma v1.2.0/go.mod h1:nz4BbCtbLyFDeC9SUHbtcT5644juEuWfUAUnGx7j5l4= github.com/acarl005/stripansi v0.0.0-20180116102854-5a71ef0e047d h1:licZJFw2RwpHMqeKTCYkitsPqHNxTmd4SNR5r94FGM8= @@ -98,37 +99,44 @@ github.com/cloudflare/circl v1.3.3/go.mod h1:5XYMA4rFBvNIrhs50XuiBJ15vF2pZn4nnUK github.com/cncf/udpa/go v0.0.0-20191209042840-269d4d468f6f/go.mod h1:M8M6+tZqaGXZJjfX53e64911xZQV5JYwmTeXPW+k8Sc= github.com/cncf/udpa/go v0.0.0-20200629203442-efcf912fb354/go.mod h1:WmhPx2Nbnhtbo57+VJT5O0JRkEi1Wbu0z5j0R8u5Hbk= github.com/cncf/udpa/go v0.0.0-20201120205902-5459f2c99403/go.mod h1:WmhPx2Nbnhtbo57+VJT5O0JRkEi1Wbu0z5j0R8u5Hbk= +github.com/containerd/aufs v1.0.0/go.mod h1:kL5kd6KM5TzQjR79jljyi4olc1Vrx6XBlcyj3gNv2PU= github.com/containerd/console v1.0.3/go.mod h1:7LqA/THxQ86k76b8c/EMSiaJ3h1eZkMkXar0TQ1gf3U= -github.com/containerd/containerd v1.7.3 h1:cKwYKkP1eTj54bP3wCdXXBymmKRQMrWjkLSWZZJDa8o= -github.com/containerd/containerd v1.7.3/go.mod h1:32FOM4/O0RkNg7AjQj3hDzN9cUGtu+HMvaKUNiqCZB8= +github.com/containerd/containerd v1.7.6 h1:oNAVsnhPoy4BTPQivLgTzI9Oleml9l/+eYIDYXRCYo8= +github.com/containerd/containerd v1.7.6/go.mod h1:SY6lrkkuJT40BVNO37tlYTSnKJnP5AXBc0fhx0q+TJ4= +github.com/containerd/go-runc v1.0.0/go.mod h1:cNU0ZbCgCQVZK4lgG3P+9tn9/PaJNmoDXPpoJhDR+Ok= +github.com/containerd/typeurl v1.0.2/go.mod h1:9trJWW2sRlGub4wZJRTW83VtbOLS6hwcDZXTn6oPz9s= +github.com/coreos/go-semver v0.3.0/go.mod h1:nnelYz7RCh+5ahJtPPxZlU+153eP4D4r3EedlOD2RNk= github.com/coreos/go-systemd/v22 v22.3.2/go.mod h1:Y58oyj3AT4RCenI/lSvhwexgC+NSVTIJ3seZv2GcEnc= github.com/cpuguy83/dockercfg v0.3.1 h1:/FpZ+JaygUR/lZP2NlFI2DVfrOEMAIKP5wWEJdoYe9E= github.com/cpuguy83/dockercfg v0.3.1/go.mod h1:sugsbF4//dDlL/i+S+rtpIWp+5h0BHJHfjj5/jFyUJc= github.com/cpuguy83/go-md2man/v2 v2.0.0-20190314233015-f79a8a8ca69d/go.mod h1:maD7wRr/U5Z6m/iR4s+kqSMx2CaBsrgA7czyZG/E6dU= github.com/cpuguy83/go-md2man/v2 v2.0.2 h1:p1EgwI/C7NhT0JmVkwCD2ZBK8j4aeHQX2pMHHBfMQ6w= github.com/cpuguy83/go-md2man/v2 v2.0.2/go.mod h1:tgQtvFlXSQOSOSIRvRPT7W67SCa46tRHOmNcaadrF8o= -github.com/creack/pty v1.1.9/go.mod h1:oKZEueFk5CKHvIhNR5MUki03XCEU+Q6VDXinZuGJ33E= github.com/creack/pty v1.1.18 h1:n56/Zwd5o6whRC5PMGretI4IdRLlmBXYNjScPaBgsbY= -github.com/cyphar/filepath-securejoin v0.2.3 h1:YX6ebbZCZP7VkM3scTTokDgBL2TY741X51MTk3ycuNI= github.com/cyphar/filepath-securejoin v0.2.3/go.mod h1:aPGpWjXOXUn2NCNjFvBE6aRxGGx79pTxQpKOJNYHHl4= +github.com/cyphar/filepath-securejoin v0.2.4 h1:Ugdm7cg7i6ZK6x3xDF1oEu1nfkyfH53EtKeQYTC3kyg= +github.com/cyphar/filepath-securejoin v0.2.4/go.mod h1:aPGpWjXOXUn2NCNjFvBE6aRxGGx79pTxQpKOJNYHHl4= github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= -github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c= github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= +github.com/davecgh/go-spew v1.1.2-0.20180830191138-d8f796af33cc h1:U9qPSI2PIWSS1VwoXQT9A3Wy9MM3WgvqSxFWenqJduM= +github.com/davecgh/go-spew v1.1.2-0.20180830191138-d8f796af33cc/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= github.com/dgryski/trifles v0.0.0-20200323201526-dd97f9abfb48 h1:fRzb/w+pyskVMQ+UbP35JkH8yB7MYb4q/qhBarqZE6g= github.com/dgryski/trifles v0.0.0-20200323201526-dd97f9abfb48/go.mod h1:if7Fbed8SFyPtHLHbg49SI7NAdJiC5WIA09pe59rfAA= github.com/docker/distribution v2.8.2+incompatible h1:T3de5rq0dB1j30rp0sA2rER+m322EBzniBPB6ZIzuh8= github.com/docker/distribution v2.8.2+incompatible/go.mod h1:J2gT2udsDAN96Uj4KfcMRqY0/ypR+oyYUYmja8H+y+w= -github.com/docker/docker v24.0.5+incompatible h1:WmgcE4fxyI6EEXxBRxsHnZXrO1pQ3smi0k/jho4HLeY= -github.com/docker/docker v24.0.5+incompatible/go.mod h1:eEKB0N0r5NX/I1kEveEz05bcu8tLC/8azJZsviup8Sk= +github.com/docker/docker v24.0.6+incompatible h1:hceabKCtUgDqPu+qm0NgsaXf28Ljf4/pWFL7xjWWDgE= +github.com/docker/docker v24.0.6+incompatible/go.mod h1:eEKB0N0r5NX/I1kEveEz05bcu8tLC/8azJZsviup8Sk= github.com/docker/go-connections v0.4.0 h1:El9xVISelRB7BuFusrZozjnkIM5YnzCViNKohAFqRJQ= github.com/docker/go-connections v0.4.0/go.mod h1:Gbd7IOopHjR8Iph03tsViu4nIes5XhDvyHbTtUxmeec= +github.com/docker/go-events v0.0.0-20190806004212-e31b211e4f1c/go.mod h1:Uw6UezgYA44ePAFQYUehOuCzmy5zmg/+nl2ZfMWGkpA= +github.com/docker/go-metrics v0.0.1/go.mod h1:cG1hvH2utMXtqgqqYE9plW6lDxS3/5ayHzueweSI3Vw= github.com/docker/go-units v0.4.0/go.mod h1:fgPhTUdO+D/Jk86RDLlptpiXQzgHJF7gydDDbaIK4Dk= github.com/docker/go-units v0.5.0 h1:69rxXcBk27SvSaaxTtLh/8llcHD8vYHT7WSdRZ/jvr4= github.com/docker/go-units v0.5.0/go.mod h1:fgPhTUdO+D/Jk86RDLlptpiXQzgHJF7gydDDbaIK4Dk= github.com/dsnet/compress v0.0.2-0.20210315054119-f66993602bf5 h1:iFaUwBSo5Svw6L7HYpRu/0lE3e0BaElwnNO1qkNQxBY= github.com/dsnet/compress v0.0.2-0.20210315054119-f66993602bf5/go.mod h1:qssHWj60/X5sZFNxpG4HBPDHVqxNm4DfnCKgrbZOT+s= github.com/dsnet/golib v0.0.0-20171103203638-1ea166775780/go.mod h1:Lj+Z9rebOhdfkVLjJ8T6VcRQv3SXugXy999NBtR9aFY= -github.com/elazarl/goproxy v0.0.0-20221015165544-a0805db90819 h1:RIB4cRk+lBqKK3Oy0r2gRX4ui7tuhiZq2SuTtTCi0/0= +github.com/elazarl/goproxy v0.0.0-20230808193330-2592e75ae04a h1:mATvB/9r/3gvcejNsXKSkQ6lcIaNec2nyfOdlTBR2lU= github.com/emirpasic/gods v1.18.1 h1:FXtiHYKDGKCW2KzwZKx0iC0PQmdlorYgdFG9jPXJ1Bc= github.com/emirpasic/gods v1.18.1/go.mod h1:8tpGGwCnJ5H4r6BWwaV6OrWmMoPhUl5jm/FMNAnJvWQ= github.com/envoyproxy/go-control-plane v0.9.0/go.mod h1:YTl/9mNaCwkRvm6d1a2C3ymFceY/DCBVvsKhRF0iEA4= @@ -143,14 +151,15 @@ github.com/frankban/quicktest v1.11.3/go.mod h1:wRf/ReqHper53s+kmmSZizM8NamnL3IM github.com/frankban/quicktest v1.14.4 h1:g2rn0vABPOOXmZUj+vbmUp0lPoXEMuhTpIluN0XL9UY= github.com/fsnotify/fsnotify v1.6.0 h1:n+5WquG0fcWoWp6xPWfHdbskMCQaFnG6PfBrh1Ky4HY= github.com/fsnotify/fsnotify v1.6.0/go.mod h1:sl3t1tCWJFWoRz9R8WJCbQihKKwmorjAbSClcnxKAGw= +github.com/ghodss/yaml v1.0.0/go.mod h1:4dBDuWmgqj2HViK6kFavaiC9ZROes6MMH2rRYeMEF04= github.com/gliderlabs/ssh v0.3.5 h1:OcaySEmAQJgyYcArR+gGGTHCyE7nvhEMTlYY+Dp8CpY= github.com/go-git/gcfg v1.5.1-0.20230307220236-3a3c6141e376 h1:+zs/tPmkDkHx3U66DAb0lQFJrpS6731Oaa12ikc+DiI= github.com/go-git/gcfg v1.5.1-0.20230307220236-3a3c6141e376/go.mod h1:an3vInlBmSxCcxctByoQdvwPiA7DTK7jaaFDBTtu0ic= -github.com/go-git/go-billy/v5 v5.4.1 h1:Uwp5tDRkPr+l/TnbHOQzp+tmJfLceOlbVucgpTz8ix4= -github.com/go-git/go-billy/v5 v5.4.1/go.mod h1:vjbugF6Fz7JIflbVpl1hJsGjSHNltrSw45YK/ukIvQg= +github.com/go-git/go-billy/v5 v5.5.0 h1:yEY4yhzCDuMGSv83oGxiBotRzhwhNr8VZyphhiu+mTU= +github.com/go-git/go-billy/v5 v5.5.0/go.mod h1:hmexnoNsr2SJU1Ju67OaNz5ASJY3+sHgFRpCtpDCKow= github.com/go-git/go-git-fixtures/v4 v4.3.2-0.20230305113008-0c11038e723f h1:Pz0DHeFij3XFhoBRGUDPzSJ+w2UcK5/0JvF8DRI58r8= -github.com/go-git/go-git/v5 v5.8.1 h1:Zo79E4p7TRk0xoRgMq0RShiTHGKcKI4+DI6BfJc/Q+A= -github.com/go-git/go-git/v5 v5.8.1/go.mod h1:FHFuoD6yGz5OSKEBK+aWN9Oah0q54Jxl0abmj6GnqAo= +github.com/go-git/go-git/v5 v5.9.0 h1:cD9SFA7sHVRdJ7AYck1ZaAa/yeuBvGPxwXDL8cxrObY= +github.com/go-git/go-git/v5 v5.9.0/go.mod h1:RKIqga24sWdMGZF+1Ekv9kylsDz6LzdTSI2s/OsZWE0= github.com/go-gl/glfw v0.0.0-20190409004039-e6da0acd62b1/go.mod h1:vR7hzQXu2zJy9AVAgeJqvqgH9Q5CA+iKCZ2gyEVpxRU= github.com/go-gl/glfw/v3.3/glfw v0.0.0-20191125211704-12ad95a8df72/go.mod h1:tQ2UAYgL5IevRw8kRxooKSPJfGvJ9fJQFa0TUsXzTg8= github.com/go-gl/glfw/v3.3/glfw v0.0.0-20200222043503-6f7a984d4dc4/go.mod h1:tQ2UAYgL5IevRw8kRxooKSPJfGvJ9fJQFa0TUsXzTg8= @@ -175,6 +184,7 @@ github.com/golang/mock v1.4.0/go.mod h1:UOMv5ysSaYNkG+OFQykRIcU/QvvxJf3p21QfJ2Bt github.com/golang/mock v1.4.1/go.mod h1:UOMv5ysSaYNkG+OFQykRIcU/QvvxJf3p21QfJ2Bt3cw= github.com/golang/mock v1.4.3/go.mod h1:UOMv5ysSaYNkG+OFQykRIcU/QvvxJf3p21QfJ2Bt3cw= github.com/golang/mock v1.4.4/go.mod h1:l3mdAwkq5BuhzHwde/uurv3sEJeZMXNpwsxVWU71h+4= +github.com/golang/mock v1.6.0/go.mod h1:p6yTPP+5HYm5mzsMV8JkE6ZKdX+/wYM6Hr+LicevLPs= github.com/golang/protobuf v1.2.0/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U= github.com/golang/protobuf v1.3.1/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U= github.com/golang/protobuf v1.3.2/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U= @@ -230,6 +240,7 @@ github.com/googleapis/gax-go/v2 v2.0.5/go.mod h1:DWXyrwAJ9X0FpwwEdw+IPEYBICEFu5m github.com/googleapis/google-cloud-go-testing v0.0.0-20200911160855-bcd43fbb19e8/go.mod h1:dvDLG8qkwmyD9a/MJJN3XJcT3xFxOKAvTZGvuZmac9g= github.com/gookit/color v1.5.4 h1:FZmqs7XOyGgCAxmWyPslpiok1k05wmY3SJTytgvYFs0= github.com/gookit/color v1.5.4/go.mod h1:pZJOeOS8DM43rXbp4AZo1n9zCU2qjpcRko0b6/QJi9w= +github.com/grpc-ecosystem/go-grpc-prometheus v1.2.0/go.mod h1:8NvIoxWQoOIhqOTXgfV/d3M/q6VIi02HzZEHgUlZvzk= github.com/hashicorp/golang-lru v0.5.0/go.mod h1:/m3WP610KZHVQ1SGc6re/UDhFvYD7pJ4Ao+sR/qLZy8= github.com/hashicorp/golang-lru v0.5.1/go.mod h1:/m3WP610KZHVQ1SGc6re/UDhFvYD7pJ4Ao+sR/qLZy8= github.com/hashicorp/hcl v1.0.0 h1:0Anlzjpi4vEasTeNFn2mLJgTSwt0+6sfsiTG8qcWGx4= @@ -254,8 +265,8 @@ github.com/kisielk/errcheck v1.5.0/go.mod h1:pFxgyoBC7bSaBwPgfKdkLd5X25qrDl4LWUI github.com/kisielk/gotool v1.0.0/go.mod h1:XhKaO+MFFWcvkIS/tQcRk01m1F5IRFswLeQ+oQHNcck= github.com/klauspost/compress v1.4.1/go.mod h1:RyIbtBH6LamlWaDj8nUwkbUhJ87Yi3uG0guNDohfE1A= github.com/klauspost/compress v1.11.4/go.mod h1:aoV0uJVorq1K+umq18yTdKaF57EivdYsUV+/s2qKfXs= -github.com/klauspost/compress v1.16.0 h1:iULayQNOReoYUe+1qtKOqw9CwJv3aNQu8ivo7lw1HU4= -github.com/klauspost/compress v1.16.0/go.mod h1:ntbaceVETuRiXiv4DpjP66DpAtAGkEQskQzEyD//IeE= +github.com/klauspost/compress v1.17.0 h1:Rnbp4K9EjcDuVuHtd0dgA4qNuv9yKDYKK1ulpJwgrqM= +github.com/klauspost/compress v1.17.0/go.mod h1:ntbaceVETuRiXiv4DpjP66DpAtAGkEQskQzEyD//IeE= github.com/klauspost/cpuid v1.2.0/go.mod h1:Pj4uuM528wm8OyEC2QMXAi2YiTZ96dNQPGgoMS4s3ek= github.com/klauspost/cpuid/v2 v2.2.3 h1:sxCkb+qR91z4vsqw4vGGZlDgPz3G7gjaLyK3V8y70BU= github.com/klauspost/cpuid/v2 v2.2.3/go.mod h1:RVVoqg1df56z8g3pUjL/3lE5UfnlrJX8tyFgg4nqhuY= @@ -269,6 +280,7 @@ github.com/kr/pty v1.1.1/go.mod h1:pFQYn66WHrOpPYNljwOMqo10TkYh1fy3cYio2l3bCsQ= github.com/kr/text v0.1.0/go.mod h1:4Jbv+DJW3UT/LiOwJeYQe1efqtUx/iVham/4vfdArNI= github.com/kr/text v0.2.0 h1:5Nx0Ya0ZqY2ygV366QzturHI13Jq95ApcVaJBhpS+AY= github.com/kr/text v0.2.0/go.mod h1:eLer722TekiGuMkidMxC/pM04lWEeraHUUmBw8l2grE= +github.com/linuxkit/virtsock v0.0.0-20201010232012-f8cee7dfc7a3/go.mod h1:3r6x7q95whyfWQpmGZTu3gk3v2YkMi05HEzl7Tf7YEo= github.com/magiconair/properties v1.8.7 h1:IeQXZAiQcpL9mgcAe1Nu6cX9LLw6ExEHKjN0VQdvPDY= github.com/magiconair/properties v1.8.7/go.mod h1:Dhd985XPs7jluiymwWYZ0G4Z61jb3vdS329zhj2hYo0= github.com/manifoldco/promptui v0.9.0 h1:3V4HzJk1TtXW1MTZMP7mdlwbBpIinw3HztaIlYthEiA= @@ -277,13 +289,14 @@ github.com/matryer/is v1.2.0 h1:92UTHpy8CDwaJ08GqLDzhhuixiBUUD1p3AU6PHddz4A= github.com/matryer/is v1.2.0/go.mod h1:2fLPjFQM9rhQ15aVEtbuwhJinnOqrmgXPNdZsdwlWXA= github.com/mattn/go-colorable v0.1.4/go.mod h1:U0ppj6V5qS13XJ6of8GYAs25YV2eR4EVcfRqFIhoBtE= github.com/mattn/go-colorable v0.1.7/go.mod h1:u6P/XSegPjTcexA+o6vUJrdnUu04hMope9wVRipJSqc= -github.com/mattn/go-colorable v0.1.12 h1:jF+Du6AlPIjs2BiUiQlKOX0rt3SujHxPnksPKZbaA40= -github.com/mattn/go-colorable v0.1.12/go.mod h1:u5H1YNBxpqRaxsYJYSkiCWKzEfiAb1Gb520KVy5xxl4= +github.com/mattn/go-colorable v0.1.13 h1:fFA4WZxdEF4tXPZVKMLwD8oUnCTTo08duU7wxecdEvA= +github.com/mattn/go-colorable v0.1.13/go.mod h1:7S9/ev0klgBDR4GtXTXX8a3vIGJpMovkB8vQcUbaXHg= github.com/mattn/go-isatty v0.0.8/go.mod h1:Iq45c/XA43vh69/j3iqttzPXn0bhXyGjM0Hdxcsrc5s= github.com/mattn/go-isatty v0.0.10/go.mod h1:qgIWMr58cqv1PHHyhnkY9lrL7etaEgOFcMEpPG5Rm84= github.com/mattn/go-isatty v0.0.12/go.mod h1:cbi8OIDigv2wuxKPP5vlRcQ1OAZbq2CE4Kysco4FUpU= -github.com/mattn/go-isatty v0.0.14 h1:yVuAays6BHfxijgZPzw+3Zlu5yQgKGP2/hcQbHb7S9Y= -github.com/mattn/go-isatty v0.0.14/go.mod h1:7GGIvUiUoEMVVmxf/4nioHXj79iQHKdU27kJ6hsGG94= +github.com/mattn/go-isatty v0.0.16/go.mod h1:kYGgaQfpe5nmfYZH+SKPsOc2e4SrIfOl2e/yFXSvRLM= +github.com/mattn/go-isatty v0.0.17 h1:BTarxUcIeDqL27Mc+vyvdWYSL28zpIhv3RoTdsLMPng= +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= @@ -294,8 +307,10 @@ github.com/mholt/archiver/v3 v3.5.1 h1:rDjOBX9JSF5BvoJGvjqK479aL70qh9DIpZCl+k7Cl github.com/mholt/archiver/v3 v3.5.1/go.mod h1:e3dqJ7H78uzsRSEACH1joayhuSyhnonssnDhppzS1L4= github.com/minio/sha256-simd v1.0.1 h1:6kaan5IFmwTNynnKKpDHe6FWHohJOHhCPchzK49dzMM= github.com/minio/sha256-simd v1.0.1/go.mod h1:Pz6AKMiUdngCLpeTL/RJY1M9rUuPMYujV5xJjtbRSN8= +github.com/mitchellh/go-homedir v1.1.0/go.mod h1:SfyaCUpYCn1Vlf4IUYiD9fPX4A5wJrkLzIz1N1q0pr0= github.com/mitchellh/mapstructure v1.5.0 h1:jeMsZIYE/09sWLaz43PL7Gy6RuMjD2eJVyuac5Z2hdY= github.com/mitchellh/mapstructure v1.5.0/go.mod h1:bFUtVrKA4DC2yAKiSyO/QUcy7e+RRV2QTWOzhPopBRo= +github.com/moby/locker v1.0.1/go.mod h1:S7SDdo5zpBK84bzzVlKr2V0hz+7x9hWbYC/kq7oQppc= github.com/moby/patternmatcher v0.5.0 h1:YCZgJOeULcxLw1Q+sVR636pmS7sPEn1Qo2iAN6M7DBo= github.com/moby/patternmatcher v0.5.0/go.mod h1:hDPoyOpDY7OrrMDLaYoY3hf52gNCR/YOUYxkhApJIxc= github.com/moby/sys/mountinfo v0.5.0/go.mod h1:3bMD3Rg+zkqx8MRYPi7Pyb0Ie97QEBmdxbhnCLlSvSU= @@ -303,12 +318,13 @@ github.com/moby/sys/sequential v0.5.0 h1:OPvI35Lzn9K04PBbCLW0g4LcFAJgHsvXsRyewg5 github.com/moby/sys/sequential v0.5.0/go.mod h1:tH2cOOs5V9MlPiXcQzRC+eEyab644PWKGRYaaV5ZZlo= github.com/moby/term v0.5.0 h1:xt8Q1nalod/v7BqbG21f8mQPqH+xAaC9C3N3wfWbVP0= github.com/moby/term v0.5.0/go.mod h1:8FzsFHVUBGZdbDsJw/ot+X+d5HLUbvklYLJ9uGfcI3Y= +github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd/go.mod h1:6dJC0mAP4ikYIbvyc7fijjWJddQyLn8Ig3JB5CqoB9Q= github.com/morikuni/aec v1.0.0 h1:nP9CBfwrvYnBRgY6qfDQkygYDmYwOilePFkwzv4dU8A= github.com/morikuni/aec v1.0.0/go.mod h1:BbKIizmSmc5MMPqRYbxO4ZU0S0+P200+tUnFx7PXmsc= github.com/mrunalp/fileutils v0.5.0/go.mod h1:M1WthSahJixYnrXQl/DFQuteStB1weuxD2QJNHXfbSQ= -github.com/niemeyer/pretty v0.0.0-20200227124842-a10e7caefd8e/go.mod h1:zD1mROLANZcx1PVRCS0qkT7pwLkGfwJo4zjcN/Tysno= github.com/nwaples/rardecode v1.1.0 h1:vSxaY8vQhOcVr4mm5e8XllHWTiM4JF507A0Katqw7MQ= github.com/nwaples/rardecode v1.1.0/go.mod h1:5DzqNKiOdpKKBH87u8VlvAnPZMXcGRhxWkRpHbbfGS0= +github.com/onsi/gomega v1.27.10 h1:naR28SdDFlqrG6kScpT8VWpu1xWY5nJRCF3XaYyBjhI= github.com/opencontainers/go-digest v1.0.0 h1:apOUWs51W5PlhuyGyz9FCeeBIOUDA/6nW8Oi/yOhh5U= github.com/opencontainers/go-digest v1.0.0/go.mod h1:0JzlMkj0TRzQZfJkVvzbP0HBR3IKzErnv2BNG4W4MAM= github.com/opencontainers/image-spec v1.1.0-rc4 h1:oOxKUJWnFC4YGHCCMNql1x4YaDfYBTS5Y4x/Cgeo1E0= @@ -318,10 +334,10 @@ github.com/opencontainers/runc v1.1.5/go.mod h1:1J5XiS+vdZ3wCyZybsuxXZWGrgSr8fFJ github.com/opencontainers/runtime-spec v1.0.3-0.20210326190908-1c3f411f0417/go.mod h1:jwyrGlmzljRJv/Fgzds9SsS/C5hL+LL3ko9hs6T5lQ0= github.com/opencontainers/selinux v1.10.0/go.mod h1:2i0OySw99QjzBBQByd1Gr9gSjvuho1lHsJxIJ3gGbJI= github.com/owenrumney/go-sarif v1.1.1/go.mod h1:dNDiPlF04ESR/6fHlPyq7gHKmrM0sHUvAGjsoh8ZH0U= -github.com/owenrumney/go-sarif/v2 v2.2.0 h1:1DmZaijK0HBZCR1fgcDSGa7VzYkU9NDmbZ7qC2QfUjE= -github.com/owenrumney/go-sarif/v2 v2.2.0/go.mod h1:MSqMMx9WqlBSY7pXoOZWgEsVB4FDNfhcaXDA1j6Sr+w= -github.com/pelletier/go-toml/v2 v2.0.8 h1:0ctb6s9mE31h0/lhu+J6OPmVeDxJn+kYnJc2jZR9tGQ= -github.com/pelletier/go-toml/v2 v2.0.8/go.mod h1:vuYfssBdrU2XDZ9bYydBu6t+6a6PYNcZljzZR9VXg+4= +github.com/owenrumney/go-sarif/v2 v2.3.0 h1:wP5yEpI53zr0v5cBmagXzLbHZp9Oylyo3AJDpfLBITs= +github.com/owenrumney/go-sarif/v2 v2.3.0/go.mod h1:MSqMMx9WqlBSY7pXoOZWgEsVB4FDNfhcaXDA1j6Sr+w= +github.com/pelletier/go-toml/v2 v2.1.0 h1:FnwAJ4oYMvbT/34k9zzHuZNrhlz48GB3/s6at6/MHO4= +github.com/pelletier/go-toml/v2 v2.1.0/go.mod h1:tJU2Z3ZkXwnxa4DPO899bsyIoywizdUvyaeZurnPPDc= github.com/pierrec/lz4/v4 v4.1.2 h1:qvY3YFXRQE/XB8MlLzJH7mSzBs74eA2gg52YTk6jUPM= github.com/pierrec/lz4/v4 v4.1.2/go.mod h1:gZWDp/Ze/IJXGXf23ltt2EXimqmTUXEy0GFuRQyBid4= github.com/pjbgf/sha1cd v0.3.0 h1:4D5XXmUUBUl/xQ6IjCkEAbqXskkq/4O7LmGn0AqMDs4= @@ -334,17 +350,22 @@ github.com/pkg/profile v1.6.0/go.mod h1:qBsxPvzyUincmltOk6iyRVxHYg4adc0OFOv72ZdL github.com/pkg/sftp v1.13.1/go.mod h1:3HaPG6Dq1ILlpPZRO0HVMrsydcdLt6HRDccSgb87qRg= 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 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM= github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4= +github.com/pmezard/go-difflib v1.0.1-0.20181226105442-5d4384ee4fb2 h1:Jamvg5psRIccs7FGNTlIRMkT8wgtp5eCXdBlqhYGL6U= +github.com/pmezard/go-difflib v1.0.1-0.20181226105442-5d4384ee4fb2/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4= github.com/prometheus/client_model v0.0.0-20190812154241-14fe0d1b01d4/go.mod h1:xMI15A0UPsDsEKsMN9yxemIoYk6Tm2C1GtYGdfGttqA= github.com/rivo/uniseg v0.2.0/go.mod h1:J6wj4VEh+S6ZtnVlnTBMWIodfgj8LQOQFoIToxlJtxc= github.com/rivo/uniseg v0.4.3 h1:utMvzDsuh3suAEnhH0RdHmoPbU648o6CvXxTx4SBMOw= github.com/rivo/uniseg v0.4.3/go.mod h1:FN3SvrM+Zdj16jyLfmOkMNblXMcoc8DfTHruCPUcx88= github.com/rogpeppe/go-internal v1.3.0/go.mod h1:M8bDsm7K2OlrFYOpmOWEs/qY81heoFRclV5y23lUDJ4= -github.com/rogpeppe/go-internal v1.9.0 h1:73kH8U+JUqXU8lRuOHeVHaa/SZPifC7BkcraZVejAe8= +github.com/rogpeppe/go-internal v1.11.0 h1:cWPaGQEPrBb5/AsnsZesgZZ9yb1OQ+GOISoDNXVBh4M= github.com/russross/blackfriday/v2 v2.0.1/go.mod h1:+Rmxgy9KzJVeS9/2gXHxylqXiyQDYRxCVz55jmeOWTM= github.com/russross/blackfriday/v2 v2.1.0 h1:JIOH55/0cWyOuilr9/qlrm0BSXldqnqwMsf35Ld67mk= github.com/russross/blackfriday/v2 v2.1.0/go.mod h1:+Rmxgy9KzJVeS9/2gXHxylqXiyQDYRxCVz55jmeOWTM= +github.com/sagikazarmark/locafero v0.3.0 h1:zT7VEGWC2DTflmccN/5T1etyKvxSxpHsjb9cJvm4SvQ= +github.com/sagikazarmark/locafero v0.3.0/go.mod h1:w+v7UsPNFwzF1cHuOajOOzoq4U7v/ig1mpRjqV+Bu1U= +github.com/sagikazarmark/slog-shim v0.1.0 h1:diDBnUNK9N/354PgrxMywXnAwEr1QZcOr6gto+ugjYE= +github.com/sagikazarmark/slog-shim v0.1.0/go.mod h1:SrcSrq8aKtyuqEI1uvTDTK1arOWRIczQRv+GVI1AkeQ= github.com/seccomp/libseccomp-golang v0.9.2-0.20220502022130-f33da4d89646/go.mod h1:JA8cRccbGaA1s33RQf7Y1+q9gHmZX1yB/z9WDN1C6fg= github.com/sergi/go-diff v1.1.0 h1:we8PVUC3FE2uYfodKH/nBHMSetSfHDR6scGdBi+erh0= github.com/sergi/go-diff v1.1.0/go.mod h1:STckp+ISIX8hZLjrqAeVduY0gWCT9IjLuqbuNXdaHfM= @@ -355,16 +376,17 @@ github.com/sirupsen/logrus v1.9.0 h1:trlNQbNUG3OdDrDil03MCb1H2o9nJ1x4/5LYw7byDE0 github.com/sirupsen/logrus v1.9.0/go.mod h1:naHLuLoDiP4jHNo9R0sCBMtWGeIprob74mVsIT4qYEQ= github.com/skeema/knownhosts v1.2.0 h1:h9r9cf0+u7wSE+M183ZtMGgOJKiL96brpaz5ekfJCpM= github.com/skeema/knownhosts v1.2.0/go.mod h1:g4fPeYpque7P0xefxtGzV81ihjC8sX2IqpAoNkjxbMo= -github.com/spf13/afero v1.9.5 h1:stMpOSZFs//0Lv29HduCmli3GUfpFoF3Y1Q/aXj/wVM= -github.com/spf13/afero v1.9.5/go.mod h1:UBogFpq8E9Hx+xc5CNTTEpTnuHVmXDwZcZcE1eb/UhQ= +github.com/sourcegraph/conc v0.3.0 h1:OQTbbt6P72L20UqAkXXuLOj79LfEanQ+YQFNpLA9ySo= +github.com/sourcegraph/conc v0.3.0/go.mod h1:Sdozi7LEKbFPqYX2/J+iBAM6HpqSLTASQIKqDmF7Mt0= +github.com/spf13/afero v1.10.0 h1:EaGW2JJh15aKOejeuJ+wpFSHnbd7GE6Wvp3TsNhb6LY= +github.com/spf13/afero v1.10.0/go.mod h1:UBogFpq8E9Hx+xc5CNTTEpTnuHVmXDwZcZcE1eb/UhQ= github.com/spf13/cast v1.5.1 h1:R+kOtfhWQE6TVQzY+4D7wJLBgkdVasCEFxSUBYBYIlA= github.com/spf13/cast v1.5.1/go.mod h1:b9PdjNptOpzXr7Rq1q9gJML/2cdGQAo69NKzQ10KN48= -github.com/spf13/jwalterweatherman v1.1.0 h1:ue6voC5bR5F8YxI5S67j9i582FU4Qvo2bmqnqMYADFk= -github.com/spf13/jwalterweatherman v1.1.0/go.mod h1:aNWZUN0dPAAO/Ljvb5BEdw96iTZ0EXowPYD95IqWIGo= github.com/spf13/pflag v1.0.5 h1:iy+VFUOCP1a+8yFto/drg2CJ5u0yRoB7fZw3DKv/JXA= github.com/spf13/pflag v1.0.5/go.mod h1:McXfInJRrz4CZXVZOBLb0bTZqETkiAhM9Iw0y3An2Bg= -github.com/spf13/viper v1.16.0 h1:rGGH0XDZhdUOryiDWjmIvUSWpbNqisK8Wk0Vyefw8hc= -github.com/spf13/viper v1.16.0/go.mod h1:yg78JgCJcbrQOvV9YLXgkLaZqUidkY9K+Dd1FofRzQg= +github.com/spf13/viper v1.17.0 h1:I5txKw7MJasPL/BrfkbA0Jyo/oELqVmux4pR/UxOMfI= +github.com/spf13/viper v1.17.0/go.mod h1:BmMMMLQXSbcHK6KAOiFLz0l5JHrU89OdIRHvsk0+yVI= +github.com/stefanberger/go-pkcs11uri v0.0.0-20201008174630-78d3cae3a980/go.mod h1:AO3tvPzVZ/ayst6UlUKUv6rcPQInYe3IknH3jYhAKu8= github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME= github.com/stretchr/objx v0.4.0/go.mod h1:YvHI0jy2hoMjB+UWwv71VJQ9isScKT/TqJzVSSt89Yw= github.com/stretchr/objx v0.5.0/go.mod h1:Yh+to48EsGEfYuaHDzXPcE3xhTkx73EhmCGUpEOglKo= @@ -376,11 +398,10 @@ github.com/stretchr/testify v1.7.0/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/ 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.3/go.mod h1:sz/lmYIOXD/1dqDmKjjqLyZ2RngseejIcXlSw2iwfAo= github.com/stretchr/testify v1.8.4 h1:CcVxjf3Q8PM0mHUKJCdn+eZZtm5yQwehR5yeSVQQcUk= github.com/stretchr/testify v1.8.4/go.mod h1:sz/lmYIOXD/1dqDmKjjqLyZ2RngseejIcXlSw2iwfAo= -github.com/subosito/gotenv v1.4.2 h1:X1TuBLAMDFbaTAChgCBLu3DU3UPyELpnF2jjJ2cz/S8= -github.com/subosito/gotenv v1.4.2/go.mod h1:ayKnFf/c6rvx/2iiLrJUk1e6plDbT3edrFNGqEflhK0= +github.com/subosito/gotenv v1.6.0 h1:9NlTDc1FTs4qu0DDq7AEtTPNw6SVm7uBMsUCUjABIf8= +github.com/subosito/gotenv v1.6.0/go.mod h1:Dk4QP5c2W3ibzajGcXpNraDfq2IrhjMIvMSWPKKo0FU= github.com/syndtr/gocapability v0.0.0-20200815063812-42c35b437635/go.mod h1:hkRG7XYTFWNJGYcbNJQlaLq0fg1yr4J4t/NcTQtrfww= github.com/terminalstatic/go-xsd-validate v0.1.5 h1:RqpJnf6HGE2CB/lZB1A8BYguk8uRtcvYAPLCF15qguo= github.com/testcontainers/testcontainers-go v0.23.0 h1:ERYTSikX01QczBLPZpqsETTBO7lInqEP349phDOVJVs= @@ -399,8 +420,9 @@ github.com/vmihailenco/msgpack/v4 v4.3.12/go.mod h1:gborTTJjAo/GWTqqRjrLCn9pgNN+ github.com/vmihailenco/tagparser v0.1.1/go.mod h1:OeAg3pn3UbLjkWt+rN9oFYB6u/cQgqMEUPoW2WPyhdI= github.com/xanzy/ssh-agent v0.3.3 h1:+/15pJfg/RsTxqYcX6fHqOXZwwMP+2VyYWJeWM2qQFM= github.com/xanzy/ssh-agent v0.3.3/go.mod h1:6dzNDKs0J9rVPHPhaGCukekBHKqfl+L3KghI1Bc68Uw= -github.com/xeipuuv/gojsonpointer v0.0.0-20180127040702-4e3ac2762d5f h1:J9EGpcZtP0E/raorCMxlFGSTBrsSlaDGf3jU/qvAE2c= github.com/xeipuuv/gojsonpointer v0.0.0-20180127040702-4e3ac2762d5f/go.mod h1:N2zxlSyiKSe5eX1tZViRH5QA0qijqEDrYZiPEAiq3wU= +github.com/xeipuuv/gojsonpointer v0.0.0-20190905194746-02993c407bfb h1:zGWFAtiMcyryUHoUjUJX0/lt1H2+i2Ka2n+D3DImSNo= +github.com/xeipuuv/gojsonpointer v0.0.0-20190905194746-02993c407bfb/go.mod h1:N2zxlSyiKSe5eX1tZViRH5QA0qijqEDrYZiPEAiq3wU= github.com/xeipuuv/gojsonreference v0.0.0-20180127040603-bd5ef7bd5415 h1:EzJWgHovont7NscjpAxXsDA8S8BMYve8Y5+7cuRE7R0= github.com/xeipuuv/gojsonreference v0.0.0-20180127040603-bd5ef7bd5415/go.mod h1:GwrjFmJcFw6At/Gs6z4yjiIwzuJ1/+UwLxMQDVQXShQ= github.com/xeipuuv/gojsonschema v1.2.0 h1:LhYJRs+L4fBtjZUfuSZIKGeVu0QRy8e5Xi7D17UxZ74= @@ -415,12 +437,17 @@ github.com/yuin/goldmark v1.1.32/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9de github.com/yuin/goldmark v1.2.1/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74= github.com/yuin/goldmark v1.4.13/go.mod h1:6yULJ656Px+3vBD8DxQVa3kxgyrAnzto9xy5taEt/CY= github.com/zclconf/go-cty v1.10.0/go.mod h1:vVKLxnk3puL4qRAv72AO+W99LUD4da90g3uUAzyuvAk= +go.mozilla.org/pkcs7 v0.0.0-20200128120323-432b2356ecb1/go.mod h1:SNgMg+EgDFwmvSmLRTNKC5fegJjB7v23qTQ0XLGUNHk= go.opencensus.io v0.21.0/go.mod h1:mSImk1erAIZhrmZN+AvHh14ztQfjbGwt4TtuofqLduU= go.opencensus.io v0.22.0/go.mod h1:+kGneAE2xo2IficOXnaByMWTGM9T73dGwxeWcUqIpI8= go.opencensus.io v0.22.2/go.mod h1:yxeiOL68Rb0Xd1ddK5vPZ/oVn4vY4Ynel7k9FzqtOIw= go.opencensus.io v0.22.3/go.mod h1:yxeiOL68Rb0Xd1ddK5vPZ/oVn4vY4Ynel7k9FzqtOIw= go.opencensus.io v0.22.4/go.mod h1:yxeiOL68Rb0Xd1ddK5vPZ/oVn4vY4Ynel7k9FzqtOIw= go.opencensus.io v0.22.5/go.mod h1:5pWMHQbX5EPX2/62yrJeAkowc+lfs/XD7Uxpq3pI6kk= +go.uber.org/atomic v1.9.0 h1:ECmE8Bn/WFTYwEW/bpKD3M8VtR/zQVbavAoalC1PYyE= +go.uber.org/atomic v1.9.0/go.mod h1:fEN4uk6kAWBTFdckzkM89CLk9XfWZrxpCo0nPH17wJc= +go.uber.org/multierr v1.9.0 h1:7fIwc/ZtS0q++VgcfqFDxSBZVv/Xo49/SYnDFupUwlI= +go.uber.org/multierr v1.9.0/go.mod h1:X2jQV1h+kxSjClGpnseKVIxpmcjrj7MNnI0bnlfKTVQ= golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w= golang.org/x/crypto v0.0.0-20190510104115-cbcb75029529/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI= golang.org/x/crypto v0.0.0-20190605123033-f99c8df09eb5/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI= @@ -432,8 +459,8 @@ golang.org/x/crypto v0.0.0-20220622213112-05595931fe9d/go.mod h1:IxCIyHEi3zRg3s0 golang.org/x/crypto v0.0.0-20220722155217-630584e8d5aa/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.12.0 h1:tFM/ta59kqch6LlvYnPa0yx5a83cL2nHflFhYKvv9Yk= -golang.org/x/crypto v0.12.0/go.mod h1:NF0Gs7EO5K4qLn+Ylc+fih8BSTeIjAP05siRnAh98yw= +golang.org/x/crypto v0.14.0 h1:wBqGXzWJW6m1XrIKlAH0Hs1JJ7+9KBwnIO8v66Q9cHc= +golang.org/x/crypto v0.14.0/go.mod h1:MVFd36DqK4CsrnJYDkBA3VC4m2GkXAM0PvzMCn4JQf4= golang.org/x/exp v0.0.0-20190121172915-509febef88a4/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA= golang.org/x/exp v0.0.0-20190306152737-a1d7652674e8/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA= golang.org/x/exp v0.0.0-20190510132918-efd6b22b2522/go.mod h1:ZjyILWgesfNpC6sMxTJOJm9Kp84zZh5NQWvqDGG3Qr8= @@ -444,8 +471,8 @@ golang.org/x/exp v0.0.0-20191227195350-da58074b4299/go.mod h1:2RIsYlXP63K8oxa1u0 golang.org/x/exp v0.0.0-20200119233911-0405dc783f0a/go.mod h1:2RIsYlXP63K8oxa1u096TMicItID8zy7Y6sNkU49FU4= golang.org/x/exp v0.0.0-20200207192155-f17229e696bd/go.mod h1:J/WKrq2StrnmMY6+EHIKF9dgMWnmCNThgcyBT1FY9mM= golang.org/x/exp v0.0.0-20200224162631-6cc2880d07d6/go.mod h1:3jZMyOhIsHpP37uCMkUooju7aAi5cS1Q23tOzKc+0MU= -golang.org/x/exp v0.0.0-20230817173708-d852ddb80c63 h1:m64FZMko/V45gv0bNmrNYoDEq8U5YUhetc9cBWKS1TQ= -golang.org/x/exp v0.0.0-20230817173708-d852ddb80c63/go.mod h1:0v4NqG35kSWCMzLaMeX+IQrlSnVE/bqGSyC2cz/9Le8= +golang.org/x/exp v0.0.0-20231006140011-7918f672742d h1:jtJma62tbqLibJ5sFQz8bKtEM8rJBtfilJ2qTU199MI= +golang.org/x/exp v0.0.0-20231006140011-7918f672742d/go.mod h1:ldy0pHrwJyGW56pPQzzkH36rKxoZW1tw7ZJpeKx+hdo= golang.org/x/image v0.0.0-20190227222117-0694c2d4d067/go.mod h1:kZ7UVZpmo3dzQBMxlp+ypCbDeSB+sBbTgSJuh5dn5js= golang.org/x/image v0.0.0-20190802002840-cff245a6509b/go.mod h1:FeLwcggjj3mMvU+oOTbSwawSJRM1uh48EjtB4UJZlP0= golang.org/x/lint v0.0.0-20181026193005-c67002cb31c3/go.mod h1:UVdnD1Gm6xHRNCYTkRU2/jEulfH38KcIWyp/GAMgvoE= @@ -471,8 +498,8 @@ golang.org/x/mod v0.4.0/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA= golang.org/x/mod v0.4.1/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA= golang.org/x/mod v0.6.0-dev.0.20220419223038-86c51ed26bb4/go.mod h1:jJ57K6gSWd91VN4djpZkiMVwK6gcyfeH4XE8wZrZaV4= golang.org/x/mod v0.8.0/go.mod h1:iBbtSCu2XBx23ZKBPSOrRkjjQPZFPuis4dIYUhu/chs= -golang.org/x/mod v0.12.0 h1:rmsUpXtvNzj340zd98LZ4KntptpfRHwpFOHG188oHXc= -golang.org/x/mod v0.12.0/go.mod h1:iBbtSCu2XBx23ZKBPSOrRkjjQPZFPuis4dIYUhu/chs= +golang.org/x/mod v0.13.0 h1:I/DsJXRlw/8l/0c24sM9yb0T4z9liZTduXvdAWYiysY= +golang.org/x/mod v0.13.0/go.mod h1:hTbmBsO62+eylJbnUtE2MGJUyE7QWk4xUqPFrRgJ+7c= golang.org/x/net v0.0.0-20180724234803-3673e40ba225/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= golang.org/x/net v0.0.0-20180826012351-8a410e7b638d/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= golang.org/x/net v0.0.0-20190108225652-1e06a53dbb7e/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= @@ -509,8 +536,8 @@ 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.14.0 h1:BONx9s002vGdD9umnlX1Po8vOZmrgH34qlHcD1MfK14= -golang.org/x/net v0.14.0/go.mod h1:PpSgVXXLK0OxS0F31C1/tv6XNguvCrnXIDrFMspZIUI= +golang.org/x/net v0.17.0 h1:pVaXccu2ozPjCXewfr1S7xza/zcXTity9cCdXQYSjIM= +golang.org/x/net v0.17.0/go.mod h1:NxSsAGuq816PNPmqtQdLE42eU2Fs7NoRIZrHJAlaCOE= golang.org/x/oauth2 v0.0.0-20180821212333-d2e6202438be/go.mod h1:N/0e6XlmueqKjAGxoOufVs8QHGRruUQn6yWY3a++T0U= golang.org/x/oauth2 v0.0.0-20190226205417-e64efc72b421/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw= golang.org/x/oauth2 v0.0.0-20190604053449-0f29369cfe45/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw= @@ -532,8 +559,8 @@ golang.org/x/sync v0.0.0-20201020160332-67f06af15bc9/go.mod h1:RxMgew5VJxzue5/jJ golang.org/x/sync v0.0.0-20201207232520-09787c993a3a/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.3.0 h1:ftCYgMx6zT/asHUrPw8BLLscYtGznsLAnjq5RH9P66E= -golang.org/x/sync v0.3.0/go.mod h1:FU7BRWz2tNW+3quACPkgCx/L+uEAv1htQ0V83Z9Rj+Y= +golang.org/x/sync v0.4.0 h1:zxkM55ReGkDlKSM+Fu41A+zmbZuaPVbGMzvvdUPznYQ= +golang.org/x/sync v0.4.0/go.mod h1:FU7BRWz2tNW+3quACPkgCx/L+uEAv1htQ0V83Z9Rj+Y= golang.org/x/sys v0.0.0-20180830151530-49385e6e1522/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= 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= @@ -582,9 +609,7 @@ golang.org/x/sys v0.0.0-20210423185535-09eb48e85fd7/go.mod h1:h1NjWce9XRLGQEsW7w golang.org/x/sys v0.0.0-20210615035016-665e8c7367d1/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20210616045830-e2b7044e8c71/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20210616094352-59db8d763f22/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= -golang.org/x/sys v0.0.0-20210630005230-0f9fa26af87c/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20210906170528-6f6e22806c34/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= -golang.org/x/sys v0.0.0-20210927094055-39ccf1dd6fa6/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20211025201205-69cdffdb9359/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20211116061358-0a5406a5449c/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20220310020820-b874c991c1a5/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= @@ -592,6 +617,7 @@ golang.org/x/sys v0.0.0-20220520151302-bc2c85ada10a/go.mod h1:oPkhp1MJrh7nUepCBc golang.org/x/sys v0.0.0-20220704084225-05e143d24a9e/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20220715151400-c0bba94af5f8/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20220722155257-8c9f86f7a55f/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.0.0-20220811171246-fbc7d0a398ab/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20220908164124-27713097b956/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20220909162455-aba9fc2a8ff2/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.1.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= @@ -599,15 +625,15 @@ golang.org/x/sys v0.2.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.3.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.5.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.6.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= -golang.org/x/sys v0.11.0 h1:eG7RXZHdqOJ1i+0lgLgCpSXAp6M3LYlAo6osgSi0xOM= -golang.org/x/sys v0.11.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.13.0 h1:Af8nKPmuFypiUBjVoU9V20FiaFXOcuZI21p0ycVYYGE= +golang.org/x/sys v0.13.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo= golang.org/x/term v0.0.0-20210927222741-03fcf44c2211/go.mod h1:jbD1KX2456YbFQfuXm/mYQcufACuNUgVhRMnK/tPxf8= golang.org/x/term v0.2.0/go.mod h1:TVmDHMZPmdnySmBfhjOoOdhjzdE1h4u1VwSiw2l1Nuc= golang.org/x/term v0.5.0/go.mod h1:jMB1sMXY+tzblOD4FWmEbocvup2/aLOaQEp7JmGp78k= golang.org/x/term v0.6.0/go.mod h1:m6U89DPEgQRMq3DNkDClhWw02AUbt2daBVO4cn4Hv9U= -golang.org/x/term v0.11.0 h1:F9tnn/DA/Im8nCwm+fX+1/eBwi4qFjRT++MhtVC4ZX0= -golang.org/x/term v0.11.0/go.mod h1:zC9APTIj3jG3FdV/Ons+XE1riIZXG4aZ4GTHiPZJPIU= +golang.org/x/term v0.13.0 h1:bb+I9cTfFazGW51MZqBVmZy7+JEJMouUHTUSKVQLBek= +golang.org/x/term v0.13.0/go.mod h1:LTmsnFJwVN6bCy1rVCoS+qHT1HhALEFxKncY3WNNh4U= golang.org/x/text v0.0.0-20170915032832-14c0d48ead0c/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= golang.org/x/text v0.3.1-0.20180807135948-17ff2d5776d2/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= @@ -620,12 +646,12 @@ golang.org/x/text v0.3.7/go.mod h1:u+2+/6zg+i71rQMx5EYifcz6MCKuco9NR6JIITiCfzQ= golang.org/x/text v0.4.0/go.mod h1:mrYo+phRRbMaCq/xk9113O4dZlRixOauAjOtrjsXDZ8= golang.org/x/text v0.7.0/go.mod h1:mrYo+phRRbMaCq/xk9113O4dZlRixOauAjOtrjsXDZ8= golang.org/x/text v0.8.0/go.mod h1:e1OnstbJyHTd6l/uOt8jFFHp6TRDWZR/bV3emEE/zU8= -golang.org/x/text v0.12.0 h1:k+n5B8goJNdU7hSvEtMUz3d1Q6D/XW4COJSJR6fN0mc= -golang.org/x/text v0.12.0/go.mod h1:TvPlkZtksWOMsz7fbANvkp4WM8x/WCo/om8BMLbz+aE= +golang.org/x/text v0.13.0 h1:ablQoSUd0tRdKxZewP80B+BaqeKJuVhuRxj/dkrun3k= +golang.org/x/text v0.13.0/go.mod h1:TvPlkZtksWOMsz7fbANvkp4WM8x/WCo/om8BMLbz+aE= golang.org/x/time v0.0.0-20181108054448-85acf8d2951c/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ= golang.org/x/time v0.0.0-20190308202827-9d24e82272b4/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ= golang.org/x/time v0.0.0-20191024005414-555d28b269f0/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ= -golang.org/x/time v0.1.0 h1:xYY+Bajn2a7VBmTM5GikTmnK8ZuX8YgnQCqZpbBNtmA= +golang.org/x/time v0.3.0 h1:rg5rLMjNzMS1RkNLzCG38eapWhnYLFYXDXj2gOlr8j4= golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= golang.org/x/tools v0.0.0-20190114222345-bf090417da8b/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= golang.org/x/tools v0.0.0-20190226205152-f727befe758c/go.mod h1:9Yl7xja0Znq3iFh3HoIrodX9oNMXvdceNzlUR8zjMvY= @@ -677,8 +703,8 @@ golang.org/x/tools v0.0.0-20210108195828-e2f9c7f1fc8e/go.mod h1:emZCQorbCU4vsT4f golang.org/x/tools v0.1.0/go.mod h1:xkSsbof2nBLbhDlRMhhhyNLN/zl3eTqcnHD5viDpcZ0= 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.12.1-0.20230815132531-74c255bcf846 h1:Vve/L0v7CXXuxUmaMGIEK/dEeq7uiqb5qBgQrZzIE7E= -golang.org/x/tools v0.12.1-0.20230815132531-74c255bcf846/go.mod h1:Sc0INKfu04TlqNoRA1hgpFZbhYXHPr4V5DzpSBTPqQM= +golang.org/x/tools v0.14.0 h1:jvNa2pY0M4r62jkRQ6RwEZZyPcymeL9XZMLBbV7U2nc= +golang.org/x/tools v0.14.0/go.mod h1:uYBEerGOWcJyEORxN+Ek8+TT266gXkNlHdJBwexUsBg= 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= @@ -745,8 +771,8 @@ google.golang.org/genproto v0.0.0-20201210142538-e3217bee35cc/go.mod h1:FWY/as6D google.golang.org/genproto v0.0.0-20201214200347-8c77b98c765d/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no= google.golang.org/genproto v0.0.0-20210108203827-ffc7fda8c3d7/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no= google.golang.org/genproto v0.0.0-20210226172003-ab064af71705/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no= -google.golang.org/genproto/googleapis/rpc v0.0.0-20230525234030-28d5490b6b19 h1:0nDDozoAU19Qb2HwhXadU8OcsiO/09cnTqhUtq2MEOM= -google.golang.org/genproto/googleapis/rpc v0.0.0-20230525234030-28d5490b6b19/go.mod h1:66JfowdXAEgad5O9NnYcsNPLCPZJD++2L9X0PCMODrA= +google.golang.org/genproto/googleapis/rpc v0.0.0-20230920204549-e6e6cdab5c13 h1:N3bU/SQDCDyD6R528GJ/PwW9KjYcJA3dgyH+MovAkIM= +google.golang.org/genproto/googleapis/rpc v0.0.0-20230920204549-e6e6cdab5c13/go.mod h1:KSqppvjFjtoCI+KGd4PELB0qLNxdJHRGqRI09mB6pQA= google.golang.org/grpc v1.19.0/go.mod h1:mqu4LbDTu4XGKhr4mRzUsmM4RtVoemTSY81AxZiDr8c= google.golang.org/grpc v1.20.1/go.mod h1:10oTOabMzJvdu6/UiuZezV6QK5dSlG84ov/aaiqXj38= google.golang.org/grpc v1.21.1/go.mod h1:oYelfM1adQP15Ek0mdvEgi9Df8B9CZIaU1084ijfRaM= @@ -763,8 +789,10 @@ google.golang.org/grpc v1.31.1/go.mod h1:N36X2cJ7JwdamYAgDz+s+rVMFjt3numwzf/HckM google.golang.org/grpc v1.33.2/go.mod h1:JMHMWHQWaTccqQQlmk3MJZS+GWXOdAesneDmEnv2fbc= google.golang.org/grpc v1.34.0/go.mod h1:WotjhfgOW/POjDeRt8vscBtXq+2VjORFy659qA51WJ8= google.golang.org/grpc v1.35.0/go.mod h1:qjiiYl8FncCW8feJPdyg3v6XW24KsRHe+dy9BAGRRjU= -google.golang.org/grpc v1.57.0 h1:kfzNeI/klCGD2YPMUlaGNT3pxvYfga7smW3Vth8Zsiw= -google.golang.org/grpc v1.57.0/go.mod h1:Sd+9RMTACXwmub0zcNY2c4arhtrbBYD1AUHI/dt16Mo= +google.golang.org/grpc v1.58.2 h1:SXUpjxeVF3FKrTYQI4f4KvbGD5u2xccdYdurwowix5I= +google.golang.org/grpc v1.58.2/go.mod h1:tgX3ZQDlNJGU96V6yHh1T/JeoBQ2TXdr43YbYSsCJk0= +google.golang.org/grpc v1.58.3 h1:BjnpXut1btbtgN/6sp+brB2Kbm2LjNXnidYujAVbSoQ= +google.golang.org/grpc v1.58.3/go.mod h1:tgX3ZQDlNJGU96V6yHh1T/JeoBQ2TXdr43YbYSsCJk0= google.golang.org/protobuf v0.0.0-20200109180630-ec00e32a8dfd/go.mod h1:DFci5gLYBciE7Vtevhsrf46CRTquxDuWsQurQQe4oz8= google.golang.org/protobuf v0.0.0-20200221191635-4d8936d0db64/go.mod h1:kwYJMbMJ01Woi6D6+Kah6886xMZcty6N08ah7+eCXa0= google.golang.org/protobuf v0.0.0-20200228230310-ab0ca4ff8a60/go.mod h1:cfTl7dwQJ+fmap5saPgwCLgHXTUD7jkjRqWcaiX5VyM= @@ -778,16 +806,17 @@ google.golang.org/protobuf v1.25.0/go.mod h1:9JNX74DMeImyA3h4bdi1ymwjUzf21/xIlba google.golang.org/protobuf v1.26.0-rc.1/go.mod h1:jlhhOSvTdKEhbULTjvd4ARK9grFBp09yW+WbY/TyQbw= google.golang.org/protobuf v1.26.0/go.mod h1:9q0QmTI4eRPtz6boOQmLYwt+qCgq0jsYwAQnmE0givc= google.golang.org/protobuf v1.27.1/go.mod h1:9q0QmTI4eRPtz6boOQmLYwt+qCgq0jsYwAQnmE0givc= -google.golang.org/protobuf v1.30.0 h1:kPPoIgf3TsEvrm0PFe15JQ+570QVxYzEvvHqChK+cng= -google.golang.org/protobuf v1.30.0/go.mod h1:HV8QOd/L58Z+nl8r43ehVNZIU/HEI6OcFqwMG9pJV4I= +google.golang.org/protobuf v1.31.0 h1:g0LDEJHgrBl9N9r17Ru3sqWhkIx2NB67okBHPwC7hs8= +google.golang.org/protobuf v1.31.0/go.mod h1:HV8QOd/L58Z+nl8r43ehVNZIU/HEI6OcFqwMG9pJV4I= gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= gopkg.in/check.v1 v1.0.0-20180628173108-788fd7840127/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= gopkg.in/check.v1 v1.0.0-20190902080502-41f04d3bba15/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c h1:Hei/4ADfdWqJk1ZMxUNpqntNwaWcugrBjAiHlqqRiVk= -gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c/go.mod h1:JHkPIbrfpd72SG/EVd6muEfDQjcINNoR0C8j2r3qZ4Q= gopkg.in/errgo.v2 v2.1.0/go.mod h1:hNsd1EY+bozCKY1Ytp96fpM3vjJbqLJn88ws8XvfDNI= +gopkg.in/inf.v0 v0.9.1/go.mod h1:cWUDdTG/fYaXco+Dcufb5Vnc6Gp2YChqWtbxRZE0mXw= gopkg.in/ini.v1 v1.67.0 h1:Dgnx+6+nfE+IfzjUEISNeydPJh9AXNNsWbGP9KzCsOA= gopkg.in/ini.v1 v1.67.0/go.mod h1:pNLf8WUiyNEtQjuu5G5vTm06TEv9tsIgeAvK8hOrP4k= +gopkg.in/square/go-jose.v2 v2.5.1/go.mod h1:M9dMgbHiYLoDGQrXy7OpJDJWiKiU//h+vD76mk0e1AI= gopkg.in/warnings.v0 v0.1.2 h1:wFXVbFY8DY5/xOe1ECiWdKCzZlxgshcYVNkBHstARME= gopkg.in/warnings.v0 v0.1.2/go.mod h1:jksf8JmL6Qr/oQM2OXTHunEvvTAsrWBLb6OOjuVWRNI= gopkg.in/yaml.v2 v2.2.2/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= diff --git a/guides/getting-started-with-jfrog-using-the-cli.md b/guides/getting-started-with-jfrog-using-the-cli.md index 301350310..84bfa87d6 100644 --- a/guides/getting-started-with-jfrog-using-the-cli.md +++ b/guides/getting-started-with-jfrog-using-the-cli.md @@ -124,6 +124,6 @@ jf rt bp ### More * Read more - about [JFrog CLI](https://www.jfrog.com/confluence/display/CLI/JFrog+CLI) & [JFrog's IDE Integrations](https://www.jfrog.com/confluence/display/JFROG/IDE+Integration) + about [JFrog CLI](https://docs.jfrog-applications.jfrog.io/jfrog-applications/jfrog-cli) & [JFrog's IDE Integrations](https://docs.jfrog-applications.jfrog.io/jfrog-applications/ide) * **Your feedback is important!** We'd love to get your feedback and answer any questions you may have. Communicate with us through [GitHub](https://github.com/jfrog/jfrog-cli/issues). diff --git a/lifecycle/cli.go b/lifecycle/cli.go index 647963c10..eaf201222 100644 --- a/lifecycle/cli.go +++ b/lifecycle/cli.go @@ -55,7 +55,6 @@ func GetCommands() []cli.Command { ArgsUsage: common.CreateEnvVars(), BashComplete: coreCommon.CreateBashCompletionFunc(), Category: lcCategory, - Hidden: true, Action: distribute, }, }) @@ -170,7 +169,7 @@ func assertSigningKeyProvided(c *cli.Context) error { } func createLifecycleDetailsByFlags(c *cli.Context) (*coreConfig.ServerDetails, error) { - lcDetails, err := cliutils.CreateServerDetailsWithConfigOffer(c, false, cliutils.Platform) + lcDetails, err := cliutils.CreateServerDetailsWithConfigOffer(c, true, cliutils.Platform) if err != nil { return nil, err } diff --git a/lifecycle_test.go b/lifecycle_test.go index 5a1516df8..16fc61248 100644 --- a/lifecycle_test.go +++ b/lifecycle_test.go @@ -14,12 +14,14 @@ import ( "github.com/jfrog/jfrog-client-go/lifecycle" "github.com/jfrog/jfrog-client-go/lifecycle/services" clientUtils "github.com/jfrog/jfrog-client-go/utils" + "github.com/jfrog/jfrog-client-go/utils/distribution" "github.com/jfrog/jfrog-client-go/utils/errorutils" "github.com/stretchr/testify/assert" "net/http" "os" "path/filepath" "testing" + "time" ) const ( @@ -64,14 +66,17 @@ func TestLifecycle(t *testing.T) { promoteRb(t, lcManager, number3) // Verify the artifacts of both the initial release bundles made it to the prod repo. - searchSpec, err := tests.CreateSpec(tests.SearchAllProdRepo) + searchProdSpec, err := tests.CreateSpec(tests.SearchAllProdRepo) assert.NoError(t, err) - inttestutils.VerifyExistInArtifactory(tests.GetExpectedLifecycleArtifacts(), searchSpec, serverDetails, t) + inttestutils.VerifyExistInArtifactory(tests.GetExpectedLifecycleArtifacts(), searchProdSpec, serverDetails, t) distributeRb(t) + defer remoteDeleteReleaseBundle(t, lcManager, tests.LcRbName3, number3) + // Verify the artifacts were distributed correctly by the provided path mappings. - expected := append(tests.GetExpectedLifecycleArtifacts(), tests.GetExpectedLifecycleMappingArtifacts()...) - inttestutils.VerifyExistInArtifactory(expected, searchSpec, serverDetails, t) + searchDevSpec, err := tests.CreateSpec(tests.SearchAllDevRepo) + assert.NoError(t, err) + inttestutils.VerifyExistInArtifactory(tests.GetExpectedLifecycleDistributedArtifacts(), searchDevSpec, serverDetails, t) } func uploadBuilds(t *testing.T) func() { @@ -107,9 +112,11 @@ func distributeRb(t *testing.T) { assert.NoError(t, lcCli.Exec( "rbd", tests.LcRbName3, number3, getOption(cliutils.DistRules, distributionRulesPath), - getOption(cliutils.PathMappingPattern, tests.RtProdRepo+"/(*)"), - getOption(cliutils.PathMappingTarget, tests.RtProdRepo+"/target/{1}"), + getOption(cliutils.PathMappingPattern, "(*)/(*)"), + getOption(cliutils.PathMappingTarget, "{1}/target/{2}"), )) + // Wait after distribution before asserting. Can be removed once distribute supports sync. + time.Sleep(5 * time.Second) } func getOption(option, value string) string { @@ -187,6 +194,20 @@ func deleteReleaseBundle(t *testing.T, lcManager *lifecycle.LifecycleServicesMan assert.NoError(t, lcManager.DeleteReleaseBundle(rbDetails, services.ReleaseBundleQueryParams{Async: false})) } +func remoteDeleteReleaseBundle(t *testing.T, lcManager *lifecycle.LifecycleServicesManager, rbName, rbVersion string) { + params := distribution.NewDistributeReleaseBundleParams(rbName, rbVersion) + rules := &distribution.DistributionCommonParams{ + SiteName: "*", + CityName: "*", + CountryCodes: []string{"*"}, + } + params.DistributionRules = append(params.DistributionRules, rules) + + assert.NoError(t, lcManager.RemoteDeleteReleaseBundle(params, false)) + // Wait after remote deleting. Can be removed once remote deleting supports sync. + time.Sleep(5 * time.Second) +} + func uploadBuild(t *testing.T, specFileName, buildName, buildNumber string) { specFile, err := tests.CreateSpec(specFileName) assert.NoError(t, err) diff --git a/main.go b/main.go index 7304e0906..8cf9155c0 100644 --- a/main.go +++ b/main.go @@ -2,13 +2,6 @@ package main import ( "fmt" - "github.com/jfrog/jfrog-cli/lifecycle" - "golang.org/x/exp/slices" - "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" @@ -23,10 +16,13 @@ import ( "github.com/jfrog/jfrog-cli/docs/common" "github.com/jfrog/jfrog-cli/docs/general/cisetup" loginDocs "github.com/jfrog/jfrog-cli/docs/general/login" + tokenDocs "github.com/jfrog/jfrog-cli/docs/general/token" cisetupcommand "github.com/jfrog/jfrog-cli/general/cisetup" "github.com/jfrog/jfrog-cli/general/envsetup" "github.com/jfrog/jfrog-cli/general/login" "github.com/jfrog/jfrog-cli/general/project" + "github.com/jfrog/jfrog-cli/general/token" + "github.com/jfrog/jfrog-cli/lifecycle" "github.com/jfrog/jfrog-cli/missioncontrol" "github.com/jfrog/jfrog-cli/pipelines" "github.com/jfrog/jfrog-cli/plugins" @@ -38,6 +34,11 @@ import ( "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}} @@ -276,6 +277,17 @@ func getCommands() []cli.Command { Category: otherCategory, Action: login.LoginCmd, }, + { + Name: "access-token-create", + Aliases: []string{"atc"}, + Flags: cliutils.GetCommandFlags(cliutils.AccessTokenCreate), + Usage: tokenDocs.GetDescription(), + HelpName: corecommon.CreateUsage("atc", tokenDocs.GetDescription(), tokenDocs.Usage), + UsageText: tokenDocs.GetArguments(), + ArgsUsage: common.CreateEnvVars(), + BashComplete: corecommon.CreateBashCompletionFunc(), + Action: token.AccessTokenCreateCmd, + }, } allCommands := append(slices.Clone(cliNameSpaces), utils.GetPlugins()...) allCommands = append(allCommands, scan.GetCommands()...) diff --git a/main_test.go b/main_test.go index 96efc1218..41805010a 100644 --- a/main_test.go +++ b/main_test.go @@ -71,7 +71,7 @@ func setupIntegrationTests() { InitXrayTests() } if *tests.TestAccess { - InitArtifactoryTests() + InitAccessTests() } if *tests.TestTransfer { InitTransferTests() diff --git a/npm_test.go b/npm_test.go index 1a5e4a683..5222e4e03 100644 --- a/npm_test.go +++ b/npm_test.go @@ -135,7 +135,7 @@ func testNpm(t *testing.T, isLegacy bool) { } func readModuleId(t *testing.T, wd string, npmVersion *version.Version) string { - packageInfo, err := buildutils.ReadPackageInfoFromPackageJson(filepath.Dir(wd), npmVersion) + packageInfo, err := buildutils.ReadPackageInfoFromPackageJsonIfExists(filepath.Dir(wd), npmVersion) assert.NoError(t, err) return packageInfo.BuildInfoModuleId() } @@ -165,6 +165,28 @@ func validateNpmLocalBuildInfo(t *testing.T, buildName, buildNumber, moduleName } } +func TestNpmWithoutPackageJson(t *testing.T) { + initNpmTest(t) + defer cleanNpmTest(t) + + // Create temp dir that does not contain an npm project + tempDirPath, createTempDirCallback := coretests.CreateTempDirWithCallbackAndAssert(t) + defer createTempDirCallback() + wd, err := os.Getwd() + assert.NoError(t, err, "Failed to get current dir") + chdirCallback := clientTestUtils.ChangeDirWithCallback(t, wd, tempDirPath) + defer chdirCallback() + + // Run config to allow resolution from Artifactory + err = createConfigFileForTest([]string{tempDirPath}, tests.NpmRemoteRepo, "", t, utils.Npm, false) + assert.NoError(t, err) + + // Run npm install and make sure that package.json and package-lock.json were created + runJfrogCli(t, "npm", "i", "json@9.0.6", "--save-exact") + assert.FileExists(t, filepath.Join(tempDirPath, "package.json")) + assert.FileExists(t, filepath.Join(tempDirPath, "package-lock.json")) +} + func TestNpmConditionalUpload(t *testing.T) { initNpmTest(t) defer cleanNpmTest(t) diff --git a/pip_test.go b/pip_test.go index baba851d5..70772e6ec 100644 --- a/pip_test.go +++ b/pip_test.go @@ -146,10 +146,13 @@ func assertDependenciesRequestedByAndChecksums(t *testing.T, module buildinfo.Mo assert.EqualValues(t, [][]string{{moduleName}}, dependency.RequestedBy) case "six:1.16.0": assert.EqualValues(t, [][]string{{"nltk:3.4.5", moduleName}}, dependency.RequestedBy) - case "altgraph:0.17.3": - assert.EqualValues(t, [][]string{{"macholib:1.11", moduleName}}, dependency.RequestedBy) default: - assert.Fail(t, "Unexpected dependency "+dependency.Id) + // Altgraph version can change + if assert.Contains(t, dependency.Id, "altgraph") { + assert.EqualValues(t, [][]string{{"macholib:1.11", moduleName}}, dependency.RequestedBy) + } else { + assert.Fail(t, "Unexpected dependency "+dependency.Id) + } } } } diff --git a/pipenv_test.go b/pipenv_test.go index f5b7b9bc6..5293e5a6e 100644 --- a/pipenv_test.go +++ b/pipenv_test.go @@ -20,6 +20,7 @@ import ( func TestPipenvInstall(t *testing.T) { // Init pipenv test. initPipenvTest(t) + tests.SkipKnownFailingTest(t) // Populate cli config with 'default' server. oldHomeDir, newHomeDir := prepareHomeDir(t) diff --git a/scan/cli.go b/scan/cli.go index 5093214ab..a53d499f8 100644 --- a/scan/cli.go +++ b/scan/cli.go @@ -2,7 +2,6 @@ package scan import ( "github.com/jfrog/jfrog-cli-core/v2/xray/commands/curation" - xrCmdUtils "github.com/jfrog/jfrog-cli-core/v2/xray/commands/utils" xrutils "github.com/jfrog/jfrog-cli-core/v2/xray/utils" curationdocs "github.com/jfrog/jfrog-cli/docs/scan/curation" "os" @@ -186,10 +185,10 @@ func AuditCmd(c *cli.Context) error { // On Maven we use '--mvn' flag techExists = c.Bool("mvn") } else { - techExists = c.Bool(tech.ToString()) + techExists = c.Bool(tech.String()) } if techExists { - technologies = append(technologies, tech.ToString()) + technologies = append(technologies, tech.String()) } } auditCmd.SetTechnologies(technologies) @@ -208,7 +207,7 @@ func AuditSpecificCmd(c *cli.Context, technology coreutils.Technology) error { } func CurationCmd(c *cli.Context) error { - threads, err := xrCmdUtils.DetectNumOfThreads(c.Int("threads")) + threads, err := curation.DetectNumOfThreads(c.Int("threads")) if err != nil { return err } @@ -236,11 +235,11 @@ func CurationCmd(c *cli.Context) error { func createAuditCmd(c *cli.Context) (*audit.AuditCommand, error) { auditCmd := audit.NewGenericAuditCommand() - err := validateXrayContext(c) + serverDetails, err := createServerDetailsWithConfigOffer(c) if err != nil { return nil, err } - serverDetails, err := createServerDetailsWithConfigOffer(c) + err = validateXrayContext(c, serverDetails) if err != nil { return nil, err } @@ -259,7 +258,8 @@ func createAuditCmd(c *cli.Context) (*audit.AuditCommand, error) { SetFail(c.BoolT("fail")). SetPrintExtendedTable(c.Bool(cliutils.ExtendedTable)). SetMinSeverityFilter(minSeverity). - SetFixableOnly(c.Bool(cliutils.FixableOnly)) + SetFixableOnly(c.Bool(cliutils.FixableOnly)). + SetThirdPartyApplicabilityScan(c.Bool(cliutils.ThirdPartyContextualAnalysis)) if c.String("watches") != "" { auditCmd.SetWatches(splitByCommaAndTrim(c.String("watches"))) @@ -282,11 +282,11 @@ 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) } - err := validateXrayContext(c) + serverDetails, err := createServerDetailsWithConfigOffer(c) if err != nil { return err } - serverDetails, err := createServerDetailsWithConfigOffer(c) + err = validateXrayContext(c, serverDetails) if err != nil { return err } @@ -344,12 +344,11 @@ func BuildScan(c *cli.Context) error { if err := buildConfiguration.ValidateBuildParams(); err != nil { return err } - serverDetails, err := createServerDetailsWithConfigOffer(c) if err != nil { return err } - err = validateXrayContext(c) + err = validateXrayContext(c, serverDetails) if err != nil { return err } @@ -378,11 +377,11 @@ func DockerScan(c *cli.Context, image string) error { if image == "" { return cli.ShowCommandHelp(c, "dockerscanhelp") } - err := validateXrayContext(c) + serverDetails, err := createServerDetailsWithConfigOffer(c) if err != nil { return err } - serverDetails, err := createServerDetailsWithConfigOffer(c) + err = validateXrayContext(c, serverDetails) if err != nil { return err } @@ -443,7 +442,10 @@ func shouldIncludeVulnerabilities(c *cli.Context) bool { return c.String("watches") == "" && !isProjectProvided(c) && c.String("repo-path") == "" } -func validateXrayContext(c *cli.Context) error { +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++ diff --git a/testdata/filespecs/search_all_dev_repo.json b/testdata/filespecs/search_all_dev_repo.json new file mode 100644 index 000000000..781226f0d --- /dev/null +++ b/testdata/filespecs/search_all_dev_repo.json @@ -0,0 +1,7 @@ +{ + "files": [ + { + "pattern": "${DEV_REPO}/*" + } + ] +} \ No newline at end of file diff --git a/testdata/xray/jas-config/.jfrog/jfrog-apps-config.yml b/testdata/xray/jas-config/.jfrog/jfrog-apps-config.yml new file mode 100644 index 000000000..b92f76728 --- /dev/null +++ b/testdata/xray/jas-config/.jfrog/jfrog-apps-config.yml @@ -0,0 +1,11 @@ +version: "1.0" + +modules: + - source_root: "." + scanners: + secrets: + exclude_patterns: + - "**/*secret_generic*/**" + iac: + exclude_patterns: + - "**/*gcp*/**" \ No newline at end of file diff --git a/testdata/xray/jas-config/iac/azure/vpc/module.tf b/testdata/xray/jas-config/iac/azure/vpc/module.tf new file mode 100644 index 000000000..9bdbab532 --- /dev/null +++ b/testdata/xray/jas-config/iac/azure/vpc/module.tf @@ -0,0 +1,116 @@ + +#Azure Generic vNet Module +resource "azurerm_resource_group" "network" { + count = var.module_enabled ? 1 : 0 + name = var.short_region != " " ? var.short_region : "${var.deploy_name}-${var.region}" + location = var.region + + tags = { + environment = var.environment + } +} + +resource "azurerm_virtual_network" "vnet" { + count = var.module_enabled ? 1 : 0 + name = "${var.deploy_name}-${var.region}" + location = var.region + address_space = [var.vpc_cidr] + resource_group_name = azurerm_resource_group.network[0].name + + tags = { + environment = var.environment + costcenter = "${var.deploy_name}-${var.region}" + } +} + +resource "azurerm_subnet" "subnet" { + count = var.module_enabled ? length(var.subnet_names) : 0 + name = var.subnet_names[count.index] + virtual_network_name = azurerm_virtual_network.vnet[0].name + resource_group_name = azurerm_resource_group.network[0].name + address_prefixes = [var.subnet_prefixes[count.index]] +# service_endpoints = [ +# "Microsoft.KeyVault" +# ] + + dynamic "delegation"{ + for_each =var.subnet_names[count.index] == "flexible-dbs" ? ["exec"] : [] + content { + name = "dlg-Microsoft.DBforPostgreSQL-flexibleServers" + service_delegation { + name = "Microsoft.DBforPostgreSQL/flexibleServers" + actions = [ + "Microsoft.Network/virtualNetworks/subnets/join/action" + ] + } + } + } + + enforce_private_link_endpoint_network_policies = var.subnet_names[count.index] == "data" + enforce_private_link_service_network_policies = var.subnet_names[count.index] == "private" && var.enforce_pl_svc_net_private + lifecycle { + ignore_changes = [ + service_endpoints, + delegation[0].name + ] + } +} + + +resource "azurerm_private_dns_zone" "postgres_private_dns" { + count = var.module_enabled ? 1 : 0 + name = "privatelink.postgres.database.azure.com" + resource_group_name = azurerm_resource_group.network[0].name +} + +resource "random_string" "postgres_private_dns_net_link_name" { + count = var.module_enabled ? 1 : 0 + length = 8 + special = false + number = false + upper = false +} + +resource "azurerm_private_dns_zone_virtual_network_link" "postgres_private_dns_net_link" { + count = var.module_enabled ? 1 : 0 + name = random_string.postgres_private_dns_net_link_name[0].result + resource_group_name = azurerm_resource_group.network[0].name + private_dns_zone_name = azurerm_private_dns_zone.postgres_private_dns[0].name + virtual_network_id = azurerm_virtual_network.vnet[0].id +} + +//resource "azurerm_network_security_group" "nsg" { +// count = "${var.module_enabled ? length(var.subnet_names) : 0}" +// name = "${var.subnet_names[count.index]}-sg" +// location = "${var.region}" +// resource_group_name = "${var.deploy_name}-${var.region}" +//} +// +//resource "azurerm_subnet_network_security_group_association" "nsg" { +// count = "${var.module_enabled ? length(var.subnet_names) : 0}" +// subnet_id = "${element(azurerm_subnet.subnet.*.id, count.index)}" +// network_security_group_id = "${element(azurerm_network_security_group.nsg.*.id, count.index)}" +//} +//resource "azurerm_subnet_route_table_association" "nat" { +// count = "${var.module_enabled ? length(var.nat_subnets) : 0}" +// subnet_id = "${element(azurerm_subnet.subnet.*.id, count.index + 1)}" +// route_table_id = "${azurerm_route_table.nattable.id}" +//} +# UDR +//resource "azurerm_route_table" "nattable" { +// count = "${var.module_enabled}" +// name = "${var.deploy_name}-${var.region}" +// location = "${var.region}" +// resource_group_name = "${azurerm_resource_group.network.name}" +// +// route { +// name = "all-traffic-via-nat" +// address_prefix = "0.0.0.0/0" +// next_hop_type = "VirtualAppliance" +// next_hop_in_ip_address = "${var.natgw_private_ip}" +// } +// +// tags = { +// environment = "${var.environment}" +// } +//} diff --git a/testdata/xray/jas-config/iac/azure/vpc/outputs.tf b/testdata/xray/jas-config/iac/azure/vpc/outputs.tf new file mode 100644 index 000000000..62a693aa7 --- /dev/null +++ b/testdata/xray/jas-config/iac/azure/vpc/outputs.tf @@ -0,0 +1,79 @@ +################################################################################## +# OUTPUT +################################################################################## + +output "resource_group_id" { + value = azurerm_resource_group.network[0].id +} + +output "resource_group_name" { + value = azurerm_resource_group.network[0].name +} + +output "vnet_id" { + value = element(concat(azurerm_virtual_network.vnet.*.id, [""]), 0) +} + +output "vnet_location" { + value = element(concat(azurerm_virtual_network.vnet.*.location, [""]), 0) +} + +output "vnet_name" { + value = element(concat(azurerm_virtual_network.vnet.*.name, [""]), 0) +} + +output "private_dns_id" { + value = element( + concat(azurerm_private_dns_zone.postgres_private_dns.*.id, [""]), + 0, + ) +} + +output "private_dns_name" { + value = element( + concat(azurerm_private_dns_zone.postgres_private_dns.*.name, [""]), + 0, + ) +} + +//output "vnet_subnets" { +// value = "${azurerm_subnet.subnet.*.id}" +//} + +### subnets ids ### +output "public_subnet" { + value = element(concat(azurerm_subnet.subnet.*.id, [""]), 0) +} + +output "private_subnet" { + value = element(concat(azurerm_subnet.subnet.*.id, [""]), 1) +} +output "flexible_subnet" { + value = element(concat(azurerm_subnet.subnet.*.id, [""]), 4) +} +output "data_subnet" { + value = element(concat(azurerm_subnet.subnet.*.id, [""]), 2) +} + +output "mgmt_subnet" { + value = element(concat(azurerm_subnet.subnet.*.id, [""]), 3) +} + +### subnets names ### +output "public_subnet_name" { + value = element(concat(azurerm_subnet.subnet.*.name, [""]), 0) +} + +output "private_subnet_name" { + value = element(concat(azurerm_subnet.subnet.*.name, [""]), 1) +} + +output "data_subnet_name" { + value = element(concat(azurerm_subnet.subnet.*.name, [""]), 2) +} + +output "mgmt_subnet_name" { + value = element(concat(azurerm_subnet.subnet.*.name, [""]), 3) +} + + diff --git a/testdata/xray/jas-config/iac/azure/vpc/variables.tf b/testdata/xray/jas-config/iac/azure/vpc/variables.tf new file mode 100644 index 000000000..e04fd5f78 --- /dev/null +++ b/testdata/xray/jas-config/iac/azure/vpc/variables.tf @@ -0,0 +1,39 @@ +variable "module_enabled" { + default = true +} + +variable "region" { +} + +variable "deploy_name" { +} + +variable "vpc_cidr" { +} + +variable "short_region" { + default = " " +} + +variable "subnet_prefixes" { + type = list(string) +} + +variable "ssh_source_ranges" { + type = list(string) +} + +variable "environment" { +} + +variable "subnet_names" { + type = list(string) +} + +variable "enforce_pl_svc_net_private" { + default = false +} +//variable "natgw_private_ip" {} +//variable "nat_subnets" { +// type = "list" +//} diff --git a/testdata/xray/jas-config/iac/azure/vpc/versions.tf b/testdata/xray/jas-config/iac/azure/vpc/versions.tf new file mode 100644 index 000000000..ac97c6ac8 --- /dev/null +++ b/testdata/xray/jas-config/iac/azure/vpc/versions.tf @@ -0,0 +1,4 @@ + +terraform { + required_version = ">= 0.12" +} diff --git a/testdata/xray/jas-config/iac/azure/vpc_pp/module.tf b/testdata/xray/jas-config/iac/azure/vpc_pp/module.tf new file mode 100644 index 000000000..5caae96c7 --- /dev/null +++ b/testdata/xray/jas-config/iac/azure/vpc_pp/module.tf @@ -0,0 +1,34 @@ + +#Azure Generic vNet Module +resource "azurerm_resource_group" "network" { + count = var.module_enabled ? 1 : 0 + name = var.short_region != " " ? var.short_region : "${var.deploy_name}-${var.region}" + location = var.region + + tags = { + environment = var.environment + } +} + +resource "azurerm_virtual_network" "vnet" { + count = var.module_enabled ? 1 : 0 + name = "${var.deploy_name}-${var.region}" + location = var.region + address_space = [var.vpc_cidr] + resource_group_name = azurerm_resource_group.network[0].name + + tags = { + environment = var.environment + costcenter = "${var.deploy_name}-${var.region}" + } +} + +resource "azurerm_subnet" "subnet" { + count = var.module_enabled ? length(var.subnet_names) : 0 + name = var.subnet_names[count.index] + virtual_network_name = azurerm_virtual_network.vnet[0].name + resource_group_name = azurerm_resource_group.network[0].name + address_prefixes = [var.subnet_prefixes[count.index]] + enforce_private_link_endpoint_network_policies = var.subnet_names[count.index] == "private" && var.enforce_private_subnet + +} \ No newline at end of file diff --git a/testdata/xray/jas-config/iac/azure/vpc_pp/outputs.tf b/testdata/xray/jas-config/iac/azure/vpc_pp/outputs.tf new file mode 100644 index 000000000..7e1472580 --- /dev/null +++ b/testdata/xray/jas-config/iac/azure/vpc_pp/outputs.tf @@ -0,0 +1,62 @@ +################################################################################## +# OUTPUT +################################################################################## + +output "resource_group_id" { + value = azurerm_resource_group.network[0].id +} + +output "resource_group_name" { + value = azurerm_resource_group.network[0].name +} + +output "vnet_id" { + value = element(concat(azurerm_virtual_network.vnet.*.id, [""]), 0) +} + +output "vnet_location" { + value = element(concat(azurerm_virtual_network.vnet.*.location, [""]), 0) +} + +output "vnet_name" { + value = element(concat(azurerm_virtual_network.vnet.*.name, [""]), 0) +} + +//output "vnet_subnets" { +// value = "${azurerm_subnet.subnet.*.id}" +//} + +### subnets ids ### +output "public_subnet" { + value = element(concat(azurerm_subnet.subnet.*.id, [""]), 0) +} + +output "private_subnet" { + value = element(concat(azurerm_subnet.subnet.*.id, [""]), 1) +} + +output "data_subnet" { + value = element(concat(azurerm_subnet.subnet.*.id, [""]), 2) +} + +output "mgmt_subnet" { + value = element(concat(azurerm_subnet.subnet.*.id, [""]), 3) +} + +### subnets names ### +output "public_subnet_name" { + value = element(concat(azurerm_subnet.subnet.*.name, [""]), 0) +} + +output "private_subnet_name" { + value = element(concat(azurerm_subnet.subnet.*.name, [""]), 1) +} + +output "data_subnet_name" { + value = element(concat(azurerm_subnet.subnet.*.name, [""]), 2) +} + +output "mgmt_subnet_name" { + value = element(concat(azurerm_subnet.subnet.*.name, [""]), 3) +} + diff --git a/testdata/xray/jas-config/iac/azure/vpc_pp/variables.tf b/testdata/xray/jas-config/iac/azure/vpc_pp/variables.tf new file mode 100644 index 000000000..784782aa7 --- /dev/null +++ b/testdata/xray/jas-config/iac/azure/vpc_pp/variables.tf @@ -0,0 +1,40 @@ +variable "module_enabled" { + default = true +} + +variable "region" { +} + +variable "deploy_name" { +} + +variable "vpc_cidr" { +} + +variable "short_region" { + default = " " +} + +variable "subnet_prefixes" { + type = list(string) +} + +variable "ssh_source_ranges" { + type = list(string) +} + +variable "environment" { +} + +variable "subnet_names" { + type = list(string) +} + +variable "enforce_private_subnet" { + default = true +} + +//variable "natgw_private_ip" {} +//variable "nat_subnets" { +// type = "list" +//} diff --git a/testdata/xray/jas-config/iac/azure/vpc_pp/versions.tf b/testdata/xray/jas-config/iac/azure/vpc_pp/versions.tf new file mode 100644 index 000000000..ac97c6ac8 --- /dev/null +++ b/testdata/xray/jas-config/iac/azure/vpc_pp/versions.tf @@ -0,0 +1,4 @@ + +terraform { + required_version = ">= 0.12" +} diff --git a/testdata/xray/jas-config/iac/gcp/k8s-oss/files/chk_k8s_nat b/testdata/xray/jas-config/iac/gcp/k8s-oss/files/chk_k8s_nat new file mode 100644 index 000000000..f4f318bbf --- /dev/null +++ b/testdata/xray/jas-config/iac/gcp/k8s-oss/files/chk_k8s_nat @@ -0,0 +1,17 @@ +#!/bin/bash + +# This script checks the functionality of the NAT gateway VM. + +tf_output=$(terraform output) +k8s_name=$(echo "$tf_output" | grep k8s_cluster_name | cut -d = -f 2) +k8s_project=$(echo "$tf_output" | grep k8s_project | cut -d = -f 2) +k8s_zone=$(echo "$tf_output" | grep k8s_zone | cut -d = -f 2) +k8s_ext_ip=$(echo "$tf_output" | grep external_ip | cut -d = -f 2) + +echo Testing k8s cluster connectivity to NAT.. +gcloud container clusters get-credentials $k8s_name --zone $k8s_zone --project $k8s_project > /dev/null + +chk_ext_ip=$(kubectl run test -it --restart=Never --image=centos:7 -- curl -s http://ipinfo.io/ip) +kubectl delete po test > /dev/null + +echo $k8s_ext_ip $chk_ext_ip diff --git a/testdata/xray/jas-config/iac/gcp/k8s-oss/module.tf b/testdata/xray/jas-config/iac/gcp/k8s-oss/module.tf new file mode 100644 index 000000000..53746b756 --- /dev/null +++ b/testdata/xray/jas-config/iac/gcp/k8s-oss/module.tf @@ -0,0 +1,158 @@ +# Create new K8S cluster with autoscaling + +data "google_container_engine_versions" "region" { + location = var.region +} + +resource "random_string" "admin-password" { + count = var.module_enabled ? 1 : 0 + length = 16 + +// lifecycle { +// ignore_changes = [ +// initial_node_count, master_authorized_networks_config +// ] +// } +} + +# New K8s Cluster, if creation failed you'll need to cleanup manually before running again. +resource "google_container_cluster" "primary" { + count = var.module_enabled ? 1 : 0 + provider = google-beta + name = "${var.deploy_name}-${var.region}" + location = var.k8s_zonal == "" ? var.region : var.region_zone + min_master_version = var.k8s_master_version == "" ? data.google_container_engine_versions.region.latest_master_version : var.k8s_master_version + network = var.network + subnetwork = var.subnetwork + logging_service = var.logging_service + monitoring_service = var.monitoring_service + enable_legacy_abac = var.enable_legacy_abac + remove_default_node_pool = "true" + initial_node_count = 1 + enable_shielded_nodes = var.gke_auth.shielded_nodes + enable_intranode_visibility = var.gke_auth.enable_intranode_visibility + + master_auth { + username = var.gke_auth.basic_auth ? "basic-admin" : "" + password = var.gke_auth.basic_auth ? random_string.admin-password[0].result : "" + + client_certificate_config { + issue_client_certificate = var.client_certificate + } + } + + private_cluster_config { + enable_private_endpoint = false + enable_private_nodes = true + master_ipv4_cidr_block = var.subnet_cidr["k8s-private"] + } + + ip_allocation_policy { + cluster_secondary_range_name = "pods-private-range" + services_secondary_range_name = "services-private-range" + } + + # Authoroized networks allowed to access the Master + + master_authorized_networks_config { + cidr_blocks { + cidr_block = "82.81.195.5/32" + display_name = "jfrog-office" + } + cidr_blocks { + cidr_block = "52.8.67.255/32" + display_name = "GlobalVpn" + } + cidr_blocks { + cidr_block = "12.252.18.78/32" + display_name = "US Office HA Public" + } + cidr_blocks { + cidr_block = "52.9.243.19/32" + display_name = "US IT AWS-NATGW" + } + cidr_blocks { + cidr_block = "52.215.237.185/32" + display_name = "EU IT AWS-NATGW" + } + cidr_blocks { + cidr_block = "52.16.203.109/32" + display_name = "GlobalVpn" + } + cidr_blocks { + cidr_block = "146.148.8.199/32" + display_name = "GCP jfrog-dev NAT" + } + cidr_blocks { + cidr_block = "192.168.20.0/24" //should be 192.168.21.0/24 + display_name = "all_local" + } + cidr_blocks { + cidr_block = "${var.natgw_ip[0]}/32" + display_name = "natgw" + } + cidr_blocks { + cidr_block = "${var.natgw_ip[1]}/32" + display_name = "natgw" + } + } + lifecycle { + ignore_changes = [ + initial_node_count, master_authorized_networks_config, master_auth + ] + } +} + +# K8s cluster node pool creation +resource "google_container_node_pool" "worker" { + count = var.module_enabled ? 1 : 0 + name = var.override_ng_name == "" ? "${var.deploy_name}-${var.region}-ng-1" : var.override_ng_name + location = var.k8s_zonal == "" ? var.region : var.region_zone + cluster = google_container_cluster.primary[0].name + node_count = 1 + version = var.k8s_node_version == "" ? data.google_container_engine_versions.region.latest_node_version : var.k8s_node_version + + autoscaling { + min_node_count = var.min_node_count + max_node_count = var.max_node_count + } + + management { + auto_repair = lookup(var.node_config, "node_auto_repair") + auto_upgrade = lookup(var.node_config, "node_auto_upgrade" ) + } + + node_config { + machine_type = var.worker_machine_type + image_type = var.image_type + disk_size_gb = var.ng_disk_size_gb + disk_type = "pd-ssd" + + shielded_instance_config { + enable_secure_boot = lookup(var.node_config, "enable_secure_boot" ) + } +// workload_metadata_config { +// node_metadata = "GKE_METADATA_SERVER" +// } +// oauth_scopes = [ +// "https://www.googleapis.com/auth/compute", +// "https://www.googleapis.com/auth/devstorage.read_only", +// "https://www.googleapis.com/auth/logging.write", +// "https://www.googleapis.com/auth/monitoring", +// ] + +// labels = { +// cluster = var.label +// } +// metadata = { +// ssh-keys = "ubuntu:${var.ssh_key} ubuntu" +// disable-legacy-endpoints = "true" +// } + tags = var.instance_tags + } + lifecycle { + ignore_changes = [ + autoscaling.0.max_node_count, node_count + ] + } +} \ No newline at end of file diff --git a/testdata/xray/jas-config/iac/gcp/k8s-oss/outputs.tf b/testdata/xray/jas-config/iac/gcp/k8s-oss/outputs.tf new file mode 100644 index 000000000..7d3cea754 --- /dev/null +++ b/testdata/xray/jas-config/iac/gcp/k8s-oss/outputs.tf @@ -0,0 +1,54 @@ +# The following outputs allow authentication and connectivity to the GKE Cluster. +output "client_certificate" { + value = google_container_cluster.primary.*.master_auth.0.client_certificate +} + +output "client_key" { + value = element( + concat( + google_container_cluster.primary.*.master_auth.0.client_key, + [""], + ), + 0, + ) +} + +output "cluster_ca_certificate" { + value = element( + concat( + google_container_cluster.primary.*.master_auth.0.cluster_ca_certificate, + [""], + ), + 0, + ) +} + +output "cluster_name" { + value = element(concat(google_container_cluster.primary.*.name, [""]), 0) +} + +output "cluster_ip" { + value = element(concat(google_container_cluster.primary.*.endpoint, [""]), 0) +} + +output "cluster_username" { + value = element( + concat( + google_container_cluster.primary.*.master_auth.0.username, + [""], + ), + 0, + ) +} + +output "cluster_password" { + value = element( + concat( + google_container_cluster.primary.*.master_auth.0.password, + [""], + ), + 0, + ) + sensitive = true +} + diff --git a/testdata/xray/jas-config/iac/gcp/k8s-oss/variables.tf b/testdata/xray/jas-config/iac/gcp/k8s-oss/variables.tf new file mode 100644 index 000000000..1124605c7 --- /dev/null +++ b/testdata/xray/jas-config/iac/gcp/k8s-oss/variables.tf @@ -0,0 +1,102 @@ +variable "module_enabled" { + default = true +} + +variable "project_name" { +} + +variable "region" { +} + +variable "region_zone" { +} + +variable "deploy_name" { +} + +variable "network" { +} + +variable "subnetwork" { +} + +variable "instance_tags" { + type = list(string) +} + +variable "subnet_cidr" { + type = map(string) +} + +variable "min_node_count" { +} + +variable "max_node_count" { +} + +variable "logging_service" { +} + +variable "monitoring_service" { +} + +variable "enable_legacy_abac" { +} + +variable "worker_machine_type" { +} + +variable "ft_machine_type" { +} + +variable "image_type" { +} + +variable "ng_disk_size_gb" { +} +variable "ft_disk_size_gb" { +} + +variable "label" { +} + +variable "natgw_ip" { +} + +variable "gcp_azs" { + type = map(string) + default = { + us-east1 = "us-east1-c,us-east1-d" + us-west1 = "us-west1-c,us-west1-a" + us-central1 = "us-central1-c,us-central1-f" + europe-west2 = "europe-west2-a,europe-west2-c" + europe-west1 = "europe-west1-c,europe-west1-d" + } +} + +variable "ssh_key" { +} + +variable "k8s_master_version" { +} + +variable "k8s_node_version" { +} + +variable "client_certificate" { +} + +variable "k8s_zonal" { +} + +variable "override_ft_name" { +} + +variable "override_ng_name" { +} + +variable "gke_auth" { +} + +variable "node_config" { +} \ No newline at end of file diff --git a/testdata/xray/jas-config/iac/gcp/k8s-oss/versions.tf b/testdata/xray/jas-config/iac/gcp/k8s-oss/versions.tf new file mode 100644 index 000000000..ac97c6ac8 --- /dev/null +++ b/testdata/xray/jas-config/iac/gcp/k8s-oss/versions.tf @@ -0,0 +1,4 @@ + +terraform { + required_version = ">= 0.12" +} diff --git a/testdata/xray/jas-config/iac/gcp/k8s-pipelines-bp/files/chk_k8s_nat b/testdata/xray/jas-config/iac/gcp/k8s-pipelines-bp/files/chk_k8s_nat new file mode 100644 index 000000000..f4f318bbf --- /dev/null +++ b/testdata/xray/jas-config/iac/gcp/k8s-pipelines-bp/files/chk_k8s_nat @@ -0,0 +1,17 @@ +#!/bin/bash + +# This script checks the functionality of the NAT gateway VM. + +tf_output=$(terraform output) +k8s_name=$(echo "$tf_output" | grep k8s_cluster_name | cut -d = -f 2) +k8s_project=$(echo "$tf_output" | grep k8s_project | cut -d = -f 2) +k8s_zone=$(echo "$tf_output" | grep k8s_zone | cut -d = -f 2) +k8s_ext_ip=$(echo "$tf_output" | grep external_ip | cut -d = -f 2) + +echo Testing k8s cluster connectivity to NAT.. +gcloud container clusters get-credentials $k8s_name --zone $k8s_zone --project $k8s_project > /dev/null + +chk_ext_ip=$(kubectl run test -it --restart=Never --image=centos:7 -- curl -s http://ipinfo.io/ip) +kubectl delete po test > /dev/null + +echo $k8s_ext_ip $chk_ext_ip diff --git a/testdata/xray/jas-config/iac/gcp/k8s-pipelines-bp/module.tf b/testdata/xray/jas-config/iac/gcp/k8s-pipelines-bp/module.tf new file mode 100644 index 000000000..f2d68663c --- /dev/null +++ b/testdata/xray/jas-config/iac/gcp/k8s-pipelines-bp/module.tf @@ -0,0 +1,207 @@ +# Create new K8S cluster with autoscaling + +data "google_container_engine_versions" "region" { + location = var.region +} + +resource "random_string" "admin-password" { + count = var.module_enabled ? 1 : 0 + length = 16 + +// lifecycle { +// ignore_changes = [ +// initial_node_count, master_authorized_networks_config +// ] +// } +} + +# New K8s Cluster, if creation failed you'll need to cleanup manually before running again. +resource "google_container_cluster" "primary" { + count = var.module_enabled ? 1 : 0 + provider = google-beta + name = "${var.deploy_name}-${var.region}" + location = var.k8s_zonal == "" ? var.region : var.region_zone + min_master_version = var.k8s_master_version == "" ? data.google_container_engine_versions.region.latest_master_version :lookup(var.gke_map.override,"k8s_master_version") + network = var.network + subnetwork = var.subnetwork + logging_service = var.logging_service + monitoring_service = var.monitoring_service + enable_legacy_abac = var.enable_legacy_abac + remove_default_node_pool = "true" + initial_node_count = 1 + enable_shielded_nodes = var.gke_auth.shielded_nodes + enable_intranode_visibility = var.enable_intranode_visibility + + master_auth { + username = var.gke_auth.basic_auth ? "basic-admin" : "" + password = var.gke_auth.basic_auth ? random_string.admin-password[0].result : "" + + client_certificate_config { + issue_client_certificate = var.client_certificate + } + } + + private_cluster_config { + enable_private_endpoint = false + enable_private_nodes = true + master_ipv4_cidr_block = var.subnet_cidr["k8s-private"] + } + + ip_allocation_policy { + cluster_secondary_range_name = "pods-private-range" + services_secondary_range_name = "services-private-range" + } + maintenance_policy { + recurring_window { + recurrence = var.maintenance_window.recurrence + start_time = var.maintenance_window.start_time + end_time = var.maintenance_window.end_time + } + } + + # Authoroized networks allowed to access the Master + + # master_authorized_networks_config { + + # cidr_blocks { + # cidr_block = "${var.natgw_ip[0]}/32" + # display_name = "natgw" + # } + # cidr_blocks { + # cidr_block = "${var.natgw_ip[1]}/32" + # display_name = "natgw" + # } + # } + master_authorized_networks_config { + dynamic "cidr_blocks" { + for_each = var.gke_map.override["public_access_cidrs"] + iterator = authorized_network + content { + cidr_block = authorized_network.value.cidr_block + display_name = authorized_network.value.display_name + } + } + } + + dynamic "resource_usage_export_config" { + for_each = toset(var.resource_usage_export_config_parameters != null ? ["exec"] : []) + content { + enable_network_egress_metering = lookup(var.resource_usage_export_config_parameters, "enable_network_egress_metering") + enable_resource_consumption_metering = lookup(var.resource_usage_export_config_parameters, "enable_resource_consumption_metering") + bigquery_destination { + dataset_id = lookup(var.resource_usage_export_config_parameters, "bigquery_destination.dataset_id") + } + } + } + + lifecycle { + ignore_changes = [ + initial_node_count, master_auth + ] + } +} + +# K8s cluster node pool creation +resource "google_container_node_pool" "worker" { + count = contains(keys(var.gke_map),"ng") ? 1 : 0 + name = lookup(var.gke_map.ng, "name", "${var.deploy_name}-${var.region}-ng-1" ) + location = var.k8s_zonal == "" ? var.region : var.region_zone + cluster = google_container_cluster.primary[0].name + node_count = 1 + version = lookup(var.gke_map.override, "k8s_node_version", data.google_container_engine_versions.region.latest_node_version) + +dynamic "autoscaling" { + for_each = toset(var.autoscaling_parameters != null ? ["exec"] : []) + content { + min_node_count = lookup(var.autoscaling_parameters, "min_node_count") + max_node_count = lookup(var.autoscaling_parameters, "max_node_count") + } + } + management { + auto_repair = lookup(var.node_config, "node_auto_repair") + auto_upgrade = lookup(var.node_config, "node_auto_upgrade" ) + } + node_config { + machine_type = lookup(var.gke_map.ng, "instance_type", "n2-highmem-2") + image_type = lookup(var.gke_map.ng, "image_type","COS") + disk_size_gb = lookup(var.gke_map.ng, "disk_size", "2000") + disk_type = "pd-ssd" + oauth_scopes = var.oauth_scopes + + shielded_instance_config { + enable_secure_boot = lookup(var.node_config, "enable_secure_boot" ) + } +// workload_metadata_config { +// node_metadata = "GKE_METADATA_SERVER" +// } + + +// labels = { +// cluster = var.label +// } + metadata = { + ssh-keys = "${var.ssh_key}" + disable-legacy-endpoints = "true" + } + tags = var.instance_tags + } + lifecycle { + ignore_changes = [ + autoscaling.0.max_node_count, node_count + ] + } +} +###node group for devops### +resource "google_container_node_pool" "devops_nodegroup" { + count = contains(keys(var.gke_map),"devops") ? 1 : 0 + name = lookup(var.gke_map.devops, "name", "${var.deploy_name}-${var.region}-ng-1" ) + location = var.k8s_zonal == "" ? var.region : var.region_zone + cluster = google_container_cluster.primary[0].name + node_count = 1 + version = lookup(var.gke_map.override, "k8s_node_version", data.google_container_engine_versions.region.latest_node_version) + management { + auto_repair = lookup(var.node_config, "node_auto_repair") + auto_upgrade = lookup(var.node_config, "node_auto_upgrade" ) + } + +dynamic "autoscaling" { + for_each = toset(var.gke_map.devops["autoscaling_parameters"] != {} ? ["exec"] : []) + content { + min_node_count = lookup(var.gke_map.devops["autoscaling_parameters"], "min_node_count") + max_node_count = lookup(var.gke_map.devops["autoscaling_parameters"], "max_node_count") + } + } + + node_config { + machine_type = lookup(var.gke_map.devops, "instance_type", "n2-standard-2") + labels = { + "k8s.jfrog.com/pool_type" = "devops" + } + image_type = var.image_type + disk_size_gb = lookup(var.gke_map.devops, "disk_size", "2000") + disk_type = "pd-ssd" + oauth_scopes= var.oauth_scopes + taint { + effect = "NO_SCHEDULE" + key = "pool_type" + value = "devops" + } + shielded_instance_config { + enable_secure_boot = lookup(var.node_config, "enable_secure_boot" ) + } +// workload_metadata_config { +// node_metadata = "GKE_METADATA_SERVER" +// } + + metadata = { + ssh-keys = "ubuntu:${var.ssh_key} ubuntu" + disable-legacy-endpoints = "true" + } + tags = var.instance_tags + } + lifecycle { + ignore_changes = [ + autoscaling.0.max_node_count, node_count + ] + } +} \ No newline at end of file diff --git a/testdata/xray/jas-config/iac/gcp/k8s-pipelines-bp/outputs.tf b/testdata/xray/jas-config/iac/gcp/k8s-pipelines-bp/outputs.tf new file mode 100644 index 000000000..7d3cea754 --- /dev/null +++ b/testdata/xray/jas-config/iac/gcp/k8s-pipelines-bp/outputs.tf @@ -0,0 +1,54 @@ +# The following outputs allow authentication and connectivity to the GKE Cluster. +output "client_certificate" { + value = google_container_cluster.primary.*.master_auth.0.client_certificate +} + +output "client_key" { + value = element( + concat( + google_container_cluster.primary.*.master_auth.0.client_key, + [""], + ), + 0, + ) +} + +output "cluster_ca_certificate" { + value = element( + concat( + google_container_cluster.primary.*.master_auth.0.cluster_ca_certificate, + [""], + ), + 0, + ) +} + +output "cluster_name" { + value = element(concat(google_container_cluster.primary.*.name, [""]), 0) +} + +output "cluster_ip" { + value = element(concat(google_container_cluster.primary.*.endpoint, [""]), 0) +} + +output "cluster_username" { + value = element( + concat( + google_container_cluster.primary.*.master_auth.0.username, + [""], + ), + 0, + ) +} + +output "cluster_password" { + value = element( + concat( + google_container_cluster.primary.*.master_auth.0.password, + [""], + ), + 0, + ) + sensitive = true +} + diff --git a/testdata/xray/jas-config/iac/gcp/k8s-pipelines-bp/rbac.tf b/testdata/xray/jas-config/iac/gcp/k8s-pipelines-bp/rbac.tf new file mode 100644 index 000000000..2b2c06dc2 --- /dev/null +++ b/testdata/xray/jas-config/iac/gcp/k8s-pipelines-bp/rbac.tf @@ -0,0 +1,61 @@ +provider "kubernetes" { + host = try(var.gke_map.override["k8s_sdm"], "https://${google_container_cluster.primary.*.endpoint}") +} + +resource "kubernetes_cluster_role_binding" "sdm-roles" { +for_each = toset(var.rbac_admin_roles) + metadata { + name = "${each.value}" + } + role_ref { + api_group = "rbac.authorization.k8s.io" + kind = "ClusterRole" + name = "cluster-admin" + } + subject { + kind = "Group" + name = "${each.value}" + api_group = "rbac.authorization.k8s.io" + } +} + + +resource "kubernetes_cluster_role" "sdm-ro-roles" { + for_each = toset(var.rbac_readonly_roles) + metadata { + name = "${each.value}" + } + + rule { + api_groups = [""] + resources = ["*"] + verbs = ["get", "list", "watch"] + } + rule { + api_groups = ["extensions"] + resources = ["*"] + verbs = ["get", "list", "watch"] + } + rule { + api_groups = ["apps"] + resources = ["*"] + verbs = ["get", "list", "watch"] + } +} + +resource "kubernetes_cluster_role_binding" "sdm-ro-roles" { + for_each = toset(var.rbac_readonly_roles) + metadata { + name = "${each.value}" + } + role_ref { + api_group = "rbac.authorization.k8s.io" + kind = "ClusterRole" + name = "${each.value}" + } + subject { + kind = "Group" + name = "${each.value}" + api_group = "rbac.authorization.k8s.io" + } +} \ No newline at end of file diff --git a/testdata/xray/jas-config/iac/gcp/k8s-pipelines-bp/variables.tf b/testdata/xray/jas-config/iac/gcp/k8s-pipelines-bp/variables.tf new file mode 100644 index 000000000..f025f6049 --- /dev/null +++ b/testdata/xray/jas-config/iac/gcp/k8s-pipelines-bp/variables.tf @@ -0,0 +1,137 @@ +variable "module_enabled" { + default = true +} + +variable "project_name" { +} + +variable "region" { +} + +variable "region_zone" { +} + +variable "deploy_name" { +} + +variable "network" { +} +variable "subnetwork" { +} + +variable "instance_tags" { + type = list(string) +} + +variable "subnet_cidr" { + type = map(string) +} + +variable "min_node_count" { +} + +variable "max_node_count" { +} + +variable "logging_service" { +} + +variable "monitoring_service" { +} + +variable "enable_legacy_abac" { +} + +variable "worker_machine_type" { +} + +variable "ft_machine_type" { +} + +variable "image_type" { +} + +variable "ng_disk_size_gb" { +} +variable "ft_disk_size_gb" { +} + +variable "label" { +} + +variable "natgw_ip" { +} + +variable "gcp_azs" { + type = map(string) + default = { + us-east1 = "us-east1-c,us-east1-d" + us-west1 = "us-west1-c,us-west1-a" + us-central1 = "us-central1-c,us-central1-f" + europe-west2 = "europe-west2-a,europe-west2-c" + europe-west1 = "europe-west1-c,europe-west1-d" + } +} + +variable "ssh_key" { +} + +variable "k8s_master_version" { +} + +variable "k8s_node_version" { +} + +variable "client_certificate" { +} + +variable "k8s_zonal" { +} + +variable "override_ft_name" { +} + +variable "override_ng_name" { +} + +variable "autoscaling_parameters"{ +} + +variable "gke_map"{ + +} +variable "network_policy" { + default = false +} +variable "maintenance_window" { + default = { + recurrence = "FREQ=WEEKLY;BYDAY=SU" + start_time = "2021-11-21T01:00:00Z" + end_time = "2021-11-21T18:00:00Z" + } +} + +variable "gke_auth" { +} + +variable "oauth_scopes" { + default = [ + "https://www.googleapis.com/auth/logging.write", + "https://www.googleapis.com/auth/monitoring", + ] +} + +variable "node_config"{ +} +variable "enable_intranode_visibility" {} +variable "rbac_admin_roles"{ +default = [] +} + +variable "rbac_readonly_roles"{ +default = [] +} + +variable "resource_usage_export_config_parameters" { + default = null +} \ No newline at end of file diff --git a/testdata/xray/jas-config/iac/gcp/k8s-pipelines-bp/versions.tf b/testdata/xray/jas-config/iac/gcp/k8s-pipelines-bp/versions.tf new file mode 100644 index 000000000..ac97c6ac8 --- /dev/null +++ b/testdata/xray/jas-config/iac/gcp/k8s-pipelines-bp/versions.tf @@ -0,0 +1,4 @@ + +terraform { + required_version = ">= 0.12" +} diff --git a/testdata/xray/jas-config/main.py b/testdata/xray/jas-config/main.py new file mode 100644 index 000000000..064571e46 --- /dev/null +++ b/testdata/xray/jas-config/main.py @@ -0,0 +1,5 @@ +import yaml + +with open('example.yaml') as f: + data = yaml.full_load(f) + print(data) \ No newline at end of file diff --git a/testdata/xray/jas-config/requirements.txt b/testdata/xray/jas-config/requirements.txt new file mode 100644 index 000000000..79bfd143b --- /dev/null +++ b/testdata/xray/jas-config/requirements.txt @@ -0,0 +1,2 @@ +PyYAML==5.2 +Werkzeug==1.0.1 \ No newline at end of file diff --git a/testdata/xray/jas-config/sast/flask_webgoat/__init__.py b/testdata/xray/jas-config/sast/flask_webgoat/__init__.py new file mode 100644 index 000000000..9e2f505a6 --- /dev/null +++ b/testdata/xray/jas-config/sast/flask_webgoat/__init__.py @@ -0,0 +1,51 @@ +import os +import sqlite3 +from pathlib import Path + +from flask import Flask, g + +DB_FILENAME = "database.db" + + +def query_db(query, args=(), one=False, commit=False): + with sqlite3.connect(DB_FILENAME) as conn: + # vulnerability: Sensitive Data Exposure + conn.set_trace_callback(print) + cur = conn.cursor().execute(query, args) + if commit: + conn.commit() + return cur.fetchone() if one else cur.fetchall() + + +def create_app(): + app = Flask(__name__) + app.secret_key = "aeZ1iwoh2ree2mo0Eereireong4baitixaixu5Ee" + + db_path = Path(DB_FILENAME) + if db_path.exists(): + db_path.unlink() + + conn = sqlite3.connect(DB_FILENAME) + create_table_query = """CREATE TABLE IF NOT EXISTS user + (id INTEGER PRIMARY KEY, username TEXT, password TEXT, access_level INTEGER)""" + conn.execute(create_table_query) + + insert_admin_query = """INSERT INTO user (id, username, password, access_level) + VALUES (1, 'admin', 'admin', 0)""" + conn.execute(insert_admin_query) + conn.commit() + conn.close() + + with app.app_context(): + from . import actions + from . import auth + from . import status + from . import ui + from . import users + + app.register_blueprint(actions.bp) + app.register_blueprint(auth.bp) + app.register_blueprint(status.bp) + app.register_blueprint(ui.bp) + app.register_blueprint(users.bp) + return app diff --git a/testdata/xray/jas-config/sast/flask_webgoat/ui.py b/testdata/xray/jas-config/sast/flask_webgoat/ui.py new file mode 100644 index 000000000..2b0bd0608 --- /dev/null +++ b/testdata/xray/jas-config/sast/flask_webgoat/ui.py @@ -0,0 +1,25 @@ +import sqlite3 + +from flask import Blueprint, request, render_template +from . import query_db + +bp = Blueprint("ui", __name__) + + +@bp.route("/search") +def search(): + query_param = request.args.get("query") + if query_param is None: + message = "please provide the query parameter" + return render_template("error.html", message=message) + + try: + query = "SELECT username, access_level FROM user WHERE username LIKE ?;" + results = query_db(query, (query_param,)) + # vulnerability: XSS + return render_template( + "search.html", results=results, num_results=len(results), query=query_param + ) + except sqlite3.Error as err: + message = "Error while executing query " + query_param + ": " + err + return render_template("error.html", message=message) diff --git a/testdata/xray/jas-config/sast/result.sarif b/testdata/xray/jas-config/sast/result.sarif new file mode 100644 index 000000000..839f34816 --- /dev/null +++ b/testdata/xray/jas-config/sast/result.sarif @@ -0,0 +1,618 @@ +{ + "runs": [ + { + "tool": { + "driver": { + "name": "USAF", + "rules": [ + { + "id": "python-flask-debug", + "defaultConfiguration": { + "parameters": { + "properties": { + "CWE": "1295" + } + } + }, + "fullDescription": { + "text": "\n### Overview\nDebug mode in a Flask app is a feature that allows the developer to see detailed\nerror messages and tracebacks when an error occurs. This can be useful for debugging\nand troubleshooting, but it can also create a security vulnerability if the app is\ndeployed in debug mode. In debug mode, Flask will display detailed error messages and\ntracebacks to the user, even if the error is caused by malicious input.\nThis can provide attackers with valuable information about the app's internal workings\nand vulnerabilities, making it easier for them to exploit those vulnerabilities.\n\n### Query operation\nIn this query we look Flask applications that set the `debug` argument to `True`\n\n### Vulnerable example\n```python\nfrom flask import Flask\n\napp = Flask(__name__)\n\n@app.route('/')\ndef hello():\n return 'Hello, World!'\n\nif __name__ == '__main__':\n app.run(debug=True)\n```\nIn this example, the Flask application is set to run in debug mode by passing\n`debug=True` as an argument to the `app.run()` function. This will make the application\nemit potentially sensitive information to the users.\n\n### Remediation\nWhen using `app.run`, omit the `debug` flag or set it to `False` -\n```diff\nif __name__ == '__main__':\n- app.run(debug=True)\n+ app.run()\n```\n", + "markdown": "\n### Overview\nDebug mode in a Flask app is a feature that allows the developer to see detailed\nerror messages and tracebacks when an error occurs. This can be useful for debugging\nand troubleshooting, but it can also create a security vulnerability if the app is\ndeployed in debug mode. In debug mode, Flask will display detailed error messages and\ntracebacks to the user, even if the error is caused by malicious input.\nThis can provide attackers with valuable information about the app's internal workings\nand vulnerabilities, making it easier for them to exploit those vulnerabilities.\n\n### Query operation\nIn this query we look Flask applications that set the `debug` argument to `True`\n\n### Vulnerable example\n```python\nfrom flask import Flask\n\napp = Flask(__name__)\n\n@app.route('/')\ndef hello():\n return 'Hello, World!'\n\nif __name__ == '__main__':\n app.run(debug=True)\n```\nIn this example, the Flask application is set to run in debug mode by passing\n`debug=True` as an argument to the `app.run()` function. This will make the application\nemit potentially sensitive information to the users.\n\n### Remediation\nWhen using `app.run`, omit the `debug` flag or set it to `False` -\n```diff\nif __name__ == '__main__':\n- app.run(debug=True)\n+ app.run()\n```\n" + }, + "shortDescription": { + "text": "Flask Running in Debug" + } + }, + { + "id": "python-stack-trace-exposure", + "defaultConfiguration": { + "parameters": { + "properties": { + "CWE": "209" + } + } + }, + "fullDescription": { + "text": "\n### Overview\nStack trace exposure is a type of security vulnerability that occurs when a program reveals\nsensitive information, such as the names and locations of internal files and variables,\nin error messages or other diagnostic output. This can happen when a program crashes or\nencounters an error, and the stack trace (a record of the program's call stack at the time\nof the error) is included in the output. Stack trace exposure can provide attackers with\nvaluable information about a program's internal workings and vulnerabilities, making it\neasier for them to exploit those vulnerabilities and gain unauthorized access\nto the system.\n\n### Query operation\nIn this query we look for any stack trace information flowing into the output.\n\n### Vulnerable example\n```python\nimport traceback\n\ndef my_function():\n try:\n # Some code that may raise an exception\n raise ValueError('Something went wrong')\n except ValueError as e:\n traceback.print_tb(e.__traceback__)\n\nmy_function()\n```\nIn this example, the `my_function()` function intentionally raises\na `ValueError` exception.\nThe `traceback.print_tb()` function is then used to print the stack trace\nwhen the exception is caught. The vulnerability lies in using `traceback.print_tb()`\nto output the stack trace directly to the console or any other output stream.\nIf this code were part of a web application or exposed through an API,\nthe stack trace would be exposed in the server logs or potentially returned\nas part of an error response to the client.\n\n### Remediation\nLog the exception to a logging framework or file, instead of outputting directly to the\nconsole-\n\n```python\ndef log_exception(exception):\n logging.exception('An exception occurred', exc_info=exception)\n```\n\n```diff\ndef my_function():\n try:\n # Some code that may raise an exception\n raise ValueError('Something went wrong')\n except ValueError as e:\n- traceback.print_tb(e.__traceback__)\n+ log_exception(e)\n```\n", + "markdown": "\n### Overview\nStack trace exposure is a type of security vulnerability that occurs when a program reveals\nsensitive information, such as the names and locations of internal files and variables,\nin error messages or other diagnostic output. This can happen when a program crashes or\nencounters an error, and the stack trace (a record of the program's call stack at the time\nof the error) is included in the output. Stack trace exposure can provide attackers with\nvaluable information about a program's internal workings and vulnerabilities, making it\neasier for them to exploit those vulnerabilities and gain unauthorized access\nto the system.\n\n### Query operation\nIn this query we look for any stack trace information flowing into the output.\n\n### Vulnerable example\n```python\nimport traceback\n\ndef my_function():\n try:\n # Some code that may raise an exception\n raise ValueError('Something went wrong')\n except ValueError as e:\n traceback.print_tb(e.__traceback__)\n\nmy_function()\n```\nIn this example, the `my_function()` function intentionally raises\na `ValueError` exception.\nThe `traceback.print_tb()` function is then used to print the stack trace\nwhen the exception is caught. The vulnerability lies in using `traceback.print_tb()`\nto output the stack trace directly to the console or any other output stream.\nIf this code were part of a web application or exposed through an API,\nthe stack trace would be exposed in the server logs or potentially returned\nas part of an error response to the client.\n\n### Remediation\nLog the exception to a logging framework or file, instead of outputting directly to the\nconsole-\n\n```python\ndef log_exception(exception):\n logging.exception('An exception occurred', exc_info=exception)\n```\n\n```diff\ndef my_function():\n try:\n # Some code that may raise an exception\n raise ValueError('Something went wrong')\n except ValueError as e:\n- traceback.print_tb(e.__traceback__)\n+ log_exception(e)\n```\n" + }, + "shortDescription": { + "text": "Stack Trace Exposure" + } + }, + { + "id": "python-xss", + "defaultConfiguration": { + "parameters": { + "properties": { + "CWE": "79" + } + } + }, + "fullDescription": { + "text": "\n### Overview\nXSS, or Cross-Site Scripting, is a type of vulnerability that allows an attacker to\ninject malicious code into a website or web application.\nThis can allow the attacker to steal sensitive information from users, such as their\ncookies or login credentials, or to perform unauthorized actions on their behalf.\n\n### Query operation\nIn the query we look for any user input that flows into\na potential output of the application.\n\n### Vulnerable example\nIn the following example, the Flask application takes a user-supplied parameter (`name`)\nfrom the query string and renders it directly into an HTML template using the\n`render_template_string` function. The issue is that\nthe user input is not properly sanitized or escaped, making it vulnerable to XSS attacks.\n```python\nfrom flask import Flask, request, render_template_string\n\napp = Flask(__name__)\n\n@app.route('/')\ndef index():\n name = request.args.get('name', 'Guest')\n message = f'Hello, {name}!'\n return render_template_string('

{}

'.format(message))\n\nif __name__ == '__main__':\napp.run()\n```\nAn attacker can exploit this vulnerability by injecting malicious JavaScript code into the\n`name` parameter. For instance, they could modify the URL to include the following payload:\n`http://localhost:5000/?name=`\n\n### Remediation\nWhen rendering templates, use parametrized variable assignments (which are automatically\nescaped) instead of direct string manipulation -\n```diff\n@app.route('/')\ndef index():\n name = request.args.get('name', 'Guest')\n message = f'Hello, {name}!'\n- return render_template_string('

{}

'.format(message))\n+ return render_template_string('

{{ message }}

', message=message)\n```\n", + "markdown": "\n### Overview\nXSS, or Cross-Site Scripting, is a type of vulnerability that allows an attacker to\ninject malicious code into a website or web application.\nThis can allow the attacker to steal sensitive information from users, such as their\ncookies or login credentials, or to perform unauthorized actions on their behalf.\n\n### Query operation\nIn the query we look for any user input that flows into\na potential output of the application.\n\n### Vulnerable example\nIn the following example, the Flask application takes a user-supplied parameter (`name`)\nfrom the query string and renders it directly into an HTML template using the\n`render_template_string` function. The issue is that\nthe user input is not properly sanitized or escaped, making it vulnerable to XSS attacks.\n```python\nfrom flask import Flask, request, render_template_string\n\napp = Flask(__name__)\n\n@app.route('/')\ndef index():\n name = request.args.get('name', 'Guest')\n message = f'Hello, {name}!'\n return render_template_string('

{}

'.format(message))\n\nif __name__ == '__main__':\napp.run()\n```\nAn attacker can exploit this vulnerability by injecting malicious JavaScript code into the\n`name` parameter. For instance, they could modify the URL to include the following payload:\n`http://localhost:5000/?name=`\n\n### Remediation\nWhen rendering templates, use parametrized variable assignments (which are automatically\nescaped) instead of direct string manipulation -\n```diff\n@app.route('/')\ndef index():\n name = request.args.get('name', 'Guest')\n message = f'Hello, {name}!'\n- return render_template_string('

{}

'.format(message))\n+ return render_template_string('

{{ message }}

', message=message)\n```\n" + }, + "shortDescription": { + "text": "XSS Vulnerability" + } + } + ] + } + }, + "invocations": [ + { + "executionSuccessful": true, + "arguments": [ + "/Users/assafa/.jfrog/dependencies/analyzerManager/zd_scanner/scanner", + "scan", + "/var/folders/xv/th4cksxn7jv9wjrdnn1h4tj00000gq/T/jfrog.cli.temp.-1693492973-1963413933/results.sarif" + ], + "workingDirectory": { + "uri": "file:///Users/assafa/Documents/code/cli-projects/jfrog-cli/testdata/xray/jas/sast" + } + } + ], + "results": [ + { + "message": { + "text": "Stack Trace Exposure" + }, + "level": "note", + "locations": [ + { + "logicalLocations": [ + { + "fullyQualifiedName": "flask_webgoat.__init__.query_db" + } + ], + "physicalLocation": { + "artifactLocation": { + "uri": "file:///Users/assafa/Documents/code/cli-projects/jfrog-cli/testdata/xray/jas/sast/flask_webgoat/__init__.py" + }, + "region": { + "endColumn": 39, + "endLine": 13, + "snippet": { + "text": "conn.set_trace_callback(print)" + }, + "startColumn": 9, + "startLine": 13 + } + } + } + ], + "ruleId": "python-stack-trace-exposure" + }, + { + "message": { + "text": "Stack Trace Exposure" + }, + "level": "note", + "locations": [ + { + "logicalLocations": [ + { + "fullyQualifiedName": "flask_webgoat.__init__.query_db" + } + ], + "physicalLocation": { + "artifactLocation": { + "uri": "file:///Users/assafa/Documents/code/cli-projects/jfrog-cli/testdata/xray/jas/sast/flask_webgoat/__init__.py" + }, + "region": { + "endColumn": 39, + "endLine": 13, + "snippet": { + "text": "conn.set_trace_callback(print)" + }, + "startColumn": 9, + "startLine": 13 + } + } + } + ], + "ruleId": "python-stack-trace-exposure" + }, + { + "message": { + "text": "XSS Vulnerability" + }, + "codeFlows": [ + { + "threadFlows": [ + { + "locations": [ + { + "location": { + "logicalLocations": [ + { + "fullyQualifiedName": "flask_webgoat.ui.search" + } + ], + "physicalLocation": { + "artifactLocation": { + "uri": "file:///Users/assafa/Documents/code/cli-projects/jfrog-cli/testdata/xray/jas/sast/flask_webgoat/ui.py" + }, + "region": { + "endColumn": 31, + "endLine": 11, + "snippet": { + "text": "request.args" + }, + "startColumn": 19, + "startLine": 11 + } + } + } + }, + { + "location": { + "logicalLocations": [ + { + "fullyQualifiedName": "flask_webgoat.ui.search" + } + ], + "physicalLocation": { + "artifactLocation": { + "uri": "file:///Users/assafa/Documents/code/cli-projects/jfrog-cli/testdata/xray/jas/sast/flask_webgoat/ui.py" + }, + "region": { + "endColumn": 35, + "endLine": 11, + "snippet": { + "text": "request.args.get" + }, + "startColumn": 19, + "startLine": 11 + } + } + } + }, + { + "location": { + "logicalLocations": [ + { + "fullyQualifiedName": "flask_webgoat.ui.search" + } + ], + "physicalLocation": { + "artifactLocation": { + "uri": "file:///Users/assafa/Documents/code/cli-projects/jfrog-cli/testdata/xray/jas/sast/flask_webgoat/ui.py" + }, + "region": { + "endColumn": 44, + "endLine": 11, + "snippet": { + "text": "request.args.get(\"query\")" + }, + "startColumn": 19, + "startLine": 11 + } + } + } + }, + { + "location": { + "logicalLocations": [ + { + "fullyQualifiedName": "flask_webgoat.ui.search" + } + ], + "physicalLocation": { + "artifactLocation": { + "uri": "file:///Users/assafa/Documents/code/cli-projects/jfrog-cli/testdata/xray/jas/sast/flask_webgoat/ui.py" + }, + "region": { + "endColumn": 16, + "endLine": 11, + "snippet": { + "text": "query_param" + }, + "startColumn": 5, + "startLine": 11 + } + } + } + }, + { + "location": { + "logicalLocations": [ + { + "fullyQualifiedName": "flask_webgoat.ui.search" + } + ], + "physicalLocation": { + "artifactLocation": { + "uri": "file:///Users/assafa/Documents/code/cli-projects/jfrog-cli/testdata/xray/jas/sast/flask_webgoat/ui.py" + }, + "region": { + "endColumn": 10, + "endLine": 22, + "snippet": { + "text": "render_template(\n \"search.html\", results=results, num_results=len(results), query=query_param\n )" + }, + "startColumn": 16, + "startLine": 20 + } + } + } + }, + { + "location": { + "logicalLocations": [ + { + "fullyQualifiedName": "flask_webgoat.ui.search" + } + ], + "physicalLocation": { + "artifactLocation": { + "uri": "file:///Users/assafa/Documents/code/cli-projects/jfrog-cli/testdata/xray/jas/sast/flask_webgoat/ui.py" + }, + "region": { + "endColumn": 10, + "endLine": 22, + "snippet": { + "text": "return render_template(\n \"search.html\", results=results, num_results=len(results), query=query_param\n )" + }, + "startColumn": 9, + "startLine": 20 + } + } + } + } + ] + } + ] + } + ], + "level": "error", + "locations": [ + { + "logicalLocations": [ + { + "fullyQualifiedName": "flask_webgoat.ui.search" + } + ], + "physicalLocation": { + "artifactLocation": { + "uri": "file:///Users/assafa/Documents/code/cli-projects/jfrog-cli/testdata/xray/jas/sast/flask_webgoat/ui.py" + }, + "region": { + "endColumn": 10, + "endLine": 22, + "snippet": { + "text": "return render_template(\n \"search.html\", results=results, num_results=len(results), query=query_param\n )" + }, + "startColumn": 9, + "startLine": 20 + } + } + } + ], + "ruleId": "python-xss" + }, + { + "message": { + "text": "XSS Vulnerability" + }, + "codeFlows": [ + { + "threadFlows": [ + { + "locations": [ + { + "location": { + "logicalLocations": [ + { + "fullyQualifiedName": "flask_webgoat.ui.search" + } + ], + "physicalLocation": { + "artifactLocation": { + "uri": "file:///Users/assafa/Documents/code/cli-projects/jfrog-cli/testdata/xray/jas/sast/flask_webgoat/ui.py" + }, + "region": { + "endColumn": 31, + "endLine": 11, + "snippet": { + "text": "request.args" + }, + "startColumn": 19, + "startLine": 11 + } + } + } + }, + { + "location": { + "logicalLocations": [ + { + "fullyQualifiedName": "flask_webgoat.ui.search" + } + ], + "physicalLocation": { + "artifactLocation": { + "uri": "file:///Users/assafa/Documents/code/cli-projects/jfrog-cli/testdata/xray/jas/sast/flask_webgoat/ui.py" + }, + "region": { + "endColumn": 35, + "endLine": 11, + "snippet": { + "text": "request.args.get" + }, + "startColumn": 19, + "startLine": 11 + } + } + } + }, + { + "location": { + "logicalLocations": [ + { + "fullyQualifiedName": "flask_webgoat.ui.search" + } + ], + "physicalLocation": { + "artifactLocation": { + "uri": "file:///Users/assafa/Documents/code/cli-projects/jfrog-cli/testdata/xray/jas/sast/flask_webgoat/ui.py" + }, + "region": { + "endColumn": 44, + "endLine": 11, + "snippet": { + "text": "request.args.get(\"query\")" + }, + "startColumn": 19, + "startLine": 11 + } + } + } + }, + { + "location": { + "logicalLocations": [ + { + "fullyQualifiedName": "flask_webgoat.ui.search" + } + ], + "physicalLocation": { + "artifactLocation": { + "uri": "file:///Users/assafa/Documents/code/cli-projects/jfrog-cli/testdata/xray/jas/sast/flask_webgoat/ui.py" + }, + "region": { + "endColumn": 16, + "endLine": 11, + "snippet": { + "text": "query_param" + }, + "startColumn": 5, + "startLine": 11 + } + } + } + }, + { + "location": { + "logicalLocations": [ + { + "fullyQualifiedName": "flask_webgoat.ui.search" + } + ], + "physicalLocation": { + "artifactLocation": { + "uri": "file:///Users/assafa/Documents/code/cli-projects/jfrog-cli/testdata/xray/jas/sast/flask_webgoat/ui.py" + }, + "region": { + "endColumn": 63, + "endLine": 24, + "snippet": { + "text": "\"Error while executing query \" + query_param" + }, + "startColumn": 19, + "startLine": 24 + } + } + } + }, + { + "location": { + "logicalLocations": [ + { + "fullyQualifiedName": "flask_webgoat.ui.search" + } + ], + "physicalLocation": { + "artifactLocation": { + "uri": "file:///Users/assafa/Documents/code/cli-projects/jfrog-cli/testdata/xray/jas/sast/flask_webgoat/ui.py" + }, + "region": { + "endColumn": 70, + "endLine": 24, + "snippet": { + "text": "\"Error while executing query \" + query_param + \": \"" + }, + "startColumn": 19, + "startLine": 24 + } + } + } + }, + { + "location": { + "logicalLocations": [ + { + "fullyQualifiedName": "flask_webgoat.ui.search" + } + ], + "physicalLocation": { + "artifactLocation": { + "uri": "file:///Users/assafa/Documents/code/cli-projects/jfrog-cli/testdata/xray/jas/sast/flask_webgoat/ui.py" + }, + "region": { + "endColumn": 76, + "endLine": 24, + "snippet": { + "text": "\"Error while executing query \" + query_param + \": \" + err" + }, + "startColumn": 19, + "startLine": 24 + } + } + } + }, + { + "location": { + "logicalLocations": [ + { + "fullyQualifiedName": "flask_webgoat.ui.search" + } + ], + "physicalLocation": { + "artifactLocation": { + "uri": "file:///Users/assafa/Documents/code/cli-projects/jfrog-cli/testdata/xray/jas/sast/flask_webgoat/ui.py" + }, + "region": { + "endColumn": 16, + "endLine": 24, + "snippet": { + "text": "message" + }, + "startColumn": 9, + "startLine": 24 + } + } + } + }, + { + "location": { + "logicalLocations": [ + { + "fullyQualifiedName": "flask_webgoat.ui.search" + } + ], + "physicalLocation": { + "artifactLocation": { + "uri": "file:///Users/assafa/Documents/code/cli-projects/jfrog-cli/testdata/xray/jas/sast/flask_webgoat/ui.py" + }, + "region": { + "endColumn": 62, + "endLine": 25, + "snippet": { + "text": "render_template(\"error.html\", message=message)" + }, + "startColumn": 16, + "startLine": 25 + } + } + } + }, + { + "location": { + "logicalLocations": [ + { + "fullyQualifiedName": "flask_webgoat.ui.search" + } + ], + "physicalLocation": { + "artifactLocation": { + "uri": "file:///Users/assafa/Documents/code/cli-projects/jfrog-cli/testdata/xray/jas/sast/flask_webgoat/ui.py" + }, + "region": { + "endColumn": 62, + "endLine": 25, + "snippet": { + "text": "return render_template(\"error.html\", message=message)" + }, + "startColumn": 9, + "startLine": 25 + } + } + } + } + ] + } + ] + } + ], + "level": "error", + "locations": [ + { + "logicalLocations": [ + { + "fullyQualifiedName": "flask_webgoat.ui.search" + } + ], + "physicalLocation": { + "artifactLocation": { + "uri": "file:///Users/assafa/Documents/code/cli-projects/jfrog-cli/testdata/xray/jas/sast/flask_webgoat/ui.py" + }, + "region": { + "endColumn": 62, + "endLine": 25, + "snippet": { + "text": "return render_template(\"error.html\", message=message)" + }, + "startColumn": 9, + "startLine": 25 + } + } + } + ], + "ruleId": "python-xss" + }, + { + "message": { + "text": "Flask Running in Debug" + }, + "locations": [ + { + "logicalLocations": [ + { + "fullyQualifiedName": "run" + } + ], + "physicalLocation": { + "artifactLocation": { + "uri": "file:///Users/assafa/Documents/code/cli-projects/jfrog-cli/testdata/xray/jas/sast/run.py" + }, + "region": { + "endColumn": 24, + "endLine": 15, + "snippet": { + "text": "app.run(debug=True)" + }, + "startColumn": 5, + "startLine": 15 + } + } + } + ], + "ruleId": "python-flask-debug" + } + ] + } + ], + "version": "2.1.0", + "$schema": "https://docs.oasis-open.org/sarif/sarif/v2.1.0/cos02/schemas/sarif-schema-2.1.0.json" +} \ No newline at end of file diff --git a/testdata/xray/jas-config/sast/run.py b/testdata/xray/jas-config/sast/run.py new file mode 100644 index 000000000..8cacc71d4 --- /dev/null +++ b/testdata/xray/jas-config/sast/run.py @@ -0,0 +1,15 @@ +from flask_webgoat import create_app + +app = create_app() + +@app.after_request +def add_csp_headers(response): + # vulnerability: Broken Access Control + response.headers['Access-Control-Allow-Origin'] = '*' + # vulnerability: Security Misconfiguration + response.headers['Content-Security-Policy'] = "script-src 'self' 'unsafe-inline'" + return response + +if __name__ == '__main__': + # vulnerability: Security Misconfiguration + app.run(debug=True) diff --git a/testdata/xray/jas-config/secrets/more_secrets/key b/testdata/xray/jas-config/secrets/more_secrets/key new file mode 100644 index 000000000..20edd3206 --- /dev/null +++ b/testdata/xray/jas-config/secrets/more_secrets/key @@ -0,0 +1,7 @@ +key:S3cr3t_K +Key:S3cr3t_K +key2:S3cr3t_K +search-key:S3cr3t_K +public-key:S3cr3t_K +publicKey:S3cr3t_K +KEY_public-key:S3cr3t_K diff --git a/testdata/xray/jas-config/secrets/more_secrets/sequence b/testdata/xray/jas-config/secrets/more_secrets/sequence new file mode 100644 index 000000000..1137d2d9e --- /dev/null +++ b/testdata/xray/jas-config/secrets/more_secrets/sequence @@ -0,0 +1,2 @@ +password: kmlkfxdngklfnl111111 +password: jnvkjcxnjvxnvk22222 \ No newline at end of file diff --git a/testdata/xray/jas-config/secrets/secret_generic/blacklist b/testdata/xray/jas-config/secrets/secret_generic/blacklist new file mode 100644 index 000000000..4ccd3ce0f --- /dev/null +++ b/testdata/xray/jas-config/secrets/secret_generic/blacklist @@ -0,0 +1,9 @@ +password=123456789 +password=0abcdef7 +common_token 81d7578788ec6b061029e875e01589xxxx2222e +API_KEY : DjyO8k7504YBl5lsGn2PDiV_testcABvnodnD +aws_access_key=ASIA2TEO6PMT6_test_4T4VM3J +aws_access_key=ASIA2TEO6PMT6_TEST_4T4VM3J +aws_access_key=ASIA2TEO6PMT6_sample_4T4VM3J +aws_access_key=ASIA2TEO6PMT6_sampLe_4T4VM3J +aws_access_key=ASIA2TEO6PMT6_example_4T4VM3J \ No newline at end of file diff --git a/testdata/xray/jas-config/secrets/secret_generic/gibberish b/testdata/xray/jas-config/secrets/secret_generic/gibberish new file mode 100644 index 000000000..bd7aef407 --- /dev/null +++ b/testdata/xray/jas-config/secrets/secret_generic/gibberish @@ -0,0 +1,10 @@ +secret_key=agwergartegbae123414 +client_secret 1557262894_291cd120eca3f3dcec3e77a66a2fc8c5_ +client_secret : QXfEexz9sVH-hTxsf7lq6P-LJhE +password:4fjNPloEVvrVj28BsVh3jI8t3pmv7Qg6 +client_secret=0FCEF92C008EC67540368586D10A3E09BC23D94F7D391A87CEC7650F1982C84A78175CBE3CAF98A3FC69EDD91B456CC1 +PAYMENT_TOKEN 381764678 +client_secret: nbhd-null +SECRET_KEY hgfu983ru6587yt10fjw83ft94847-3h4rgjh3yg490hg3745g4hr87gh5-276hg-9 +SECRET_KEY=12347502348 + diff --git a/testdata/xray/jas-test/requirements.txt b/testdata/xray/jas-test/requirements.txt index ddff94966..79bfd143b 100644 --- a/testdata/xray/jas-test/requirements.txt +++ b/testdata/xray/jas-test/requirements.txt @@ -1 +1,2 @@ -PyYAML==5.2 \ No newline at end of file +PyYAML==5.2 +Werkzeug==1.0.1 \ No newline at end of file diff --git a/testdata/xray/jas-test/sast/flask_webgoat/__init__.py b/testdata/xray/jas-test/sast/flask_webgoat/__init__.py new file mode 100644 index 000000000..9e2f505a6 --- /dev/null +++ b/testdata/xray/jas-test/sast/flask_webgoat/__init__.py @@ -0,0 +1,51 @@ +import os +import sqlite3 +from pathlib import Path + +from flask import Flask, g + +DB_FILENAME = "database.db" + + +def query_db(query, args=(), one=False, commit=False): + with sqlite3.connect(DB_FILENAME) as conn: + # vulnerability: Sensitive Data Exposure + conn.set_trace_callback(print) + cur = conn.cursor().execute(query, args) + if commit: + conn.commit() + return cur.fetchone() if one else cur.fetchall() + + +def create_app(): + app = Flask(__name__) + app.secret_key = "aeZ1iwoh2ree2mo0Eereireong4baitixaixu5Ee" + + db_path = Path(DB_FILENAME) + if db_path.exists(): + db_path.unlink() + + conn = sqlite3.connect(DB_FILENAME) + create_table_query = """CREATE TABLE IF NOT EXISTS user + (id INTEGER PRIMARY KEY, username TEXT, password TEXT, access_level INTEGER)""" + conn.execute(create_table_query) + + insert_admin_query = """INSERT INTO user (id, username, password, access_level) + VALUES (1, 'admin', 'admin', 0)""" + conn.execute(insert_admin_query) + conn.commit() + conn.close() + + with app.app_context(): + from . import actions + from . import auth + from . import status + from . import ui + from . import users + + app.register_blueprint(actions.bp) + app.register_blueprint(auth.bp) + app.register_blueprint(status.bp) + app.register_blueprint(ui.bp) + app.register_blueprint(users.bp) + return app diff --git a/testdata/xray/jas-test/sast/flask_webgoat/ui.py b/testdata/xray/jas-test/sast/flask_webgoat/ui.py new file mode 100644 index 000000000..2b0bd0608 --- /dev/null +++ b/testdata/xray/jas-test/sast/flask_webgoat/ui.py @@ -0,0 +1,25 @@ +import sqlite3 + +from flask import Blueprint, request, render_template +from . import query_db + +bp = Blueprint("ui", __name__) + + +@bp.route("/search") +def search(): + query_param = request.args.get("query") + if query_param is None: + message = "please provide the query parameter" + return render_template("error.html", message=message) + + try: + query = "SELECT username, access_level FROM user WHERE username LIKE ?;" + results = query_db(query, (query_param,)) + # vulnerability: XSS + return render_template( + "search.html", results=results, num_results=len(results), query=query_param + ) + except sqlite3.Error as err: + message = "Error while executing query " + query_param + ": " + err + return render_template("error.html", message=message) diff --git a/testdata/xray/jas-test/sast/result.sarif b/testdata/xray/jas-test/sast/result.sarif new file mode 100644 index 000000000..839f34816 --- /dev/null +++ b/testdata/xray/jas-test/sast/result.sarif @@ -0,0 +1,618 @@ +{ + "runs": [ + { + "tool": { + "driver": { + "name": "USAF", + "rules": [ + { + "id": "python-flask-debug", + "defaultConfiguration": { + "parameters": { + "properties": { + "CWE": "1295" + } + } + }, + "fullDescription": { + "text": "\n### Overview\nDebug mode in a Flask app is a feature that allows the developer to see detailed\nerror messages and tracebacks when an error occurs. This can be useful for debugging\nand troubleshooting, but it can also create a security vulnerability if the app is\ndeployed in debug mode. In debug mode, Flask will display detailed error messages and\ntracebacks to the user, even if the error is caused by malicious input.\nThis can provide attackers with valuable information about the app's internal workings\nand vulnerabilities, making it easier for them to exploit those vulnerabilities.\n\n### Query operation\nIn this query we look Flask applications that set the `debug` argument to `True`\n\n### Vulnerable example\n```python\nfrom flask import Flask\n\napp = Flask(__name__)\n\n@app.route('/')\ndef hello():\n return 'Hello, World!'\n\nif __name__ == '__main__':\n app.run(debug=True)\n```\nIn this example, the Flask application is set to run in debug mode by passing\n`debug=True` as an argument to the `app.run()` function. This will make the application\nemit potentially sensitive information to the users.\n\n### Remediation\nWhen using `app.run`, omit the `debug` flag or set it to `False` -\n```diff\nif __name__ == '__main__':\n- app.run(debug=True)\n+ app.run()\n```\n", + "markdown": "\n### Overview\nDebug mode in a Flask app is a feature that allows the developer to see detailed\nerror messages and tracebacks when an error occurs. This can be useful for debugging\nand troubleshooting, but it can also create a security vulnerability if the app is\ndeployed in debug mode. In debug mode, Flask will display detailed error messages and\ntracebacks to the user, even if the error is caused by malicious input.\nThis can provide attackers with valuable information about the app's internal workings\nand vulnerabilities, making it easier for them to exploit those vulnerabilities.\n\n### Query operation\nIn this query we look Flask applications that set the `debug` argument to `True`\n\n### Vulnerable example\n```python\nfrom flask import Flask\n\napp = Flask(__name__)\n\n@app.route('/')\ndef hello():\n return 'Hello, World!'\n\nif __name__ == '__main__':\n app.run(debug=True)\n```\nIn this example, the Flask application is set to run in debug mode by passing\n`debug=True` as an argument to the `app.run()` function. This will make the application\nemit potentially sensitive information to the users.\n\n### Remediation\nWhen using `app.run`, omit the `debug` flag or set it to `False` -\n```diff\nif __name__ == '__main__':\n- app.run(debug=True)\n+ app.run()\n```\n" + }, + "shortDescription": { + "text": "Flask Running in Debug" + } + }, + { + "id": "python-stack-trace-exposure", + "defaultConfiguration": { + "parameters": { + "properties": { + "CWE": "209" + } + } + }, + "fullDescription": { + "text": "\n### Overview\nStack trace exposure is a type of security vulnerability that occurs when a program reveals\nsensitive information, such as the names and locations of internal files and variables,\nin error messages or other diagnostic output. This can happen when a program crashes or\nencounters an error, and the stack trace (a record of the program's call stack at the time\nof the error) is included in the output. Stack trace exposure can provide attackers with\nvaluable information about a program's internal workings and vulnerabilities, making it\neasier for them to exploit those vulnerabilities and gain unauthorized access\nto the system.\n\n### Query operation\nIn this query we look for any stack trace information flowing into the output.\n\n### Vulnerable example\n```python\nimport traceback\n\ndef my_function():\n try:\n # Some code that may raise an exception\n raise ValueError('Something went wrong')\n except ValueError as e:\n traceback.print_tb(e.__traceback__)\n\nmy_function()\n```\nIn this example, the `my_function()` function intentionally raises\na `ValueError` exception.\nThe `traceback.print_tb()` function is then used to print the stack trace\nwhen the exception is caught. The vulnerability lies in using `traceback.print_tb()`\nto output the stack trace directly to the console or any other output stream.\nIf this code were part of a web application or exposed through an API,\nthe stack trace would be exposed in the server logs or potentially returned\nas part of an error response to the client.\n\n### Remediation\nLog the exception to a logging framework or file, instead of outputting directly to the\nconsole-\n\n```python\ndef log_exception(exception):\n logging.exception('An exception occurred', exc_info=exception)\n```\n\n```diff\ndef my_function():\n try:\n # Some code that may raise an exception\n raise ValueError('Something went wrong')\n except ValueError as e:\n- traceback.print_tb(e.__traceback__)\n+ log_exception(e)\n```\n", + "markdown": "\n### Overview\nStack trace exposure is a type of security vulnerability that occurs when a program reveals\nsensitive information, such as the names and locations of internal files and variables,\nin error messages or other diagnostic output. This can happen when a program crashes or\nencounters an error, and the stack trace (a record of the program's call stack at the time\nof the error) is included in the output. Stack trace exposure can provide attackers with\nvaluable information about a program's internal workings and vulnerabilities, making it\neasier for them to exploit those vulnerabilities and gain unauthorized access\nto the system.\n\n### Query operation\nIn this query we look for any stack trace information flowing into the output.\n\n### Vulnerable example\n```python\nimport traceback\n\ndef my_function():\n try:\n # Some code that may raise an exception\n raise ValueError('Something went wrong')\n except ValueError as e:\n traceback.print_tb(e.__traceback__)\n\nmy_function()\n```\nIn this example, the `my_function()` function intentionally raises\na `ValueError` exception.\nThe `traceback.print_tb()` function is then used to print the stack trace\nwhen the exception is caught. The vulnerability lies in using `traceback.print_tb()`\nto output the stack trace directly to the console or any other output stream.\nIf this code were part of a web application or exposed through an API,\nthe stack trace would be exposed in the server logs or potentially returned\nas part of an error response to the client.\n\n### Remediation\nLog the exception to a logging framework or file, instead of outputting directly to the\nconsole-\n\n```python\ndef log_exception(exception):\n logging.exception('An exception occurred', exc_info=exception)\n```\n\n```diff\ndef my_function():\n try:\n # Some code that may raise an exception\n raise ValueError('Something went wrong')\n except ValueError as e:\n- traceback.print_tb(e.__traceback__)\n+ log_exception(e)\n```\n" + }, + "shortDescription": { + "text": "Stack Trace Exposure" + } + }, + { + "id": "python-xss", + "defaultConfiguration": { + "parameters": { + "properties": { + "CWE": "79" + } + } + }, + "fullDescription": { + "text": "\n### Overview\nXSS, or Cross-Site Scripting, is a type of vulnerability that allows an attacker to\ninject malicious code into a website or web application.\nThis can allow the attacker to steal sensitive information from users, such as their\ncookies or login credentials, or to perform unauthorized actions on their behalf.\n\n### Query operation\nIn the query we look for any user input that flows into\na potential output of the application.\n\n### Vulnerable example\nIn the following example, the Flask application takes a user-supplied parameter (`name`)\nfrom the query string and renders it directly into an HTML template using the\n`render_template_string` function. The issue is that\nthe user input is not properly sanitized or escaped, making it vulnerable to XSS attacks.\n```python\nfrom flask import Flask, request, render_template_string\n\napp = Flask(__name__)\n\n@app.route('/')\ndef index():\n name = request.args.get('name', 'Guest')\n message = f'Hello, {name}!'\n return render_template_string('

{}

'.format(message))\n\nif __name__ == '__main__':\napp.run()\n```\nAn attacker can exploit this vulnerability by injecting malicious JavaScript code into the\n`name` parameter. For instance, they could modify the URL to include the following payload:\n`http://localhost:5000/?name=`\n\n### Remediation\nWhen rendering templates, use parametrized variable assignments (which are automatically\nescaped) instead of direct string manipulation -\n```diff\n@app.route('/')\ndef index():\n name = request.args.get('name', 'Guest')\n message = f'Hello, {name}!'\n- return render_template_string('

{}

'.format(message))\n+ return render_template_string('

{{ message }}

', message=message)\n```\n", + "markdown": "\n### Overview\nXSS, or Cross-Site Scripting, is a type of vulnerability that allows an attacker to\ninject malicious code into a website or web application.\nThis can allow the attacker to steal sensitive information from users, such as their\ncookies or login credentials, or to perform unauthorized actions on their behalf.\n\n### Query operation\nIn the query we look for any user input that flows into\na potential output of the application.\n\n### Vulnerable example\nIn the following example, the Flask application takes a user-supplied parameter (`name`)\nfrom the query string and renders it directly into an HTML template using the\n`render_template_string` function. The issue is that\nthe user input is not properly sanitized or escaped, making it vulnerable to XSS attacks.\n```python\nfrom flask import Flask, request, render_template_string\n\napp = Flask(__name__)\n\n@app.route('/')\ndef index():\n name = request.args.get('name', 'Guest')\n message = f'Hello, {name}!'\n return render_template_string('

{}

'.format(message))\n\nif __name__ == '__main__':\napp.run()\n```\nAn attacker can exploit this vulnerability by injecting malicious JavaScript code into the\n`name` parameter. For instance, they could modify the URL to include the following payload:\n`http://localhost:5000/?name=`\n\n### Remediation\nWhen rendering templates, use parametrized variable assignments (which are automatically\nescaped) instead of direct string manipulation -\n```diff\n@app.route('/')\ndef index():\n name = request.args.get('name', 'Guest')\n message = f'Hello, {name}!'\n- return render_template_string('

{}

'.format(message))\n+ return render_template_string('

{{ message }}

', message=message)\n```\n" + }, + "shortDescription": { + "text": "XSS Vulnerability" + } + } + ] + } + }, + "invocations": [ + { + "executionSuccessful": true, + "arguments": [ + "/Users/assafa/.jfrog/dependencies/analyzerManager/zd_scanner/scanner", + "scan", + "/var/folders/xv/th4cksxn7jv9wjrdnn1h4tj00000gq/T/jfrog.cli.temp.-1693492973-1963413933/results.sarif" + ], + "workingDirectory": { + "uri": "file:///Users/assafa/Documents/code/cli-projects/jfrog-cli/testdata/xray/jas/sast" + } + } + ], + "results": [ + { + "message": { + "text": "Stack Trace Exposure" + }, + "level": "note", + "locations": [ + { + "logicalLocations": [ + { + "fullyQualifiedName": "flask_webgoat.__init__.query_db" + } + ], + "physicalLocation": { + "artifactLocation": { + "uri": "file:///Users/assafa/Documents/code/cli-projects/jfrog-cli/testdata/xray/jas/sast/flask_webgoat/__init__.py" + }, + "region": { + "endColumn": 39, + "endLine": 13, + "snippet": { + "text": "conn.set_trace_callback(print)" + }, + "startColumn": 9, + "startLine": 13 + } + } + } + ], + "ruleId": "python-stack-trace-exposure" + }, + { + "message": { + "text": "Stack Trace Exposure" + }, + "level": "note", + "locations": [ + { + "logicalLocations": [ + { + "fullyQualifiedName": "flask_webgoat.__init__.query_db" + } + ], + "physicalLocation": { + "artifactLocation": { + "uri": "file:///Users/assafa/Documents/code/cli-projects/jfrog-cli/testdata/xray/jas/sast/flask_webgoat/__init__.py" + }, + "region": { + "endColumn": 39, + "endLine": 13, + "snippet": { + "text": "conn.set_trace_callback(print)" + }, + "startColumn": 9, + "startLine": 13 + } + } + } + ], + "ruleId": "python-stack-trace-exposure" + }, + { + "message": { + "text": "XSS Vulnerability" + }, + "codeFlows": [ + { + "threadFlows": [ + { + "locations": [ + { + "location": { + "logicalLocations": [ + { + "fullyQualifiedName": "flask_webgoat.ui.search" + } + ], + "physicalLocation": { + "artifactLocation": { + "uri": "file:///Users/assafa/Documents/code/cli-projects/jfrog-cli/testdata/xray/jas/sast/flask_webgoat/ui.py" + }, + "region": { + "endColumn": 31, + "endLine": 11, + "snippet": { + "text": "request.args" + }, + "startColumn": 19, + "startLine": 11 + } + } + } + }, + { + "location": { + "logicalLocations": [ + { + "fullyQualifiedName": "flask_webgoat.ui.search" + } + ], + "physicalLocation": { + "artifactLocation": { + "uri": "file:///Users/assafa/Documents/code/cli-projects/jfrog-cli/testdata/xray/jas/sast/flask_webgoat/ui.py" + }, + "region": { + "endColumn": 35, + "endLine": 11, + "snippet": { + "text": "request.args.get" + }, + "startColumn": 19, + "startLine": 11 + } + } + } + }, + { + "location": { + "logicalLocations": [ + { + "fullyQualifiedName": "flask_webgoat.ui.search" + } + ], + "physicalLocation": { + "artifactLocation": { + "uri": "file:///Users/assafa/Documents/code/cli-projects/jfrog-cli/testdata/xray/jas/sast/flask_webgoat/ui.py" + }, + "region": { + "endColumn": 44, + "endLine": 11, + "snippet": { + "text": "request.args.get(\"query\")" + }, + "startColumn": 19, + "startLine": 11 + } + } + } + }, + { + "location": { + "logicalLocations": [ + { + "fullyQualifiedName": "flask_webgoat.ui.search" + } + ], + "physicalLocation": { + "artifactLocation": { + "uri": "file:///Users/assafa/Documents/code/cli-projects/jfrog-cli/testdata/xray/jas/sast/flask_webgoat/ui.py" + }, + "region": { + "endColumn": 16, + "endLine": 11, + "snippet": { + "text": "query_param" + }, + "startColumn": 5, + "startLine": 11 + } + } + } + }, + { + "location": { + "logicalLocations": [ + { + "fullyQualifiedName": "flask_webgoat.ui.search" + } + ], + "physicalLocation": { + "artifactLocation": { + "uri": "file:///Users/assafa/Documents/code/cli-projects/jfrog-cli/testdata/xray/jas/sast/flask_webgoat/ui.py" + }, + "region": { + "endColumn": 10, + "endLine": 22, + "snippet": { + "text": "render_template(\n \"search.html\", results=results, num_results=len(results), query=query_param\n )" + }, + "startColumn": 16, + "startLine": 20 + } + } + } + }, + { + "location": { + "logicalLocations": [ + { + "fullyQualifiedName": "flask_webgoat.ui.search" + } + ], + "physicalLocation": { + "artifactLocation": { + "uri": "file:///Users/assafa/Documents/code/cli-projects/jfrog-cli/testdata/xray/jas/sast/flask_webgoat/ui.py" + }, + "region": { + "endColumn": 10, + "endLine": 22, + "snippet": { + "text": "return render_template(\n \"search.html\", results=results, num_results=len(results), query=query_param\n )" + }, + "startColumn": 9, + "startLine": 20 + } + } + } + } + ] + } + ] + } + ], + "level": "error", + "locations": [ + { + "logicalLocations": [ + { + "fullyQualifiedName": "flask_webgoat.ui.search" + } + ], + "physicalLocation": { + "artifactLocation": { + "uri": "file:///Users/assafa/Documents/code/cli-projects/jfrog-cli/testdata/xray/jas/sast/flask_webgoat/ui.py" + }, + "region": { + "endColumn": 10, + "endLine": 22, + "snippet": { + "text": "return render_template(\n \"search.html\", results=results, num_results=len(results), query=query_param\n )" + }, + "startColumn": 9, + "startLine": 20 + } + } + } + ], + "ruleId": "python-xss" + }, + { + "message": { + "text": "XSS Vulnerability" + }, + "codeFlows": [ + { + "threadFlows": [ + { + "locations": [ + { + "location": { + "logicalLocations": [ + { + "fullyQualifiedName": "flask_webgoat.ui.search" + } + ], + "physicalLocation": { + "artifactLocation": { + "uri": "file:///Users/assafa/Documents/code/cli-projects/jfrog-cli/testdata/xray/jas/sast/flask_webgoat/ui.py" + }, + "region": { + "endColumn": 31, + "endLine": 11, + "snippet": { + "text": "request.args" + }, + "startColumn": 19, + "startLine": 11 + } + } + } + }, + { + "location": { + "logicalLocations": [ + { + "fullyQualifiedName": "flask_webgoat.ui.search" + } + ], + "physicalLocation": { + "artifactLocation": { + "uri": "file:///Users/assafa/Documents/code/cli-projects/jfrog-cli/testdata/xray/jas/sast/flask_webgoat/ui.py" + }, + "region": { + "endColumn": 35, + "endLine": 11, + "snippet": { + "text": "request.args.get" + }, + "startColumn": 19, + "startLine": 11 + } + } + } + }, + { + "location": { + "logicalLocations": [ + { + "fullyQualifiedName": "flask_webgoat.ui.search" + } + ], + "physicalLocation": { + "artifactLocation": { + "uri": "file:///Users/assafa/Documents/code/cli-projects/jfrog-cli/testdata/xray/jas/sast/flask_webgoat/ui.py" + }, + "region": { + "endColumn": 44, + "endLine": 11, + "snippet": { + "text": "request.args.get(\"query\")" + }, + "startColumn": 19, + "startLine": 11 + } + } + } + }, + { + "location": { + "logicalLocations": [ + { + "fullyQualifiedName": "flask_webgoat.ui.search" + } + ], + "physicalLocation": { + "artifactLocation": { + "uri": "file:///Users/assafa/Documents/code/cli-projects/jfrog-cli/testdata/xray/jas/sast/flask_webgoat/ui.py" + }, + "region": { + "endColumn": 16, + "endLine": 11, + "snippet": { + "text": "query_param" + }, + "startColumn": 5, + "startLine": 11 + } + } + } + }, + { + "location": { + "logicalLocations": [ + { + "fullyQualifiedName": "flask_webgoat.ui.search" + } + ], + "physicalLocation": { + "artifactLocation": { + "uri": "file:///Users/assafa/Documents/code/cli-projects/jfrog-cli/testdata/xray/jas/sast/flask_webgoat/ui.py" + }, + "region": { + "endColumn": 63, + "endLine": 24, + "snippet": { + "text": "\"Error while executing query \" + query_param" + }, + "startColumn": 19, + "startLine": 24 + } + } + } + }, + { + "location": { + "logicalLocations": [ + { + "fullyQualifiedName": "flask_webgoat.ui.search" + } + ], + "physicalLocation": { + "artifactLocation": { + "uri": "file:///Users/assafa/Documents/code/cli-projects/jfrog-cli/testdata/xray/jas/sast/flask_webgoat/ui.py" + }, + "region": { + "endColumn": 70, + "endLine": 24, + "snippet": { + "text": "\"Error while executing query \" + query_param + \": \"" + }, + "startColumn": 19, + "startLine": 24 + } + } + } + }, + { + "location": { + "logicalLocations": [ + { + "fullyQualifiedName": "flask_webgoat.ui.search" + } + ], + "physicalLocation": { + "artifactLocation": { + "uri": "file:///Users/assafa/Documents/code/cli-projects/jfrog-cli/testdata/xray/jas/sast/flask_webgoat/ui.py" + }, + "region": { + "endColumn": 76, + "endLine": 24, + "snippet": { + "text": "\"Error while executing query \" + query_param + \": \" + err" + }, + "startColumn": 19, + "startLine": 24 + } + } + } + }, + { + "location": { + "logicalLocations": [ + { + "fullyQualifiedName": "flask_webgoat.ui.search" + } + ], + "physicalLocation": { + "artifactLocation": { + "uri": "file:///Users/assafa/Documents/code/cli-projects/jfrog-cli/testdata/xray/jas/sast/flask_webgoat/ui.py" + }, + "region": { + "endColumn": 16, + "endLine": 24, + "snippet": { + "text": "message" + }, + "startColumn": 9, + "startLine": 24 + } + } + } + }, + { + "location": { + "logicalLocations": [ + { + "fullyQualifiedName": "flask_webgoat.ui.search" + } + ], + "physicalLocation": { + "artifactLocation": { + "uri": "file:///Users/assafa/Documents/code/cli-projects/jfrog-cli/testdata/xray/jas/sast/flask_webgoat/ui.py" + }, + "region": { + "endColumn": 62, + "endLine": 25, + "snippet": { + "text": "render_template(\"error.html\", message=message)" + }, + "startColumn": 16, + "startLine": 25 + } + } + } + }, + { + "location": { + "logicalLocations": [ + { + "fullyQualifiedName": "flask_webgoat.ui.search" + } + ], + "physicalLocation": { + "artifactLocation": { + "uri": "file:///Users/assafa/Documents/code/cli-projects/jfrog-cli/testdata/xray/jas/sast/flask_webgoat/ui.py" + }, + "region": { + "endColumn": 62, + "endLine": 25, + "snippet": { + "text": "return render_template(\"error.html\", message=message)" + }, + "startColumn": 9, + "startLine": 25 + } + } + } + } + ] + } + ] + } + ], + "level": "error", + "locations": [ + { + "logicalLocations": [ + { + "fullyQualifiedName": "flask_webgoat.ui.search" + } + ], + "physicalLocation": { + "artifactLocation": { + "uri": "file:///Users/assafa/Documents/code/cli-projects/jfrog-cli/testdata/xray/jas/sast/flask_webgoat/ui.py" + }, + "region": { + "endColumn": 62, + "endLine": 25, + "snippet": { + "text": "return render_template(\"error.html\", message=message)" + }, + "startColumn": 9, + "startLine": 25 + } + } + } + ], + "ruleId": "python-xss" + }, + { + "message": { + "text": "Flask Running in Debug" + }, + "locations": [ + { + "logicalLocations": [ + { + "fullyQualifiedName": "run" + } + ], + "physicalLocation": { + "artifactLocation": { + "uri": "file:///Users/assafa/Documents/code/cli-projects/jfrog-cli/testdata/xray/jas/sast/run.py" + }, + "region": { + "endColumn": 24, + "endLine": 15, + "snippet": { + "text": "app.run(debug=True)" + }, + "startColumn": 5, + "startLine": 15 + } + } + } + ], + "ruleId": "python-flask-debug" + } + ] + } + ], + "version": "2.1.0", + "$schema": "https://docs.oasis-open.org/sarif/sarif/v2.1.0/cos02/schemas/sarif-schema-2.1.0.json" +} \ No newline at end of file diff --git a/testdata/xray/jas-test/sast/run.py b/testdata/xray/jas-test/sast/run.py new file mode 100644 index 000000000..8cacc71d4 --- /dev/null +++ b/testdata/xray/jas-test/sast/run.py @@ -0,0 +1,15 @@ +from flask_webgoat import create_app + +app = create_app() + +@app.after_request +def add_csp_headers(response): + # vulnerability: Broken Access Control + response.headers['Access-Control-Allow-Origin'] = '*' + # vulnerability: Security Misconfiguration + response.headers['Content-Security-Policy'] = "script-src 'self' 'unsafe-inline'" + return response + +if __name__ == '__main__': + # vulnerability: Security Misconfiguration + app.run(debug=True) diff --git a/testdata/xray/nuget/single/core/Multi1.cs b/testdata/xray/nuget/single4.0/core/Multi1.cs similarity index 100% rename from testdata/xray/nuget/single/core/Multi1.cs rename to testdata/xray/nuget/single4.0/core/Multi1.cs diff --git a/testdata/xray/nuget/single/core/Properties/AssemblyInfo.cs b/testdata/xray/nuget/single4.0/core/Properties/AssemblyInfo.cs similarity index 100% rename from testdata/xray/nuget/single/core/Properties/AssemblyInfo.cs rename to testdata/xray/nuget/single4.0/core/Properties/AssemblyInfo.cs diff --git a/testdata/xray/nuget/single/core/core.csproj b/testdata/xray/nuget/single4.0/core/core.csproj similarity index 100% rename from testdata/xray/nuget/single/core/core.csproj rename to testdata/xray/nuget/single4.0/core/core.csproj diff --git a/testdata/xray/nuget/single/core/core.nuspec b/testdata/xray/nuget/single4.0/core/core.nuspec similarity index 100% rename from testdata/xray/nuget/single/core/core.nuspec rename to testdata/xray/nuget/single4.0/core/core.nuspec diff --git a/testdata/xray/nuget/single/core/packages.config b/testdata/xray/nuget/single4.0/core/packages.config similarity index 100% rename from testdata/xray/nuget/single/core/packages.config rename to testdata/xray/nuget/single4.0/core/packages.config diff --git a/testdata/xray/nuget/single/example.sln b/testdata/xray/nuget/single4.0/example.sln similarity index 100% rename from testdata/xray/nuget/single/example.sln rename to testdata/xray/nuget/single4.0/example.sln diff --git a/testdata/xray/nuget/single5.0/ClassLibrary1/ClassLibrary1.csproj b/testdata/xray/nuget/single5.0/ClassLibrary1/ClassLibrary1.csproj new file mode 100644 index 000000000..849a472aa --- /dev/null +++ b/testdata/xray/nuget/single5.0/ClassLibrary1/ClassLibrary1.csproj @@ -0,0 +1,22 @@ + + + + netstandard2.0 + AnyCPU;x64 + Some Compnay + Some product + Copyright © 2020 by Foobar. + 1.0.0 + + + + + + + + + + + + + diff --git a/testdata/xray/nuget/single5.0/TestSolution.sln b/testdata/xray/nuget/single5.0/TestSolution.sln new file mode 100644 index 000000000..28ab809cb --- /dev/null +++ b/testdata/xray/nuget/single5.0/TestSolution.sln @@ -0,0 +1,29 @@ + +Microsoft Visual Studio Solution File, Format Version 12.00 +# Visual Studio Version 16 +VisualStudioVersion = 16.0.32106.194 +MinimumVisualStudioVersion = 10.0.40219.1 +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "ClassLibrary1", "ClassLibrary1\ClassLibrary1.csproj", "{D9C89660-69F8-46E2-B9C4-78E9B505E8B1}" +EndProject +Global + GlobalSection(SolutionConfigurationPlatforms) = preSolution + Debug|Any CPU = Debug|Any CPU + Release|Any CPU = Release|Any CPU + EndGlobalSection + GlobalSection(ProjectConfigurationPlatforms) = postSolution + {757C1D5A-BAA9-453F-B612-A0571390976B}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {757C1D5A-BAA9-453F-B612-A0571390976B}.Debug|Any CPU.Build.0 = Debug|Any CPU + {757C1D5A-BAA9-453F-B612-A0571390976B}.Release|Any CPU.ActiveCfg = Release|Any CPU + {757C1D5A-BAA9-453F-B612-A0571390976B}.Release|Any CPU.Build.0 = Release|Any CPU + {D9C89660-69F8-46E2-B9C4-78E9B505E8B1}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {D9C89660-69F8-46E2-B9C4-78E9B505E8B1}.Debug|Any CPU.Build.0 = Debug|Any CPU + {D9C89660-69F8-46E2-B9C4-78E9B505E8B1}.Release|Any CPU.ActiveCfg = Release|Any CPU + {D9C89660-69F8-46E2-B9C4-78E9B505E8B1}.Release|Any CPU.Build.0 = Release|Any CPU + EndGlobalSection + GlobalSection(SolutionProperties) = preSolution + HideSolutionNode = FALSE + EndGlobalSection + GlobalSection(ExtensibilityGlobals) = postSolution + SolutionGuid = {D8547989-C4F7-4FCC-95BB-F7478F7D2484} + EndGlobalSection +EndGlobal diff --git a/transfer_test.go b/transfer_test.go index 40c735552..c748afadf 100644 --- a/transfer_test.go +++ b/transfer_test.go @@ -493,12 +493,12 @@ func updateDockerRepoParams(t *testing.T, targetServicesManager artifactory.Arti params.AllowAnyHostAuth = inverseBooleanPointer(params.AllowAnyHostAuth) params.EnableCookieManagement = inverseBooleanPointer(params.EnableCookieManagement) params.BypassHeadRequests = inverseBooleanPointer(params.BypassHeadRequests) - params.SocketTimeoutMillis += 100 - params.RetrievalCachePeriodSecs += 100 - params.MetadataRetrievalTimeoutSecs += 100 - params.MissedRetrievalCachePeriodSecs += 100 - params.UnusedArtifactsCleanupPeriodHours += 100 - params.AssumedOfflinePeriodSecs += 100 + *params.SocketTimeoutMillis += 100 + *params.RetrievalCachePeriodSecs += 100 + *params.MetadataRetrievalTimeoutSecs += 100 + *params.MissedRetrievalCachePeriodSecs += 100 + *params.UnusedArtifactsCleanupPeriodHours += 100 + *params.AssumedOfflinePeriodSecs += 100 params.Username = "test123" params.ContentSynchronisation.Enabled = inverseBooleanPointer(params.ContentSynchronisation.Enabled) @@ -546,17 +546,16 @@ func createTestProject(t *testing.T) func() error { deleteProjectIfExists(t, accessManager, tests.ProjectKey) // Create new project - falseValue := false adminPrivileges := accessServices.AdminPrivileges{ - ManageMembers: &falseValue, - ManageResources: &falseValue, - IndexResources: &falseValue, + ManageMembers: utils.Pointer(false), + ManageResources: utils.Pointer(false), + IndexResources: utils.Pointer(false), } projectDetails := accessServices.Project{ DisplayName: tests.ProjectKey + "MyProject", Description: "My Test Project", AdminPrivileges: &adminPrivileges, - SoftLimit: &falseValue, + SoftLimit: utils.Pointer(false), StorageQuotaBytes: 1073741825, ProjectKey: tests.ProjectKey, } @@ -570,10 +569,9 @@ func createTestProject(t *testing.T) func() error { } func updateProjectParams(t *testing.T, projectParams *accessServices.Project, targetAccessManager *access.AccessServicesManager) { - trueValue := true projectParams.Description = "123123123123" - projectParams.AdminPrivileges.IndexResources = &trueValue - projectParams.SoftLimit = &trueValue + projectParams.AdminPrivileges.IndexResources = utils.Pointer(true) + projectParams.SoftLimit = utils.Pointer(true) projectParams.StorageQuotaBytes += 1 assert.NoError(t, targetAccessManager.UpdateProject(accessServices.ProjectParams{ProjectDetails: *projectParams})) } diff --git a/utils/accesstoken/utils.go b/utils/accesstoken/utils.go new file mode 100644 index 000000000..f5ab0fc42 --- /dev/null +++ b/utils/accesstoken/utils.go @@ -0,0 +1,15 @@ +package accesstoken + +import ( + coreConfig "github.com/jfrog/jfrog-cli-core/v2/utils/config" + "github.com/urfave/cli" +) + +// If the username is provided as an argument, then it is used when creating the token. +// If not, then the configured username (or the value of the --user option) is used. +func GetSubjectUsername(c *cli.Context, serverDetails *coreConfig.ServerDetails) string { + if c.NArg() > 0 { + return c.Args().Get(0) + } + return serverDetails.GetUser() +} diff --git a/utils/cliutils/cli_consts.go b/utils/cliutils/cli_consts.go index bc0d2f16a..bb5cac980 100644 --- a/utils/cliutils/cli_consts.go +++ b/utils/cliutils/cli_consts.go @@ -4,7 +4,7 @@ import "time" const ( // General CLI constants - CliVersion = "2.46.2" + CliVersion = "2.50.4" ClientAgent = "jfrog-cli-go" // CLI base commands constants: @@ -28,7 +28,7 @@ const ( Retries = 3 RetryWaitMilliSecs = 0 Threads = 3 - TokenExpiry = 3600 + ArtifactoryTokenExpiry = 3600 DefaultLicenseCount = 1 LatestCliVersionCheckInterval = time.Hour * 6 diff --git a/utils/cliutils/commandsflags.go b/utils/cliutils/commandsflags.go index e3b2f33af..d87766773 100644 --- a/utils/cliutils/commandsflags.go +++ b/utils/cliutils/commandsflags.go @@ -76,16 +76,17 @@ const ( RepoDelete = "repo-delete" ReplicationDelete = "replication-delete" PermissionTargetDelete = "permission-target-delete" - AccessTokenCreate = "access-token-create" - UserCreate = "user-create" - UsersCreate = "users-create" - UsersDelete = "users-delete" - GroupCreate = "group-create" - GroupAddUsers = "group-add-users" - GroupDelete = "group-delete" - TransferConfig = "transfer-config" - TransferConfigMerge = "transfer-config-merge" - passphrase = "passphrase" + // #nosec G101 -- False positive - no hardcoded credentials. + ArtifactoryAccessTokenCreate = "artifactory-access-token-create" + UserCreate = "user-create" + UsersCreate = "users-create" + UsersDelete = "users-delete" + GroupCreate = "group-create" + GroupAddUsers = "group-add-users" + GroupDelete = "group-delete" + TransferConfig = "transfer-config" + TransferConfigMerge = "transfer-config-merge" + passphrase = "passphrase" // Distribution's Command Keys ReleaseBundleV1Create = "release-bundle-v1-create" @@ -135,9 +136,13 @@ const ( ReleaseBundlePromote = "release-bundle-promote" ReleaseBundleDistribute = "release-bundle-distribute" + // Access Token Create commands keys + AccessTokenCreate = "access-token-create" + // *** Artifactory Commands' flags *** // Base flags url = "url" + platformUrl = "platform-url" user = "user" password = "password" accessToken = "access-token" @@ -281,7 +286,7 @@ const ( envInclude = "env-include" envExclude = "env-exclude" buildUrl = "build-url" - project = "project" + Project = "project" // Unique build-add-dependencies flags badPrefix = "bad-" @@ -394,12 +399,35 @@ const ( Replace = "replace" Admin = "admin" + // Mutual *-access-token-create flags + Groups = "groups" + GrantAdmin = "grant-admin" + Expiry = "expiry" + Refreshable = "refreshable" + Audience = "audience" + + // Unique artifactory-access-token-create flags + artifactoryAccessTokenCreatePrefix = "rt-atc-" + rtAtcGroups = artifactoryAccessTokenCreatePrefix + Groups + rtAtcGrantAdmin = artifactoryAccessTokenCreatePrefix + GrantAdmin + rtAtcExpiry = artifactoryAccessTokenCreatePrefix + Expiry + rtAtcRefreshable = artifactoryAccessTokenCreatePrefix + Refreshable + rtAtcAudience = artifactoryAccessTokenCreatePrefix + Audience + // Unique access-token-create flags - groups = "groups" - grantAdmin = "grant-admin" - expiry = "expiry" - refreshable = "refreshable" - audience = "audience" + accessTokenCreatePrefix = "atc-" + atcProject = accessTokenCreatePrefix + Project + Scope = "scope" + atcScope = accessTokenCreatePrefix + Scope + Description = "description" + atcDescription = accessTokenCreatePrefix + Description + Reference = "reference" + atcReference = accessTokenCreatePrefix + Reference + atcGroups = accessTokenCreatePrefix + Groups + atcGrantAdmin = accessTokenCreatePrefix + GrantAdmin + atcExpiry = accessTokenCreatePrefix + Expiry + atcRefreshable = accessTokenCreatePrefix + Refreshable + atcAudience = accessTokenCreatePrefix + Audience // Unique Xray Flags for upload/publish commands xrayScan = "scan" @@ -458,19 +486,20 @@ const ( BypassArchiveLimits = "bypass-archive-limits" // Audit commands - auditPrefix = "audit-" - useWrapperAudit = auditPrefix + UseWrapper - ExcludeTestDeps = "exclude-test-deps" - DepType = "dep-type" - RequirementsFile = "requirements-file" - watches = "watches" - workingDirs = "working-dirs" - repoPath = "repo-path" - licenses = "licenses" - vuln = "vuln" - ExtendedTable = "extended-table" - MinSeverity = "min-severity" - FixableOnly = "fixable-only" + auditPrefix = "audit-" + useWrapperAudit = auditPrefix + UseWrapper + ExcludeTestDeps = "exclude-test-deps" + DepType = "dep-type" + ThirdPartyContextualAnalysis = "third-party-contextual-analysis" + RequirementsFile = "requirements-file" + watches = "watches" + workingDirs = "working-dirs" + repoPath = "repo-path" + licenses = "licenses" + vuln = "vuln" + ExtendedTable = "extended-table" + MinSeverity = "min-severity" + FixableOnly = "fixable-only" // *** Mission Control Commands' flags *** missionControlPrefix = "mc-" curationThreads = "curation-threads" @@ -553,7 +582,7 @@ const ( lifecyclePrefix = "lc-" lcUrl = lifecyclePrefix + url lcSync = lifecyclePrefix + Sync - lcProject = lifecyclePrefix + project + lcProject = lifecyclePrefix + Project Builds = "builds" lcBuilds = lifecyclePrefix + Builds ReleaseBundles = "release-bundles" @@ -570,6 +599,10 @@ const ( var flagsMap = map[string]cli.Flag{ // Common commands flags + platformUrl: cli.StringFlag{ + Name: url, + Usage: "[Optional] JFrog platform URL.` `", + }, user: cli.StringFlag{ Name: user, Usage: "[Optional] JFrog username.` `", @@ -912,8 +945,8 @@ var flagsMap = map[string]cli.Flag{ Name: buildUrl, Usage: "[Optional] Can be used for setting the CI server build URL in the build-info.` `", }, - project: cli.StringFlag{ - Name: project, + Project: cli.StringFlag{ + Name: Project, Usage: "[Optional] JFrog Artifactory project key.` `", }, bpDryRun: cli.BoolFlag{ @@ -1145,26 +1178,26 @@ var flagsMap = map[string]cli.Flag{ Name: vars, Usage: "[Optional] List of variables in the form of \"key1=value1;key2=value2;...\" to be replaced in the template. In the template, the variables should be used as follows: ${key1}.` `", }, - groups: cli.StringFlag{ - Name: groups, + rtAtcGroups: cli.StringFlag{ + Name: Groups, Usage: "[Default: *] A list of comma-separated groups for the access token to be associated with. " + "Specify * to indicate that this is a 'user-scoped token', i.e., the token provides the same access privileges that the current subject has, and is therefore evaluated dynamically. " + "A non-admin user can only provide a scope that is a subset of the groups to which he belongs` `", }, - grantAdmin: cli.BoolFlag{ - Name: grantAdmin, + rtAtcGrantAdmin: cli.BoolFlag{ + Name: GrantAdmin, Usage: "[Default: false] Set to true to provide admin privileges to the access token. This is only available for administrators.` `", }, - expiry: cli.StringFlag{ - Name: expiry, - Usage: "[Default: " + strconv.Itoa(TokenExpiry) + "] The time in seconds for which the token will be valid. To specify a token that never expires, set to zero. Non-admin can only set a value that is equal to or less than the default 3600.` `", + rtAtcExpiry: cli.StringFlag{ + Name: Expiry, + Usage: "[Default: " + strconv.Itoa(ArtifactoryTokenExpiry) + "] The time in seconds for which the token will be valid. To specify a token that never expires, set to zero. Non-admin may only set a value that is equal to or less than the default 3600.` `", }, - refreshable: cli.BoolFlag{ - Name: refreshable, + rtAtcRefreshable: cli.BoolFlag{ + Name: Refreshable, Usage: "[Default: false] Set to true if you'd like the token to be refreshable. A refresh token will also be returned in order to be used to generate a new token once it expires.` `", }, - audience: cli.StringFlag{ - Name: audience, + rtAtcAudience: cli.StringFlag{ + Name: Audience, Usage: "[Optional] A space-separate list of the other Artifactory instances or services that should accept this token identified by their Artifactory Service IDs, as obtained by the 'jfrog rt curl api/system/service_id' command.` `", }, usersCreateCsv: cli.StringFlag{ @@ -1607,7 +1640,7 @@ var flagsMap = map[string]cli.Flag{ Usage: "[Default: false] Set to true to run synchronously.` `", }, lcProject: cli.StringFlag{ - Name: project, + Name: Project, Usage: "[Optional] Project key associated with the Release Bundle version.` `", }, lcBuilds: cli.StringFlag{ @@ -1655,6 +1688,50 @@ var flagsMap = map[string]cli.Flag{ Name: releaseBundleVersion, Usage: "[Optional] Provide release bundle version to perform validation of signed pipelines", }, + ThirdPartyContextualAnalysis: cli.BoolFlag{ + Name: ThirdPartyContextualAnalysis, + Usage: "Default: false] [npm] when set, the Contextual Analysis scan also uses the code of the project dependencies to determine the applicability of the vulnerability.", + Hidden: true, + }, + atcProject: cli.StringFlag{ + Name: Project, + Usage: "[Optional] The project for which this token is created. Enter the project name on which you want to apply this token.` `", + }, + atcGrantAdmin: cli.BoolFlag{ + Name: GrantAdmin, + Usage: "[Default: false] Set to true to provide admin privileges to the access token. This is only available for administrators.` `", + }, + atcGroups: cli.StringFlag{ + Name: Groups, + Usage: "[Optional] A list of comma-separated groups for the access token to be associated with. " + + "This is only available for administrators.` `", + }, + atcScope: cli.StringFlag{ + Name: Scope, + Usage: "[Optional] The scope of access that the token provides. This is only available for administrators.` `", + }, + atcExpiry: cli.StringFlag{ + Name: Expiry, + Usage: "[Optional] The amount of time, in seconds, it would take for the token to expire. Must be non-negative." + + "If not provided, the platform default will be used. To specify a token that never expires, set to zero. " + + "Non-admin may only set a value that is equal or lower than the platform default that was set by an administrator (1 year by default).` `", + }, + atcRefreshable: cli.BoolFlag{ + Name: Refreshable, + Usage: "[Default: false] Set to true if you'd like the token to be refreshable. A refresh token will also be returned in order to be used to generate a new token once it expires.` `", + }, + atcDescription: cli.StringFlag{ + Name: Description, + Usage: "[Optional] Free text token description. Useful for filtering and managing tokens. Limited to 1024 characters.` `", + }, + atcAudience: cli.StringFlag{ + Name: Audience, + Usage: "[Optional] A space-separated list of the other instances or services that should accept this token identified by their Service-IDs.` `", + }, + atcReference: cli.BoolFlag{ + Name: Reference, + Usage: "[Default: false] Generate a Reference Token (alias to Access Token) in addition to the full token (available from Artifactory 7.38.10)` `", + }, } var commandFlags = map[string][]string{ @@ -1673,7 +1750,7 @@ var commandFlags = map[string][]string{ url, user, password, accessToken, sshPassphrase, sshKeyPath, serverId, ClientCertPath, uploadTargetProps, ClientCertKeyPath, specFlag, specVars, buildName, buildNumber, module, uploadExclusions, deb, uploadRecursive, uploadFlat, uploadRegexp, retries, retryWaitTime, dryRun, uploadExplode, symlinks, includeDirs, - failNoOp, threads, uploadSyncDeletes, syncDeletesQuiet, InsecureTls, detailedSummary, project, + failNoOp, threads, uploadSyncDeletes, syncDeletesQuiet, InsecureTls, detailedSummary, Project, uploadAnt, uploadArchive, }, Download: { @@ -1681,74 +1758,74 @@ var commandFlags = map[string][]string{ ClientCertKeyPath, specFlag, specVars, buildName, buildNumber, module, exclusions, sortBy, sortOrder, limit, offset, downloadRecursive, downloadFlat, build, includeDeps, excludeArtifacts, minSplit, splitCount, retries, retryWaitTime, dryRun, downloadExplode, bypassArchiveInspection, validateSymlinks, bundle, publicGpgKey, includeDirs, - downloadProps, downloadExcludeProps, failNoOp, threads, archiveEntries, downloadSyncDeletes, syncDeletesQuiet, InsecureTls, detailedSummary, project, + downloadProps, downloadExcludeProps, failNoOp, threads, archiveEntries, downloadSyncDeletes, syncDeletesQuiet, InsecureTls, detailedSummary, Project, skipChecksum, }, Move: { url, user, password, accessToken, sshPassphrase, sshKeyPath, serverId, ClientCertPath, ClientCertKeyPath, specFlag, specVars, exclusions, sortBy, sortOrder, limit, offset, moveRecursive, moveFlat, dryRun, build, includeDeps, excludeArtifacts, moveProps, moveExcludeProps, failNoOp, threads, archiveEntries, - InsecureTls, retries, retryWaitTime, project, + InsecureTls, retries, retryWaitTime, Project, }, Copy: { url, user, password, accessToken, sshPassphrase, sshKeyPath, serverId, ClientCertPath, ClientCertKeyPath, specFlag, specVars, exclusions, sortBy, sortOrder, limit, offset, copyRecursive, copyFlat, dryRun, build, includeDeps, excludeArtifacts, bundle, copyProps, copyExcludeProps, failNoOp, threads, - archiveEntries, InsecureTls, retries, retryWaitTime, project, + archiveEntries, InsecureTls, retries, retryWaitTime, Project, }, Delete: { url, user, password, accessToken, sshPassphrase, sshKeyPath, serverId, ClientCertPath, ClientCertKeyPath, specFlag, specVars, exclusions, sortBy, sortOrder, limit, offset, deleteRecursive, dryRun, build, includeDeps, excludeArtifacts, deleteQuiet, deleteProps, deleteExcludeProps, failNoOp, threads, archiveEntries, - InsecureTls, retries, retryWaitTime, project, + InsecureTls, retries, retryWaitTime, Project, }, Search: { url, user, password, accessToken, sshPassphrase, sshKeyPath, serverId, ClientCertPath, ClientCertKeyPath, specFlag, specVars, exclusions, sortBy, sortOrder, limit, offset, searchRecursive, build, includeDeps, excludeArtifacts, count, bundle, includeDirs, searchProps, searchExcludeProps, failNoOp, archiveEntries, - InsecureTls, searchTransitive, retries, retryWaitTime, project, searchInclude, + InsecureTls, searchTransitive, retries, retryWaitTime, Project, searchInclude, }, Properties: { url, user, password, accessToken, sshPassphrase, sshKeyPath, serverId, ClientCertPath, ClientCertKeyPath, specFlag, specVars, exclusions, sortBy, sortOrder, limit, offset, propsRecursive, build, includeDeps, excludeArtifacts, bundle, includeDirs, failNoOp, threads, archiveEntries, propsProps, propsExcludeProps, - InsecureTls, retries, retryWaitTime, project, + InsecureTls, retries, retryWaitTime, Project, }, BuildPublish: { url, user, password, accessToken, sshPassphrase, sshKeyPath, serverId, buildUrl, bpDryRun, - envInclude, envExclude, InsecureTls, project, bpDetailedSummary, + envInclude, envExclude, InsecureTls, Project, bpDetailedSummary, }, BuildAppend: { url, user, password, accessToken, sshPassphrase, sshKeyPath, serverId, buildUrl, bpDryRun, - envInclude, envExclude, InsecureTls, project, + envInclude, envExclude, InsecureTls, Project, }, BuildAddDependencies: { - specFlag, specVars, uploadExclusions, badRecursive, badRegexp, badDryRun, project, badFromRt, serverId, badModule, + specFlag, specVars, uploadExclusions, badRecursive, badRegexp, badDryRun, Project, badFromRt, serverId, badModule, }, BuildAddGit: { - configFlag, serverId, project, + configFlag, serverId, Project, }, BuildCollectEnv: { - project, + Project, }, BuildDockerCreate: { buildName, buildNumber, module, url, user, password, accessToken, sshPassphrase, sshKeyPath, - serverId, imageFile, project, + serverId, imageFile, Project, }, OcStartBuild: { - buildName, buildNumber, module, project, serverId, ocStartBuildRepo, + buildName, buildNumber, module, Project, serverId, ocStartBuildRepo, }, BuildScanLegacy: { url, user, password, accessToken, sshPassphrase, sshKeyPath, serverId, fail, InsecureTls, - project, + Project, }, BuildPromote: { url, user, password, accessToken, sshPassphrase, sshKeyPath, serverId, Status, comment, - sourceRepo, includeDependencies, copyFlag, failFast, bprDryRun, bprProps, InsecureTls, project, + sourceRepo, includeDependencies, copyFlag, failFast, bprDryRun, bprProps, InsecureTls, Project, }, BuildDiscard: { url, user, password, accessToken, sshPassphrase, sshKeyPath, serverId, maxDays, maxBuilds, - excludeBuilds, deleteArtifacts, bdiAsync, InsecureTls, project, + excludeBuilds, deleteArtifacts, bdiAsync, InsecureTls, Project, }, GitLfsClean: { url, user, password, accessToken, sshPassphrase, sshKeyPath, serverId, refs, glcRepo, glcDryRun, @@ -1762,21 +1839,21 @@ var commandFlags = map[string][]string{ deployIvyDesc, ivyDescPattern, ivyArtifactsPattern, }, Mvn: { - buildName, buildNumber, deploymentThreads, InsecureTls, project, detailedSummary, xrayScan, xrOutput, + buildName, buildNumber, deploymentThreads, InsecureTls, Project, detailedSummary, xrayScan, xrOutput, }, Gradle: { - buildName, buildNumber, deploymentThreads, project, detailedSummary, xrayScan, xrOutput, + buildName, buildNumber, deploymentThreads, Project, detailedSummary, xrayScan, xrOutput, }, Docker: { - buildName, buildNumber, module, project, + buildName, buildNumber, module, Project, serverId, skipLogin, threads, detailedSummary, watches, repoPath, licenses, xrOutput, fail, ExtendedTable, BypassArchiveLimits, MinSeverity, FixableOnly, }, DockerPush: { - buildName, buildNumber, module, project, + buildName, buildNumber, module, Project, serverId, skipLogin, threads, detailedSummary, }, DockerPull: { - buildName, buildNumber, module, project, + buildName, buildNumber, module, Project, serverId, skipLogin, }, DockerPromote: { @@ -1785,54 +1862,54 @@ var commandFlags = map[string][]string{ }, ContainerPush: { buildName, buildNumber, module, url, user, password, accessToken, sshPassphrase, sshKeyPath, - serverId, skipLogin, threads, project, detailedSummary, + serverId, skipLogin, threads, Project, detailedSummary, }, ContainerPull: { buildName, buildNumber, module, url, user, password, accessToken, sshPassphrase, sshKeyPath, - serverId, skipLogin, project, + serverId, skipLogin, Project, }, NpmConfig: { global, serverIdResolve, serverIdDeploy, repoResolve, repoDeploy, }, NpmInstallCi: { - buildName, buildNumber, module, project, + buildName, buildNumber, module, Project, }, NpmPublish: { - buildName, buildNumber, module, project, npmDetailedSummary, xrayScan, xrOutput, + buildName, buildNumber, module, Project, npmDetailedSummary, xrayScan, xrOutput, }, YarnConfig: { global, serverIdResolve, repoResolve, }, Yarn: { - buildName, buildNumber, module, project, + buildName, buildNumber, module, Project, }, NugetConfig: { global, serverIdResolve, repoResolve, nugetV2, }, Nuget: { - buildName, buildNumber, module, project, + buildName, buildNumber, module, Project, }, DotnetConfig: { global, serverIdResolve, repoResolve, nugetV2, }, Dotnet: { - buildName, buildNumber, module, project, + buildName, buildNumber, module, Project, }, GoConfig: { global, serverIdResolve, serverIdDeploy, repoResolve, repoDeploy, }, GoPublish: { - url, user, password, accessToken, buildName, buildNumber, module, project, detailedSummary, goPublishExclusions, + url, user, password, accessToken, buildName, buildNumber, module, Project, detailedSummary, goPublishExclusions, }, Go: { - buildName, buildNumber, module, project, noFallback, + buildName, buildNumber, module, Project, noFallback, }, TerraformConfig: { global, serverIdDeploy, repoDeploy, }, Terraform: { namespace, provider, tag, exclusions, - buildName, buildNumber, module, project, + buildName, buildNumber, module, Project, }, TransferConfig: { Force, Verbose, IncludeRepos, ExcludeRepos, SourceWorkingDir, TargetWorkingDir, PreChecks, @@ -1851,19 +1928,19 @@ var commandFlags = map[string][]string{ global, serverIdResolve, repoResolve, }, PipInstall: { - buildName, buildNumber, module, project, + buildName, buildNumber, module, Project, }, PipenvConfig: { global, serverIdResolve, repoResolve, }, PipenvInstall: { - buildName, buildNumber, module, project, + buildName, buildNumber, module, Project, }, PoetryConfig: { global, serverIdResolve, repoResolve, }, Poetry: { - buildName, buildNumber, module, project, + buildName, buildNumber, module, Project, }, ReleaseBundleV1Create: { distUrl, user, password, accessToken, serverId, specFlag, specVars, targetProps, @@ -1901,9 +1978,14 @@ var commandFlags = map[string][]string{ url, user, password, accessToken, sshPassphrase, sshKeyPath, serverId, ClientCertPath, ClientCertKeyPath, deleteQuiet, }, - AccessTokenCreate: { + ArtifactoryAccessTokenCreate: { url, user, password, accessToken, sshPassphrase, sshKeyPath, serverId, ClientCertPath, - ClientCertKeyPath, groups, grantAdmin, expiry, refreshable, audience, + ClientCertKeyPath, rtAtcGroups, rtAtcGrantAdmin, rtAtcExpiry, rtAtcRefreshable, rtAtcAudience, + }, + AccessTokenCreate: { + platformUrl, user, password, accessToken, sshPassphrase, sshKeyPath, serverId, ClientCertPath, ClientCertKeyPath, + atcProject, atcGrantAdmin, atcGroups, atcScope, atcExpiry, + atcRefreshable, atcDescription, atcAudience, atcReference, }, UserCreate: { url, user, password, accessToken, sshPassphrase, sshKeyPath, serverId, @@ -1954,37 +2036,37 @@ var commandFlags = map[string][]string{ curationOutput, workingDirs, curationThreads, }, Audit: { - xrUrl, user, password, accessToken, serverId, InsecureTls, project, watches, repoPath, licenses, xrOutput, ExcludeTestDeps, - useWrapperAudit, DepType, RequirementsFile, fail, ExtendedTable, workingDirs, Mvn, Gradle, Npm, Yarn, Go, Nuget, Pip, Pipenv, Poetry, MinSeverity, FixableOnly, + xrUrl, user, password, accessToken, serverId, InsecureTls, Project, watches, repoPath, licenses, xrOutput, ExcludeTestDeps, + useWrapperAudit, DepType, RequirementsFile, fail, ExtendedTable, workingDirs, Mvn, Gradle, Npm, Yarn, Go, Nuget, Pip, Pipenv, Poetry, MinSeverity, FixableOnly, ThirdPartyContextualAnalysis, }, AuditMvn: { - xrUrl, user, password, accessToken, serverId, InsecureTls, project, watches, repoPath, licenses, xrOutput, fail, ExtendedTable, useWrapperAudit, + xrUrl, user, password, accessToken, serverId, InsecureTls, Project, watches, repoPath, licenses, xrOutput, fail, ExtendedTable, useWrapperAudit, }, AuditGradle: { - xrUrl, user, password, accessToken, serverId, ExcludeTestDeps, useWrapperAudit, project, watches, repoPath, licenses, xrOutput, fail, ExtendedTable, + xrUrl, user, password, accessToken, serverId, ExcludeTestDeps, useWrapperAudit, Project, watches, repoPath, licenses, xrOutput, fail, ExtendedTable, }, AuditNpm: { - xrUrl, user, password, accessToken, serverId, DepType, project, watches, repoPath, licenses, xrOutput, fail, ExtendedTable, + xrUrl, user, password, accessToken, serverId, DepType, Project, watches, repoPath, licenses, xrOutput, fail, ExtendedTable, }, AuditGo: { - xrUrl, user, password, accessToken, serverId, project, watches, repoPath, licenses, xrOutput, fail, ExtendedTable, + xrUrl, user, password, accessToken, serverId, Project, watches, repoPath, licenses, xrOutput, fail, ExtendedTable, }, AuditPip: { - xrUrl, user, password, accessToken, serverId, RequirementsFile, project, watches, repoPath, licenses, xrOutput, fail, ExtendedTable, + xrUrl, user, password, accessToken, serverId, RequirementsFile, Project, watches, repoPath, licenses, xrOutput, fail, ExtendedTable, }, AuditPipenv: { - xrUrl, user, password, accessToken, serverId, project, watches, repoPath, licenses, xrOutput, ExtendedTable, + xrUrl, user, password, accessToken, serverId, Project, 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, + 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, + serverId, Project, watches, repoPath, licenses, xrOutput, fail, ExtendedTable, BypassArchiveLimits, MinSeverity, FixableOnly, }, BuildScan: { - xrUrl, user, password, accessToken, serverId, project, vuln, xrOutput, fail, ExtendedTable, rescan, + xrUrl, user, password, accessToken, serverId, Project, vuln, xrOutput, fail, ExtendedTable, rescan, }, // Mission Control's commands McConfig: { diff --git a/utils/cliutils/utils.go b/utils/cliutils/utils.go index d8f3044fb..e101b863c 100644 --- a/utils/cliutils/utils.go +++ b/utils/cliutils/utils.go @@ -776,12 +776,9 @@ func isEnvFailNoOp() bool { return strings.ToLower(os.Getenv(coreutils.FailNoOp)) == "true" } -func CleanupResult(result *commandUtils.Result, originError *error) { +func CleanupResult(result *commandUtils.Result, err *error) { if result != nil && result.Reader() != nil { - e := result.Reader().Close() - if originError == nil { - *originError = e - } + *err = errors.Join(*err, result.Reader().Close()) } } diff --git a/utils/tests/consts.go b/utils/tests/consts.go index f415aa1f0..fdd8729c0 100644 --- a/utils/tests/consts.go +++ b/utils/tests/consts.go @@ -91,6 +91,7 @@ const ( SearchAllMaven = "search_all_maven.json" SearchAllNpm = "search_all_npm.json" SearchAllRepo1 = "search_all_repo1.json" + SearchAllDevRepo = "search_all_dev_repo.json" SearchAllProdRepo = "search_all_prod_repo.json" SearchDistRepoByInSuffix = "search_dist_repo_by_in_suffix.json" SearchRepo1ByInSuffix = "search_repo1_by_in_suffix.json" @@ -2099,17 +2100,26 @@ func GetExpectedLifecycleArtifacts() []string { } } -func GetExpectedLifecycleMappingArtifacts() []string { - return []string{ - RtProdRepo + "/target/a1.in", - RtProdRepo + "/target/a2.in", - RtProdRepo + "/target/a3.in", - RtProdRepo + "/target/b1.in", - RtProdRepo + "/target/b2.in", - RtProdRepo + "/target/b3.in", - RtProdRepo + "/target/c1.in", - RtProdRepo + "/target/c2.in", - RtProdRepo + "/target/c3.in", +func GetExpectedLifecycleDistributedArtifacts() []string { + return []string{ + RtDevRepo + "/a1.in", + RtDevRepo + "/a2.in", + RtDevRepo + "/a3.in", + RtDevRepo + "/b1.in", + RtDevRepo + "/b2.in", + RtDevRepo + "/b3.in", + RtDevRepo + "/c1.in", + RtDevRepo + "/c2.in", + RtDevRepo + "/c3.in", + RtDevRepo + "/target/a1.in", + RtDevRepo + "/target/a2.in", + RtDevRepo + "/target/a3.in", + RtDevRepo + "/target/b1.in", + RtDevRepo + "/target/b2.in", + RtDevRepo + "/target/b3.in", + RtDevRepo + "/target/c1.in", + RtDevRepo + "/target/c2.in", + RtDevRepo + "/target/c3.in", } } diff --git a/xray_test.go b/xray_test.go index 3698bb151..00bfe268d 100644 --- a/xray_test.go +++ b/xray_test.go @@ -6,6 +6,7 @@ import ( "flag" "fmt" biutils "github.com/jfrog/build-info-go/utils" + "github.com/jfrog/jfrog-cli-core/v2/xray/scangraph" "net/http" "net/http/httptest" "os" @@ -24,13 +25,12 @@ import ( artUtils "github.com/jfrog/jfrog-cli-core/v2/artifactory/utils" "github.com/jfrog/jfrog-cli-core/v2/artifactory/utils/container" coreCmd "github.com/jfrog/jfrog-cli-core/v2/common/commands" - tests2 "github.com/jfrog/jfrog-cli-core/v2/common/tests" + 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" 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" - commands "github.com/jfrog/jfrog-cli-core/v2/xray/commands/utils" "github.com/jfrog/jfrog-cli-core/v2/xray/formats" "github.com/jfrog/jfrog-cli-core/v2/xray/utils" "github.com/jfrog/jfrog-cli/inttestutils" @@ -114,13 +114,13 @@ func TestXrayBinaryScanSimpleJsonWithProgress(t *testing.T) { } func testXrayBinaryScan(t *testing.T, format string) string { - initXrayTest(t, commands.GraphScanMinXrayVersion) + 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, commands.BypassArchiveLimitsMinXrayVersion) + initXrayTest(t, scan.BypassArchiveLimitsMinXrayVersion) unsetEnv := clientTestUtils.SetEnvWithCallbackAndAssert(t, "JF_INDEXER_COMPRESS_MAXENTITIES", "10") defer unsetEnv() binariesPath := filepath.Join(filepath.FromSlash(tests.GetTestResourcesPath()), "xray", "binaries", "*") @@ -148,7 +148,7 @@ func TestXrayAuditNpmSimpleJson(t *testing.T) { } func testXrayAuditNpm(t *testing.T, format string) string { - initXrayTest(t, commands.GraphScanMinXrayVersion) + initXrayTest(t, scangraph.GraphScanMinXrayVersion) tempDirPath, createTempDirCallback := coretests.CreateTempDirWithCallbackAndAssert(t) defer createTempDirCallback() npmProjectPath := filepath.Join(filepath.FromSlash(tests.GetTestResourcesPath()), "xray", "npm") @@ -183,6 +183,19 @@ func TestXrayAuditYarnV1Json(t *testing.T) { 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(utils.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(utils.SimpleJson)) @@ -191,7 +204,7 @@ func TestXrayAuditYarnV1SimpleJson(t *testing.T) { } func testXrayAuditYarn(t *testing.T, projectDirName string, yarnCmd func()) { - initXrayTest(t, commands.GraphScanMinXrayVersion) + initXrayTest(t, scangraph.GraphScanMinXrayVersion) tempDirPath, createTempDirCallback := coretests.CreateTempDirWithCallbackAndAssert(t) defer createTempDirCallback() yarnProjectPath := filepath.Join(filepath.FromSlash(tests.GetTestResourcesPath()), "xray", projectDirName) @@ -210,25 +223,103 @@ 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 and asserts any error. +// Tests NuGet audit by providing simple NuGet project + multi-project NuGet project and asserts any error. func TestXrayAuditNugetJson(t *testing.T) { - output := testXrayAuditNuget(t, "single", string(utils.Json)) - verifyJsonScanResults(t, output, 0, 2, 0) + var testdata = []struct { + projectName string + format string + restoreTech string + minVulnerabilities int + minLicences int + }{ + { + projectName: "single4.0", + format: string(utils.Json), + restoreTech: "nuget", + minVulnerabilities: 2, + minLicences: 0, + }, + { + projectName: "single5.0", + format: string(utils.Json), + restoreTech: "dotnet", + minVulnerabilities: 3, + minLicences: 2, + }, + { + projectName: "single5.0", + format: string(utils.Json), + restoreTech: "", + minVulnerabilities: 3, + minLicences: 2, + }, + { + projectName: "multi", + format: string(utils.Json), + restoreTech: "dotnet", + minVulnerabilities: 5, + minLicences: 3, + }, + { + projectName: "multi", + format: string(utils.Json), + restoreTech: "", + minVulnerabilities: 5, + 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) { - output := testXrayAuditNuget(t, "single", string(utils.SimpleJson)) - verifySimpleJsonScanResults(t, output, 2, 0) -} - -// Tests NuGet audit by providing a multi-project NuGet project and asserts any error. -func TestXrayAuditNugetMultiProject(t *testing.T) { - output := testXrayAuditNuget(t, "multi", string(utils.Json)) - verifyJsonScanResults(t, output, 0, 5, 0) + var testdata = []struct { + projectName string + format string + restoreTech string + minVulnerabilities int + minLicences int + }{ + { + projectName: "single4.0", + format: string(utils.SimpleJson), + restoreTech: "nuget", + minVulnerabilities: 2, + minLicences: 0, + }, + { + projectName: "single5.0", + format: string(utils.SimpleJson), + restoreTech: "dotnet", + minVulnerabilities: 3, + minLicences: 2, + }, + { + projectName: "single5.0", + format: string(utils.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) string { - initXrayTest(t, commands.GraphScanMinXrayVersion) +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) @@ -238,9 +329,12 @@ func testXrayAuditNuget(t *testing.T, projectName, format string) string { defer clientTestUtils.ChangeDirAndAssert(t, prevWd) // Add dummy descriptor file to check that we run only specific audit addDummyPackageDescriptor(t, false) - // Run NuGet restore before executing jfrog xr audit (NuGet) - assert.NoError(t, exec.Command("nuget", "restore").Run()) - return xrayCli.RunCliCmdWithOutput(t, "audit", "--nuget", "--format="+format) + // 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) { @@ -254,7 +348,7 @@ func TestXrayAuditGradleSimpleJson(t *testing.T) { } func testXrayAuditGradle(t *testing.T, format string) string { - initXrayTest(t, commands.GraphScanMinXrayVersion) + initXrayTest(t, scangraph.GraphScanMinXrayVersion) tempDirPath, createTempDirCallback := coretests.CreateTempDirWithCallbackAndAssert(t) defer createTempDirCallback() gradleProjectPath := filepath.Join(filepath.FromSlash(tests.GetTestResourcesPath()), "xray", "gradle") @@ -278,7 +372,7 @@ func TestXrayAuditMavenSimpleJson(t *testing.T) { } func testXrayAuditMaven(t *testing.T, format string) string { - initXrayTest(t, commands.GraphScanMinXrayVersion) + initXrayTest(t, scangraph.GraphScanMinXrayVersion) tempDirPath, createTempDirCallback := coretests.CreateTempDirWithCallbackAndAssert(t) defer createTempDirCallback() mvnProjectPath := filepath.Join(filepath.FromSlash(tests.GetTestResourcesPath()), "xray", "maven") @@ -292,7 +386,7 @@ func testXrayAuditMaven(t *testing.T, format string) string { } func TestXrayAuditNoTech(t *testing.T) { - initXrayTest(t, commands.GraphScanMinXrayVersion) + initXrayTest(t, scangraph.GraphScanMinXrayVersion) tempDirPath, createTempDirCallback := coretests.CreateTempDirWithCallbackAndAssert(t) defer createTempDirCallback() prevWd := changeWD(t, tempDirPath) @@ -303,7 +397,7 @@ func TestXrayAuditNoTech(t *testing.T) { } func TestXrayAuditDetectTech(t *testing.T) { - initXrayTest(t, commands.GraphScanMinXrayVersion) + initXrayTest(t, scangraph.GraphScanMinXrayVersion) tempDirPath, createTempDirCallback := coretests.CreateTempDirWithCallbackAndAssert(t) defer createTempDirCallback() mvnProjectPath := filepath.Join(filepath.FromSlash(tests.GetTestResourcesPath()), "xray", "maven") @@ -321,21 +415,21 @@ func TestXrayAuditDetectTech(t *testing.T) { } func TestXrayAuditMultiProjects(t *testing.T) { - initXrayTest(t, commands.GraphScanMinXrayVersion) + 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)) workingDirsFlag := fmt.Sprintf("--working-dirs=%s, %s ,%s, %s", - filepath.Join(tempDirPath, "maven"), filepath.Join(tempDirPath, "nuget", "single"), + 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(utils.SimpleJson), workingDirsFlag) verifySimpleJsonScanResults(t, output, 35, 0) - verifySimpleJsonJasResults(t, output, 9, 7, 0, 1) + verifySimpleJsonJasResults(t, output, 3, 9, 7, 3) } func TestXrayAuditPipJson(t *testing.T) { @@ -359,7 +453,7 @@ func TestXrayAuditPipSimpleJsonWithRequirementsFile(t *testing.T) { } func testXrayAuditPip(t *testing.T, format, requirementsFile string) string { - initXrayTest(t, commands.GraphScanMinXrayVersion) + initXrayTest(t, scangraph.GraphScanMinXrayVersion) tempDirPath, createTempDirCallback := coretests.CreateTempDirWithCallbackAndAssert(t) defer createTempDirCallback() pipProjectPath := filepath.Join(filepath.FromSlash(tests.GetTestResourcesPath()), "xray", "python", "pip") @@ -388,7 +482,7 @@ func TestXrayAuditPipenvSimpleJson(t *testing.T) { } func testXrayAuditPipenv(t *testing.T, format string) string { - initXrayTest(t, commands.GraphScanMinXrayVersion) + initXrayTest(t, scangraph.GraphScanMinXrayVersion) tempDirPath, createTempDirCallback := coretests.CreateTempDirWithCallbackAndAssert(t) defer createTempDirCallback() pipenvProjectPath := filepath.Join(filepath.FromSlash(tests.GetTestResourcesPath()), "xray", "python", "pipenv") @@ -450,7 +544,7 @@ func TestXrayAuditPoetrySimpleJson(t *testing.T) { } func testXrayAuditPoetry(t *testing.T, format string) string { - initXrayTest(t, commands.GraphScanMinXrayVersion) + initXrayTest(t, scangraph.GraphScanMinXrayVersion) tempDirPath, createTempDirCallback := coretests.CreateTempDirWithCallbackAndAssert(t) defer createTempDirCallback() poetryProjectPath := filepath.Join(filepath.FromSlash(tests.GetTestResourcesPath()), "xray", "python", "poetry") @@ -614,8 +708,7 @@ func runDockerScan(t *testing.T, imageName, watchName string, minViolations, min } func createTestWatch(t *testing.T) (string, func()) { - trueValue := true - xrayManager, err := commands.CreateXrayServiceManager(xrayDetails) + xrayManager, err := utils.CreateXrayServiceManager(xrayDetails) assert.NoError(t, err) // Create new default policy. policyParams := xrayUtils.PolicyParams{ @@ -626,7 +719,7 @@ func createTestWatch(t *testing.T) (string, func()) { Criteria: *xrayUtils.CreateSeverityPolicyCriteria(xrayUtils.Low), Priority: 1, Actions: &xrayUtils.PolicyAction{ - FailBuild: &trueValue, + FailBuild: clientUtils.Pointer(true), }, }}, } @@ -669,17 +762,22 @@ func TestXrayOfflineDBSyncV3(t *testing.T) { func TestXrayAuditJasSimpleJson(t *testing.T) { output := testXrayAuditJas(t, string(utils.SimpleJson), "jas-test") - verifySimpleJsonJasResults(t, output, 9, 7, 2, 1) + verifySimpleJsonJasResults(t, output, 3, 9, 7, 2) +} + +func TestXrayAuditJasSimpleJsonWithConfig(t *testing.T) { + output := testXrayAuditJas(t, string(utils.SimpleJson), "jas-config") + verifySimpleJsonJasResults(t, output, 0, 0, 1, 2) } func TestXrayAuditJasNoViolationsSimpleJson(t *testing.T) { output := testXrayAuditJas(t, string(utils.SimpleJson), "npm") verifySimpleJsonScanResults(t, output, 2, 0) - verifySimpleJsonJasResults(t, output, 0, 0, 0, 1) + verifySimpleJsonJasResults(t, output, 0, 0, 0, 0) } func testXrayAuditJas(t *testing.T, format string, project string) string { - initXrayTest(t, commands.GraphScanMinXrayVersion) + initXrayTest(t, scangraph.GraphScanMinXrayVersion) tempDirPath, createTempDirCallback := coretests.CreateTempDirWithCallbackAndAssert(t) defer createTempDirCallback() projectDir := filepath.Join(filepath.FromSlash(tests.GetTestResourcesPath()), filepath.Join("xray", project)) @@ -695,10 +793,11 @@ func testXrayAuditJas(t *testing.T, format string, project string) string { return xrayCli.WithoutCredentials().RunCliCmdWithOutput(t, "audit", "--format="+format) } -func verifySimpleJsonJasResults(t *testing.T, content string, minIacViolations, minSecrets, minApplicable, minNotApplicable int) { +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 @@ -710,7 +809,7 @@ func verifySimpleJsonJasResults(t *testing.T, content string, minIacViolations, } } assert.GreaterOrEqual(t, applicableResults, minApplicable, "Found less applicableResults then expected") - assert.GreaterOrEqual(t, notApplicableResults, minNotApplicable, "Found less notApplicableResults then expected") + assert.GreaterOrEqual(t, notApplicableResults, 1, "Found less notApplicableResults then expected") } } @@ -803,7 +902,7 @@ func getCurationExpectedResponse(config *config.ServerDetails) []coreCuration.Pa func curationServer(t *testing.T, expectedRequest map[string]bool, requestToFail map[string]bool) (*httptest.Server, *config.ServerDetails) { mapLockReadWrite := sync.Mutex{} - serverMock, config, _ := tests2.CreateRtRestsMockServer(t, func(w http.ResponseWriter, r *http.Request) { + 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 {