diff --git a/.github/workflows/action-test.yml b/.github/workflows/action-test.yml index da3808ad9..9eec58fd0 100644 --- a/.github/workflows/action-test.yml +++ b/.github/workflows/action-test.yml @@ -17,7 +17,7 @@ jobs: steps: # Checkout and install prerequisites - name: Checkout - uses: actions/checkout@v3 + uses: actions/checkout@v4 - name: Setup NodeJS uses: actions/setup-node@v3 with: diff --git a/.github/workflows/analysis.yml b/.github/workflows/analysis.yml index c886eaad6..373cdfde7 100644 --- a/.github/workflows/analysis.yml +++ b/.github/workflows/analysis.yml @@ -9,12 +9,20 @@ jobs: runs-on: ubuntu-latest steps: - name: Checkout Source - uses: actions/checkout@v3 + uses: actions/checkout@v4 + - name: Install Go uses: actions/setup-go@v3 with: go-version: 1.20.x + - name: Go Cache + uses: actions/cache@v3 + with: + path: ~/go/pkg/mod + key: ${{ runner.os }}-go-${{ hashFiles('**/go.sum') }} + restore-keys: ${{ runner.os }}-go- + # Generate mocks - name: Generate mocks run: go generate ./... @@ -31,7 +39,8 @@ jobs: GOFLAGS: -buildvcs=false steps: - name: Checkout Source - uses: actions/checkout@v3 + uses: actions/checkout@v4 + - name: Install Go uses: actions/setup-go@v3 with: diff --git a/.github/workflows/frogbot-scan-pr.yml b/.github/workflows/frogbot-scan-pr.yml deleted file mode 100644 index f6d1b4ed9..000000000 --- a/.github/workflows/frogbot-scan-pr.yml +++ /dev/null @@ -1,39 +0,0 @@ -name: "Frogbot Scan Pull Request" -on: - pull_request_target: - types: [ opened, synchronize ] -permissions: - pull-requests: write - contents: read -jobs: - scan-pull-request: - runs-on: ubuntu-latest - # A pull request needs to be approved, before Frogbot scans it. Any GitHub user who is associated with the - # "frogbot" GitHub environment can approve the pull request to be scanned. - environment: frogbot - steps: - # Install prerequisites - - name: Setup Go - uses: actions/setup-go@v3 - with: - go-version: 1.20.x - - - uses: jfrog/frogbot@v2 - env: - # [Mandatory] - # JFrog platform URL (This functionality requires version 3.29.0 or above of Xray) - JF_URL: ${{ secrets.FROGBOT_URL }} - - # [Mandatory if JF_USER and JF_PASSWORD are not provided] - # JFrog access token with 'read' permissions on Xray service - JF_ACCESS_TOKEN: ${{ secrets.FROGBOT_ACCESS_TOKEN }} - - # [Mandatory] - # The GitHub token automatically generated for the job - JF_GIT_TOKEN: ${{ secrets.GITHUB_TOKEN }} - - JF_SMTP_SERVER: ${{ secrets.JF_SMTP_SERVER }} - - JF_SMTP_PASSWORD: ${{ JF_SMTP_PASSWORD }} - - JF_SMTP_USER: ${{ JF_SMTP_USER }} diff --git a/.github/workflows/frogbot-scan-pull-request.yml b/.github/workflows/frogbot-scan-pull-request.yml new file mode 100644 index 000000000..998c8c91f --- /dev/null +++ b/.github/workflows/frogbot-scan-pull-request.yml @@ -0,0 +1,119 @@ +name: "Frogbot Scan Pull Request" +on: + pull_request_target: + types: [ opened, synchronize ] +permissions: + pull-requests: write + contents: read +jobs: + scan-pull-request: + runs-on: ubuntu-latest + # A pull request needs to be approved before Frogbot scans it. Any GitHub user who is associated with the + # "frogbot" GitHub environment can approve the pull request to be scanned. + environment: frogbot + steps: + - 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 }} + + # [Mandatory if JF_USER and JF_PASSWORD are not provided] + # JFrog access token with 'read' permissions on Xray service + JF_ACCESS_TOKEN: ${{ secrets.FROGBOT_ACCESS_TOKEN }} + + # [Mandatory] + # The GitHub token is automatically generated for the job + JF_GIT_TOKEN: ${{ secrets.GITHUB_TOKEN }} + + # [Optional, default: https://api.github.com] + # API endpoint to GitHub + # JF_GIT_API_ENDPOINT: https://github.example.com + + # [Optional] + # By default, the Frogbot workflows download the Frogbot executable as well as other tools + # needed from https://releases.jfrog.io + # If the machine that runs Frogbot has no access to the internet, follow these steps to allow the + # executable to be downloaded from an Artifactory instance, which the machine has access to: + # + # 1. Login to the Artifactory UI, with a user who has admin credentials. + # 2. Create a Remote Repository with the following properties set. + # Under the 'Basic' tab: + # Package Type: Generic + # URL: https://releases.jfrog.io + # Under the 'Advanced' tab: + # Uncheck the 'Store Artifacts Locally' option + # 3. Set the value of the 'JF_RELEASES_REPO' variable with the Repository Key you created. + # JF_RELEASES_REPO: "" + + # [Optional] + # Configure the SMTP server to enable Frogbot to send emails with detected secrets in pull request scans. + # SMTP server URL including should the relevant port: (Example: smtp.server.com:8080) + JF_SMTP_SERVER: ${{ secrets.JF_SMTP_SERVER }} + + # [Mandatory if JF_SMTP_SERVER is set] + # The username required for authenticating with the SMTP server. + JF_SMTP_USER: ${{ secrets.JF_SMTP_USER }} + + # [Mandatory if JF_SMTP_SERVER is set] + # The password associated with the username required for authentication with the SMTP server. + JF_SMTP_PASSWORD: ${{ secrets.JF_SMTP_PASSWORD }} + + # [Optional] + # List of comma separated email addresses to receive email notifications about secrets + # detected during pull request scanning. The notification is also sent to the email set + # in the committer git profile regardless of whether this variable is set or not. + JF_EMAIL_RECEIVERS: "eco-system@jfrog.com" + + ########################################################################## + ## If your project uses a 'frogbot-config.yml' file, you can define ## + ## the following variables inside the file, instead of here. ## + ########################################################################## + + # [Mandatory if the two conditions below are met] + # 1. The project uses yarn 2, NuGet or .NET Core to download its dependencies + # 2. The `installCommand` variable isn't set in your frogbot-config.yml file. + # + # The command that installs the project dependencies (e.g "nuget restore") + # JF_INSTALL_DEPS_CMD: "" + + # [Optional, default: "."] + # Relative path to the root of the project in the Git repository + # JF_WORKING_DIR: path/to/project/dir + + # [Optional] + # Xray Watches. Learn more about them here: https://www.jfrog.com/confluence/display/JFROG/Configuring+Xray+Watches + # JF_WATCHES: ,... + + # [Optional] + # JFrog project. Learn more about it here: https://www.jfrog.com/confluence/display/JFROG/Projects + # JF_PROJECT: + + # [Optional, default: "FALSE"] + # Displays all existing vulnerabilities, including the ones that were added by the pull request. + # JF_INCLUDE_ALL_VULNERABILITIES: "TRUE" + + # [Optional, default: "TRUE"] + # Fails the Frogbot task if any security issue is found. + # JF_FAIL: "FALSE" + + # [Optional] + # Frogbot will download the project dependencies if they're not cached locally. To download the + # dependencies from a virtual repository in Artifactory, set the name of the repository. There's no + # need to set this value, if it is set in the frogbot-config.yml file. + # JF_DEPS_REPO: "" + + # [Optional, Default: "FALSE"] + # If TRUE, Frogbot creates a single pull request with all the fixes. + # If false, Frogbot creates a separate pull request for each fix. + # JF_GIT_AGGREGATE_FIXES: "FALSE" + + # [Optional, Default: "FALSE"] + # Handle vulnerabilities with fix versions only + # JF_FIXABLE_ONLY: "TRUE" + + # [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 diff --git a/.github/workflows/frogbot-scan-and-fix.yml b/.github/workflows/frogbot-scan-repository.yml similarity index 87% rename from .github/workflows/frogbot-scan-and-fix.yml rename to .github/workflows/frogbot-scan-repository.yml index bb2b42c03..710be0162 100644 --- a/.github/workflows/frogbot-scan-and-fix.yml +++ b/.github/workflows/frogbot-scan-repository.yml @@ -1,4 +1,4 @@ -name: "Frogbot Scan and Fix" +name: "Frogbot Scan Repository" on: workflow_dispatch: schedule: @@ -11,38 +11,25 @@ permissions: jobs: scan-repository: runs-on: ubuntu-latest + name: Scan Repository (${{ matrix.branch }} branch) strategy: matrix: # The repository scanning will be triggered periodically on the following branches. branch: [ "dev" ] steps: - - # Install prerequisites - - name: Setup Go - uses: actions/setup-go@v3 - with: - go-version: 1.20.x - - uses: jfrog/frogbot@v2 env: + JFROG_CLI_LOG_LEVEL: "DEBUG" # [Mandatory] - # JFrog platform URL + # JFrog platform URL (This functionality requires version 3.29.0 or above of Xray) JF_URL: ${{ secrets.FROGBOT_URL }} # [Mandatory if JF_USER and JF_PASSWORD are not provided] # JFrog access token with 'read' permissions on Xray service JF_ACCESS_TOKEN: ${{ secrets.FROGBOT_ACCESS_TOKEN }} - # [Mandatory if JF_ACCESS_TOKEN is not provided] - # JFrog username with 'read' permissions for Xray. Must be provided with JF_PASSWORD - # JF_USER: ${{ secrets.JF_USER }} - - # [Mandatory if JF_ACCESS_TOKEN is not provided] - # JFrog password. Must be provided with JF_USER - # JF_PASSWORD: ${{ secrets.JF_PASSWORD }} - # [Mandatory] - # The GitHub token automatically generated for the job + # The GitHub token is automatically generated for the job JF_GIT_TOKEN: ${{ secrets.GITHUB_TOKEN }} # [Mandatory] @@ -59,7 +46,7 @@ jobs: # If the machine that runs Frogbot has no access to the internet, follow these steps to allow the # executable to be downloaded from an Artifactory instance, which the machine has access to: # - # 1. Login to the Artifactory UI, with a user which has admin credentials. + # 1. Login to the Artifactory UI, with a user who has admin credentials. # 2. Create a Remote Repository with the following properties set. # Under the 'Basic' tab: # Package Type: Generic @@ -125,3 +112,7 @@ jobs: # 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: "" + + # [Optional, Default: eco-system+frogbot@jfrog.com] + # Set the email of the commit author + # JF_GIT_EMAIL_AUTHOR: "" \ No newline at end of file diff --git a/.github/workflows/test.yml b/.github/workflows/test.yml index 3b563826b..86843c8f7 100644 --- a/.github/workflows/test.yml +++ b/.github/workflows/test.yml @@ -23,7 +23,7 @@ jobs: labels: "safe to test" - name: Checkout code - uses: actions/checkout@v3 + uses: actions/checkout@v4 with: ref: ${{ github.event.pull_request.head.sha }} @@ -42,25 +42,34 @@ jobs: # Generate mocks - name: Generate mocks run: go generate ./... - if: ${{ matrix.suite != 'unit' }} - name: Lint run: go vet -v ./... tests: needs: Pretest - name: ${{ matrix.suite }} Tests (${{ matrix.os }}) + name: ${{ matrix.suite.name }} Tests (${{ matrix.os }}) runs-on: ${{ matrix.os }}-latest env: JFROG_CLI_LOG_LEVEL: "DEBUG" strategy: fail-fast: false matrix: - suite: [ unit, scanrepository, scanpullrequest, packagehandlers ] + suite: + - name: 'Unit' + + - name: 'Scan Repository' + package: 'scanrepository' + + - name: 'Scan Pull Request' + package: 'scanpullrequest' + + - name: 'Package Handlers' + package: 'packagehandlers' os: [ ubuntu, windows, macos ] steps: # Configure prerequisites - - uses: actions/checkout@v3 + - uses: actions/checkout@v4 with: ref: ${{ github.event.pull_request.head.sha }} - name: Setup Go @@ -89,24 +98,21 @@ jobs: - name: Install python components run: python -m pip install pipenv poetry + - name: Install dotnet + uses: actions/setup-dotnet@v3 + with: + dotnet-version: "6.x" + # Generate mocks - name: Generate mocks run: go generate ./... - if: ${{ matrix.suite != 'unit' }} - - - name: unit Tests - run: go test github.com/jfrog/frogbot -v -race -timeout 30m -cover - env: - JF_URL: ${{ secrets.PLATFORM_URL }} - JF_ACCESS_TOKEN: ${{ secrets.PLATFORM_ADMIN_TOKEN }} - if: ${{ matrix.suite == 'unit' }} + if: ${{ matrix.suite.name != 'Unit' }} - - name: ${{ matrix.test }} Tests - run: go test github.com/jfrog/frogbot/${{ matrix.suite }} -v -race -timeout 30m -cover + - name: Run Tests + run: go test github.com/jfrog/frogbot/${{ matrix.suite.package }} -v -race -timeout 30m -cover env: JF_URL: ${{ secrets.PLATFORM_URL }} JF_ACCESS_TOKEN: ${{ secrets.PLATFORM_ADMIN_TOKEN }} - if: ${{ matrix.suite != 'unit' }} # Build and run frogbot current changes for visual sanity check. sanity-pull-request: @@ -120,7 +126,7 @@ jobs: go-version: 1.20.x - name: Checkout code - uses: actions/checkout@v3 + uses: actions/checkout@v4 with: ref: ${{ github.event.pull_request.head.sha }} diff --git a/.github/workflows/update-v2-tag.yml b/.github/workflows/update-v2-tag.yml index 094827d71..8208b48ea 100644 --- a/.github/workflows/update-v2-tag.yml +++ b/.github/workflows/update-v2-tag.yml @@ -8,7 +8,7 @@ jobs: runs-on: ubuntu-latest steps: - name: Checkout - uses: actions/checkout@v3 + uses: actions/checkout@v4 - name: Update v2 tag run: git tag -f v2 - name: Push changes diff --git a/README.md b/README.md index d60ce0b93..514d2cb83 100644 --- a/README.md +++ b/README.md @@ -254,6 +254,8 @@ Supported package management tools: - Go - Maven +- NuGet +- .NET - npm - Pip - Pipenv diff --git a/action/node_modules/.package-lock.json b/action/node_modules/.package-lock.json index b61afb6da..d5a139759 100644 --- a/action/node_modules/.package-lock.json +++ b/action/node_modules/.package-lock.json @@ -1,7 +1,7 @@ { "name": "@jfrog/frogbot", "version": "1.0.0", - "lockfileVersion": 3, + "lockfileVersion": 2, "requires": true, "packages": { "node_modules/@actions/core": { diff --git a/action/package-lock.json b/action/package-lock.json index e7efca06e..b4167de60 100644 --- a/action/package-lock.json +++ b/action/package-lock.json @@ -17,17 +17,17 @@ "simple-git": "^3.19.1" }, "devDependencies": { - "@types/jest": "^29.5.0", - "@types/node": "^20.5.0", - "@typescript-eslint/eslint-plugin": "^6.4.0", - "@typescript-eslint/parser": "^6.3.0", - "eslint": "^8.47.0", + "@types/jest": "^29.5.4", + "@types/node": "^20.5.9", + "@typescript-eslint/eslint-plugin": "^6.5.0", + "@typescript-eslint/parser": "^6.5.0", + "eslint": "^8.48.0", "eslint-config-prettier": "^9.0.0", "husky": "^8.0.3", - "jest": "^29.6.2", - "prettier": "^3.0.2", + "jest": "^29.6.4", + "prettier": "^3.0.3", "ts-jest": "^29.1.1", - "typescript": "^5.1.6" + "typescript": "^5.2.2" }, "engines": { "node": ">=16.0.0", @@ -803,9 +803,9 @@ } }, "node_modules/@eslint/js": { - "version": "8.47.0", - "resolved": "https://registry.npmjs.org/@eslint/js/-/js-8.47.0.tgz", - "integrity": "sha512-P6omY1zv5MItm93kLM8s2vr1HICJH8v0dvddDhysbIuZ+vcjOHg5Zbkf1mTkcmi2JA9oBG2anOkRnW8WJTS8Og==", + "version": "8.48.0", + "resolved": "https://registry.npmjs.org/@eslint/js/-/js-8.48.0.tgz", + "integrity": "sha512-ZSjtmelB7IJfWD2Fvb7+Z+ChTIKWq6kjda95fLcQKNS5aheVHn4IkfgRQE3sIIzTcSLwLcLZUD9UBt+V7+h+Pw==", "dev": true, "engines": { "node": "^12.22.0 || ^14.17.0 || >=16.0.0" @@ -953,16 +953,16 @@ } }, "node_modules/@jest/console": { - "version": "29.6.2", - "resolved": "https://registry.npmjs.org/@jest/console/-/console-29.6.2.tgz", - "integrity": "sha512-0N0yZof5hi44HAR2pPS+ikJ3nzKNoZdVu8FffRf3wy47I7Dm7etk/3KetMdRUqzVd16V4O2m2ISpNTbnIuqy1w==", + "version": "29.6.4", + "resolved": "https://registry.npmjs.org/@jest/console/-/console-29.6.4.tgz", + "integrity": "sha512-wNK6gC0Ha9QeEPSkeJedQuTQqxZYnDPuDcDhVuVatRvMkL4D0VTvFVZj+Yuh6caG2aOfzkUZ36KtCmLNtR02hw==", "dev": true, "dependencies": { - "@jest/types": "^29.6.1", + "@jest/types": "^29.6.3", "@types/node": "*", "chalk": "^4.0.0", - "jest-message-util": "^29.6.2", - "jest-util": "^29.6.2", + "jest-message-util": "^29.6.3", + "jest-util": "^29.6.3", "slash": "^3.0.0" }, "engines": { @@ -970,37 +970,37 @@ } }, "node_modules/@jest/core": { - "version": "29.6.2", - "resolved": "https://registry.npmjs.org/@jest/core/-/core-29.6.2.tgz", - "integrity": "sha512-Oj+5B+sDMiMWLhPFF+4/DvHOf+U10rgvCLGPHP8Xlsy/7QxS51aU/eBngudHlJXnaWD5EohAgJ4js+T6pa+zOg==", + "version": "29.6.4", + "resolved": "https://registry.npmjs.org/@jest/core/-/core-29.6.4.tgz", + "integrity": "sha512-U/vq5ccNTSVgYH7mHnodHmCffGWHJnz/E1BEWlLuK5pM4FZmGfBn/nrJGLjUsSmyx3otCeqc1T31F4y08AMDLg==", "dev": true, "dependencies": { - "@jest/console": "^29.6.2", - "@jest/reporters": "^29.6.2", - "@jest/test-result": "^29.6.2", - "@jest/transform": "^29.6.2", - "@jest/types": "^29.6.1", + "@jest/console": "^29.6.4", + "@jest/reporters": "^29.6.4", + "@jest/test-result": "^29.6.4", + "@jest/transform": "^29.6.4", + "@jest/types": "^29.6.3", "@types/node": "*", "ansi-escapes": "^4.2.1", "chalk": "^4.0.0", "ci-info": "^3.2.0", "exit": "^0.1.2", "graceful-fs": "^4.2.9", - "jest-changed-files": "^29.5.0", - "jest-config": "^29.6.2", - "jest-haste-map": "^29.6.2", - "jest-message-util": "^29.6.2", - "jest-regex-util": "^29.4.3", - "jest-resolve": "^29.6.2", - "jest-resolve-dependencies": "^29.6.2", - "jest-runner": "^29.6.2", - "jest-runtime": "^29.6.2", - "jest-snapshot": "^29.6.2", - "jest-util": "^29.6.2", - "jest-validate": "^29.6.2", - "jest-watcher": "^29.6.2", + "jest-changed-files": "^29.6.3", + "jest-config": "^29.6.4", + "jest-haste-map": "^29.6.4", + "jest-message-util": "^29.6.3", + "jest-regex-util": "^29.6.3", + "jest-resolve": "^29.6.4", + "jest-resolve-dependencies": "^29.6.4", + "jest-runner": "^29.6.4", + "jest-runtime": "^29.6.4", + "jest-snapshot": "^29.6.4", + "jest-util": "^29.6.3", + "jest-validate": "^29.6.3", + "jest-watcher": "^29.6.4", "micromatch": "^4.0.4", - "pretty-format": "^29.6.2", + "pretty-format": "^29.6.3", "slash": "^3.0.0", "strip-ansi": "^6.0.0" }, @@ -1016,134 +1016,89 @@ } } }, - "node_modules/@jest/core/node_modules/jest-config": { - "version": "29.6.2", - "resolved": "https://registry.npmjs.org/jest-config/-/jest-config-29.6.2.tgz", - "integrity": "sha512-VxwFOC8gkiJbuodG9CPtMRjBUNZEHxwfQXmIudSTzFWxaci3Qub1ddTRbFNQlD/zUeaifLndh/eDccFX4wCMQw==", - "dev": true, - "dependencies": { - "@babel/core": "^7.11.6", - "@jest/test-sequencer": "^29.6.2", - "@jest/types": "^29.6.1", - "babel-jest": "^29.6.2", - "chalk": "^4.0.0", - "ci-info": "^3.2.0", - "deepmerge": "^4.2.2", - "glob": "^7.1.3", - "graceful-fs": "^4.2.9", - "jest-circus": "^29.6.2", - "jest-environment-node": "^29.6.2", - "jest-get-type": "^29.4.3", - "jest-regex-util": "^29.4.3", - "jest-resolve": "^29.6.2", - "jest-runner": "^29.6.2", - "jest-util": "^29.6.2", - "jest-validate": "^29.6.2", - "micromatch": "^4.0.4", - "parse-json": "^5.2.0", - "pretty-format": "^29.6.2", - "slash": "^3.0.0", - "strip-json-comments": "^3.1.1" - }, - "engines": { - "node": "^14.15.0 || ^16.10.0 || >=18.0.0" - }, - "peerDependencies": { - "@types/node": "*", - "ts-node": ">=9.0.0" - }, - "peerDependenciesMeta": { - "@types/node": { - "optional": true - }, - "ts-node": { - "optional": true - } - } - }, "node_modules/@jest/environment": { - "version": "29.6.2", - "resolved": "https://registry.npmjs.org/@jest/environment/-/environment-29.6.2.tgz", - "integrity": "sha512-AEcW43C7huGd/vogTddNNTDRpO6vQ2zaQNrttvWV18ArBx9Z56h7BIsXkNFJVOO4/kblWEQz30ckw0+L3izc+Q==", + "version": "29.6.4", + "resolved": "https://registry.npmjs.org/@jest/environment/-/environment-29.6.4.tgz", + "integrity": "sha512-sQ0SULEjA1XUTHmkBRl7A1dyITM9yb1yb3ZNKPX3KlTd6IG7mWUe3e2yfExtC2Zz1Q+mMckOLHmL/qLiuQJrBQ==", "dev": true, "dependencies": { - "@jest/fake-timers": "^29.6.2", - "@jest/types": "^29.6.1", + "@jest/fake-timers": "^29.6.4", + "@jest/types": "^29.6.3", "@types/node": "*", - "jest-mock": "^29.6.2" + "jest-mock": "^29.6.3" }, "engines": { "node": "^14.15.0 || ^16.10.0 || >=18.0.0" } }, "node_modules/@jest/expect": { - "version": "29.6.2", - "resolved": "https://registry.npmjs.org/@jest/expect/-/expect-29.6.2.tgz", - "integrity": "sha512-m6DrEJxVKjkELTVAztTLyS/7C92Y2b0VYqmDROYKLLALHn8T/04yPs70NADUYPrV3ruI+H3J0iUIuhkjp7vkfg==", + "version": "29.6.4", + "resolved": "https://registry.npmjs.org/@jest/expect/-/expect-29.6.4.tgz", + "integrity": "sha512-Warhsa7d23+3X5bLbrbYvaehcgX5TLYhI03JKoedTiI8uJU4IhqYBWF7OSSgUyz4IgLpUYPkK0AehA5/fRclAA==", "dev": true, "dependencies": { - "expect": "^29.6.2", - "jest-snapshot": "^29.6.2" + "expect": "^29.6.4", + "jest-snapshot": "^29.6.4" }, "engines": { "node": "^14.15.0 || ^16.10.0 || >=18.0.0" } }, "node_modules/@jest/expect-utils": { - "version": "29.6.2", - "resolved": "https://registry.npmjs.org/@jest/expect-utils/-/expect-utils-29.6.2.tgz", - "integrity": "sha512-6zIhM8go3RV2IG4aIZaZbxwpOzz3ZiM23oxAlkquOIole+G6TrbeXnykxWYlqF7kz2HlBjdKtca20x9atkEQYg==", + "version": "29.6.4", + "resolved": "https://registry.npmjs.org/@jest/expect-utils/-/expect-utils-29.6.4.tgz", + "integrity": "sha512-FEhkJhqtvBwgSpiTrocquJCdXPsyvNKcl/n7A3u7X4pVoF4bswm11c9d4AV+kfq2Gpv/mM8x7E7DsRvH+djkrg==", "dev": true, "dependencies": { - "jest-get-type": "^29.4.3" + "jest-get-type": "^29.6.3" }, "engines": { "node": "^14.15.0 || ^16.10.0 || >=18.0.0" } }, "node_modules/@jest/fake-timers": { - "version": "29.6.2", - "resolved": "https://registry.npmjs.org/@jest/fake-timers/-/fake-timers-29.6.2.tgz", - "integrity": "sha512-euZDmIlWjm1Z0lJ1D0f7a0/y5Kh/koLFMUBE5SUYWrmy8oNhJpbTBDAP6CxKnadcMLDoDf4waRYCe35cH6G6PA==", + "version": "29.6.4", + "resolved": "https://registry.npmjs.org/@jest/fake-timers/-/fake-timers-29.6.4.tgz", + "integrity": "sha512-6UkCwzoBK60edXIIWb0/KWkuj7R7Qq91vVInOe3De6DSpaEiqjKcJw4F7XUet24Wupahj9J6PlR09JqJ5ySDHw==", "dev": true, "dependencies": { - "@jest/types": "^29.6.1", + "@jest/types": "^29.6.3", "@sinonjs/fake-timers": "^10.0.2", "@types/node": "*", - "jest-message-util": "^29.6.2", - "jest-mock": "^29.6.2", - "jest-util": "^29.6.2" + "jest-message-util": "^29.6.3", + "jest-mock": "^29.6.3", + "jest-util": "^29.6.3" }, "engines": { "node": "^14.15.0 || ^16.10.0 || >=18.0.0" } }, "node_modules/@jest/globals": { - "version": "29.6.2", - "resolved": "https://registry.npmjs.org/@jest/globals/-/globals-29.6.2.tgz", - "integrity": "sha512-cjuJmNDjs6aMijCmSa1g2TNG4Lby/AeU7/02VtpW+SLcZXzOLK2GpN2nLqcFjmhy3B3AoPeQVx7BnyOf681bAw==", + "version": "29.6.4", + "resolved": "https://registry.npmjs.org/@jest/globals/-/globals-29.6.4.tgz", + "integrity": "sha512-wVIn5bdtjlChhXAzVXavcY/3PEjf4VqM174BM3eGL5kMxLiZD5CLnbmkEyA1Dwh9q8XjP6E8RwjBsY/iCWrWsA==", "dev": true, "dependencies": { - "@jest/environment": "^29.6.2", - "@jest/expect": "^29.6.2", - "@jest/types": "^29.6.1", - "jest-mock": "^29.6.2" + "@jest/environment": "^29.6.4", + "@jest/expect": "^29.6.4", + "@jest/types": "^29.6.3", + "jest-mock": "^29.6.3" }, "engines": { "node": "^14.15.0 || ^16.10.0 || >=18.0.0" } }, "node_modules/@jest/reporters": { - "version": "29.6.2", - "resolved": "https://registry.npmjs.org/@jest/reporters/-/reporters-29.6.2.tgz", - "integrity": "sha512-sWtijrvIav8LgfJZlrGCdN0nP2EWbakglJY49J1Y5QihcQLfy7ovyxxjJBRXMNltgt4uPtEcFmIMbVshEDfFWw==", + "version": "29.6.4", + "resolved": "https://registry.npmjs.org/@jest/reporters/-/reporters-29.6.4.tgz", + "integrity": "sha512-sxUjWxm7QdchdrD3NfWKrL8FBsortZeibSJv4XLjESOOjSUOkjQcb0ZHJwfhEGIvBvTluTzfG2yZWZhkrXJu8g==", "dev": true, "dependencies": { "@bcoe/v8-coverage": "^0.2.3", - "@jest/console": "^29.6.2", - "@jest/test-result": "^29.6.2", - "@jest/transform": "^29.6.2", - "@jest/types": "^29.6.1", + "@jest/console": "^29.6.4", + "@jest/test-result": "^29.6.4", + "@jest/transform": "^29.6.4", + "@jest/types": "^29.6.3", "@jridgewell/trace-mapping": "^0.3.18", "@types/node": "*", "chalk": "^4.0.0", @@ -1152,13 +1107,13 @@ "glob": "^7.1.3", "graceful-fs": "^4.2.9", "istanbul-lib-coverage": "^3.0.0", - "istanbul-lib-instrument": "^5.1.0", + "istanbul-lib-instrument": "^6.0.0", "istanbul-lib-report": "^3.0.0", "istanbul-lib-source-maps": "^4.0.0", "istanbul-reports": "^3.1.3", - "jest-message-util": "^29.6.2", - "jest-util": "^29.6.2", - "jest-worker": "^29.6.2", + "jest-message-util": "^29.6.3", + "jest-util": "^29.6.3", + "jest-worker": "^29.6.4", "slash": "^3.0.0", "string-length": "^4.0.1", "strip-ansi": "^6.0.0", @@ -1176,10 +1131,59 @@ } } }, + "node_modules/@jest/reporters/node_modules/istanbul-lib-instrument": { + "version": "6.0.0", + "resolved": "https://registry.npmjs.org/istanbul-lib-instrument/-/istanbul-lib-instrument-6.0.0.tgz", + "integrity": "sha512-x58orMzEVfzPUKqlbLd1hXCnySCxKdDKa6Rjg97CwuLLRI4g3FHTdnExu1OqffVFay6zeMW+T6/DowFLndWnIw==", + "dev": true, + "dependencies": { + "@babel/core": "^7.12.3", + "@babel/parser": "^7.14.7", + "@istanbuljs/schema": "^0.1.2", + "istanbul-lib-coverage": "^3.2.0", + "semver": "^7.5.4" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/@jest/reporters/node_modules/lru-cache": { + "version": "6.0.0", + "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-6.0.0.tgz", + "integrity": "sha512-Jo6dJ04CmSjuznwJSS3pUeWmd/H0ffTlkXXgwZi+eq1UCmqQwCh+eLsYOYCwY991i2Fah4h1BEMCx4qThGbsiA==", + "dev": true, + "dependencies": { + "yallist": "^4.0.0" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/@jest/reporters/node_modules/semver": { + "version": "7.5.4", + "resolved": "https://registry.npmjs.org/semver/-/semver-7.5.4.tgz", + "integrity": "sha512-1bCSESV6Pv+i21Hvpxp3Dx+pSD8lIPt8uVjRrxAUt/nbswYc+tK6Y2btiULjd4+fnq15PX+nqQDC7Oft7WkwcA==", + "dev": true, + "dependencies": { + "lru-cache": "^6.0.0" + }, + "bin": { + "semver": "bin/semver.js" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/@jest/reporters/node_modules/yallist": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/yallist/-/yallist-4.0.0.tgz", + "integrity": "sha512-3wdGidZyq5PB084XLES5TpOSRA3wjXAlIWMhum2kRcv/41Sn2emQ0dycQW4uZXLejwKvg6EsvbdlVL+FYEct7A==", + "dev": true + }, "node_modules/@jest/schemas": { - "version": "29.6.0", - "resolved": "https://registry.npmjs.org/@jest/schemas/-/schemas-29.6.0.tgz", - "integrity": "sha512-rxLjXyJBTL4LQeJW3aKo0M/+GkCOXsO+8i9Iu7eDb6KwtP65ayoDsitrdPBtujxQ88k4wI2FNYfa6TOGwSn6cQ==", + "version": "29.6.3", + "resolved": "https://registry.npmjs.org/@jest/schemas/-/schemas-29.6.3.tgz", + "integrity": "sha512-mo5j5X+jIZmJQveBKeS/clAueipV7KgiX1vMgCxam1RNYiqE1w62n0/tJJnHtjW8ZHcQco5gY85jA3mi0L+nSA==", "dev": true, "dependencies": { "@sinclair/typebox": "^0.27.8" @@ -1189,9 +1193,9 @@ } }, "node_modules/@jest/source-map": { - "version": "29.6.0", - "resolved": "https://registry.npmjs.org/@jest/source-map/-/source-map-29.6.0.tgz", - "integrity": "sha512-oA+I2SHHQGxDCZpbrsCQSoMLb3Bz547JnM+jUr9qEbuw0vQlWZfpPS7CO9J7XiwKicEz9OFn/IYoLkkiUD7bzA==", + "version": "29.6.3", + "resolved": "https://registry.npmjs.org/@jest/source-map/-/source-map-29.6.3.tgz", + "integrity": "sha512-MHjT95QuipcPrpLM+8JMSzFx6eHp5Bm+4XeFDJlwsvVBjmKNiIAvasGK2fxz2WbGRlnvqehFbh07MMa7n3YJnw==", "dev": true, "dependencies": { "@jridgewell/trace-mapping": "^0.3.18", @@ -1203,13 +1207,13 @@ } }, "node_modules/@jest/test-result": { - "version": "29.6.2", - "resolved": "https://registry.npmjs.org/@jest/test-result/-/test-result-29.6.2.tgz", - "integrity": "sha512-3VKFXzcV42EYhMCsJQURptSqnyjqCGbtLuX5Xxb6Pm6gUf1wIRIl+mandIRGJyWKgNKYF9cnstti6Ls5ekduqw==", + "version": "29.6.4", + "resolved": "https://registry.npmjs.org/@jest/test-result/-/test-result-29.6.4.tgz", + "integrity": "sha512-uQ1C0AUEN90/dsyEirgMLlouROgSY+Wc/JanVVk0OiUKa5UFh7sJpMEM3aoUBAz2BRNvUJ8j3d294WFuRxSyOQ==", "dev": true, "dependencies": { - "@jest/console": "^29.6.2", - "@jest/types": "^29.6.1", + "@jest/console": "^29.6.4", + "@jest/types": "^29.6.3", "@types/istanbul-lib-coverage": "^2.0.0", "collect-v8-coverage": "^1.0.0" }, @@ -1218,14 +1222,14 @@ } }, "node_modules/@jest/test-sequencer": { - "version": "29.6.2", - "resolved": "https://registry.npmjs.org/@jest/test-sequencer/-/test-sequencer-29.6.2.tgz", - "integrity": "sha512-GVYi6PfPwVejO7slw6IDO0qKVum5jtrJ3KoLGbgBWyr2qr4GaxFV6su+ZAjdTX75Sr1DkMFRk09r2ZVa+wtCGw==", + "version": "29.6.4", + "resolved": "https://registry.npmjs.org/@jest/test-sequencer/-/test-sequencer-29.6.4.tgz", + "integrity": "sha512-E84M6LbpcRq3fT4ckfKs9ryVanwkaIB0Ws9bw3/yP4seRLg/VaCZ/LgW0MCq5wwk4/iP/qnilD41aj2fsw2RMg==", "dev": true, "dependencies": { - "@jest/test-result": "^29.6.2", + "@jest/test-result": "^29.6.4", "graceful-fs": "^4.2.9", - "jest-haste-map": "^29.6.2", + "jest-haste-map": "^29.6.4", "slash": "^3.0.0" }, "engines": { @@ -1233,22 +1237,22 @@ } }, "node_modules/@jest/transform": { - "version": "29.6.2", - "resolved": "https://registry.npmjs.org/@jest/transform/-/transform-29.6.2.tgz", - "integrity": "sha512-ZqCqEISr58Ce3U+buNFJYUktLJZOggfyvR+bZMaiV1e8B1SIvJbwZMrYz3gx/KAPn9EXmOmN+uB08yLCjWkQQg==", + "version": "29.6.4", + "resolved": "https://registry.npmjs.org/@jest/transform/-/transform-29.6.4.tgz", + "integrity": "sha512-8thgRSiXUqtr/pPGY/OsyHuMjGyhVnWrFAwoxmIemlBuiMyU1WFs0tXoNxzcr4A4uErs/ABre76SGmrr5ab/AA==", "dev": true, "dependencies": { "@babel/core": "^7.11.6", - "@jest/types": "^29.6.1", + "@jest/types": "^29.6.3", "@jridgewell/trace-mapping": "^0.3.18", "babel-plugin-istanbul": "^6.1.1", "chalk": "^4.0.0", "convert-source-map": "^2.0.0", "fast-json-stable-stringify": "^2.1.0", "graceful-fs": "^4.2.9", - "jest-haste-map": "^29.6.2", - "jest-regex-util": "^29.4.3", - "jest-util": "^29.6.2", + "jest-haste-map": "^29.6.4", + "jest-regex-util": "^29.6.3", + "jest-util": "^29.6.3", "micromatch": "^4.0.4", "pirates": "^4.0.4", "slash": "^3.0.0", @@ -1259,12 +1263,12 @@ } }, "node_modules/@jest/types": { - "version": "29.6.1", - "resolved": "https://registry.npmjs.org/@jest/types/-/types-29.6.1.tgz", - "integrity": "sha512-tPKQNMPuXgvdOn2/Lg9HNfUvjYVGolt04Hp03f5hAk878uwOLikN+JzeLY0HcVgKgFl9Hs3EIqpu3WX27XNhnw==", + "version": "29.6.3", + "resolved": "https://registry.npmjs.org/@jest/types/-/types-29.6.3.tgz", + "integrity": "sha512-u3UPsIilWKOM3F9CXtrG8LEJmNxwoCQC/XVj4IKYXvvpx7QIi/Kg1LI5uDmDpKlac62NUtX7eLjRh+jVZcLOzw==", "dev": true, "dependencies": { - "@jest/schemas": "^29.6.0", + "@jest/schemas": "^29.6.3", "@types/istanbul-lib-coverage": "^2.0.0", "@types/istanbul-reports": "^3.0.0", "@types/node": "*", @@ -1571,9 +1575,9 @@ } }, "node_modules/@types/jest": { - "version": "29.5.3", - "resolved": "https://registry.npmjs.org/@types/jest/-/jest-29.5.3.tgz", - "integrity": "sha512-1Nq7YrO/vJE/FYnqYyw0FS8LdrjExSgIiHyKg7xPpn+yi8Q4huZryKnkJatN1ZRH89Kw2v33/8ZMB7DuZeSLlA==", + "version": "29.5.4", + "resolved": "https://registry.npmjs.org/@types/jest/-/jest-29.5.4.tgz", + "integrity": "sha512-PhglGmhWeD46FYOVLt3X7TiWjzwuVGW9wG/4qocPevXMjCmrIc5b6db9WjeGE4QYVpUAWMDv3v0IiBwObY289A==", "dev": true, "dependencies": { "expect": "^29.0.0", @@ -1587,15 +1591,15 @@ "dev": true }, "node_modules/@types/node": { - "version": "20.5.0", - "resolved": "https://registry.npmjs.org/@types/node/-/node-20.5.0.tgz", - "integrity": "sha512-Mgq7eCtoTjT89FqNoTzzXg2XvCi5VMhRV6+I2aYanc6kQCBImeNaAYRs/DyoVqk1YEUJK5gN9VO7HRIdz4Wo3Q==", + "version": "20.5.9", + "resolved": "https://registry.npmjs.org/@types/node/-/node-20.5.9.tgz", + "integrity": "sha512-PcGNd//40kHAS3sTlzKB9C9XL4K0sTup8nbG5lC14kzEteTNuAFh9u5nA0o5TWnSG2r/JNPRXFVcHJIIeRlmqQ==", "dev": true }, "node_modules/@types/semver": { - "version": "7.5.0", - "resolved": "https://registry.npmjs.org/@types/semver/-/semver-7.5.0.tgz", - "integrity": "sha512-G8hZ6XJiHnuhQKR7ZmysCeJWE08o8T0AXtk5darsCaTVsYZhhgUrq53jizaR2FvsoeCwJhlmwTjkXBY5Pn/ZHw==", + "version": "7.5.1", + "resolved": "https://registry.npmjs.org/@types/semver/-/semver-7.5.1.tgz", + "integrity": "sha512-cJRQXpObxfNKkFAZbJl2yjWtJCqELQIdShsogr1d2MilP8dKD9TE/nEKHkJgUNHdGKCQaf9HbIynuV2csLGVLg==", "dev": true }, "node_modules/@types/stack-utils": { @@ -1620,16 +1624,16 @@ "dev": true }, "node_modules/@typescript-eslint/eslint-plugin": { - "version": "6.4.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/eslint-plugin/-/eslint-plugin-6.4.0.tgz", - "integrity": "sha512-62o2Hmc7Gs3p8SLfbXcipjWAa6qk2wZGChXG2JbBtYpwSRmti/9KHLqfbLs9uDigOexG+3PaQ9G2g3201FWLKg==", + "version": "6.5.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/eslint-plugin/-/eslint-plugin-6.5.0.tgz", + "integrity": "sha512-2pktILyjvMaScU6iK3925uvGU87E+N9rh372uGZgiMYwafaw9SXq86U04XPq3UH6tzRvNgBsub6x2DacHc33lw==", "dev": true, "dependencies": { "@eslint-community/regexpp": "^4.5.1", - "@typescript-eslint/scope-manager": "6.4.0", - "@typescript-eslint/type-utils": "6.4.0", - "@typescript-eslint/utils": "6.4.0", - "@typescript-eslint/visitor-keys": "6.4.0", + "@typescript-eslint/scope-manager": "6.5.0", + "@typescript-eslint/type-utils": "6.5.0", + "@typescript-eslint/utils": "6.5.0", + "@typescript-eslint/visitor-keys": "6.5.0", "debug": "^4.3.4", "graphemer": "^1.4.0", "ignore": "^5.2.4", @@ -1654,53 +1658,6 @@ } } }, - "node_modules/@typescript-eslint/eslint-plugin/node_modules/@typescript-eslint/scope-manager": { - "version": "6.4.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/scope-manager/-/scope-manager-6.4.0.tgz", - "integrity": "sha512-TUS7vaKkPWDVvl7GDNHFQMsMruD+zhkd3SdVW0d7b+7Zo+bd/hXJQ8nsiUZMi1jloWo6c9qt3B7Sqo+flC1nig==", - "dev": true, - "dependencies": { - "@typescript-eslint/types": "6.4.0", - "@typescript-eslint/visitor-keys": "6.4.0" - }, - "engines": { - "node": "^16.0.0 || >=18.0.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/typescript-eslint" - } - }, - "node_modules/@typescript-eslint/eslint-plugin/node_modules/@typescript-eslint/types": { - "version": "6.4.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/types/-/types-6.4.0.tgz", - "integrity": "sha512-+FV9kVFrS7w78YtzkIsNSoYsnOtrYVnKWSTVXoL1761CsCRv5wpDOINgsXpxD67YCLZtVQekDDyaxfjVWUJmmg==", - "dev": true, - "engines": { - "node": "^16.0.0 || >=18.0.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/typescript-eslint" - } - }, - "node_modules/@typescript-eslint/eslint-plugin/node_modules/@typescript-eslint/visitor-keys": { - "version": "6.4.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/visitor-keys/-/visitor-keys-6.4.0.tgz", - "integrity": "sha512-yJSfyT+uJm+JRDWYRYdCm2i+pmvXJSMtPR9Cq5/XQs4QIgNoLcoRtDdzsLbLsFM/c6um6ohQkg/MLxWvoIndJA==", - "dev": true, - "dependencies": { - "@typescript-eslint/types": "6.4.0", - "eslint-visitor-keys": "^3.4.1" - }, - "engines": { - "node": "^16.0.0 || >=18.0.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/typescript-eslint" - } - }, "node_modules/@typescript-eslint/eslint-plugin/node_modules/lru-cache": { "version": "6.0.0", "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-6.0.0.tgz", @@ -1735,15 +1692,15 @@ "dev": true }, "node_modules/@typescript-eslint/parser": { - "version": "6.3.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/parser/-/parser-6.3.0.tgz", - "integrity": "sha512-ibP+y2Gr6p0qsUkhs7InMdXrwldjxZw66wpcQq9/PzAroM45wdwyu81T+7RibNCh8oc0AgrsyCwJByncY0Ongg==", + "version": "6.5.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/parser/-/parser-6.5.0.tgz", + "integrity": "sha512-LMAVtR5GN8nY0G0BadkG0XIe4AcNMeyEy3DyhKGAh9k4pLSMBO7rF29JvDBpZGCmp5Pgz5RLHP6eCpSYZJQDuQ==", "dev": true, "dependencies": { - "@typescript-eslint/scope-manager": "6.3.0", - "@typescript-eslint/types": "6.3.0", - "@typescript-eslint/typescript-estree": "6.3.0", - "@typescript-eslint/visitor-keys": "6.3.0", + "@typescript-eslint/scope-manager": "6.5.0", + "@typescript-eslint/types": "6.5.0", + "@typescript-eslint/typescript-estree": "6.5.0", + "@typescript-eslint/visitor-keys": "6.5.0", "debug": "^4.3.4" }, "engines": { @@ -1763,13 +1720,13 @@ } }, "node_modules/@typescript-eslint/scope-manager": { - "version": "6.3.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/scope-manager/-/scope-manager-6.3.0.tgz", - "integrity": "sha512-WlNFgBEuGu74ahrXzgefiz/QlVb+qg8KDTpknKwR7hMH+lQygWyx0CQFoUmMn1zDkQjTBBIn75IxtWss77iBIQ==", + "version": "6.5.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/scope-manager/-/scope-manager-6.5.0.tgz", + "integrity": "sha512-A8hZ7OlxURricpycp5kdPTH3XnjG85UpJS6Fn4VzeoH4T388gQJ/PGP4ole5NfKt4WDVhmLaQ/dBLNDC4Xl/Kw==", "dev": true, "dependencies": { - "@typescript-eslint/types": "6.3.0", - "@typescript-eslint/visitor-keys": "6.3.0" + "@typescript-eslint/types": "6.5.0", + "@typescript-eslint/visitor-keys": "6.5.0" }, "engines": { "node": "^16.0.0 || >=18.0.0" @@ -1780,13 +1737,13 @@ } }, "node_modules/@typescript-eslint/type-utils": { - "version": "6.4.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/type-utils/-/type-utils-6.4.0.tgz", - "integrity": "sha512-TvqrUFFyGY0cX3WgDHcdl2/mMCWCDv/0thTtx/ODMY1QhEiyFtv/OlLaNIiYLwRpAxAtOLOY9SUf1H3Q3dlwAg==", + "version": "6.5.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/type-utils/-/type-utils-6.5.0.tgz", + "integrity": "sha512-f7OcZOkRivtujIBQ4yrJNIuwyCQO1OjocVqntl9dgSIZAdKqicj3xFDqDOzHDlGCZX990LqhLQXWRnQvsapq8A==", "dev": true, "dependencies": { - "@typescript-eslint/typescript-estree": "6.4.0", - "@typescript-eslint/utils": "6.4.0", + "@typescript-eslint/typescript-estree": "6.5.0", + "@typescript-eslint/utils": "6.5.0", "debug": "^4.3.4", "ts-api-utils": "^1.0.1" }, @@ -1806,100 +1763,10 @@ } } }, - "node_modules/@typescript-eslint/type-utils/node_modules/@typescript-eslint/types": { - "version": "6.4.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/types/-/types-6.4.0.tgz", - "integrity": "sha512-+FV9kVFrS7w78YtzkIsNSoYsnOtrYVnKWSTVXoL1761CsCRv5wpDOINgsXpxD67YCLZtVQekDDyaxfjVWUJmmg==", - "dev": true, - "engines": { - "node": "^16.0.0 || >=18.0.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/typescript-eslint" - } - }, - "node_modules/@typescript-eslint/type-utils/node_modules/@typescript-eslint/typescript-estree": { - "version": "6.4.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/typescript-estree/-/typescript-estree-6.4.0.tgz", - "integrity": "sha512-iDPJArf/K2sxvjOR6skeUCNgHR/tCQXBsa+ee1/clRKr3olZjZ/dSkXPZjG6YkPtnW6p5D1egeEPMCW6Gn4yLA==", - "dev": true, - "dependencies": { - "@typescript-eslint/types": "6.4.0", - "@typescript-eslint/visitor-keys": "6.4.0", - "debug": "^4.3.4", - "globby": "^11.1.0", - "is-glob": "^4.0.3", - "semver": "^7.5.4", - "ts-api-utils": "^1.0.1" - }, - "engines": { - "node": "^16.0.0 || >=18.0.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/typescript-eslint" - }, - "peerDependenciesMeta": { - "typescript": { - "optional": true - } - } - }, - "node_modules/@typescript-eslint/type-utils/node_modules/@typescript-eslint/visitor-keys": { - "version": "6.4.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/visitor-keys/-/visitor-keys-6.4.0.tgz", - "integrity": "sha512-yJSfyT+uJm+JRDWYRYdCm2i+pmvXJSMtPR9Cq5/XQs4QIgNoLcoRtDdzsLbLsFM/c6um6ohQkg/MLxWvoIndJA==", - "dev": true, - "dependencies": { - "@typescript-eslint/types": "6.4.0", - "eslint-visitor-keys": "^3.4.1" - }, - "engines": { - "node": "^16.0.0 || >=18.0.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/typescript-eslint" - } - }, - "node_modules/@typescript-eslint/type-utils/node_modules/lru-cache": { - "version": "6.0.0", - "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-6.0.0.tgz", - "integrity": "sha512-Jo6dJ04CmSjuznwJSS3pUeWmd/H0ffTlkXXgwZi+eq1UCmqQwCh+eLsYOYCwY991i2Fah4h1BEMCx4qThGbsiA==", - "dev": true, - "dependencies": { - "yallist": "^4.0.0" - }, - "engines": { - "node": ">=10" - } - }, - "node_modules/@typescript-eslint/type-utils/node_modules/semver": { - "version": "7.5.4", - "resolved": "https://registry.npmjs.org/semver/-/semver-7.5.4.tgz", - "integrity": "sha512-1bCSESV6Pv+i21Hvpxp3Dx+pSD8lIPt8uVjRrxAUt/nbswYc+tK6Y2btiULjd4+fnq15PX+nqQDC7Oft7WkwcA==", - "dev": true, - "dependencies": { - "lru-cache": "^6.0.0" - }, - "bin": { - "semver": "bin/semver.js" - }, - "engines": { - "node": ">=10" - } - }, - "node_modules/@typescript-eslint/type-utils/node_modules/yallist": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/yallist/-/yallist-4.0.0.tgz", - "integrity": "sha512-3wdGidZyq5PB084XLES5TpOSRA3wjXAlIWMhum2kRcv/41Sn2emQ0dycQW4uZXLejwKvg6EsvbdlVL+FYEct7A==", - "dev": true - }, "node_modules/@typescript-eslint/types": { - "version": "6.3.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/types/-/types-6.3.0.tgz", - "integrity": "sha512-K6TZOvfVyc7MO9j60MkRNWyFSf86IbOatTKGrpTQnzarDZPYPVy0oe3myTMq7VjhfsUAbNUW8I5s+2lZvtx1gg==", + "version": "6.5.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/types/-/types-6.5.0.tgz", + "integrity": "sha512-eqLLOEF5/lU8jW3Bw+8auf4lZSbbljHR2saKnYqON12G/WsJrGeeDHWuQePoEf9ro22+JkbPfWQwKEC5WwLQ3w==", "dev": true, "engines": { "node": "^16.0.0 || >=18.0.0" @@ -1910,13 +1777,13 @@ } }, "node_modules/@typescript-eslint/typescript-estree": { - "version": "6.3.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/typescript-estree/-/typescript-estree-6.3.0.tgz", - "integrity": "sha512-Xh4NVDaC4eYKY4O3QGPuQNp5NxBAlEvNQYOqJquR2MePNxO11E5K3t5x4M4Mx53IZvtpW+mBxIT0s274fLUocg==", + "version": "6.5.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/typescript-estree/-/typescript-estree-6.5.0.tgz", + "integrity": "sha512-q0rGwSe9e5Kk/XzliB9h2LBc9tmXX25G0833r7kffbl5437FPWb2tbpIV9wAATebC/018pGa9fwPDuvGN+LxWQ==", "dev": true, "dependencies": { - "@typescript-eslint/types": "6.3.0", - "@typescript-eslint/visitor-keys": "6.3.0", + "@typescript-eslint/types": "6.5.0", + "@typescript-eslint/visitor-keys": "6.5.0", "debug": "^4.3.4", "globby": "^11.1.0", "is-glob": "^4.0.3", @@ -1970,17 +1837,17 @@ "dev": true }, "node_modules/@typescript-eslint/utils": { - "version": "6.4.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/utils/-/utils-6.4.0.tgz", - "integrity": "sha512-BvvwryBQpECPGo8PwF/y/q+yacg8Hn/2XS+DqL/oRsOPK+RPt29h5Ui5dqOKHDlbXrAeHUTnyG3wZA0KTDxRZw==", + "version": "6.5.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/utils/-/utils-6.5.0.tgz", + "integrity": "sha512-9nqtjkNykFzeVtt9Pj6lyR9WEdd8npPhhIPM992FWVkZuS6tmxHfGVnlUcjpUP2hv8r4w35nT33mlxd+Be1ACQ==", "dev": true, "dependencies": { "@eslint-community/eslint-utils": "^4.4.0", "@types/json-schema": "^7.0.12", "@types/semver": "^7.5.0", - "@typescript-eslint/scope-manager": "6.4.0", - "@typescript-eslint/types": "6.4.0", - "@typescript-eslint/typescript-estree": "6.4.0", + "@typescript-eslint/scope-manager": "6.5.0", + "@typescript-eslint/types": "6.5.0", + "@typescript-eslint/typescript-estree": "6.5.0", "semver": "^7.5.4" }, "engines": { @@ -1994,80 +1861,6 @@ "eslint": "^7.0.0 || ^8.0.0" } }, - "node_modules/@typescript-eslint/utils/node_modules/@typescript-eslint/scope-manager": { - "version": "6.4.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/scope-manager/-/scope-manager-6.4.0.tgz", - "integrity": "sha512-TUS7vaKkPWDVvl7GDNHFQMsMruD+zhkd3SdVW0d7b+7Zo+bd/hXJQ8nsiUZMi1jloWo6c9qt3B7Sqo+flC1nig==", - "dev": true, - "dependencies": { - "@typescript-eslint/types": "6.4.0", - "@typescript-eslint/visitor-keys": "6.4.0" - }, - "engines": { - "node": "^16.0.0 || >=18.0.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/typescript-eslint" - } - }, - "node_modules/@typescript-eslint/utils/node_modules/@typescript-eslint/types": { - "version": "6.4.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/types/-/types-6.4.0.tgz", - "integrity": "sha512-+FV9kVFrS7w78YtzkIsNSoYsnOtrYVnKWSTVXoL1761CsCRv5wpDOINgsXpxD67YCLZtVQekDDyaxfjVWUJmmg==", - "dev": true, - "engines": { - "node": "^16.0.0 || >=18.0.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/typescript-eslint" - } - }, - "node_modules/@typescript-eslint/utils/node_modules/@typescript-eslint/typescript-estree": { - "version": "6.4.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/typescript-estree/-/typescript-estree-6.4.0.tgz", - "integrity": "sha512-iDPJArf/K2sxvjOR6skeUCNgHR/tCQXBsa+ee1/clRKr3olZjZ/dSkXPZjG6YkPtnW6p5D1egeEPMCW6Gn4yLA==", - "dev": true, - "dependencies": { - "@typescript-eslint/types": "6.4.0", - "@typescript-eslint/visitor-keys": "6.4.0", - "debug": "^4.3.4", - "globby": "^11.1.0", - "is-glob": "^4.0.3", - "semver": "^7.5.4", - "ts-api-utils": "^1.0.1" - }, - "engines": { - "node": "^16.0.0 || >=18.0.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/typescript-eslint" - }, - "peerDependenciesMeta": { - "typescript": { - "optional": true - } - } - }, - "node_modules/@typescript-eslint/utils/node_modules/@typescript-eslint/visitor-keys": { - "version": "6.4.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/visitor-keys/-/visitor-keys-6.4.0.tgz", - "integrity": "sha512-yJSfyT+uJm+JRDWYRYdCm2i+pmvXJSMtPR9Cq5/XQs4QIgNoLcoRtDdzsLbLsFM/c6um6ohQkg/MLxWvoIndJA==", - "dev": true, - "dependencies": { - "@typescript-eslint/types": "6.4.0", - "eslint-visitor-keys": "^3.4.1" - }, - "engines": { - "node": "^16.0.0 || >=18.0.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/typescript-eslint" - } - }, "node_modules/@typescript-eslint/utils/node_modules/lru-cache": { "version": "6.0.0", "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-6.0.0.tgz", @@ -2102,12 +1895,12 @@ "dev": true }, "node_modules/@typescript-eslint/visitor-keys": { - "version": "6.3.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/visitor-keys/-/visitor-keys-6.3.0.tgz", - "integrity": "sha512-kEhRRj7HnvaSjux1J9+7dBen15CdWmDnwrpyiHsFX6Qx2iW5LOBUgNefOFeh2PjWPlNwN8TOn6+4eBU3J/gupw==", + "version": "6.5.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/visitor-keys/-/visitor-keys-6.5.0.tgz", + "integrity": "sha512-yCB/2wkbv3hPsh02ZS8dFQnij9VVQXJMN/gbQsaaY+zxALkZnxa/wagvLEFsAWMPv7d7lxQmNsIzGU1w/T/WyA==", "dev": true, "dependencies": { - "@typescript-eslint/types": "6.3.0", + "@typescript-eslint/types": "6.5.0", "eslint-visitor-keys": "^3.4.1" }, "engines": { @@ -2235,15 +2028,15 @@ } }, "node_modules/babel-jest": { - "version": "29.6.2", - "resolved": "https://registry.npmjs.org/babel-jest/-/babel-jest-29.6.2.tgz", - "integrity": "sha512-BYCzImLos6J3BH/+HvUCHG1dTf2MzmAB4jaVxHV+29RZLjR29XuYTmsf2sdDwkrb+FczkGo3kOhE7ga6sI0P4A==", + "version": "29.6.4", + "resolved": "https://registry.npmjs.org/babel-jest/-/babel-jest-29.6.4.tgz", + "integrity": "sha512-meLj23UlSLddj6PC+YTOFRgDAtjnZom8w/ACsrx0gtPtv5cJZk0A5Unk5bV4wixD7XaPCN1fQvpww8czkZURmw==", "dev": true, "dependencies": { - "@jest/transform": "^29.6.2", + "@jest/transform": "^29.6.4", "@types/babel__core": "^7.1.14", "babel-plugin-istanbul": "^6.1.1", - "babel-preset-jest": "^29.5.0", + "babel-preset-jest": "^29.6.3", "chalk": "^4.0.0", "graceful-fs": "^4.2.9", "slash": "^3.0.0" @@ -2272,9 +2065,9 @@ } }, "node_modules/babel-plugin-jest-hoist": { - "version": "29.5.0", - "resolved": "https://registry.npmjs.org/babel-plugin-jest-hoist/-/babel-plugin-jest-hoist-29.5.0.tgz", - "integrity": "sha512-zSuuuAlTMT4mzLj2nPnUm6fsE6270vdOfnpbJ+RmruU75UhLFvL0N2NgI7xpeS7NaB6hGqmd5pVpGTDYvi4Q3w==", + "version": "29.6.3", + "resolved": "https://registry.npmjs.org/babel-plugin-jest-hoist/-/babel-plugin-jest-hoist-29.6.3.tgz", + "integrity": "sha512-ESAc/RJvGTFEzRwOTT4+lNDk/GNHMkKbNzsvT0qKRfDyyYTskxB5rnU2njIDYVxXCBHHEI1c0YwHob3WaYujOg==", "dev": true, "dependencies": { "@babel/template": "^7.3.3", @@ -2310,12 +2103,12 @@ } }, "node_modules/babel-preset-jest": { - "version": "29.5.0", - "resolved": "https://registry.npmjs.org/babel-preset-jest/-/babel-preset-jest-29.5.0.tgz", - "integrity": "sha512-JOMloxOqdiBSxMAzjRaH023/vvcaSaec49zvg+2LmNsktC7ei39LTJGw02J+9uUtTZUq6xbLyJ4dxe9sSmIuAg==", + "version": "29.6.3", + "resolved": "https://registry.npmjs.org/babel-preset-jest/-/babel-preset-jest-29.6.3.tgz", + "integrity": "sha512-0B3bhxR6snWXJZtR/RliHTDPRgn1sNHOR0yVtq/IiQFyuOVjFS+wuio/R4gSNkyYmKmJB4wGZv2NZanmKmTnNA==", "dev": true, "dependencies": { - "babel-plugin-jest-hoist": "^29.5.0", + "babel-plugin-jest-hoist": "^29.6.3", "babel-preset-current-node-syntax": "^1.0.0" }, "engines": { @@ -2635,9 +2428,9 @@ } }, "node_modules/diff-sequences": { - "version": "29.4.3", - "resolved": "https://registry.npmjs.org/diff-sequences/-/diff-sequences-29.4.3.tgz", - "integrity": "sha512-ofrBgwpPhCD85kMKtE9RYFFq6OC1A89oW2vvgWZNCwxrUpRUILopY7lsYyMDSjc8g6U6aiO0Qubg6r4Wgt5ZnA==", + "version": "29.6.3", + "resolved": "https://registry.npmjs.org/diff-sequences/-/diff-sequences-29.6.3.tgz", + "integrity": "sha512-EjePK1srD3P08o2j4f0ExnylqRs5B9tJjcp9t1krH2qRi8CCdsYfwe9JgSLurFBWwq4uOlipzfk5fHNvwFKr8Q==", "dev": true, "engines": { "node": "^14.15.0 || ^16.10.0 || >=18.0.0" @@ -2722,15 +2515,15 @@ } }, "node_modules/eslint": { - "version": "8.47.0", - "resolved": "https://registry.npmjs.org/eslint/-/eslint-8.47.0.tgz", - "integrity": "sha512-spUQWrdPt+pRVP1TTJLmfRNJJHHZryFmptzcafwSvHsceV81djHOdnEeDmkdotZyLNjDhrOasNK8nikkoG1O8Q==", + "version": "8.48.0", + "resolved": "https://registry.npmjs.org/eslint/-/eslint-8.48.0.tgz", + "integrity": "sha512-sb6DLeIuRXxeM1YljSe1KEx9/YYeZFQWcV8Rq9HfigmdDEugjLEVEa1ozDjL6YDjBpQHPJxJzze+alxi4T3OLg==", "dev": true, "dependencies": { "@eslint-community/eslint-utils": "^4.2.0", "@eslint-community/regexpp": "^4.6.1", "@eslint/eslintrc": "^2.1.2", - "@eslint/js": "^8.47.0", + "@eslint/js": "8.48.0", "@humanwhocodes/config-array": "^0.11.10", "@humanwhocodes/module-importer": "^1.0.1", "@nodelib/fs.walk": "^1.2.8", @@ -2938,17 +2731,16 @@ } }, "node_modules/expect": { - "version": "29.6.2", - "resolved": "https://registry.npmjs.org/expect/-/expect-29.6.2.tgz", - "integrity": "sha512-iAErsLxJ8C+S02QbLAwgSGSezLQK+XXRDt8IuFXFpwCNw2ECmzZSmjKcCaFVp5VRMk+WAvz6h6jokzEzBFZEuA==", + "version": "29.6.4", + "resolved": "https://registry.npmjs.org/expect/-/expect-29.6.4.tgz", + "integrity": "sha512-F2W2UyQ8XYyftHT57dtfg8Ue3X5qLgm2sSug0ivvLRH/VKNRL/pDxg/TH7zVzbQB0tu80clNFy6LU7OS/VSEKA==", "dev": true, "dependencies": { - "@jest/expect-utils": "^29.6.2", - "@types/node": "*", - "jest-get-type": "^29.4.3", - "jest-matcher-utils": "^29.6.2", - "jest-message-util": "^29.6.2", - "jest-util": "^29.6.2" + "@jest/expect-utils": "^29.6.4", + "jest-get-type": "^29.6.3", + "jest-matcher-utils": "^29.6.4", + "jest-message-util": "^29.6.3", + "jest-util": "^29.6.3" }, "engines": { "node": "^14.15.0 || ^16.10.0 || >=18.0.0" @@ -3084,9 +2876,9 @@ "dev": true }, "node_modules/fsevents": { - "version": "2.3.2", - "resolved": "https://registry.npmjs.org/fsevents/-/fsevents-2.3.2.tgz", - "integrity": "sha512-xiqMQR4xAeHTuB9uWm+fFRcIOgKBMiOBP+eXiyT7jsgVCq1bkVygt00oASowB7EdtpOHaaPgKt812P9ab+DDKA==", + "version": "2.3.3", + "resolved": "https://registry.npmjs.org/fsevents/-/fsevents-2.3.3.tgz", + "integrity": "sha512-5xoDfX+fL7faATnagmWPpbFtwh/R77WmMMqqHGS65C3vvB0YHrgF+B1YmZ3441tMj5n63k0212XNoJwzlhffQw==", "dev": true, "hasInstallScript": true, "optional": true, @@ -3509,15 +3301,15 @@ } }, "node_modules/jest": { - "version": "29.6.2", - "resolved": "https://registry.npmjs.org/jest/-/jest-29.6.2.tgz", - "integrity": "sha512-8eQg2mqFbaP7CwfsTpCxQ+sHzw1WuNWL5UUvjnWP4hx2riGz9fPSzYOaU5q8/GqWn1TfgZIVTqYJygbGbWAANg==", + "version": "29.6.4", + "resolved": "https://registry.npmjs.org/jest/-/jest-29.6.4.tgz", + "integrity": "sha512-tEFhVQFF/bzoYV1YuGyzLPZ6vlPrdfvDmmAxudA1dLEuiztqg2Rkx20vkKY32xiDROcD2KXlgZ7Cu8RPeEHRKw==", "dev": true, "dependencies": { - "@jest/core": "^29.6.2", - "@jest/types": "^29.6.1", + "@jest/core": "^29.6.4", + "@jest/types": "^29.6.3", "import-local": "^3.0.2", - "jest-cli": "^29.6.2" + "jest-cli": "^29.6.4" }, "bin": { "jest": "bin/jest.js" @@ -3535,12 +3327,13 @@ } }, "node_modules/jest-changed-files": { - "version": "29.5.0", - "resolved": "https://registry.npmjs.org/jest-changed-files/-/jest-changed-files-29.5.0.tgz", - "integrity": "sha512-IFG34IUMUaNBIxjQXF/iu7g6EcdMrGRRxaUSw92I/2g2YC6vCdTltl4nHvt7Ci5nSJwXIkCu8Ka1DKF+X7Z1Ag==", + "version": "29.6.3", + "resolved": "https://registry.npmjs.org/jest-changed-files/-/jest-changed-files-29.6.3.tgz", + "integrity": "sha512-G5wDnElqLa4/c66ma5PG9eRjE342lIbF6SUnTJi26C3J28Fv2TVY2rOyKB9YGbSA5ogwevgmxc4j4aVjrEK6Yg==", "dev": true, "dependencies": { "execa": "^5.0.0", + "jest-util": "^29.6.3", "p-limit": "^3.1.0" }, "engines": { @@ -3548,28 +3341,28 @@ } }, "node_modules/jest-circus": { - "version": "29.6.2", - "resolved": "https://registry.npmjs.org/jest-circus/-/jest-circus-29.6.2.tgz", - "integrity": "sha512-G9mN+KOYIUe2sB9kpJkO9Bk18J4dTDArNFPwoZ7WKHKel55eKIS/u2bLthxgojwlf9NLCVQfgzM/WsOVvoC6Fw==", + "version": "29.6.4", + "resolved": "https://registry.npmjs.org/jest-circus/-/jest-circus-29.6.4.tgz", + "integrity": "sha512-YXNrRyntVUgDfZbjXWBMPslX1mQ8MrSG0oM/Y06j9EYubODIyHWP8hMUbjbZ19M3M+zamqEur7O80HODwACoJw==", "dev": true, "dependencies": { - "@jest/environment": "^29.6.2", - "@jest/expect": "^29.6.2", - "@jest/test-result": "^29.6.2", - "@jest/types": "^29.6.1", + "@jest/environment": "^29.6.4", + "@jest/expect": "^29.6.4", + "@jest/test-result": "^29.6.4", + "@jest/types": "^29.6.3", "@types/node": "*", "chalk": "^4.0.0", "co": "^4.6.0", "dedent": "^1.0.0", "is-generator-fn": "^2.0.0", - "jest-each": "^29.6.2", - "jest-matcher-utils": "^29.6.2", - "jest-message-util": "^29.6.2", - "jest-runtime": "^29.6.2", - "jest-snapshot": "^29.6.2", - "jest-util": "^29.6.2", + "jest-each": "^29.6.3", + "jest-matcher-utils": "^29.6.4", + "jest-message-util": "^29.6.3", + "jest-runtime": "^29.6.4", + "jest-snapshot": "^29.6.4", + "jest-util": "^29.6.3", "p-limit": "^3.1.0", - "pretty-format": "^29.6.2", + "pretty-format": "^29.6.3", "pure-rand": "^6.0.0", "slash": "^3.0.0", "stack-utils": "^2.0.3" @@ -3579,21 +3372,21 @@ } }, "node_modules/jest-cli": { - "version": "29.6.2", - "resolved": "https://registry.npmjs.org/jest-cli/-/jest-cli-29.6.2.tgz", - "integrity": "sha512-TT6O247v6dCEX2UGHGyflMpxhnrL0DNqP2fRTKYm3nJJpCTfXX3GCMQPGFjXDoj0i5/Blp3jriKXFgdfmbYB6Q==", + "version": "29.6.4", + "resolved": "https://registry.npmjs.org/jest-cli/-/jest-cli-29.6.4.tgz", + "integrity": "sha512-+uMCQ7oizMmh8ZwRfZzKIEszFY9ksjjEQnTEMTaL7fYiL3Kw4XhqT9bYh+A4DQKUb67hZn2KbtEnDuHvcgK4pQ==", "dev": true, "dependencies": { - "@jest/core": "^29.6.2", - "@jest/test-result": "^29.6.2", - "@jest/types": "^29.6.1", + "@jest/core": "^29.6.4", + "@jest/test-result": "^29.6.4", + "@jest/types": "^29.6.3", "chalk": "^4.0.0", "exit": "^0.1.2", "graceful-fs": "^4.2.9", "import-local": "^3.0.2", - "jest-config": "^29.6.2", - "jest-util": "^29.6.2", - "jest-validate": "^29.6.2", + "jest-config": "^29.6.4", + "jest-util": "^29.6.3", + "jest-validate": "^29.6.3", "prompts": "^2.0.1", "yargs": "^17.3.1" }, @@ -3612,32 +3405,32 @@ } } }, - "node_modules/jest-cli/node_modules/jest-config": { - "version": "29.6.2", - "resolved": "https://registry.npmjs.org/jest-config/-/jest-config-29.6.2.tgz", - "integrity": "sha512-VxwFOC8gkiJbuodG9CPtMRjBUNZEHxwfQXmIudSTzFWxaci3Qub1ddTRbFNQlD/zUeaifLndh/eDccFX4wCMQw==", + "node_modules/jest-config": { + "version": "29.6.4", + "resolved": "https://registry.npmjs.org/jest-config/-/jest-config-29.6.4.tgz", + "integrity": "sha512-JWohr3i9m2cVpBumQFv2akMEnFEPVOh+9L2xIBJhJ0zOaci2ZXuKJj0tgMKQCBZAKA09H049IR4HVS/43Qb19A==", "dev": true, "dependencies": { "@babel/core": "^7.11.6", - "@jest/test-sequencer": "^29.6.2", - "@jest/types": "^29.6.1", - "babel-jest": "^29.6.2", + "@jest/test-sequencer": "^29.6.4", + "@jest/types": "^29.6.3", + "babel-jest": "^29.6.4", "chalk": "^4.0.0", "ci-info": "^3.2.0", "deepmerge": "^4.2.2", "glob": "^7.1.3", "graceful-fs": "^4.2.9", - "jest-circus": "^29.6.2", - "jest-environment-node": "^29.6.2", - "jest-get-type": "^29.4.3", - "jest-regex-util": "^29.4.3", - "jest-resolve": "^29.6.2", - "jest-runner": "^29.6.2", - "jest-util": "^29.6.2", - "jest-validate": "^29.6.2", + "jest-circus": "^29.6.4", + "jest-environment-node": "^29.6.4", + "jest-get-type": "^29.6.3", + "jest-regex-util": "^29.6.3", + "jest-resolve": "^29.6.4", + "jest-runner": "^29.6.4", + "jest-util": "^29.6.3", + "jest-validate": "^29.6.3", "micromatch": "^4.0.4", "parse-json": "^5.2.0", - "pretty-format": "^29.6.2", + "pretty-format": "^29.6.3", "slash": "^3.0.0", "strip-json-comments": "^3.1.1" }, @@ -3658,24 +3451,24 @@ } }, "node_modules/jest-diff": { - "version": "29.6.2", - "resolved": "https://registry.npmjs.org/jest-diff/-/jest-diff-29.6.2.tgz", - "integrity": "sha512-t+ST7CB9GX5F2xKwhwCf0TAR17uNDiaPTZnVymP9lw0lssa9vG+AFyDZoeIHStU3WowFFwT+ky+er0WVl2yGhA==", + "version": "29.6.4", + "resolved": "https://registry.npmjs.org/jest-diff/-/jest-diff-29.6.4.tgz", + "integrity": "sha512-9F48UxR9e4XOEZvoUXEHSWY4qC4zERJaOfrbBg9JpbJOO43R1vN76REt/aMGZoY6GD5g84nnJiBIVlscegefpw==", "dev": true, "dependencies": { "chalk": "^4.0.0", - "diff-sequences": "^29.4.3", - "jest-get-type": "^29.4.3", - "pretty-format": "^29.6.2" + "diff-sequences": "^29.6.3", + "jest-get-type": "^29.6.3", + "pretty-format": "^29.6.3" }, "engines": { "node": "^14.15.0 || ^16.10.0 || >=18.0.0" } }, "node_modules/jest-docblock": { - "version": "29.4.3", - "resolved": "https://registry.npmjs.org/jest-docblock/-/jest-docblock-29.4.3.tgz", - "integrity": "sha512-fzdTftThczeSD9nZ3fzA/4KkHtnmllawWrXO69vtI+L9WjEIuXWs4AmyME7lN5hU7dB0sHhuPfcKofRsUb/2Fg==", + "version": "29.6.3", + "resolved": "https://registry.npmjs.org/jest-docblock/-/jest-docblock-29.6.3.tgz", + "integrity": "sha512-2+H+GOTQBEm2+qFSQ7Ma+BvyV+waiIFxmZF5LdpBsAEjWX8QYjSCa4FrkIYtbfXUJJJnFCYrOtt6TZ+IAiTjBQ==", "dev": true, "dependencies": { "detect-newline": "^3.0.0" @@ -3685,62 +3478,62 @@ } }, "node_modules/jest-each": { - "version": "29.6.2", - "resolved": "https://registry.npmjs.org/jest-each/-/jest-each-29.6.2.tgz", - "integrity": "sha512-MsrsqA0Ia99cIpABBc3izS1ZYoYfhIy0NNWqPSE0YXbQjwchyt6B1HD2khzyPe1WiJA7hbxXy77ZoUQxn8UlSw==", + "version": "29.6.3", + "resolved": "https://registry.npmjs.org/jest-each/-/jest-each-29.6.3.tgz", + "integrity": "sha512-KoXfJ42k8cqbkfshW7sSHcdfnv5agDdHCPA87ZBdmHP+zJstTJc0ttQaJ/x7zK6noAL76hOuTIJ6ZkQRS5dcyg==", "dev": true, "dependencies": { - "@jest/types": "^29.6.1", + "@jest/types": "^29.6.3", "chalk": "^4.0.0", - "jest-get-type": "^29.4.3", - "jest-util": "^29.6.2", - "pretty-format": "^29.6.2" + "jest-get-type": "^29.6.3", + "jest-util": "^29.6.3", + "pretty-format": "^29.6.3" }, "engines": { "node": "^14.15.0 || ^16.10.0 || >=18.0.0" } }, "node_modules/jest-environment-node": { - "version": "29.6.2", - "resolved": "https://registry.npmjs.org/jest-environment-node/-/jest-environment-node-29.6.2.tgz", - "integrity": "sha512-YGdFeZ3T9a+/612c5mTQIllvWkddPbYcN2v95ZH24oWMbGA4GGS2XdIF92QMhUhvrjjuQWYgUGW2zawOyH63MQ==", + "version": "29.6.4", + "resolved": "https://registry.npmjs.org/jest-environment-node/-/jest-environment-node-29.6.4.tgz", + "integrity": "sha512-i7SbpH2dEIFGNmxGCpSc2w9cA4qVD+wfvg2ZnfQ7XVrKL0NA5uDVBIiGH8SR4F0dKEv/0qI5r+aDomDf04DpEQ==", "dev": true, "dependencies": { - "@jest/environment": "^29.6.2", - "@jest/fake-timers": "^29.6.2", - "@jest/types": "^29.6.1", + "@jest/environment": "^29.6.4", + "@jest/fake-timers": "^29.6.4", + "@jest/types": "^29.6.3", "@types/node": "*", - "jest-mock": "^29.6.2", - "jest-util": "^29.6.2" + "jest-mock": "^29.6.3", + "jest-util": "^29.6.3" }, "engines": { "node": "^14.15.0 || ^16.10.0 || >=18.0.0" } }, "node_modules/jest-get-type": { - "version": "29.4.3", - "resolved": "https://registry.npmjs.org/jest-get-type/-/jest-get-type-29.4.3.tgz", - "integrity": "sha512-J5Xez4nRRMjk8emnTpWrlkyb9pfRQQanDrvWHhsR1+VUfbwxi30eVcZFlcdGInRibU4G5LwHXpI7IRHU0CY+gg==", + "version": "29.6.3", + "resolved": "https://registry.npmjs.org/jest-get-type/-/jest-get-type-29.6.3.tgz", + "integrity": "sha512-zrteXnqYxfQh7l5FHyL38jL39di8H8rHoecLH3JNxH3BwOrBsNeabdap5e0I23lD4HHI8W5VFBZqG4Eaq5LNcw==", "dev": true, "engines": { "node": "^14.15.0 || ^16.10.0 || >=18.0.0" } }, "node_modules/jest-haste-map": { - "version": "29.6.2", - "resolved": "https://registry.npmjs.org/jest-haste-map/-/jest-haste-map-29.6.2.tgz", - "integrity": "sha512-+51XleTDAAysvU8rT6AnS1ZJ+WHVNqhj1k6nTvN2PYP+HjU3kqlaKQ1Lnw3NYW3bm2r8vq82X0Z1nDDHZMzHVA==", + "version": "29.6.4", + "resolved": "https://registry.npmjs.org/jest-haste-map/-/jest-haste-map-29.6.4.tgz", + "integrity": "sha512-12Ad+VNTDHxKf7k+M65sviyynRoZYuL1/GTuhEVb8RYsNSNln71nANRb/faSyWvx0j+gHcivChXHIoMJrGYjog==", "dev": true, "dependencies": { - "@jest/types": "^29.6.1", + "@jest/types": "^29.6.3", "@types/graceful-fs": "^4.1.3", "@types/node": "*", "anymatch": "^3.0.3", "fb-watchman": "^2.0.0", "graceful-fs": "^4.2.9", - "jest-regex-util": "^29.4.3", - "jest-util": "^29.6.2", - "jest-worker": "^29.6.2", + "jest-regex-util": "^29.6.3", + "jest-util": "^29.6.3", + "jest-worker": "^29.6.4", "micromatch": "^4.0.4", "walker": "^1.0.8" }, @@ -3752,46 +3545,46 @@ } }, "node_modules/jest-leak-detector": { - "version": "29.6.2", - "resolved": "https://registry.npmjs.org/jest-leak-detector/-/jest-leak-detector-29.6.2.tgz", - "integrity": "sha512-aNqYhfp5uYEO3tdWMb2bfWv6f0b4I0LOxVRpnRLAeque2uqOVVMLh6khnTcE2qJ5wAKop0HcreM1btoysD6bPQ==", + "version": "29.6.3", + "resolved": "https://registry.npmjs.org/jest-leak-detector/-/jest-leak-detector-29.6.3.tgz", + "integrity": "sha512-0kfbESIHXYdhAdpLsW7xdwmYhLf1BRu4AA118/OxFm0Ho1b2RcTmO4oF6aAMaxpxdxnJ3zve2rgwzNBD4Zbm7Q==", "dev": true, "dependencies": { - "jest-get-type": "^29.4.3", - "pretty-format": "^29.6.2" + "jest-get-type": "^29.6.3", + "pretty-format": "^29.6.3" }, "engines": { "node": "^14.15.0 || ^16.10.0 || >=18.0.0" } }, "node_modules/jest-matcher-utils": { - "version": "29.6.2", - "resolved": "https://registry.npmjs.org/jest-matcher-utils/-/jest-matcher-utils-29.6.2.tgz", - "integrity": "sha512-4LiAk3hSSobtomeIAzFTe+N8kL6z0JtF3n6I4fg29iIW7tt99R7ZcIFW34QkX+DuVrf+CUe6wuVOpm7ZKFJzZQ==", + "version": "29.6.4", + "resolved": "https://registry.npmjs.org/jest-matcher-utils/-/jest-matcher-utils-29.6.4.tgz", + "integrity": "sha512-KSzwyzGvK4HcfnserYqJHYi7sZVqdREJ9DMPAKVbS98JsIAvumihaNUbjrWw0St7p9IY7A9UskCW5MYlGmBQFQ==", "dev": true, "dependencies": { "chalk": "^4.0.0", - "jest-diff": "^29.6.2", - "jest-get-type": "^29.4.3", - "pretty-format": "^29.6.2" + "jest-diff": "^29.6.4", + "jest-get-type": "^29.6.3", + "pretty-format": "^29.6.3" }, "engines": { "node": "^14.15.0 || ^16.10.0 || >=18.0.0" } }, "node_modules/jest-message-util": { - "version": "29.6.2", - "resolved": "https://registry.npmjs.org/jest-message-util/-/jest-message-util-29.6.2.tgz", - "integrity": "sha512-vnIGYEjoPSuRqV8W9t+Wow95SDp6KPX2Uf7EoeG9G99J2OVh7OSwpS4B6J0NfpEIpfkBNHlBZpA2rblEuEFhZQ==", + "version": "29.6.3", + "resolved": "https://registry.npmjs.org/jest-message-util/-/jest-message-util-29.6.3.tgz", + "integrity": "sha512-FtzaEEHzjDpQp51HX4UMkPZjy46ati4T5pEMyM6Ik48ztu4T9LQplZ6OsimHx7EuM9dfEh5HJa6D3trEftu3dA==", "dev": true, "dependencies": { "@babel/code-frame": "^7.12.13", - "@jest/types": "^29.6.1", + "@jest/types": "^29.6.3", "@types/stack-utils": "^2.0.0", "chalk": "^4.0.0", "graceful-fs": "^4.2.9", "micromatch": "^4.0.4", - "pretty-format": "^29.6.2", + "pretty-format": "^29.6.3", "slash": "^3.0.0", "stack-utils": "^2.0.3" }, @@ -3800,14 +3593,14 @@ } }, "node_modules/jest-mock": { - "version": "29.6.2", - "resolved": "https://registry.npmjs.org/jest-mock/-/jest-mock-29.6.2.tgz", - "integrity": "sha512-hoSv3lb3byzdKfwqCuT6uTscan471GUECqgNYykg6ob0yiAw3zYc7OrPnI9Qv8Wwoa4lC7AZ9hyS4AiIx5U2zg==", + "version": "29.6.3", + "resolved": "https://registry.npmjs.org/jest-mock/-/jest-mock-29.6.3.tgz", + "integrity": "sha512-Z7Gs/mOyTSR4yPsaZ72a/MtuK6RnC3JYqWONe48oLaoEcYwEDxqvbXz85G4SJrm2Z5Ar9zp6MiHF4AlFlRM4Pg==", "dev": true, "dependencies": { - "@jest/types": "^29.6.1", + "@jest/types": "^29.6.3", "@types/node": "*", - "jest-util": "^29.6.2" + "jest-util": "^29.6.3" }, "engines": { "node": "^14.15.0 || ^16.10.0 || >=18.0.0" @@ -3831,26 +3624,26 @@ } }, "node_modules/jest-regex-util": { - "version": "29.4.3", - "resolved": "https://registry.npmjs.org/jest-regex-util/-/jest-regex-util-29.4.3.tgz", - "integrity": "sha512-O4FglZaMmWXbGHSQInfXewIsd1LMn9p3ZXB/6r4FOkyhX2/iP/soMG98jGvk/A3HAN78+5VWcBGO0BJAPRh4kg==", + "version": "29.6.3", + "resolved": "https://registry.npmjs.org/jest-regex-util/-/jest-regex-util-29.6.3.tgz", + "integrity": "sha512-KJJBsRCyyLNWCNBOvZyRDnAIfUiRJ8v+hOBQYGn8gDyF3UegwiP4gwRR3/SDa42g1YbVycTidUF3rKjyLFDWbg==", "dev": true, "engines": { "node": "^14.15.0 || ^16.10.0 || >=18.0.0" } }, "node_modules/jest-resolve": { - "version": "29.6.2", - "resolved": "https://registry.npmjs.org/jest-resolve/-/jest-resolve-29.6.2.tgz", - "integrity": "sha512-G/iQUvZWI5e3SMFssc4ug4dH0aZiZpsDq9o1PtXTV1210Ztyb2+w+ZgQkB3iOiC5SmAEzJBOHWz6Hvrd+QnNPw==", + "version": "29.6.4", + "resolved": "https://registry.npmjs.org/jest-resolve/-/jest-resolve-29.6.4.tgz", + "integrity": "sha512-fPRq+0vcxsuGlG0O3gyoqGTAxasagOxEuyoxHeyxaZbc9QNek0AmJWSkhjlMG+mTsj+8knc/mWb3fXlRNVih7Q==", "dev": true, "dependencies": { "chalk": "^4.0.0", "graceful-fs": "^4.2.9", - "jest-haste-map": "^29.6.2", + "jest-haste-map": "^29.6.4", "jest-pnp-resolver": "^1.2.2", - "jest-util": "^29.6.2", - "jest-validate": "^29.6.2", + "jest-util": "^29.6.3", + "jest-validate": "^29.6.3", "resolve": "^1.20.0", "resolve.exports": "^2.0.0", "slash": "^3.0.0" @@ -3860,43 +3653,43 @@ } }, "node_modules/jest-resolve-dependencies": { - "version": "29.6.2", - "resolved": "https://registry.npmjs.org/jest-resolve-dependencies/-/jest-resolve-dependencies-29.6.2.tgz", - "integrity": "sha512-LGqjDWxg2fuQQm7ypDxduLu/m4+4Lb4gczc13v51VMZbVP5tSBILqVx8qfWcsdP8f0G7aIqByIALDB0R93yL+w==", + "version": "29.6.4", + "resolved": "https://registry.npmjs.org/jest-resolve-dependencies/-/jest-resolve-dependencies-29.6.4.tgz", + "integrity": "sha512-7+6eAmr1ZBF3vOAJVsfLj1QdqeXG+WYhidfLHBRZqGN24MFRIiKG20ItpLw2qRAsW/D2ZUUmCNf6irUr/v6KHA==", "dev": true, "dependencies": { - "jest-regex-util": "^29.4.3", - "jest-snapshot": "^29.6.2" + "jest-regex-util": "^29.6.3", + "jest-snapshot": "^29.6.4" }, "engines": { "node": "^14.15.0 || ^16.10.0 || >=18.0.0" } }, "node_modules/jest-runner": { - "version": "29.6.2", - "resolved": "https://registry.npmjs.org/jest-runner/-/jest-runner-29.6.2.tgz", - "integrity": "sha512-wXOT/a0EspYgfMiYHxwGLPCZfC0c38MivAlb2lMEAlwHINKemrttu1uSbcGbfDV31sFaPWnWJPmb2qXM8pqZ4w==", + "version": "29.6.4", + "resolved": "https://registry.npmjs.org/jest-runner/-/jest-runner-29.6.4.tgz", + "integrity": "sha512-SDaLrMmtVlQYDuG0iSPYLycG8P9jLI+fRm8AF/xPKhYDB2g6xDWjXBrR5M8gEWsK6KVFlebpZ4QsrxdyIX1Jaw==", "dev": true, "dependencies": { - "@jest/console": "^29.6.2", - "@jest/environment": "^29.6.2", - "@jest/test-result": "^29.6.2", - "@jest/transform": "^29.6.2", - "@jest/types": "^29.6.1", + "@jest/console": "^29.6.4", + "@jest/environment": "^29.6.4", + "@jest/test-result": "^29.6.4", + "@jest/transform": "^29.6.4", + "@jest/types": "^29.6.3", "@types/node": "*", "chalk": "^4.0.0", "emittery": "^0.13.1", "graceful-fs": "^4.2.9", - "jest-docblock": "^29.4.3", - "jest-environment-node": "^29.6.2", - "jest-haste-map": "^29.6.2", - "jest-leak-detector": "^29.6.2", - "jest-message-util": "^29.6.2", - "jest-resolve": "^29.6.2", - "jest-runtime": "^29.6.2", - "jest-util": "^29.6.2", - "jest-watcher": "^29.6.2", - "jest-worker": "^29.6.2", + "jest-docblock": "^29.6.3", + "jest-environment-node": "^29.6.4", + "jest-haste-map": "^29.6.4", + "jest-leak-detector": "^29.6.3", + "jest-message-util": "^29.6.3", + "jest-resolve": "^29.6.4", + "jest-runtime": "^29.6.4", + "jest-util": "^29.6.3", + "jest-watcher": "^29.6.4", + "jest-worker": "^29.6.4", "p-limit": "^3.1.0", "source-map-support": "0.5.13" }, @@ -3905,31 +3698,31 @@ } }, "node_modules/jest-runtime": { - "version": "29.6.2", - "resolved": "https://registry.npmjs.org/jest-runtime/-/jest-runtime-29.6.2.tgz", - "integrity": "sha512-2X9dqK768KufGJyIeLmIzToDmsN0m7Iek8QNxRSI/2+iPFYHF0jTwlO3ftn7gdKd98G/VQw9XJCk77rbTGZnJg==", - "dev": true, - "dependencies": { - "@jest/environment": "^29.6.2", - "@jest/fake-timers": "^29.6.2", - "@jest/globals": "^29.6.2", - "@jest/source-map": "^29.6.0", - "@jest/test-result": "^29.6.2", - "@jest/transform": "^29.6.2", - "@jest/types": "^29.6.1", + "version": "29.6.4", + "resolved": "https://registry.npmjs.org/jest-runtime/-/jest-runtime-29.6.4.tgz", + "integrity": "sha512-s/QxMBLvmwLdchKEjcLfwzP7h+jsHvNEtxGP5P+Fl1FMaJX2jMiIqe4rJw4tFprzCwuSvVUo9bn0uj4gNRXsbA==", + "dev": true, + "dependencies": { + "@jest/environment": "^29.6.4", + "@jest/fake-timers": "^29.6.4", + "@jest/globals": "^29.6.4", + "@jest/source-map": "^29.6.3", + "@jest/test-result": "^29.6.4", + "@jest/transform": "^29.6.4", + "@jest/types": "^29.6.3", "@types/node": "*", "chalk": "^4.0.0", "cjs-module-lexer": "^1.0.0", "collect-v8-coverage": "^1.0.0", "glob": "^7.1.3", "graceful-fs": "^4.2.9", - "jest-haste-map": "^29.6.2", - "jest-message-util": "^29.6.2", - "jest-mock": "^29.6.2", - "jest-regex-util": "^29.4.3", - "jest-resolve": "^29.6.2", - "jest-snapshot": "^29.6.2", - "jest-util": "^29.6.2", + "jest-haste-map": "^29.6.4", + "jest-message-util": "^29.6.3", + "jest-mock": "^29.6.3", + "jest-regex-util": "^29.6.3", + "jest-resolve": "^29.6.4", + "jest-snapshot": "^29.6.4", + "jest-util": "^29.6.3", "slash": "^3.0.0", "strip-bom": "^4.0.0" }, @@ -3938,9 +3731,9 @@ } }, "node_modules/jest-snapshot": { - "version": "29.6.2", - "resolved": "https://registry.npmjs.org/jest-snapshot/-/jest-snapshot-29.6.2.tgz", - "integrity": "sha512-1OdjqvqmRdGNvWXr/YZHuyhh5DeaLp1p/F8Tht/MrMw4Kr1Uu/j4lRG+iKl1DAqUJDWxtQBMk41Lnf/JETYBRA==", + "version": "29.6.4", + "resolved": "https://registry.npmjs.org/jest-snapshot/-/jest-snapshot-29.6.4.tgz", + "integrity": "sha512-VC1N8ED7+4uboUKGIDsbvNAZb6LakgIPgAF4RSpF13dN6YaMokfRqO+BaqK4zIh6X3JffgwbzuGqDEjHm/MrvA==", "dev": true, "dependencies": { "@babel/core": "^7.11.6", @@ -3948,20 +3741,20 @@ "@babel/plugin-syntax-jsx": "^7.7.2", "@babel/plugin-syntax-typescript": "^7.7.2", "@babel/types": "^7.3.3", - "@jest/expect-utils": "^29.6.2", - "@jest/transform": "^29.6.2", - "@jest/types": "^29.6.1", + "@jest/expect-utils": "^29.6.4", + "@jest/transform": "^29.6.4", + "@jest/types": "^29.6.3", "babel-preset-current-node-syntax": "^1.0.0", "chalk": "^4.0.0", - "expect": "^29.6.2", + "expect": "^29.6.4", "graceful-fs": "^4.2.9", - "jest-diff": "^29.6.2", - "jest-get-type": "^29.4.3", - "jest-matcher-utils": "^29.6.2", - "jest-message-util": "^29.6.2", - "jest-util": "^29.6.2", + "jest-diff": "^29.6.4", + "jest-get-type": "^29.6.3", + "jest-matcher-utils": "^29.6.4", + "jest-message-util": "^29.6.3", + "jest-util": "^29.6.3", "natural-compare": "^1.4.0", - "pretty-format": "^29.6.2", + "pretty-format": "^29.6.3", "semver": "^7.5.3" }, "engines": { @@ -4002,12 +3795,12 @@ "dev": true }, "node_modules/jest-util": { - "version": "29.6.2", - "resolved": "https://registry.npmjs.org/jest-util/-/jest-util-29.6.2.tgz", - "integrity": "sha512-3eX1qb6L88lJNCFlEADKOkjpXJQyZRiavX1INZ4tRnrBVr2COd3RgcTLyUiEXMNBlDU/cgYq6taUS0fExrWW4w==", + "version": "29.6.3", + "resolved": "https://registry.npmjs.org/jest-util/-/jest-util-29.6.3.tgz", + "integrity": "sha512-QUjna/xSy4B32fzcKTSz1w7YYzgiHrjjJjevdRf61HYk998R5vVMMNmrHESYZVDS5DSWs+1srPLPKxXPkeSDOA==", "dev": true, "dependencies": { - "@jest/types": "^29.6.1", + "@jest/types": "^29.6.3", "@types/node": "*", "chalk": "^4.0.0", "ci-info": "^3.2.0", @@ -4019,17 +3812,17 @@ } }, "node_modules/jest-validate": { - "version": "29.6.2", - "resolved": "https://registry.npmjs.org/jest-validate/-/jest-validate-29.6.2.tgz", - "integrity": "sha512-vGz0yMN5fUFRRbpJDPwxMpgSXW1LDKROHfBopAvDcmD6s+B/s8WJrwi+4bfH4SdInBA5C3P3BI19dBtKzx1Arg==", + "version": "29.6.3", + "resolved": "https://registry.npmjs.org/jest-validate/-/jest-validate-29.6.3.tgz", + "integrity": "sha512-e7KWZcAIX+2W1o3cHfnqpGajdCs1jSM3DkXjGeLSNmCazv1EeI1ggTeK5wdZhF+7N+g44JI2Od3veojoaumlfg==", "dev": true, "dependencies": { - "@jest/types": "^29.6.1", + "@jest/types": "^29.6.3", "camelcase": "^6.2.0", "chalk": "^4.0.0", - "jest-get-type": "^29.4.3", + "jest-get-type": "^29.6.3", "leven": "^3.1.0", - "pretty-format": "^29.6.2" + "pretty-format": "^29.6.3" }, "engines": { "node": "^14.15.0 || ^16.10.0 || >=18.0.0" @@ -4048,18 +3841,18 @@ } }, "node_modules/jest-watcher": { - "version": "29.6.2", - "resolved": "https://registry.npmjs.org/jest-watcher/-/jest-watcher-29.6.2.tgz", - "integrity": "sha512-GZitlqkMkhkefjfN/p3SJjrDaxPflqxEAv3/ik10OirZqJGYH5rPiIsgVcfof0Tdqg3shQGdEIxDBx+B4tuLzA==", + "version": "29.6.4", + "resolved": "https://registry.npmjs.org/jest-watcher/-/jest-watcher-29.6.4.tgz", + "integrity": "sha512-oqUWvx6+On04ShsT00Ir9T4/FvBeEh2M9PTubgITPxDa739p4hoQweWPRGyYeaojgT0xTpZKF0Y/rSY1UgMxvQ==", "dev": true, "dependencies": { - "@jest/test-result": "^29.6.2", - "@jest/types": "^29.6.1", + "@jest/test-result": "^29.6.4", + "@jest/types": "^29.6.3", "@types/node": "*", "ansi-escapes": "^4.2.1", "chalk": "^4.0.0", "emittery": "^0.13.1", - "jest-util": "^29.6.2", + "jest-util": "^29.6.3", "string-length": "^4.0.1" }, "engines": { @@ -4067,13 +3860,13 @@ } }, "node_modules/jest-worker": { - "version": "29.6.2", - "resolved": "https://registry.npmjs.org/jest-worker/-/jest-worker-29.6.2.tgz", - "integrity": "sha512-l3ccBOabTdkng8I/ORCkADz4eSMKejTYv1vB/Z83UiubqhC1oQ5Li6dWCyqOIvSifGjUBxuvxvlm6KGK2DtuAQ==", + "version": "29.6.4", + "resolved": "https://registry.npmjs.org/jest-worker/-/jest-worker-29.6.4.tgz", + "integrity": "sha512-6dpvFV4WjcWbDVGgHTWo/aupl8/LbBx2NSKfiwqf79xC/yeJjKHT1+StcKy/2KTmW16hE68ccKVOtXf+WZGz7Q==", "dev": true, "dependencies": { "@types/node": "*", - "jest-util": "^29.6.2", + "jest-util": "^29.6.3", "merge-stream": "^2.0.0", "supports-color": "^8.0.0" }, @@ -4675,9 +4468,9 @@ } }, "node_modules/prettier": { - "version": "3.0.2", - "resolved": "https://registry.npmjs.org/prettier/-/prettier-3.0.2.tgz", - "integrity": "sha512-o2YR9qtniXvwEZlOKbveKfDQVyqxbEIWn48Z8m3ZJjBjcCmUy3xZGIv+7AkaeuaTr6yPXJjwv07ZWlsWbEy1rQ==", + "version": "3.0.3", + "resolved": "https://registry.npmjs.org/prettier/-/prettier-3.0.3.tgz", + "integrity": "sha512-L/4pUDMxcNa8R/EthV08Zt42WBO4h1rarVtK0K+QJG0X187OLo7l699jWw0GKuwzkPQ//jMFA/8Xm6Fh3J/DAg==", "dev": true, "bin": { "prettier": "bin/prettier.cjs" @@ -4690,12 +4483,12 @@ } }, "node_modules/pretty-format": { - "version": "29.6.2", - "resolved": "https://registry.npmjs.org/pretty-format/-/pretty-format-29.6.2.tgz", - "integrity": "sha512-1q0oC8eRveTg5nnBEWMXAU2qpv65Gnuf2eCQzSjxpWFkPaPARwqZZDGuNE0zPAZfTCHzIk3A8dIjwlQKKLphyg==", + "version": "29.6.3", + "resolved": "https://registry.npmjs.org/pretty-format/-/pretty-format-29.6.3.tgz", + "integrity": "sha512-ZsBgjVhFAj5KeK+nHfF1305/By3lechHQSMWCTl8iHSbfOm2TN5nHEtFc/+W7fAyUeCs2n5iow72gld4gW0xDw==", "dev": true, "dependencies": { - "@jest/schemas": "^29.6.0", + "@jest/schemas": "^29.6.3", "ansi-styles": "^5.0.0", "react-is": "^18.0.0" }, @@ -5272,9 +5065,9 @@ } }, "node_modules/typescript": { - "version": "5.1.6", - "resolved": "https://registry.npmjs.org/typescript/-/typescript-5.1.6.tgz", - "integrity": "sha512-zaWCozRZ6DLEWAWFrVDz1H6FVXzUSfTy5FUMWsQlU8Ym5JP9eO4xkTIROFCQvhQf61z6O/G6ugw3SgAnvvm+HA==", + "version": "5.2.2", + "resolved": "https://registry.npmjs.org/typescript/-/typescript-5.2.2.tgz", + "integrity": "sha512-mI4WrpHsbCIcwT9cF4FZvr80QUeKvsUsUvKDoR+X/7XHQH98xYD8YHZg7ANtz2GtZt/CBq2QJ0thkGJMHfqc1w==", "dev": true, "bin": { "tsc": "bin/tsc", @@ -6067,9 +5860,9 @@ } }, "@eslint/js": { - "version": "8.47.0", - "resolved": "https://registry.npmjs.org/@eslint/js/-/js-8.47.0.tgz", - "integrity": "sha512-P6omY1zv5MItm93kLM8s2vr1HICJH8v0dvddDhysbIuZ+vcjOHg5Zbkf1mTkcmi2JA9oBG2anOkRnW8WJTS8Og==", + "version": "8.48.0", + "resolved": "https://registry.npmjs.org/@eslint/js/-/js-8.48.0.tgz", + "integrity": "sha512-ZSjtmelB7IJfWD2Fvb7+Z+ChTIKWq6kjda95fLcQKNS5aheVHn4IkfgRQE3sIIzTcSLwLcLZUD9UBt+V7+h+Pw==", "dev": true }, "@humanwhocodes/config-array": { @@ -6179,155 +5972,123 @@ "dev": true }, "@jest/console": { - "version": "29.6.2", - "resolved": "https://registry.npmjs.org/@jest/console/-/console-29.6.2.tgz", - "integrity": "sha512-0N0yZof5hi44HAR2pPS+ikJ3nzKNoZdVu8FffRf3wy47I7Dm7etk/3KetMdRUqzVd16V4O2m2ISpNTbnIuqy1w==", + "version": "29.6.4", + "resolved": "https://registry.npmjs.org/@jest/console/-/console-29.6.4.tgz", + "integrity": "sha512-wNK6gC0Ha9QeEPSkeJedQuTQqxZYnDPuDcDhVuVatRvMkL4D0VTvFVZj+Yuh6caG2aOfzkUZ36KtCmLNtR02hw==", "dev": true, "requires": { - "@jest/types": "^29.6.1", + "@jest/types": "^29.6.3", "@types/node": "*", "chalk": "^4.0.0", - "jest-message-util": "^29.6.2", - "jest-util": "^29.6.2", + "jest-message-util": "^29.6.3", + "jest-util": "^29.6.3", "slash": "^3.0.0" } }, "@jest/core": { - "version": "29.6.2", - "resolved": "https://registry.npmjs.org/@jest/core/-/core-29.6.2.tgz", - "integrity": "sha512-Oj+5B+sDMiMWLhPFF+4/DvHOf+U10rgvCLGPHP8Xlsy/7QxS51aU/eBngudHlJXnaWD5EohAgJ4js+T6pa+zOg==", + "version": "29.6.4", + "resolved": "https://registry.npmjs.org/@jest/core/-/core-29.6.4.tgz", + "integrity": "sha512-U/vq5ccNTSVgYH7mHnodHmCffGWHJnz/E1BEWlLuK5pM4FZmGfBn/nrJGLjUsSmyx3otCeqc1T31F4y08AMDLg==", "dev": true, "requires": { - "@jest/console": "^29.6.2", - "@jest/reporters": "^29.6.2", - "@jest/test-result": "^29.6.2", - "@jest/transform": "^29.6.2", - "@jest/types": "^29.6.1", + "@jest/console": "^29.6.4", + "@jest/reporters": "^29.6.4", + "@jest/test-result": "^29.6.4", + "@jest/transform": "^29.6.4", + "@jest/types": "^29.6.3", "@types/node": "*", "ansi-escapes": "^4.2.1", "chalk": "^4.0.0", "ci-info": "^3.2.0", "exit": "^0.1.2", "graceful-fs": "^4.2.9", - "jest-changed-files": "^29.5.0", - "jest-config": "^29.6.2", - "jest-haste-map": "^29.6.2", - "jest-message-util": "^29.6.2", - "jest-regex-util": "^29.4.3", - "jest-resolve": "^29.6.2", - "jest-resolve-dependencies": "^29.6.2", - "jest-runner": "^29.6.2", - "jest-runtime": "^29.6.2", - "jest-snapshot": "^29.6.2", - "jest-util": "^29.6.2", - "jest-validate": "^29.6.2", - "jest-watcher": "^29.6.2", + "jest-changed-files": "^29.6.3", + "jest-config": "^29.6.4", + "jest-haste-map": "^29.6.4", + "jest-message-util": "^29.6.3", + "jest-regex-util": "^29.6.3", + "jest-resolve": "^29.6.4", + "jest-resolve-dependencies": "^29.6.4", + "jest-runner": "^29.6.4", + "jest-runtime": "^29.6.4", + "jest-snapshot": "^29.6.4", + "jest-util": "^29.6.3", + "jest-validate": "^29.6.3", + "jest-watcher": "^29.6.4", "micromatch": "^4.0.4", - "pretty-format": "^29.6.2", + "pretty-format": "^29.6.3", "slash": "^3.0.0", "strip-ansi": "^6.0.0" - }, - "dependencies": { - "jest-config": { - "version": "29.6.2", - "resolved": "https://registry.npmjs.org/jest-config/-/jest-config-29.6.2.tgz", - "integrity": "sha512-VxwFOC8gkiJbuodG9CPtMRjBUNZEHxwfQXmIudSTzFWxaci3Qub1ddTRbFNQlD/zUeaifLndh/eDccFX4wCMQw==", - "dev": true, - "requires": { - "@babel/core": "^7.11.6", - "@jest/test-sequencer": "^29.6.2", - "@jest/types": "^29.6.1", - "babel-jest": "^29.6.2", - "chalk": "^4.0.0", - "ci-info": "^3.2.0", - "deepmerge": "^4.2.2", - "glob": "^7.1.3", - "graceful-fs": "^4.2.9", - "jest-circus": "^29.6.2", - "jest-environment-node": "^29.6.2", - "jest-get-type": "^29.4.3", - "jest-regex-util": "^29.4.3", - "jest-resolve": "^29.6.2", - "jest-runner": "^29.6.2", - "jest-util": "^29.6.2", - "jest-validate": "^29.6.2", - "micromatch": "^4.0.4", - "parse-json": "^5.2.0", - "pretty-format": "^29.6.2", - "slash": "^3.0.0", - "strip-json-comments": "^3.1.1" - } - } } }, "@jest/environment": { - "version": "29.6.2", - "resolved": "https://registry.npmjs.org/@jest/environment/-/environment-29.6.2.tgz", - "integrity": "sha512-AEcW43C7huGd/vogTddNNTDRpO6vQ2zaQNrttvWV18ArBx9Z56h7BIsXkNFJVOO4/kblWEQz30ckw0+L3izc+Q==", + "version": "29.6.4", + "resolved": "https://registry.npmjs.org/@jest/environment/-/environment-29.6.4.tgz", + "integrity": "sha512-sQ0SULEjA1XUTHmkBRl7A1dyITM9yb1yb3ZNKPX3KlTd6IG7mWUe3e2yfExtC2Zz1Q+mMckOLHmL/qLiuQJrBQ==", "dev": true, "requires": { - "@jest/fake-timers": "^29.6.2", - "@jest/types": "^29.6.1", + "@jest/fake-timers": "^29.6.4", + "@jest/types": "^29.6.3", "@types/node": "*", - "jest-mock": "^29.6.2" + "jest-mock": "^29.6.3" } }, "@jest/expect": { - "version": "29.6.2", - "resolved": "https://registry.npmjs.org/@jest/expect/-/expect-29.6.2.tgz", - "integrity": "sha512-m6DrEJxVKjkELTVAztTLyS/7C92Y2b0VYqmDROYKLLALHn8T/04yPs70NADUYPrV3ruI+H3J0iUIuhkjp7vkfg==", + "version": "29.6.4", + "resolved": "https://registry.npmjs.org/@jest/expect/-/expect-29.6.4.tgz", + "integrity": "sha512-Warhsa7d23+3X5bLbrbYvaehcgX5TLYhI03JKoedTiI8uJU4IhqYBWF7OSSgUyz4IgLpUYPkK0AehA5/fRclAA==", "dev": true, "requires": { - "expect": "^29.6.2", - "jest-snapshot": "^29.6.2" + "expect": "^29.6.4", + "jest-snapshot": "^29.6.4" } }, "@jest/expect-utils": { - "version": "29.6.2", - "resolved": "https://registry.npmjs.org/@jest/expect-utils/-/expect-utils-29.6.2.tgz", - "integrity": "sha512-6zIhM8go3RV2IG4aIZaZbxwpOzz3ZiM23oxAlkquOIole+G6TrbeXnykxWYlqF7kz2HlBjdKtca20x9atkEQYg==", + "version": "29.6.4", + "resolved": "https://registry.npmjs.org/@jest/expect-utils/-/expect-utils-29.6.4.tgz", + "integrity": "sha512-FEhkJhqtvBwgSpiTrocquJCdXPsyvNKcl/n7A3u7X4pVoF4bswm11c9d4AV+kfq2Gpv/mM8x7E7DsRvH+djkrg==", "dev": true, "requires": { - "jest-get-type": "^29.4.3" + "jest-get-type": "^29.6.3" } }, "@jest/fake-timers": { - "version": "29.6.2", - "resolved": "https://registry.npmjs.org/@jest/fake-timers/-/fake-timers-29.6.2.tgz", - "integrity": "sha512-euZDmIlWjm1Z0lJ1D0f7a0/y5Kh/koLFMUBE5SUYWrmy8oNhJpbTBDAP6CxKnadcMLDoDf4waRYCe35cH6G6PA==", + "version": "29.6.4", + "resolved": "https://registry.npmjs.org/@jest/fake-timers/-/fake-timers-29.6.4.tgz", + "integrity": "sha512-6UkCwzoBK60edXIIWb0/KWkuj7R7Qq91vVInOe3De6DSpaEiqjKcJw4F7XUet24Wupahj9J6PlR09JqJ5ySDHw==", "dev": true, "requires": { - "@jest/types": "^29.6.1", + "@jest/types": "^29.6.3", "@sinonjs/fake-timers": "^10.0.2", "@types/node": "*", - "jest-message-util": "^29.6.2", - "jest-mock": "^29.6.2", - "jest-util": "^29.6.2" + "jest-message-util": "^29.6.3", + "jest-mock": "^29.6.3", + "jest-util": "^29.6.3" } }, "@jest/globals": { - "version": "29.6.2", - "resolved": "https://registry.npmjs.org/@jest/globals/-/globals-29.6.2.tgz", - "integrity": "sha512-cjuJmNDjs6aMijCmSa1g2TNG4Lby/AeU7/02VtpW+SLcZXzOLK2GpN2nLqcFjmhy3B3AoPeQVx7BnyOf681bAw==", + "version": "29.6.4", + "resolved": "https://registry.npmjs.org/@jest/globals/-/globals-29.6.4.tgz", + "integrity": "sha512-wVIn5bdtjlChhXAzVXavcY/3PEjf4VqM174BM3eGL5kMxLiZD5CLnbmkEyA1Dwh9q8XjP6E8RwjBsY/iCWrWsA==", "dev": true, "requires": { - "@jest/environment": "^29.6.2", - "@jest/expect": "^29.6.2", - "@jest/types": "^29.6.1", - "jest-mock": "^29.6.2" + "@jest/environment": "^29.6.4", + "@jest/expect": "^29.6.4", + "@jest/types": "^29.6.3", + "jest-mock": "^29.6.3" } }, "@jest/reporters": { - "version": "29.6.2", - "resolved": "https://registry.npmjs.org/@jest/reporters/-/reporters-29.6.2.tgz", - "integrity": "sha512-sWtijrvIav8LgfJZlrGCdN0nP2EWbakglJY49J1Y5QihcQLfy7ovyxxjJBRXMNltgt4uPtEcFmIMbVshEDfFWw==", + "version": "29.6.4", + "resolved": "https://registry.npmjs.org/@jest/reporters/-/reporters-29.6.4.tgz", + "integrity": "sha512-sxUjWxm7QdchdrD3NfWKrL8FBsortZeibSJv4XLjESOOjSUOkjQcb0ZHJwfhEGIvBvTluTzfG2yZWZhkrXJu8g==", "dev": true, "requires": { "@bcoe/v8-coverage": "^0.2.3", - "@jest/console": "^29.6.2", - "@jest/test-result": "^29.6.2", - "@jest/transform": "^29.6.2", - "@jest/types": "^29.6.1", + "@jest/console": "^29.6.4", + "@jest/test-result": "^29.6.4", + "@jest/transform": "^29.6.4", + "@jest/types": "^29.6.3", "@jridgewell/trace-mapping": "^0.3.18", "@types/node": "*", "chalk": "^4.0.0", @@ -6336,32 +6097,71 @@ "glob": "^7.1.3", "graceful-fs": "^4.2.9", "istanbul-lib-coverage": "^3.0.0", - "istanbul-lib-instrument": "^5.1.0", + "istanbul-lib-instrument": "^6.0.0", "istanbul-lib-report": "^3.0.0", "istanbul-lib-source-maps": "^4.0.0", "istanbul-reports": "^3.1.3", - "jest-message-util": "^29.6.2", - "jest-util": "^29.6.2", - "jest-worker": "^29.6.2", + "jest-message-util": "^29.6.3", + "jest-util": "^29.6.3", + "jest-worker": "^29.6.4", "slash": "^3.0.0", "string-length": "^4.0.1", "strip-ansi": "^6.0.0", "v8-to-istanbul": "^9.0.1" + }, + "dependencies": { + "istanbul-lib-instrument": { + "version": "6.0.0", + "resolved": "https://registry.npmjs.org/istanbul-lib-instrument/-/istanbul-lib-instrument-6.0.0.tgz", + "integrity": "sha512-x58orMzEVfzPUKqlbLd1hXCnySCxKdDKa6Rjg97CwuLLRI4g3FHTdnExu1OqffVFay6zeMW+T6/DowFLndWnIw==", + "dev": true, + "requires": { + "@babel/core": "^7.12.3", + "@babel/parser": "^7.14.7", + "@istanbuljs/schema": "^0.1.2", + "istanbul-lib-coverage": "^3.2.0", + "semver": "^7.5.4" + } + }, + "lru-cache": { + "version": "6.0.0", + "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-6.0.0.tgz", + "integrity": "sha512-Jo6dJ04CmSjuznwJSS3pUeWmd/H0ffTlkXXgwZi+eq1UCmqQwCh+eLsYOYCwY991i2Fah4h1BEMCx4qThGbsiA==", + "dev": true, + "requires": { + "yallist": "^4.0.0" + } + }, + "semver": { + "version": "7.5.4", + "resolved": "https://registry.npmjs.org/semver/-/semver-7.5.4.tgz", + "integrity": "sha512-1bCSESV6Pv+i21Hvpxp3Dx+pSD8lIPt8uVjRrxAUt/nbswYc+tK6Y2btiULjd4+fnq15PX+nqQDC7Oft7WkwcA==", + "dev": true, + "requires": { + "lru-cache": "^6.0.0" + } + }, + "yallist": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/yallist/-/yallist-4.0.0.tgz", + "integrity": "sha512-3wdGidZyq5PB084XLES5TpOSRA3wjXAlIWMhum2kRcv/41Sn2emQ0dycQW4uZXLejwKvg6EsvbdlVL+FYEct7A==", + "dev": true + } } }, "@jest/schemas": { - "version": "29.6.0", - "resolved": "https://registry.npmjs.org/@jest/schemas/-/schemas-29.6.0.tgz", - "integrity": "sha512-rxLjXyJBTL4LQeJW3aKo0M/+GkCOXsO+8i9Iu7eDb6KwtP65ayoDsitrdPBtujxQ88k4wI2FNYfa6TOGwSn6cQ==", + "version": "29.6.3", + "resolved": "https://registry.npmjs.org/@jest/schemas/-/schemas-29.6.3.tgz", + "integrity": "sha512-mo5j5X+jIZmJQveBKeS/clAueipV7KgiX1vMgCxam1RNYiqE1w62n0/tJJnHtjW8ZHcQco5gY85jA3mi0L+nSA==", "dev": true, "requires": { "@sinclair/typebox": "^0.27.8" } }, "@jest/source-map": { - "version": "29.6.0", - "resolved": "https://registry.npmjs.org/@jest/source-map/-/source-map-29.6.0.tgz", - "integrity": "sha512-oA+I2SHHQGxDCZpbrsCQSoMLb3Bz547JnM+jUr9qEbuw0vQlWZfpPS7CO9J7XiwKicEz9OFn/IYoLkkiUD7bzA==", + "version": "29.6.3", + "resolved": "https://registry.npmjs.org/@jest/source-map/-/source-map-29.6.3.tgz", + "integrity": "sha512-MHjT95QuipcPrpLM+8JMSzFx6eHp5Bm+4XeFDJlwsvVBjmKNiIAvasGK2fxz2WbGRlnvqehFbh07MMa7n3YJnw==", "dev": true, "requires": { "@jridgewell/trace-mapping": "^0.3.18", @@ -6370,46 +6170,46 @@ } }, "@jest/test-result": { - "version": "29.6.2", - "resolved": "https://registry.npmjs.org/@jest/test-result/-/test-result-29.6.2.tgz", - "integrity": "sha512-3VKFXzcV42EYhMCsJQURptSqnyjqCGbtLuX5Xxb6Pm6gUf1wIRIl+mandIRGJyWKgNKYF9cnstti6Ls5ekduqw==", + "version": "29.6.4", + "resolved": "https://registry.npmjs.org/@jest/test-result/-/test-result-29.6.4.tgz", + "integrity": "sha512-uQ1C0AUEN90/dsyEirgMLlouROgSY+Wc/JanVVk0OiUKa5UFh7sJpMEM3aoUBAz2BRNvUJ8j3d294WFuRxSyOQ==", "dev": true, "requires": { - "@jest/console": "^29.6.2", - "@jest/types": "^29.6.1", + "@jest/console": "^29.6.4", + "@jest/types": "^29.6.3", "@types/istanbul-lib-coverage": "^2.0.0", "collect-v8-coverage": "^1.0.0" } }, "@jest/test-sequencer": { - "version": "29.6.2", - "resolved": "https://registry.npmjs.org/@jest/test-sequencer/-/test-sequencer-29.6.2.tgz", - "integrity": "sha512-GVYi6PfPwVejO7slw6IDO0qKVum5jtrJ3KoLGbgBWyr2qr4GaxFV6su+ZAjdTX75Sr1DkMFRk09r2ZVa+wtCGw==", + "version": "29.6.4", + "resolved": "https://registry.npmjs.org/@jest/test-sequencer/-/test-sequencer-29.6.4.tgz", + "integrity": "sha512-E84M6LbpcRq3fT4ckfKs9ryVanwkaIB0Ws9bw3/yP4seRLg/VaCZ/LgW0MCq5wwk4/iP/qnilD41aj2fsw2RMg==", "dev": true, "requires": { - "@jest/test-result": "^29.6.2", + "@jest/test-result": "^29.6.4", "graceful-fs": "^4.2.9", - "jest-haste-map": "^29.6.2", + "jest-haste-map": "^29.6.4", "slash": "^3.0.0" } }, "@jest/transform": { - "version": "29.6.2", - "resolved": "https://registry.npmjs.org/@jest/transform/-/transform-29.6.2.tgz", - "integrity": "sha512-ZqCqEISr58Ce3U+buNFJYUktLJZOggfyvR+bZMaiV1e8B1SIvJbwZMrYz3gx/KAPn9EXmOmN+uB08yLCjWkQQg==", + "version": "29.6.4", + "resolved": "https://registry.npmjs.org/@jest/transform/-/transform-29.6.4.tgz", + "integrity": "sha512-8thgRSiXUqtr/pPGY/OsyHuMjGyhVnWrFAwoxmIemlBuiMyU1WFs0tXoNxzcr4A4uErs/ABre76SGmrr5ab/AA==", "dev": true, "requires": { "@babel/core": "^7.11.6", - "@jest/types": "^29.6.1", + "@jest/types": "^29.6.3", "@jridgewell/trace-mapping": "^0.3.18", "babel-plugin-istanbul": "^6.1.1", "chalk": "^4.0.0", "convert-source-map": "^2.0.0", "fast-json-stable-stringify": "^2.1.0", "graceful-fs": "^4.2.9", - "jest-haste-map": "^29.6.2", - "jest-regex-util": "^29.4.3", - "jest-util": "^29.6.2", + "jest-haste-map": "^29.6.4", + "jest-regex-util": "^29.6.3", + "jest-util": "^29.6.3", "micromatch": "^4.0.4", "pirates": "^4.0.4", "slash": "^3.0.0", @@ -6417,12 +6217,12 @@ } }, "@jest/types": { - "version": "29.6.1", - "resolved": "https://registry.npmjs.org/@jest/types/-/types-29.6.1.tgz", - "integrity": "sha512-tPKQNMPuXgvdOn2/Lg9HNfUvjYVGolt04Hp03f5hAk878uwOLikN+JzeLY0HcVgKgFl9Hs3EIqpu3WX27XNhnw==", + "version": "29.6.3", + "resolved": "https://registry.npmjs.org/@jest/types/-/types-29.6.3.tgz", + "integrity": "sha512-u3UPsIilWKOM3F9CXtrG8LEJmNxwoCQC/XVj4IKYXvvpx7QIi/Kg1LI5uDmDpKlac62NUtX7eLjRh+jVZcLOzw==", "dev": true, "requires": { - "@jest/schemas": "^29.6.0", + "@jest/schemas": "^29.6.3", "@types/istanbul-lib-coverage": "^2.0.0", "@types/istanbul-reports": "^3.0.0", "@types/node": "*", @@ -6702,9 +6502,9 @@ } }, "@types/jest": { - "version": "29.5.3", - "resolved": "https://registry.npmjs.org/@types/jest/-/jest-29.5.3.tgz", - "integrity": "sha512-1Nq7YrO/vJE/FYnqYyw0FS8LdrjExSgIiHyKg7xPpn+yi8Q4huZryKnkJatN1ZRH89Kw2v33/8ZMB7DuZeSLlA==", + "version": "29.5.4", + "resolved": "https://registry.npmjs.org/@types/jest/-/jest-29.5.4.tgz", + "integrity": "sha512-PhglGmhWeD46FYOVLt3X7TiWjzwuVGW9wG/4qocPevXMjCmrIc5b6db9WjeGE4QYVpUAWMDv3v0IiBwObY289A==", "dev": true, "requires": { "expect": "^29.0.0", @@ -6718,15 +6518,15 @@ "dev": true }, "@types/node": { - "version": "20.5.0", - "resolved": "https://registry.npmjs.org/@types/node/-/node-20.5.0.tgz", - "integrity": "sha512-Mgq7eCtoTjT89FqNoTzzXg2XvCi5VMhRV6+I2aYanc6kQCBImeNaAYRs/DyoVqk1YEUJK5gN9VO7HRIdz4Wo3Q==", + "version": "20.5.9", + "resolved": "https://registry.npmjs.org/@types/node/-/node-20.5.9.tgz", + "integrity": "sha512-PcGNd//40kHAS3sTlzKB9C9XL4K0sTup8nbG5lC14kzEteTNuAFh9u5nA0o5TWnSG2r/JNPRXFVcHJIIeRlmqQ==", "dev": true }, "@types/semver": { - "version": "7.5.0", - "resolved": "https://registry.npmjs.org/@types/semver/-/semver-7.5.0.tgz", - "integrity": "sha512-G8hZ6XJiHnuhQKR7ZmysCeJWE08o8T0AXtk5darsCaTVsYZhhgUrq53jizaR2FvsoeCwJhlmwTjkXBY5Pn/ZHw==", + "version": "7.5.1", + "resolved": "https://registry.npmjs.org/@types/semver/-/semver-7.5.1.tgz", + "integrity": "sha512-cJRQXpObxfNKkFAZbJl2yjWtJCqELQIdShsogr1d2MilP8dKD9TE/nEKHkJgUNHdGKCQaf9HbIynuV2csLGVLg==", "dev": true }, "@types/stack-utils": { @@ -6751,16 +6551,16 @@ "dev": true }, "@typescript-eslint/eslint-plugin": { - "version": "6.4.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/eslint-plugin/-/eslint-plugin-6.4.0.tgz", - "integrity": "sha512-62o2Hmc7Gs3p8SLfbXcipjWAa6qk2wZGChXG2JbBtYpwSRmti/9KHLqfbLs9uDigOexG+3PaQ9G2g3201FWLKg==", + "version": "6.5.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/eslint-plugin/-/eslint-plugin-6.5.0.tgz", + "integrity": "sha512-2pktILyjvMaScU6iK3925uvGU87E+N9rh372uGZgiMYwafaw9SXq86U04XPq3UH6tzRvNgBsub6x2DacHc33lw==", "dev": true, "requires": { "@eslint-community/regexpp": "^4.5.1", - "@typescript-eslint/scope-manager": "6.4.0", - "@typescript-eslint/type-utils": "6.4.0", - "@typescript-eslint/utils": "6.4.0", - "@typescript-eslint/visitor-keys": "6.4.0", + "@typescript-eslint/scope-manager": "6.5.0", + "@typescript-eslint/type-utils": "6.5.0", + "@typescript-eslint/utils": "6.5.0", + "@typescript-eslint/visitor-keys": "6.5.0", "debug": "^4.3.4", "graphemer": "^1.4.0", "ignore": "^5.2.4", @@ -6769,32 +6569,6 @@ "ts-api-utils": "^1.0.1" }, "dependencies": { - "@typescript-eslint/scope-manager": { - "version": "6.4.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/scope-manager/-/scope-manager-6.4.0.tgz", - "integrity": "sha512-TUS7vaKkPWDVvl7GDNHFQMsMruD+zhkd3SdVW0d7b+7Zo+bd/hXJQ8nsiUZMi1jloWo6c9qt3B7Sqo+flC1nig==", - "dev": true, - "requires": { - "@typescript-eslint/types": "6.4.0", - "@typescript-eslint/visitor-keys": "6.4.0" - } - }, - "@typescript-eslint/types": { - "version": "6.4.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/types/-/types-6.4.0.tgz", - "integrity": "sha512-+FV9kVFrS7w78YtzkIsNSoYsnOtrYVnKWSTVXoL1761CsCRv5wpDOINgsXpxD67YCLZtVQekDDyaxfjVWUJmmg==", - "dev": true - }, - "@typescript-eslint/visitor-keys": { - "version": "6.4.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/visitor-keys/-/visitor-keys-6.4.0.tgz", - "integrity": "sha512-yJSfyT+uJm+JRDWYRYdCm2i+pmvXJSMtPR9Cq5/XQs4QIgNoLcoRtDdzsLbLsFM/c6um6ohQkg/MLxWvoIndJA==", - "dev": true, - "requires": { - "@typescript-eslint/types": "6.4.0", - "eslint-visitor-keys": "^3.4.1" - } - }, "lru-cache": { "version": "6.0.0", "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-6.0.0.tgz", @@ -6822,111 +6596,54 @@ } }, "@typescript-eslint/parser": { - "version": "6.3.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/parser/-/parser-6.3.0.tgz", - "integrity": "sha512-ibP+y2Gr6p0qsUkhs7InMdXrwldjxZw66wpcQq9/PzAroM45wdwyu81T+7RibNCh8oc0AgrsyCwJByncY0Ongg==", + "version": "6.5.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/parser/-/parser-6.5.0.tgz", + "integrity": "sha512-LMAVtR5GN8nY0G0BadkG0XIe4AcNMeyEy3DyhKGAh9k4pLSMBO7rF29JvDBpZGCmp5Pgz5RLHP6eCpSYZJQDuQ==", "dev": true, "requires": { - "@typescript-eslint/scope-manager": "6.3.0", - "@typescript-eslint/types": "6.3.0", - "@typescript-eslint/typescript-estree": "6.3.0", - "@typescript-eslint/visitor-keys": "6.3.0", + "@typescript-eslint/scope-manager": "6.5.0", + "@typescript-eslint/types": "6.5.0", + "@typescript-eslint/typescript-estree": "6.5.0", + "@typescript-eslint/visitor-keys": "6.5.0", "debug": "^4.3.4" } }, "@typescript-eslint/scope-manager": { - "version": "6.3.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/scope-manager/-/scope-manager-6.3.0.tgz", - "integrity": "sha512-WlNFgBEuGu74ahrXzgefiz/QlVb+qg8KDTpknKwR7hMH+lQygWyx0CQFoUmMn1zDkQjTBBIn75IxtWss77iBIQ==", + "version": "6.5.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/scope-manager/-/scope-manager-6.5.0.tgz", + "integrity": "sha512-A8hZ7OlxURricpycp5kdPTH3XnjG85UpJS6Fn4VzeoH4T388gQJ/PGP4ole5NfKt4WDVhmLaQ/dBLNDC4Xl/Kw==", "dev": true, "requires": { - "@typescript-eslint/types": "6.3.0", - "@typescript-eslint/visitor-keys": "6.3.0" + "@typescript-eslint/types": "6.5.0", + "@typescript-eslint/visitor-keys": "6.5.0" } }, "@typescript-eslint/type-utils": { - "version": "6.4.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/type-utils/-/type-utils-6.4.0.tgz", - "integrity": "sha512-TvqrUFFyGY0cX3WgDHcdl2/mMCWCDv/0thTtx/ODMY1QhEiyFtv/OlLaNIiYLwRpAxAtOLOY9SUf1H3Q3dlwAg==", + "version": "6.5.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/type-utils/-/type-utils-6.5.0.tgz", + "integrity": "sha512-f7OcZOkRivtujIBQ4yrJNIuwyCQO1OjocVqntl9dgSIZAdKqicj3xFDqDOzHDlGCZX990LqhLQXWRnQvsapq8A==", "dev": true, "requires": { - "@typescript-eslint/typescript-estree": "6.4.0", - "@typescript-eslint/utils": "6.4.0", + "@typescript-eslint/typescript-estree": "6.5.0", + "@typescript-eslint/utils": "6.5.0", "debug": "^4.3.4", "ts-api-utils": "^1.0.1" - }, - "dependencies": { - "@typescript-eslint/types": { - "version": "6.4.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/types/-/types-6.4.0.tgz", - "integrity": "sha512-+FV9kVFrS7w78YtzkIsNSoYsnOtrYVnKWSTVXoL1761CsCRv5wpDOINgsXpxD67YCLZtVQekDDyaxfjVWUJmmg==", - "dev": true - }, - "@typescript-eslint/typescript-estree": { - "version": "6.4.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/typescript-estree/-/typescript-estree-6.4.0.tgz", - "integrity": "sha512-iDPJArf/K2sxvjOR6skeUCNgHR/tCQXBsa+ee1/clRKr3olZjZ/dSkXPZjG6YkPtnW6p5D1egeEPMCW6Gn4yLA==", - "dev": true, - "requires": { - "@typescript-eslint/types": "6.4.0", - "@typescript-eslint/visitor-keys": "6.4.0", - "debug": "^4.3.4", - "globby": "^11.1.0", - "is-glob": "^4.0.3", - "semver": "^7.5.4", - "ts-api-utils": "^1.0.1" - } - }, - "@typescript-eslint/visitor-keys": { - "version": "6.4.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/visitor-keys/-/visitor-keys-6.4.0.tgz", - "integrity": "sha512-yJSfyT+uJm+JRDWYRYdCm2i+pmvXJSMtPR9Cq5/XQs4QIgNoLcoRtDdzsLbLsFM/c6um6ohQkg/MLxWvoIndJA==", - "dev": true, - "requires": { - "@typescript-eslint/types": "6.4.0", - "eslint-visitor-keys": "^3.4.1" - } - }, - "lru-cache": { - "version": "6.0.0", - "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-6.0.0.tgz", - "integrity": "sha512-Jo6dJ04CmSjuznwJSS3pUeWmd/H0ffTlkXXgwZi+eq1UCmqQwCh+eLsYOYCwY991i2Fah4h1BEMCx4qThGbsiA==", - "dev": true, - "requires": { - "yallist": "^4.0.0" - } - }, - "semver": { - "version": "7.5.4", - "resolved": "https://registry.npmjs.org/semver/-/semver-7.5.4.tgz", - "integrity": "sha512-1bCSESV6Pv+i21Hvpxp3Dx+pSD8lIPt8uVjRrxAUt/nbswYc+tK6Y2btiULjd4+fnq15PX+nqQDC7Oft7WkwcA==", - "dev": true, - "requires": { - "lru-cache": "^6.0.0" - } - }, - "yallist": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/yallist/-/yallist-4.0.0.tgz", - "integrity": "sha512-3wdGidZyq5PB084XLES5TpOSRA3wjXAlIWMhum2kRcv/41Sn2emQ0dycQW4uZXLejwKvg6EsvbdlVL+FYEct7A==", - "dev": true - } } }, "@typescript-eslint/types": { - "version": "6.3.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/types/-/types-6.3.0.tgz", - "integrity": "sha512-K6TZOvfVyc7MO9j60MkRNWyFSf86IbOatTKGrpTQnzarDZPYPVy0oe3myTMq7VjhfsUAbNUW8I5s+2lZvtx1gg==", + "version": "6.5.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/types/-/types-6.5.0.tgz", + "integrity": "sha512-eqLLOEF5/lU8jW3Bw+8auf4lZSbbljHR2saKnYqON12G/WsJrGeeDHWuQePoEf9ro22+JkbPfWQwKEC5WwLQ3w==", "dev": true }, "@typescript-eslint/typescript-estree": { - "version": "6.3.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/typescript-estree/-/typescript-estree-6.3.0.tgz", - "integrity": "sha512-Xh4NVDaC4eYKY4O3QGPuQNp5NxBAlEvNQYOqJquR2MePNxO11E5K3t5x4M4Mx53IZvtpW+mBxIT0s274fLUocg==", + "version": "6.5.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/typescript-estree/-/typescript-estree-6.5.0.tgz", + "integrity": "sha512-q0rGwSe9e5Kk/XzliB9h2LBc9tmXX25G0833r7kffbl5437FPWb2tbpIV9wAATebC/018pGa9fwPDuvGN+LxWQ==", "dev": true, "requires": { - "@typescript-eslint/types": "6.3.0", - "@typescript-eslint/visitor-keys": "6.3.0", + "@typescript-eslint/types": "6.5.0", + "@typescript-eslint/visitor-keys": "6.5.0", "debug": "^4.3.4", "globby": "^11.1.0", "is-glob": "^4.0.3", @@ -6961,61 +6678,20 @@ } }, "@typescript-eslint/utils": { - "version": "6.4.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/utils/-/utils-6.4.0.tgz", - "integrity": "sha512-BvvwryBQpECPGo8PwF/y/q+yacg8Hn/2XS+DqL/oRsOPK+RPt29h5Ui5dqOKHDlbXrAeHUTnyG3wZA0KTDxRZw==", + "version": "6.5.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/utils/-/utils-6.5.0.tgz", + "integrity": "sha512-9nqtjkNykFzeVtt9Pj6lyR9WEdd8npPhhIPM992FWVkZuS6tmxHfGVnlUcjpUP2hv8r4w35nT33mlxd+Be1ACQ==", "dev": true, "requires": { "@eslint-community/eslint-utils": "^4.4.0", "@types/json-schema": "^7.0.12", "@types/semver": "^7.5.0", - "@typescript-eslint/scope-manager": "6.4.0", - "@typescript-eslint/types": "6.4.0", - "@typescript-eslint/typescript-estree": "6.4.0", + "@typescript-eslint/scope-manager": "6.5.0", + "@typescript-eslint/types": "6.5.0", + "@typescript-eslint/typescript-estree": "6.5.0", "semver": "^7.5.4" }, "dependencies": { - "@typescript-eslint/scope-manager": { - "version": "6.4.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/scope-manager/-/scope-manager-6.4.0.tgz", - "integrity": "sha512-TUS7vaKkPWDVvl7GDNHFQMsMruD+zhkd3SdVW0d7b+7Zo+bd/hXJQ8nsiUZMi1jloWo6c9qt3B7Sqo+flC1nig==", - "dev": true, - "requires": { - "@typescript-eslint/types": "6.4.0", - "@typescript-eslint/visitor-keys": "6.4.0" - } - }, - "@typescript-eslint/types": { - "version": "6.4.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/types/-/types-6.4.0.tgz", - "integrity": "sha512-+FV9kVFrS7w78YtzkIsNSoYsnOtrYVnKWSTVXoL1761CsCRv5wpDOINgsXpxD67YCLZtVQekDDyaxfjVWUJmmg==", - "dev": true - }, - "@typescript-eslint/typescript-estree": { - "version": "6.4.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/typescript-estree/-/typescript-estree-6.4.0.tgz", - "integrity": "sha512-iDPJArf/K2sxvjOR6skeUCNgHR/tCQXBsa+ee1/clRKr3olZjZ/dSkXPZjG6YkPtnW6p5D1egeEPMCW6Gn4yLA==", - "dev": true, - "requires": { - "@typescript-eslint/types": "6.4.0", - "@typescript-eslint/visitor-keys": "6.4.0", - "debug": "^4.3.4", - "globby": "^11.1.0", - "is-glob": "^4.0.3", - "semver": "^7.5.4", - "ts-api-utils": "^1.0.1" - } - }, - "@typescript-eslint/visitor-keys": { - "version": "6.4.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/visitor-keys/-/visitor-keys-6.4.0.tgz", - "integrity": "sha512-yJSfyT+uJm+JRDWYRYdCm2i+pmvXJSMtPR9Cq5/XQs4QIgNoLcoRtDdzsLbLsFM/c6um6ohQkg/MLxWvoIndJA==", - "dev": true, - "requires": { - "@typescript-eslint/types": "6.4.0", - "eslint-visitor-keys": "^3.4.1" - } - }, "lru-cache": { "version": "6.0.0", "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-6.0.0.tgz", @@ -7043,12 +6719,12 @@ } }, "@typescript-eslint/visitor-keys": { - "version": "6.3.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/visitor-keys/-/visitor-keys-6.3.0.tgz", - "integrity": "sha512-kEhRRj7HnvaSjux1J9+7dBen15CdWmDnwrpyiHsFX6Qx2iW5LOBUgNefOFeh2PjWPlNwN8TOn6+4eBU3J/gupw==", + "version": "6.5.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/visitor-keys/-/visitor-keys-6.5.0.tgz", + "integrity": "sha512-yCB/2wkbv3hPsh02ZS8dFQnij9VVQXJMN/gbQsaaY+zxALkZnxa/wagvLEFsAWMPv7d7lxQmNsIzGU1w/T/WyA==", "dev": true, "requires": { - "@typescript-eslint/types": "6.3.0", + "@typescript-eslint/types": "6.5.0", "eslint-visitor-keys": "^3.4.1" } }, @@ -7132,15 +6808,15 @@ "dev": true }, "babel-jest": { - "version": "29.6.2", - "resolved": "https://registry.npmjs.org/babel-jest/-/babel-jest-29.6.2.tgz", - "integrity": "sha512-BYCzImLos6J3BH/+HvUCHG1dTf2MzmAB4jaVxHV+29RZLjR29XuYTmsf2sdDwkrb+FczkGo3kOhE7ga6sI0P4A==", + "version": "29.6.4", + "resolved": "https://registry.npmjs.org/babel-jest/-/babel-jest-29.6.4.tgz", + "integrity": "sha512-meLj23UlSLddj6PC+YTOFRgDAtjnZom8w/ACsrx0gtPtv5cJZk0A5Unk5bV4wixD7XaPCN1fQvpww8czkZURmw==", "dev": true, "requires": { - "@jest/transform": "^29.6.2", + "@jest/transform": "^29.6.4", "@types/babel__core": "^7.1.14", "babel-plugin-istanbul": "^6.1.1", - "babel-preset-jest": "^29.5.0", + "babel-preset-jest": "^29.6.3", "chalk": "^4.0.0", "graceful-fs": "^4.2.9", "slash": "^3.0.0" @@ -7160,9 +6836,9 @@ } }, "babel-plugin-jest-hoist": { - "version": "29.5.0", - "resolved": "https://registry.npmjs.org/babel-plugin-jest-hoist/-/babel-plugin-jest-hoist-29.5.0.tgz", - "integrity": "sha512-zSuuuAlTMT4mzLj2nPnUm6fsE6270vdOfnpbJ+RmruU75UhLFvL0N2NgI7xpeS7NaB6hGqmd5pVpGTDYvi4Q3w==", + "version": "29.6.3", + "resolved": "https://registry.npmjs.org/babel-plugin-jest-hoist/-/babel-plugin-jest-hoist-29.6.3.tgz", + "integrity": "sha512-ESAc/RJvGTFEzRwOTT4+lNDk/GNHMkKbNzsvT0qKRfDyyYTskxB5rnU2njIDYVxXCBHHEI1c0YwHob3WaYujOg==", "dev": true, "requires": { "@babel/template": "^7.3.3", @@ -7192,12 +6868,12 @@ } }, "babel-preset-jest": { - "version": "29.5.0", - "resolved": "https://registry.npmjs.org/babel-preset-jest/-/babel-preset-jest-29.5.0.tgz", - "integrity": "sha512-JOMloxOqdiBSxMAzjRaH023/vvcaSaec49zvg+2LmNsktC7ei39LTJGw02J+9uUtTZUq6xbLyJ4dxe9sSmIuAg==", + "version": "29.6.3", + "resolved": "https://registry.npmjs.org/babel-preset-jest/-/babel-preset-jest-29.6.3.tgz", + "integrity": "sha512-0B3bhxR6snWXJZtR/RliHTDPRgn1sNHOR0yVtq/IiQFyuOVjFS+wuio/R4gSNkyYmKmJB4wGZv2NZanmKmTnNA==", "dev": true, "requires": { - "babel-plugin-jest-hoist": "^29.5.0", + "babel-plugin-jest-hoist": "^29.6.3", "babel-preset-current-node-syntax": "^1.0.0" } }, @@ -7413,9 +7089,9 @@ "dev": true }, "diff-sequences": { - "version": "29.4.3", - "resolved": "https://registry.npmjs.org/diff-sequences/-/diff-sequences-29.4.3.tgz", - "integrity": "sha512-ofrBgwpPhCD85kMKtE9RYFFq6OC1A89oW2vvgWZNCwxrUpRUILopY7lsYyMDSjc8g6U6aiO0Qubg6r4Wgt5ZnA==", + "version": "29.6.3", + "resolved": "https://registry.npmjs.org/diff-sequences/-/diff-sequences-29.6.3.tgz", + "integrity": "sha512-EjePK1srD3P08o2j4f0ExnylqRs5B9tJjcp9t1krH2qRi8CCdsYfwe9JgSLurFBWwq4uOlipzfk5fHNvwFKr8Q==", "dev": true }, "dir-glob": { @@ -7476,15 +7152,15 @@ "dev": true }, "eslint": { - "version": "8.47.0", - "resolved": "https://registry.npmjs.org/eslint/-/eslint-8.47.0.tgz", - "integrity": "sha512-spUQWrdPt+pRVP1TTJLmfRNJJHHZryFmptzcafwSvHsceV81djHOdnEeDmkdotZyLNjDhrOasNK8nikkoG1O8Q==", + "version": "8.48.0", + "resolved": "https://registry.npmjs.org/eslint/-/eslint-8.48.0.tgz", + "integrity": "sha512-sb6DLeIuRXxeM1YljSe1KEx9/YYeZFQWcV8Rq9HfigmdDEugjLEVEa1ozDjL6YDjBpQHPJxJzze+alxi4T3OLg==", "dev": true, "requires": { "@eslint-community/eslint-utils": "^4.2.0", "@eslint-community/regexpp": "^4.6.1", "@eslint/eslintrc": "^2.1.2", - "@eslint/js": "^8.47.0", + "@eslint/js": "8.48.0", "@humanwhocodes/config-array": "^0.11.10", "@humanwhocodes/module-importer": "^1.0.1", "@nodelib/fs.walk": "^1.2.8", @@ -7632,17 +7308,16 @@ "dev": true }, "expect": { - "version": "29.6.2", - "resolved": "https://registry.npmjs.org/expect/-/expect-29.6.2.tgz", - "integrity": "sha512-iAErsLxJ8C+S02QbLAwgSGSezLQK+XXRDt8IuFXFpwCNw2ECmzZSmjKcCaFVp5VRMk+WAvz6h6jokzEzBFZEuA==", + "version": "29.6.4", + "resolved": "https://registry.npmjs.org/expect/-/expect-29.6.4.tgz", + "integrity": "sha512-F2W2UyQ8XYyftHT57dtfg8Ue3X5qLgm2sSug0ivvLRH/VKNRL/pDxg/TH7zVzbQB0tu80clNFy6LU7OS/VSEKA==", "dev": true, "requires": { - "@jest/expect-utils": "^29.6.2", - "@types/node": "*", - "jest-get-type": "^29.4.3", - "jest-matcher-utils": "^29.6.2", - "jest-message-util": "^29.6.2", - "jest-util": "^29.6.2" + "@jest/expect-utils": "^29.6.4", + "jest-get-type": "^29.6.3", + "jest-matcher-utils": "^29.6.4", + "jest-message-util": "^29.6.3", + "jest-util": "^29.6.3" } }, "fast-deep-equal": { @@ -7756,9 +7431,9 @@ "dev": true }, "fsevents": { - "version": "2.3.2", - "resolved": "https://registry.npmjs.org/fsevents/-/fsevents-2.3.2.tgz", - "integrity": "sha512-xiqMQR4xAeHTuB9uWm+fFRcIOgKBMiOBP+eXiyT7jsgVCq1bkVygt00oASowB7EdtpOHaaPgKt812P9ab+DDKA==", + "version": "2.3.3", + "resolved": "https://registry.npmjs.org/fsevents/-/fsevents-2.3.3.tgz", + "integrity": "sha512-5xoDfX+fL7faATnagmWPpbFtwh/R77WmMMqqHGS65C3vvB0YHrgF+B1YmZ3441tMj5n63k0212XNoJwzlhffQw==", "dev": true, "optional": true }, @@ -8054,229 +7729,228 @@ } }, "jest": { - "version": "29.6.2", - "resolved": "https://registry.npmjs.org/jest/-/jest-29.6.2.tgz", - "integrity": "sha512-8eQg2mqFbaP7CwfsTpCxQ+sHzw1WuNWL5UUvjnWP4hx2riGz9fPSzYOaU5q8/GqWn1TfgZIVTqYJygbGbWAANg==", + "version": "29.6.4", + "resolved": "https://registry.npmjs.org/jest/-/jest-29.6.4.tgz", + "integrity": "sha512-tEFhVQFF/bzoYV1YuGyzLPZ6vlPrdfvDmmAxudA1dLEuiztqg2Rkx20vkKY32xiDROcD2KXlgZ7Cu8RPeEHRKw==", "dev": true, "requires": { - "@jest/core": "^29.6.2", - "@jest/types": "^29.6.1", + "@jest/core": "^29.6.4", + "@jest/types": "^29.6.3", "import-local": "^3.0.2", - "jest-cli": "^29.6.2" + "jest-cli": "^29.6.4" } }, "jest-changed-files": { - "version": "29.5.0", - "resolved": "https://registry.npmjs.org/jest-changed-files/-/jest-changed-files-29.5.0.tgz", - "integrity": "sha512-IFG34IUMUaNBIxjQXF/iu7g6EcdMrGRRxaUSw92I/2g2YC6vCdTltl4nHvt7Ci5nSJwXIkCu8Ka1DKF+X7Z1Ag==", + "version": "29.6.3", + "resolved": "https://registry.npmjs.org/jest-changed-files/-/jest-changed-files-29.6.3.tgz", + "integrity": "sha512-G5wDnElqLa4/c66ma5PG9eRjE342lIbF6SUnTJi26C3J28Fv2TVY2rOyKB9YGbSA5ogwevgmxc4j4aVjrEK6Yg==", "dev": true, "requires": { "execa": "^5.0.0", + "jest-util": "^29.6.3", "p-limit": "^3.1.0" } }, "jest-circus": { - "version": "29.6.2", - "resolved": "https://registry.npmjs.org/jest-circus/-/jest-circus-29.6.2.tgz", - "integrity": "sha512-G9mN+KOYIUe2sB9kpJkO9Bk18J4dTDArNFPwoZ7WKHKel55eKIS/u2bLthxgojwlf9NLCVQfgzM/WsOVvoC6Fw==", + "version": "29.6.4", + "resolved": "https://registry.npmjs.org/jest-circus/-/jest-circus-29.6.4.tgz", + "integrity": "sha512-YXNrRyntVUgDfZbjXWBMPslX1mQ8MrSG0oM/Y06j9EYubODIyHWP8hMUbjbZ19M3M+zamqEur7O80HODwACoJw==", "dev": true, "requires": { - "@jest/environment": "^29.6.2", - "@jest/expect": "^29.6.2", - "@jest/test-result": "^29.6.2", - "@jest/types": "^29.6.1", + "@jest/environment": "^29.6.4", + "@jest/expect": "^29.6.4", + "@jest/test-result": "^29.6.4", + "@jest/types": "^29.6.3", "@types/node": "*", "chalk": "^4.0.0", "co": "^4.6.0", "dedent": "^1.0.0", "is-generator-fn": "^2.0.0", - "jest-each": "^29.6.2", - "jest-matcher-utils": "^29.6.2", - "jest-message-util": "^29.6.2", - "jest-runtime": "^29.6.2", - "jest-snapshot": "^29.6.2", - "jest-util": "^29.6.2", + "jest-each": "^29.6.3", + "jest-matcher-utils": "^29.6.4", + "jest-message-util": "^29.6.3", + "jest-runtime": "^29.6.4", + "jest-snapshot": "^29.6.4", + "jest-util": "^29.6.3", "p-limit": "^3.1.0", - "pretty-format": "^29.6.2", + "pretty-format": "^29.6.3", "pure-rand": "^6.0.0", "slash": "^3.0.0", "stack-utils": "^2.0.3" } }, "jest-cli": { - "version": "29.6.2", - "resolved": "https://registry.npmjs.org/jest-cli/-/jest-cli-29.6.2.tgz", - "integrity": "sha512-TT6O247v6dCEX2UGHGyflMpxhnrL0DNqP2fRTKYm3nJJpCTfXX3GCMQPGFjXDoj0i5/Blp3jriKXFgdfmbYB6Q==", + "version": "29.6.4", + "resolved": "https://registry.npmjs.org/jest-cli/-/jest-cli-29.6.4.tgz", + "integrity": "sha512-+uMCQ7oizMmh8ZwRfZzKIEszFY9ksjjEQnTEMTaL7fYiL3Kw4XhqT9bYh+A4DQKUb67hZn2KbtEnDuHvcgK4pQ==", "dev": true, "requires": { - "@jest/core": "^29.6.2", - "@jest/test-result": "^29.6.2", - "@jest/types": "^29.6.1", + "@jest/core": "^29.6.4", + "@jest/test-result": "^29.6.4", + "@jest/types": "^29.6.3", "chalk": "^4.0.0", "exit": "^0.1.2", "graceful-fs": "^4.2.9", "import-local": "^3.0.2", - "jest-config": "^29.6.2", - "jest-util": "^29.6.2", - "jest-validate": "^29.6.2", + "jest-config": "^29.6.4", + "jest-util": "^29.6.3", + "jest-validate": "^29.6.3", "prompts": "^2.0.1", "yargs": "^17.3.1" - }, - "dependencies": { - "jest-config": { - "version": "29.6.2", - "resolved": "https://registry.npmjs.org/jest-config/-/jest-config-29.6.2.tgz", - "integrity": "sha512-VxwFOC8gkiJbuodG9CPtMRjBUNZEHxwfQXmIudSTzFWxaci3Qub1ddTRbFNQlD/zUeaifLndh/eDccFX4wCMQw==", - "dev": true, - "requires": { - "@babel/core": "^7.11.6", - "@jest/test-sequencer": "^29.6.2", - "@jest/types": "^29.6.1", - "babel-jest": "^29.6.2", - "chalk": "^4.0.0", - "ci-info": "^3.2.0", - "deepmerge": "^4.2.2", - "glob": "^7.1.3", - "graceful-fs": "^4.2.9", - "jest-circus": "^29.6.2", - "jest-environment-node": "^29.6.2", - "jest-get-type": "^29.4.3", - "jest-regex-util": "^29.4.3", - "jest-resolve": "^29.6.2", - "jest-runner": "^29.6.2", - "jest-util": "^29.6.2", - "jest-validate": "^29.6.2", - "micromatch": "^4.0.4", - "parse-json": "^5.2.0", - "pretty-format": "^29.6.2", - "slash": "^3.0.0", - "strip-json-comments": "^3.1.1" - } - } + } + }, + "jest-config": { + "version": "29.6.4", + "resolved": "https://registry.npmjs.org/jest-config/-/jest-config-29.6.4.tgz", + "integrity": "sha512-JWohr3i9m2cVpBumQFv2akMEnFEPVOh+9L2xIBJhJ0zOaci2ZXuKJj0tgMKQCBZAKA09H049IR4HVS/43Qb19A==", + "dev": true, + "requires": { + "@babel/core": "^7.11.6", + "@jest/test-sequencer": "^29.6.4", + "@jest/types": "^29.6.3", + "babel-jest": "^29.6.4", + "chalk": "^4.0.0", + "ci-info": "^3.2.0", + "deepmerge": "^4.2.2", + "glob": "^7.1.3", + "graceful-fs": "^4.2.9", + "jest-circus": "^29.6.4", + "jest-environment-node": "^29.6.4", + "jest-get-type": "^29.6.3", + "jest-regex-util": "^29.6.3", + "jest-resolve": "^29.6.4", + "jest-runner": "^29.6.4", + "jest-util": "^29.6.3", + "jest-validate": "^29.6.3", + "micromatch": "^4.0.4", + "parse-json": "^5.2.0", + "pretty-format": "^29.6.3", + "slash": "^3.0.0", + "strip-json-comments": "^3.1.1" } }, "jest-diff": { - "version": "29.6.2", - "resolved": "https://registry.npmjs.org/jest-diff/-/jest-diff-29.6.2.tgz", - "integrity": "sha512-t+ST7CB9GX5F2xKwhwCf0TAR17uNDiaPTZnVymP9lw0lssa9vG+AFyDZoeIHStU3WowFFwT+ky+er0WVl2yGhA==", + "version": "29.6.4", + "resolved": "https://registry.npmjs.org/jest-diff/-/jest-diff-29.6.4.tgz", + "integrity": "sha512-9F48UxR9e4XOEZvoUXEHSWY4qC4zERJaOfrbBg9JpbJOO43R1vN76REt/aMGZoY6GD5g84nnJiBIVlscegefpw==", "dev": true, "requires": { "chalk": "^4.0.0", - "diff-sequences": "^29.4.3", - "jest-get-type": "^29.4.3", - "pretty-format": "^29.6.2" + "diff-sequences": "^29.6.3", + "jest-get-type": "^29.6.3", + "pretty-format": "^29.6.3" } }, "jest-docblock": { - "version": "29.4.3", - "resolved": "https://registry.npmjs.org/jest-docblock/-/jest-docblock-29.4.3.tgz", - "integrity": "sha512-fzdTftThczeSD9nZ3fzA/4KkHtnmllawWrXO69vtI+L9WjEIuXWs4AmyME7lN5hU7dB0sHhuPfcKofRsUb/2Fg==", + "version": "29.6.3", + "resolved": "https://registry.npmjs.org/jest-docblock/-/jest-docblock-29.6.3.tgz", + "integrity": "sha512-2+H+GOTQBEm2+qFSQ7Ma+BvyV+waiIFxmZF5LdpBsAEjWX8QYjSCa4FrkIYtbfXUJJJnFCYrOtt6TZ+IAiTjBQ==", "dev": true, "requires": { "detect-newline": "^3.0.0" } }, "jest-each": { - "version": "29.6.2", - "resolved": "https://registry.npmjs.org/jest-each/-/jest-each-29.6.2.tgz", - "integrity": "sha512-MsrsqA0Ia99cIpABBc3izS1ZYoYfhIy0NNWqPSE0YXbQjwchyt6B1HD2khzyPe1WiJA7hbxXy77ZoUQxn8UlSw==", + "version": "29.6.3", + "resolved": "https://registry.npmjs.org/jest-each/-/jest-each-29.6.3.tgz", + "integrity": "sha512-KoXfJ42k8cqbkfshW7sSHcdfnv5agDdHCPA87ZBdmHP+zJstTJc0ttQaJ/x7zK6noAL76hOuTIJ6ZkQRS5dcyg==", "dev": true, "requires": { - "@jest/types": "^29.6.1", + "@jest/types": "^29.6.3", "chalk": "^4.0.0", - "jest-get-type": "^29.4.3", - "jest-util": "^29.6.2", - "pretty-format": "^29.6.2" + "jest-get-type": "^29.6.3", + "jest-util": "^29.6.3", + "pretty-format": "^29.6.3" } }, "jest-environment-node": { - "version": "29.6.2", - "resolved": "https://registry.npmjs.org/jest-environment-node/-/jest-environment-node-29.6.2.tgz", - "integrity": "sha512-YGdFeZ3T9a+/612c5mTQIllvWkddPbYcN2v95ZH24oWMbGA4GGS2XdIF92QMhUhvrjjuQWYgUGW2zawOyH63MQ==", + "version": "29.6.4", + "resolved": "https://registry.npmjs.org/jest-environment-node/-/jest-environment-node-29.6.4.tgz", + "integrity": "sha512-i7SbpH2dEIFGNmxGCpSc2w9cA4qVD+wfvg2ZnfQ7XVrKL0NA5uDVBIiGH8SR4F0dKEv/0qI5r+aDomDf04DpEQ==", "dev": true, "requires": { - "@jest/environment": "^29.6.2", - "@jest/fake-timers": "^29.6.2", - "@jest/types": "^29.6.1", + "@jest/environment": "^29.6.4", + "@jest/fake-timers": "^29.6.4", + "@jest/types": "^29.6.3", "@types/node": "*", - "jest-mock": "^29.6.2", - "jest-util": "^29.6.2" + "jest-mock": "^29.6.3", + "jest-util": "^29.6.3" } }, "jest-get-type": { - "version": "29.4.3", - "resolved": "https://registry.npmjs.org/jest-get-type/-/jest-get-type-29.4.3.tgz", - "integrity": "sha512-J5Xez4nRRMjk8emnTpWrlkyb9pfRQQanDrvWHhsR1+VUfbwxi30eVcZFlcdGInRibU4G5LwHXpI7IRHU0CY+gg==", + "version": "29.6.3", + "resolved": "https://registry.npmjs.org/jest-get-type/-/jest-get-type-29.6.3.tgz", + "integrity": "sha512-zrteXnqYxfQh7l5FHyL38jL39di8H8rHoecLH3JNxH3BwOrBsNeabdap5e0I23lD4HHI8W5VFBZqG4Eaq5LNcw==", "dev": true }, "jest-haste-map": { - "version": "29.6.2", - "resolved": "https://registry.npmjs.org/jest-haste-map/-/jest-haste-map-29.6.2.tgz", - "integrity": "sha512-+51XleTDAAysvU8rT6AnS1ZJ+WHVNqhj1k6nTvN2PYP+HjU3kqlaKQ1Lnw3NYW3bm2r8vq82X0Z1nDDHZMzHVA==", + "version": "29.6.4", + "resolved": "https://registry.npmjs.org/jest-haste-map/-/jest-haste-map-29.6.4.tgz", + "integrity": "sha512-12Ad+VNTDHxKf7k+M65sviyynRoZYuL1/GTuhEVb8RYsNSNln71nANRb/faSyWvx0j+gHcivChXHIoMJrGYjog==", "dev": true, "requires": { - "@jest/types": "^29.6.1", + "@jest/types": "^29.6.3", "@types/graceful-fs": "^4.1.3", "@types/node": "*", "anymatch": "^3.0.3", "fb-watchman": "^2.0.0", "fsevents": "^2.3.2", "graceful-fs": "^4.2.9", - "jest-regex-util": "^29.4.3", - "jest-util": "^29.6.2", - "jest-worker": "^29.6.2", + "jest-regex-util": "^29.6.3", + "jest-util": "^29.6.3", + "jest-worker": "^29.6.4", "micromatch": "^4.0.4", "walker": "^1.0.8" } }, "jest-leak-detector": { - "version": "29.6.2", - "resolved": "https://registry.npmjs.org/jest-leak-detector/-/jest-leak-detector-29.6.2.tgz", - "integrity": "sha512-aNqYhfp5uYEO3tdWMb2bfWv6f0b4I0LOxVRpnRLAeque2uqOVVMLh6khnTcE2qJ5wAKop0HcreM1btoysD6bPQ==", + "version": "29.6.3", + "resolved": "https://registry.npmjs.org/jest-leak-detector/-/jest-leak-detector-29.6.3.tgz", + "integrity": "sha512-0kfbESIHXYdhAdpLsW7xdwmYhLf1BRu4AA118/OxFm0Ho1b2RcTmO4oF6aAMaxpxdxnJ3zve2rgwzNBD4Zbm7Q==", "dev": true, "requires": { - "jest-get-type": "^29.4.3", - "pretty-format": "^29.6.2" + "jest-get-type": "^29.6.3", + "pretty-format": "^29.6.3" } }, "jest-matcher-utils": { - "version": "29.6.2", - "resolved": "https://registry.npmjs.org/jest-matcher-utils/-/jest-matcher-utils-29.6.2.tgz", - "integrity": "sha512-4LiAk3hSSobtomeIAzFTe+N8kL6z0JtF3n6I4fg29iIW7tt99R7ZcIFW34QkX+DuVrf+CUe6wuVOpm7ZKFJzZQ==", + "version": "29.6.4", + "resolved": "https://registry.npmjs.org/jest-matcher-utils/-/jest-matcher-utils-29.6.4.tgz", + "integrity": "sha512-KSzwyzGvK4HcfnserYqJHYi7sZVqdREJ9DMPAKVbS98JsIAvumihaNUbjrWw0St7p9IY7A9UskCW5MYlGmBQFQ==", "dev": true, "requires": { "chalk": "^4.0.0", - "jest-diff": "^29.6.2", - "jest-get-type": "^29.4.3", - "pretty-format": "^29.6.2" + "jest-diff": "^29.6.4", + "jest-get-type": "^29.6.3", + "pretty-format": "^29.6.3" } }, "jest-message-util": { - "version": "29.6.2", - "resolved": "https://registry.npmjs.org/jest-message-util/-/jest-message-util-29.6.2.tgz", - "integrity": "sha512-vnIGYEjoPSuRqV8W9t+Wow95SDp6KPX2Uf7EoeG9G99J2OVh7OSwpS4B6J0NfpEIpfkBNHlBZpA2rblEuEFhZQ==", + "version": "29.6.3", + "resolved": "https://registry.npmjs.org/jest-message-util/-/jest-message-util-29.6.3.tgz", + "integrity": "sha512-FtzaEEHzjDpQp51HX4UMkPZjy46ati4T5pEMyM6Ik48ztu4T9LQplZ6OsimHx7EuM9dfEh5HJa6D3trEftu3dA==", "dev": true, "requires": { "@babel/code-frame": "^7.12.13", - "@jest/types": "^29.6.1", + "@jest/types": "^29.6.3", "@types/stack-utils": "^2.0.0", "chalk": "^4.0.0", "graceful-fs": "^4.2.9", "micromatch": "^4.0.4", - "pretty-format": "^29.6.2", + "pretty-format": "^29.6.3", "slash": "^3.0.0", "stack-utils": "^2.0.3" } }, "jest-mock": { - "version": "29.6.2", - "resolved": "https://registry.npmjs.org/jest-mock/-/jest-mock-29.6.2.tgz", - "integrity": "sha512-hoSv3lb3byzdKfwqCuT6uTscan471GUECqgNYykg6ob0yiAw3zYc7OrPnI9Qv8Wwoa4lC7AZ9hyS4AiIx5U2zg==", + "version": "29.6.3", + "resolved": "https://registry.npmjs.org/jest-mock/-/jest-mock-29.6.3.tgz", + "integrity": "sha512-Z7Gs/mOyTSR4yPsaZ72a/MtuK6RnC3JYqWONe48oLaoEcYwEDxqvbXz85G4SJrm2Z5Ar9zp6MiHF4AlFlRM4Pg==", "dev": true, "requires": { - "@jest/types": "^29.6.1", + "@jest/types": "^29.6.3", "@types/node": "*", - "jest-util": "^29.6.2" + "jest-util": "^29.6.3" } }, "jest-pnp-resolver": { @@ -8287,101 +7961,101 @@ "requires": {} }, "jest-regex-util": { - "version": "29.4.3", - "resolved": "https://registry.npmjs.org/jest-regex-util/-/jest-regex-util-29.4.3.tgz", - "integrity": "sha512-O4FglZaMmWXbGHSQInfXewIsd1LMn9p3ZXB/6r4FOkyhX2/iP/soMG98jGvk/A3HAN78+5VWcBGO0BJAPRh4kg==", + "version": "29.6.3", + "resolved": "https://registry.npmjs.org/jest-regex-util/-/jest-regex-util-29.6.3.tgz", + "integrity": "sha512-KJJBsRCyyLNWCNBOvZyRDnAIfUiRJ8v+hOBQYGn8gDyF3UegwiP4gwRR3/SDa42g1YbVycTidUF3rKjyLFDWbg==", "dev": true }, "jest-resolve": { - "version": "29.6.2", - "resolved": "https://registry.npmjs.org/jest-resolve/-/jest-resolve-29.6.2.tgz", - "integrity": "sha512-G/iQUvZWI5e3SMFssc4ug4dH0aZiZpsDq9o1PtXTV1210Ztyb2+w+ZgQkB3iOiC5SmAEzJBOHWz6Hvrd+QnNPw==", + "version": "29.6.4", + "resolved": "https://registry.npmjs.org/jest-resolve/-/jest-resolve-29.6.4.tgz", + "integrity": "sha512-fPRq+0vcxsuGlG0O3gyoqGTAxasagOxEuyoxHeyxaZbc9QNek0AmJWSkhjlMG+mTsj+8knc/mWb3fXlRNVih7Q==", "dev": true, "requires": { "chalk": "^4.0.0", "graceful-fs": "^4.2.9", - "jest-haste-map": "^29.6.2", + "jest-haste-map": "^29.6.4", "jest-pnp-resolver": "^1.2.2", - "jest-util": "^29.6.2", - "jest-validate": "^29.6.2", + "jest-util": "^29.6.3", + "jest-validate": "^29.6.3", "resolve": "^1.20.0", "resolve.exports": "^2.0.0", "slash": "^3.0.0" } }, "jest-resolve-dependencies": { - "version": "29.6.2", - "resolved": "https://registry.npmjs.org/jest-resolve-dependencies/-/jest-resolve-dependencies-29.6.2.tgz", - "integrity": "sha512-LGqjDWxg2fuQQm7ypDxduLu/m4+4Lb4gczc13v51VMZbVP5tSBILqVx8qfWcsdP8f0G7aIqByIALDB0R93yL+w==", + "version": "29.6.4", + "resolved": "https://registry.npmjs.org/jest-resolve-dependencies/-/jest-resolve-dependencies-29.6.4.tgz", + "integrity": "sha512-7+6eAmr1ZBF3vOAJVsfLj1QdqeXG+WYhidfLHBRZqGN24MFRIiKG20ItpLw2qRAsW/D2ZUUmCNf6irUr/v6KHA==", "dev": true, "requires": { - "jest-regex-util": "^29.4.3", - "jest-snapshot": "^29.6.2" + "jest-regex-util": "^29.6.3", + "jest-snapshot": "^29.6.4" } }, "jest-runner": { - "version": "29.6.2", - "resolved": "https://registry.npmjs.org/jest-runner/-/jest-runner-29.6.2.tgz", - "integrity": "sha512-wXOT/a0EspYgfMiYHxwGLPCZfC0c38MivAlb2lMEAlwHINKemrttu1uSbcGbfDV31sFaPWnWJPmb2qXM8pqZ4w==", + "version": "29.6.4", + "resolved": "https://registry.npmjs.org/jest-runner/-/jest-runner-29.6.4.tgz", + "integrity": "sha512-SDaLrMmtVlQYDuG0iSPYLycG8P9jLI+fRm8AF/xPKhYDB2g6xDWjXBrR5M8gEWsK6KVFlebpZ4QsrxdyIX1Jaw==", "dev": true, "requires": { - "@jest/console": "^29.6.2", - "@jest/environment": "^29.6.2", - "@jest/test-result": "^29.6.2", - "@jest/transform": "^29.6.2", - "@jest/types": "^29.6.1", + "@jest/console": "^29.6.4", + "@jest/environment": "^29.6.4", + "@jest/test-result": "^29.6.4", + "@jest/transform": "^29.6.4", + "@jest/types": "^29.6.3", "@types/node": "*", "chalk": "^4.0.0", "emittery": "^0.13.1", "graceful-fs": "^4.2.9", - "jest-docblock": "^29.4.3", - "jest-environment-node": "^29.6.2", - "jest-haste-map": "^29.6.2", - "jest-leak-detector": "^29.6.2", - "jest-message-util": "^29.6.2", - "jest-resolve": "^29.6.2", - "jest-runtime": "^29.6.2", - "jest-util": "^29.6.2", - "jest-watcher": "^29.6.2", - "jest-worker": "^29.6.2", + "jest-docblock": "^29.6.3", + "jest-environment-node": "^29.6.4", + "jest-haste-map": "^29.6.4", + "jest-leak-detector": "^29.6.3", + "jest-message-util": "^29.6.3", + "jest-resolve": "^29.6.4", + "jest-runtime": "^29.6.4", + "jest-util": "^29.6.3", + "jest-watcher": "^29.6.4", + "jest-worker": "^29.6.4", "p-limit": "^3.1.0", "source-map-support": "0.5.13" } }, "jest-runtime": { - "version": "29.6.2", - "resolved": "https://registry.npmjs.org/jest-runtime/-/jest-runtime-29.6.2.tgz", - "integrity": "sha512-2X9dqK768KufGJyIeLmIzToDmsN0m7Iek8QNxRSI/2+iPFYHF0jTwlO3ftn7gdKd98G/VQw9XJCk77rbTGZnJg==", - "dev": true, - "requires": { - "@jest/environment": "^29.6.2", - "@jest/fake-timers": "^29.6.2", - "@jest/globals": "^29.6.2", - "@jest/source-map": "^29.6.0", - "@jest/test-result": "^29.6.2", - "@jest/transform": "^29.6.2", - "@jest/types": "^29.6.1", + "version": "29.6.4", + "resolved": "https://registry.npmjs.org/jest-runtime/-/jest-runtime-29.6.4.tgz", + "integrity": "sha512-s/QxMBLvmwLdchKEjcLfwzP7h+jsHvNEtxGP5P+Fl1FMaJX2jMiIqe4rJw4tFprzCwuSvVUo9bn0uj4gNRXsbA==", + "dev": true, + "requires": { + "@jest/environment": "^29.6.4", + "@jest/fake-timers": "^29.6.4", + "@jest/globals": "^29.6.4", + "@jest/source-map": "^29.6.3", + "@jest/test-result": "^29.6.4", + "@jest/transform": "^29.6.4", + "@jest/types": "^29.6.3", "@types/node": "*", "chalk": "^4.0.0", "cjs-module-lexer": "^1.0.0", "collect-v8-coverage": "^1.0.0", "glob": "^7.1.3", "graceful-fs": "^4.2.9", - "jest-haste-map": "^29.6.2", - "jest-message-util": "^29.6.2", - "jest-mock": "^29.6.2", - "jest-regex-util": "^29.4.3", - "jest-resolve": "^29.6.2", - "jest-snapshot": "^29.6.2", - "jest-util": "^29.6.2", + "jest-haste-map": "^29.6.4", + "jest-message-util": "^29.6.3", + "jest-mock": "^29.6.3", + "jest-regex-util": "^29.6.3", + "jest-resolve": "^29.6.4", + "jest-snapshot": "^29.6.4", + "jest-util": "^29.6.3", "slash": "^3.0.0", "strip-bom": "^4.0.0" } }, "jest-snapshot": { - "version": "29.6.2", - "resolved": "https://registry.npmjs.org/jest-snapshot/-/jest-snapshot-29.6.2.tgz", - "integrity": "sha512-1OdjqvqmRdGNvWXr/YZHuyhh5DeaLp1p/F8Tht/MrMw4Kr1Uu/j4lRG+iKl1DAqUJDWxtQBMk41Lnf/JETYBRA==", + "version": "29.6.4", + "resolved": "https://registry.npmjs.org/jest-snapshot/-/jest-snapshot-29.6.4.tgz", + "integrity": "sha512-VC1N8ED7+4uboUKGIDsbvNAZb6LakgIPgAF4RSpF13dN6YaMokfRqO+BaqK4zIh6X3JffgwbzuGqDEjHm/MrvA==", "dev": true, "requires": { "@babel/core": "^7.11.6", @@ -8389,20 +8063,20 @@ "@babel/plugin-syntax-jsx": "^7.7.2", "@babel/plugin-syntax-typescript": "^7.7.2", "@babel/types": "^7.3.3", - "@jest/expect-utils": "^29.6.2", - "@jest/transform": "^29.6.2", - "@jest/types": "^29.6.1", + "@jest/expect-utils": "^29.6.4", + "@jest/transform": "^29.6.4", + "@jest/types": "^29.6.3", "babel-preset-current-node-syntax": "^1.0.0", "chalk": "^4.0.0", - "expect": "^29.6.2", + "expect": "^29.6.4", "graceful-fs": "^4.2.9", - "jest-diff": "^29.6.2", - "jest-get-type": "^29.4.3", - "jest-matcher-utils": "^29.6.2", - "jest-message-util": "^29.6.2", - "jest-util": "^29.6.2", + "jest-diff": "^29.6.4", + "jest-get-type": "^29.6.3", + "jest-matcher-utils": "^29.6.4", + "jest-message-util": "^29.6.3", + "jest-util": "^29.6.3", "natural-compare": "^1.4.0", - "pretty-format": "^29.6.2", + "pretty-format": "^29.6.3", "semver": "^7.5.3" }, "dependencies": { @@ -8433,12 +8107,12 @@ } }, "jest-util": { - "version": "29.6.2", - "resolved": "https://registry.npmjs.org/jest-util/-/jest-util-29.6.2.tgz", - "integrity": "sha512-3eX1qb6L88lJNCFlEADKOkjpXJQyZRiavX1INZ4tRnrBVr2COd3RgcTLyUiEXMNBlDU/cgYq6taUS0fExrWW4w==", + "version": "29.6.3", + "resolved": "https://registry.npmjs.org/jest-util/-/jest-util-29.6.3.tgz", + "integrity": "sha512-QUjna/xSy4B32fzcKTSz1w7YYzgiHrjjJjevdRf61HYk998R5vVMMNmrHESYZVDS5DSWs+1srPLPKxXPkeSDOA==", "dev": true, "requires": { - "@jest/types": "^29.6.1", + "@jest/types": "^29.6.3", "@types/node": "*", "chalk": "^4.0.0", "ci-info": "^3.2.0", @@ -8447,17 +8121,17 @@ } }, "jest-validate": { - "version": "29.6.2", - "resolved": "https://registry.npmjs.org/jest-validate/-/jest-validate-29.6.2.tgz", - "integrity": "sha512-vGz0yMN5fUFRRbpJDPwxMpgSXW1LDKROHfBopAvDcmD6s+B/s8WJrwi+4bfH4SdInBA5C3P3BI19dBtKzx1Arg==", + "version": "29.6.3", + "resolved": "https://registry.npmjs.org/jest-validate/-/jest-validate-29.6.3.tgz", + "integrity": "sha512-e7KWZcAIX+2W1o3cHfnqpGajdCs1jSM3DkXjGeLSNmCazv1EeI1ggTeK5wdZhF+7N+g44JI2Od3veojoaumlfg==", "dev": true, "requires": { - "@jest/types": "^29.6.1", + "@jest/types": "^29.6.3", "camelcase": "^6.2.0", "chalk": "^4.0.0", - "jest-get-type": "^29.4.3", + "jest-get-type": "^29.6.3", "leven": "^3.1.0", - "pretty-format": "^29.6.2" + "pretty-format": "^29.6.3" }, "dependencies": { "camelcase": { @@ -8469,29 +8143,29 @@ } }, "jest-watcher": { - "version": "29.6.2", - "resolved": "https://registry.npmjs.org/jest-watcher/-/jest-watcher-29.6.2.tgz", - "integrity": "sha512-GZitlqkMkhkefjfN/p3SJjrDaxPflqxEAv3/ik10OirZqJGYH5rPiIsgVcfof0Tdqg3shQGdEIxDBx+B4tuLzA==", + "version": "29.6.4", + "resolved": "https://registry.npmjs.org/jest-watcher/-/jest-watcher-29.6.4.tgz", + "integrity": "sha512-oqUWvx6+On04ShsT00Ir9T4/FvBeEh2M9PTubgITPxDa739p4hoQweWPRGyYeaojgT0xTpZKF0Y/rSY1UgMxvQ==", "dev": true, "requires": { - "@jest/test-result": "^29.6.2", - "@jest/types": "^29.6.1", + "@jest/test-result": "^29.6.4", + "@jest/types": "^29.6.3", "@types/node": "*", "ansi-escapes": "^4.2.1", "chalk": "^4.0.0", "emittery": "^0.13.1", - "jest-util": "^29.6.2", + "jest-util": "^29.6.3", "string-length": "^4.0.1" } }, "jest-worker": { - "version": "29.6.2", - "resolved": "https://registry.npmjs.org/jest-worker/-/jest-worker-29.6.2.tgz", - "integrity": "sha512-l3ccBOabTdkng8I/ORCkADz4eSMKejTYv1vB/Z83UiubqhC1oQ5Li6dWCyqOIvSifGjUBxuvxvlm6KGK2DtuAQ==", + "version": "29.6.4", + "resolved": "https://registry.npmjs.org/jest-worker/-/jest-worker-29.6.4.tgz", + "integrity": "sha512-6dpvFV4WjcWbDVGgHTWo/aupl8/LbBx2NSKfiwqf79xC/yeJjKHT1+StcKy/2KTmW16hE68ccKVOtXf+WZGz7Q==", "dev": true, "requires": { "@types/node": "*", - "jest-util": "^29.6.2", + "jest-util": "^29.6.3", "merge-stream": "^2.0.0", "supports-color": "^8.0.0" }, @@ -8943,18 +8617,18 @@ "dev": true }, "prettier": { - "version": "3.0.2", - "resolved": "https://registry.npmjs.org/prettier/-/prettier-3.0.2.tgz", - "integrity": "sha512-o2YR9qtniXvwEZlOKbveKfDQVyqxbEIWn48Z8m3ZJjBjcCmUy3xZGIv+7AkaeuaTr6yPXJjwv07ZWlsWbEy1rQ==", + "version": "3.0.3", + "resolved": "https://registry.npmjs.org/prettier/-/prettier-3.0.3.tgz", + "integrity": "sha512-L/4pUDMxcNa8R/EthV08Zt42WBO4h1rarVtK0K+QJG0X187OLo7l699jWw0GKuwzkPQ//jMFA/8Xm6Fh3J/DAg==", "dev": true }, "pretty-format": { - "version": "29.6.2", - "resolved": "https://registry.npmjs.org/pretty-format/-/pretty-format-29.6.2.tgz", - "integrity": "sha512-1q0oC8eRveTg5nnBEWMXAU2qpv65Gnuf2eCQzSjxpWFkPaPARwqZZDGuNE0zPAZfTCHzIk3A8dIjwlQKKLphyg==", + "version": "29.6.3", + "resolved": "https://registry.npmjs.org/pretty-format/-/pretty-format-29.6.3.tgz", + "integrity": "sha512-ZsBgjVhFAj5KeK+nHfF1305/By3lechHQSMWCTl8iHSbfOm2TN5nHEtFc/+W7fAyUeCs2n5iow72gld4gW0xDw==", "dev": true, "requires": { - "@jest/schemas": "^29.6.0", + "@jest/schemas": "^29.6.3", "ansi-styles": "^5.0.0", "react-is": "^18.0.0" }, @@ -9335,9 +9009,9 @@ "dev": true }, "typescript": { - "version": "5.1.6", - "resolved": "https://registry.npmjs.org/typescript/-/typescript-5.1.6.tgz", - "integrity": "sha512-zaWCozRZ6DLEWAWFrVDz1H6FVXzUSfTy5FUMWsQlU8Ym5JP9eO4xkTIROFCQvhQf61z6O/G6ugw3SgAnvvm+HA==", + "version": "5.2.2", + "resolved": "https://registry.npmjs.org/typescript/-/typescript-5.2.2.tgz", + "integrity": "sha512-mI4WrpHsbCIcwT9cF4FZvr80QUeKvsUsUvKDoR+X/7XHQH98xYD8YHZg7ANtz2GtZt/CBq2QJ0thkGJMHfqc1w==", "dev": true }, "universal-user-agent": { diff --git a/action/package.json b/action/package.json index 8836ea83c..29bf4657f 100644 --- a/action/package.json +++ b/action/package.json @@ -39,20 +39,20 @@ "@actions/core": "~1.10.0", "@actions/exec": "~1.1.1", "@actions/github": "~5.1.1", - "@actions/tool-cache": "~2.0.1", - "simple-git": "^3.19.1" + "@actions/tool-cache": "~2.0.1", + "simple-git": "^3.19.1" }, "devDependencies": { - "@types/jest": "^29.5.0", - "@types/node": "^20.5.0", - "@typescript-eslint/eslint-plugin": "^6.4.0", - "@typescript-eslint/parser": "^6.3.0", - "eslint": "^8.47.0", + "@types/jest": "^29.5.4", + "@types/node": "^20.5.9", + "@typescript-eslint/eslint-plugin": "^6.5.0", + "@typescript-eslint/parser": "^6.5.0", + "eslint": "^8.48.0", "eslint-config-prettier": "^9.0.0", "husky": "^8.0.3", - "jest": "^29.6.2", - "prettier": "^3.0.2", + "jest": "^29.6.4", + "prettier": "^3.0.3", "ts-jest": "^29.1.1", - "typescript": "^5.1.6" + "typescript": "^5.2.2" } } diff --git a/docs/install-bitbucket-server.md b/docs/install-bitbucket-server.md deleted file mode 100644 index f729f0a77..000000000 --- a/docs/install-bitbucket-server.md +++ /dev/null @@ -1,248 +0,0 @@ -[Go back to the main documentation page](https://github.com/jfrog/frogbot) - -# Installing Frogbot on Bitbucket Server repositories - -| Important: Using Frogbot on Bitbucket Server using JFrog Pipelines or Jenkins isn't recommended for open-source projects. Read more about it in the [Security note for pull requests scanning](../README.md#-security-note-for-pull-requests-scanning) section. | -| -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | - -
- Install Frogbot Using JFrog Pipelines - - * Make sure you have the connection details of your JFrog environment. - * Save the JFrog connection details as a [JFrog Platform Access Token Integration](https://www.jfrog.com/confluence/display/JFROG/JFrog+Platform+Access+Token+Integration) - named **jfrogPlatform**. - * Save your Bitbucket access token in a [Bitbucket Server Integration](https://www.jfrog.com/confluence/display/JFROG/Bitbucket+Server+Integration) named - **gitIntegration**. - * Create a **pipelines.yml** file using one of the available [templates](templates/jfrog-pipelines) and push the file into one of your Git repositories, under a directory named `.jfrog-pipelines`. - * In the **pipelines.yml**, make sure to set values for all the mandatory variables. - * In the **pipelines.yml**, if you're using a Windows agent, modify the code inside the onExecute sections as described in the template comments. - - **Important** - - Make sure all the build tools that are used to build the project are installed on the build agent. -
-
- Install Frogbot Using Jenkins - - - Make sure you have the connection details of your JFrog environment. - - Save the JFrog connection details as Credentials in Jenkins with the following Credential IDs: **JF_URL**, - **JF_USER** and **JF_PASSWORD** (You can also use **JF_XRAY_URL** and **JF_ARTIFACTORY_URL** instead of **JF_URL** - and **JF_ACCESS_TOKEN** instead of **JF_USER** and **JF_PASSWORD**). - - Save your Bitbucket access token as a Credential in Jenkins with the `FROGBOT_GIT_TOKEN` Credential ID. - - Create a Jenkinsfile with the below template content, and push it to the root of one of your Git repositories. - - In the Jenkinsfile, set the values of all the mandatory variables. - - In the Jenkinsfile, modify the code inside the `Download Frogbot` and `Scan Pull Requests` according to the Jenkins agent operating system. - - Create a Pipeline job in Jenkins pointing to the Jenkinsfile in your Git repository. - -
- Template - - ```groovy - // Run the job once an hour - CRON_SETTINGS = '''* */1 * * *''' - - pipeline { - agent any - - triggers { - cron(CRON_SETTINGS) - } - - environment { - // [Mandatory] - // JFrog platform URL (This functionality requires version 3.29.0 or above of Xray) - JF_URL= credentials("JF_URL") - - // [Mandatory if JF_USER and JF_PASSWORD are not provided] - // JFrog access token with 'read' permissions for Xray - JF_ACCESS_TOKEN= credentials("JF_ACCESS_TOKEN") - - // [Mandatory if JF_ACCESS_TOKEN is not provided] - // JFrog user and password with 'read' permissions for Xray - // JF_USER= credentials("JF_USER") - // JF_PASSWORD= credentials("JF_PASSWORD") - - // [Mandatory] - // Bitbucket access token with the write repository permissions - JF_GIT_TOKEN= credentials("FROGBOT_GIT_TOKEN") - JF_GIT_PROVIDER= "bitbucketServer" - - // [Mandatory] - // Username of the account associated with the token - JF_GIT_USERNAME= "" - - // [Mandatory] - // Bitbucket project namespace - // Private projects should start with the prefix: "~" - JF_GIT_OWNER= "" - - // [Mandatory] - // API endpoint to Bitbucket server - JF_GIT_API_ENDPOINT= "" - - // [Optional] - // By default, the Frogbot workflows download the Frogbot executable as well as other tools - // needed from https://releases.jfrog.io - // If the machine that runs Frogbot has no access to the internet, follow these steps to allow the - // executable to be downloaded from an Artifactory instance, which the machine has access to: - // - // 1. Login to the Artifactory UI, with a user who has admin credentials. - // 2. Create a Remote Repository with the following properties set. - // Under the 'Basic' tab: - // Package Type: Generic - // URL: https://releases.jfrog.io - // Under the 'Advanced' tab: - // Uncheck the 'Store Artifacts Locally' option - // 3. Set the value of the 'JF_RELEASES_REPO' variable with the Repository Key you created. - // JF_RELEASES_REPO= "" - - // [Optional] - // Configure the SMTP server to enable Frogbot to send emails with detected secrets in pull request scans. - // SMTP server URL including should the relevant port: (Example: smtp.server.com:8080) - // JF_SMTP_SERVER= "" - - // [Mandatory if JF_SMTP_SERVER is set] - // The username required for authenticating with the SMTP server. - // JF_SMTP_USER= "" - - // [Mandatory if JF_SMTP_SERVER is set] - // The password associated with the username required for authentication with the SMTP server. - // JF_SMTP_PASSWORD= "" - - /////////////////////////////////////////////////////////////////////////// - // If your project uses a 'frogbot-config.yml' file, you should define // - // the following variables inside the file, instead of here. // - /////////////////////////////////////////////////////////////////////////// - - // [Mandatory] - // The name of the repository - JF_GIT_REPO= "" - - // [Mandatory] - // The name of the branch on which Frogbot will perform the scan - JF_GIT_BASE_BRANCH= "" - - // [Mandatory if the two conditions below are met] - // 1. The project uses yarn 2, NuGet, or .NET to download its dependencies - // 2. The `installCommand` variable isn't set in your frogbot-config.yml file. - // - // The command that installs the project dependencies (e.g "nuget restore") - JF_INSTALL_DEPS_CMD= "" - - // [Optional, default: "."] - // Relative path to the root of the project in the Git repository - // JF_WORKING_DIR= path/to/project/dir - - // [Optional] - // Xray Watches. Learn more about them here: https://www.jfrog.com/confluence/display/JFROG/Configuring+Xray+Watches - // JF_WATCHES= ,... - - // [Optional] - // JFrog project. Learn more about it here: https://www.jfrog.com/confluence/display/JFROG/Projects - // JF_PROJECT= - - // [Optional, default: "FALSE"] - // Displays all existing vulnerabilities, including the ones that were added by the pull request. - // JF_INCLUDE_ALL_VULNERABILITIES= "TRUE" - - // [Optional, default: "TRUE"] - // Fails the Frogbot task if any security issue is found. - // JF_FAIL= "FALSE" - - // [Optional, default: "TRUE"] - // Relative path to a Pip requirements.txt file. If not set, the python project's dependencies are determined and scanned using the project setup.py file. - // JF_REQUIREMENTS_FILE= "" - - // [Optional, Default: "TRUE"] - // Use Gradle wrapper. - // JF_USE_WRAPPER= "FALSE" - - // [Optional] - // Frogbot will download the project dependencies if they're not cached locally. To download the - // dependencies from a virtual repository in Artifactory set the name of the repository. There's no - // need to set this value, if it is set in the frogbot-config.yml file. - // JF_DEPS_REPO= "" - - // [Optional] - // Template for the branch name generated by Frogbot when creating pull requests with fixes. - // The template must include ${BRANCH_NAME_HASH}, to ensure that the generated branch name is unique. - // The template can optionally include the ${IMPACTED_PACKAGE} and ${FIX_VERSION} variables. - // JF_BRANCH_NAME_TEMPLATE= "frogbot-${IMPACTED_PACKAGE}-${BRANCH_NAME_HASH}" - - // [Optional] - // Template for the commit message generated by Frogbot when creating pull requests with fixes - // The template can optionally include the ${IMPACTED_PACKAGE} and ${FIX_VERSION} variables. - // JF_COMMIT_MESSAGE_TEMPLATE= "Upgrade ${IMPACTED_PACKAGE} to ${FIX_VERSION}" - - // [Optional] - // Template for the pull request title generated by Frogbot when creating pull requests with fixes. - // The template can optionally include the ${IMPACTED_PACKAGE} and ${FIX_VERSION} variables. - // JF_PULL_REQUEST_TITLE_TEMPLATE= "[🐸 Frogbot] Upgrade ${IMPACTED_PACKAGE} to ${FIX_VERSION}" - - // [Optional, Default: "FALSE"] - // If TRUE, Frogbot creates a single pull request with all the fixes. - // If FALSE, Frogbot creates a separate pull request for each fix. - // JF_GIT_AGGREGATE_FIXES= "FALSE" - - // [Optional, Default: "FALSE"] - // Handle vulnerabilities with fix versions only - // JF_FIXABLE_ONLY= "TRUE" - - // [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= "" - - // [Optional, Default: eco-system+frogbot@jfrog.com] - // Set the email of the commit author - // JF_GIT_EMAIL_AUTHOR: "" - - // [Optional] - // List of comma separated email addresses to receive email notifications about secrets - // detected during pull request scanning. The notification is also sent to the email set - // in the committer git profile regardless of whether this variable is set or not. - // JF_EMAIL_RECEIVERS: "" - } - - stages { - stage('Download Frogbot') { - steps { - if (env.JF_RELEASES_REPO == "") { - // For Linux / MacOS runner: - sh """ curl -fLg "https://releases.jfrog.io/artifactory/frogbot/v2/[RELEASE]/getFrogbot.sh" | sh""" - // For Windows runner: - // powershell """iwr https://releases.jfrog.io/artifactory/frogbot/v2/[RELEASE]/frogbot-windows-amd64/frogbot.exe -OutFile .\frogbot.exe""" - } else { - // For Linux / MacOS air-gapped environments: - sh """ curl -fLg "${env.JF_URL}/artifactory/${env.JF_RELEASES_REPO}/artifactory/frogbot/v2/[RELEASE]/getFrogbot.sh" | sh""" - // For Windows air-gapped environments: - // powershell """iwr ${env.JF_URL}/artifactory/${env.JF_RELEASES_REPO}/artifactory/frogbot/v2/[RELEASE]/frogbot-windows-amd64/frogbot.exe -OutFile .\frogbot.exe""" - } - } - } - - stage('Scan Pull Requests') { - steps { - sh "./frogbot scan-all-pull-requests" - - // For Windows runner: - // powershell """.\frogbot.exe scan-all-pull-requests""" - } - } - - stage('Scan and Fix Repos') { - steps { - sh "./frogbot scan-multiple-repositories" - - // For Windows runner: - // powershell """.\frogbot.exe scan-multiple-repositories""" - } - } - } - } -
-
- -**Important** -- Make sure that either **JF_USER** and **JF_PASSWORD** or **JF_ACCESS_TOKEN** are set in the Jenkinsfile, but not both. -- Make sure that all the build tools that are used to build the project are installed on the Jenkins agent. - diff --git a/go.mod b/go.mod index aa1db34f7..4521381e2 100644 --- a/go.mod +++ b/go.mod @@ -6,25 +6,26 @@ require ( github.com/go-git/go-git/v5 v5.8.1 github.com/golang/mock v1.6.0 github.com/google/go-github/v45 v45.2.0 - github.com/jfrog/build-info-go v1.9.9 - github.com/jfrog/froggit-go v1.13.4 + github.com/jfrog/build-info-go v1.9.10 + github.com/jfrog/froggit-go v1.14.0 github.com/jfrog/gofrog v1.3.0 - github.com/jfrog/jfrog-cli-core/v2 v2.41.4 - github.com/jfrog/jfrog-client-go v1.31.6 + github.com/jfrog/jfrog-cli-core/v2 v2.41.6 + github.com/jfrog/jfrog-client-go v1.32.1 github.com/jordan-wright/email v4.0.1-0.20210109023952-943e75fe5223+incompatible + github.com/owenrumney/go-sarif/v2 v2.2.0 github.com/stretchr/testify v1.8.4 github.com/urfave/cli/v2 v2.25.7 github.com/xeipuuv/gojsonschema v1.2.0 - golang.org/x/exp v0.0.0-20230817173708-d852ddb80c63 + golang.org/x/exp v0.0.0-20230905200255-921286631fa9 gopkg.in/yaml.v3 v3.0.1 ) require ( dario.cat/mergo v1.0.0 // indirect github.com/BurntSushi/toml v1.3.2 // indirect - github.com/CycloneDX/cyclonedx-go v0.7.1 // 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-20230717121422-5aa5874ade95 // indirect + github.com/ProtonMail/go-crypto v0.0.0-20230828082145-3c4c8a2d2371 // 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 @@ -36,6 +37,7 @@ require ( github.com/cpuguy83/go-md2man/v2 v2.0.2 // indirect github.com/davecgh/go-spew v1.1.1 // indirect github.com/dsnet/compress v0.0.2-0.20210315054119-f66993602bf5 // indirect + github.com/elazarl/goproxy v0.0.0-20230731152917-f99041a5c027 // indirect github.com/emirpasic/gods v1.18.1 // indirect github.com/forPelevin/gomoji v1.1.8 // indirect github.com/fsnotify/fsnotify v1.6.0 // indirect @@ -48,14 +50,14 @@ require ( github.com/golang/protobuf v1.5.3 // indirect github.com/golang/snappy v0.0.4 // indirect github.com/google/go-querystring v1.1.0 // indirect - github.com/google/uuid v1.3.0 // indirect + github.com/google/uuid v1.3.1 // indirect github.com/gookit/color v1.5.4 // indirect github.com/grokify/mogo v0.50.0 // indirect github.com/hashicorp/go-cleanhttp v0.5.2 // indirect github.com/hashicorp/go-retryablehttp v0.7.2 // 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.6 // indirect + github.com/jedib0t/go-pretty/v6 v6.4.7 // indirect github.com/kevinburke/ssh_config v1.2.0 // indirect github.com/klauspost/compress v1.15.9 // indirect github.com/klauspost/cpuid/v2 v2.2.3 // indirect @@ -72,7 +74,6 @@ require ( github.com/minio/sha256-simd v1.0.1 // indirect github.com/mitchellh/mapstructure v1.5.0 // indirect github.com/nwaples/rardecode v1.1.0 // indirect - github.com/owenrumney/go-sarif/v2 v2.2.0 // indirect github.com/pelletier/go-toml/v2 v2.0.8 // indirect github.com/pierrec/lz4/v4 v4.1.15 // indirect github.com/pjbgf/sha1cd v0.3.0 // indirect @@ -99,20 +100,20 @@ require ( github.com/xi2/xz v0.0.0-20171230120015-48954b6210f8 // indirect github.com/xo/terminfo v0.0.0-20210125001918-ca9a967f8778 // indirect github.com/xrash/smetrics v0.0.0-20201216005158-039620a65673 // indirect - golang.org/x/crypto v0.12.0 // indirect + golang.org/x/crypto v0.13.0 // indirect golang.org/x/mod v0.12.0 // indirect - golang.org/x/net v0.14.0 // indirect + golang.org/x/net v0.15.0 // indirect golang.org/x/oauth2 v0.10.0 // indirect golang.org/x/sync v0.3.0 // indirect - golang.org/x/sys v0.11.0 // indirect - golang.org/x/term v0.11.0 // indirect - golang.org/x/text v0.12.0 // indirect + golang.org/x/sys v0.12.0 // indirect + golang.org/x/term v0.12.0 // indirect + golang.org/x/text v0.13.0 // indirect golang.org/x/time v0.3.0 // indirect - golang.org/x/tools v0.12.1-0.20230815132531-74c255bcf846 // indirect + golang.org/x/tools v0.13.0 // indirect google.golang.org/appengine v1.6.7 // 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 ) -// replace github.com/jfrog/jfrog-cli-core/v2 => github.com/jfrog/jfrog-cli-core/v2 v2.31.1-0.20230824124821-a7f84a425af1 +//replace github.com/jfrog/jfrog-cli-core/v2 => github.com/jfrog/jfrog-cli-core/v2 dev diff --git a/go.sum b/go.sum index 7d43302b9..955fd8335 100644 --- a/go.sum +++ b/go.sum @@ -603,15 +603,15 @@ github.com/BurntSushi/toml v0.3.1/go.mod h1:xHWCNGjB5oqiDr8zfno3MHue2Ht5sIBksp03 github.com/BurntSushi/toml v1.3.2 h1:o7IhLm0Msx3BaB+n3Ag7L8EVlByGnpq14C4YWiu/gL8= github.com/BurntSushi/toml v1.3.2/go.mod h1:CxXYINrC8qIiEnFrOxCa7Jy5BFHlXnUU2pbicEuybxQ= github.com/BurntSushi/xgb v0.0.0-20160522181843-27f122750802/go.mod h1:IVnqGOEym/WlBOVXweHU+Q+/VP0lqqI8lqeDx9IjBqo= -github.com/CycloneDX/cyclonedx-go v0.7.1 h1:5w1SxjGm9MTMNTuRbEPyw21ObdbaagTWF/KfF0qHTRE= -github.com/CycloneDX/cyclonedx-go v0.7.1/go.mod h1:N/nrdWQI2SIjaACyyDs/u7+ddCkyl/zkNs8xFsHF2Ps= +github.com/CycloneDX/cyclonedx-go v0.7.2 h1:kKQ0t1dPOlugSIYVOMiMtFqeXI2wp/f5DBIdfux8gnQ= +github.com/CycloneDX/cyclonedx-go v0.7.2/go.mod h1:K2bA+324+Og0X84fA8HhN2X066K7Bxz4rpMQ4ZhjtSk= github.com/JohnCGriffin/overflow v0.0.0-20211019200055-46fa312c352c/go.mod h1:X0CRv0ky0k6m906ixxpzmDRLvX58TFUKS2eePweuyxk= 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/OneOfOne/xxhash v1.2.2/go.mod h1:HSdplMjZKSmBqAxg5vPj2TmRDmfkzw+cTzAElWljhcU= -github.com/ProtonMail/go-crypto v0.0.0-20230717121422-5aa5874ade95 h1:KLq8BE0KwCL+mmXnjLWEAOYO+2l2AE4YMmqG1ZpZHBs= -github.com/ProtonMail/go-crypto v0.0.0-20230717121422-5aa5874ade95/go.mod h1:EjAoLdwvbIOoOQr3ihjnSoLZRtE8azugULFRteWMNc0= +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/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= @@ -683,7 +683,9 @@ github.com/dsnet/compress v0.0.2-0.20210315054119-f66993602bf5 h1:iFaUwBSo5Svw6L 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/dustin/go-humanize v1.0.0/go.mod h1:HtrtbFcZ19U5GC7JDqmcUSB87Iq5E25KnS6fMYU6eOk= -github.com/elazarl/goproxy v0.0.0-20221015165544-a0805db90819 h1:RIB4cRk+lBqKK3Oy0r2gRX4ui7tuhiZq2SuTtTCi0/0= +github.com/elazarl/goproxy v0.0.0-20230731152917-f99041a5c027 h1:1L0aalTpPz7YlMxETKpmQoWMBkeiuorElZIXoNmgiPE= +github.com/elazarl/goproxy v0.0.0-20230731152917-f99041a5c027/go.mod h1:Ro8st/ElPeALwNFlcTpWmkr6IoMFfkjXAvTHpevnDsM= +github.com/elazarl/goproxy/ext v0.0.0-20190711103511-473e67f1d7d2/go.mod h1:gNh8nYJoAm43RfaxurUnxr+N1PwuFV3ZMl/efxlIlY8= 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= @@ -828,8 +830,9 @@ github.com/google/s2a-go v0.1.3/go.mod h1:Ej+mSEMGRnqRzjc7VtF+jdBwYG5fuJfiZ8ELkj github.com/google/s2a-go v0.1.4/go.mod h1:Ej+mSEMGRnqRzjc7VtF+jdBwYG5fuJfiZ8ELkjEwM0A= github.com/google/uuid v1.1.1/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo= github.com/google/uuid v1.1.2/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo= -github.com/google/uuid v1.3.0 h1:t6JiXgmwXMjEs8VusXIJk2BXHsn+wx8BZdTaoZ5fu7I= github.com/google/uuid v1.3.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo= +github.com/google/uuid v1.3.1 h1:KjJaJ9iWZ3jOFZIf1Lqf4laDRCasjl0BCmnEGxkdLb4= +github.com/google/uuid v1.3.1/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo= github.com/googleapis/enterprise-certificate-proxy v0.0.0-20220520183353-fd19c99a87aa/go.mod h1:17drOmN3MwGY7t0e+Ei9b45FFGA3fBs3x36SsCg1hq8= github.com/googleapis/enterprise-certificate-proxy v0.1.0/go.mod h1:17drOmN3MwGY7t0e+Ei9b45FFGA3fBs3x36SsCg1hq8= github.com/googleapis/enterprise-certificate-proxy v0.2.0/go.mod h1:8C0jb7/mgJe/9KK8Lm7X9ctZC2t60YyIpYEI16jx0Qg= @@ -873,18 +876,18 @@ github.com/ianlancetaylor/demangle v0.0.0-20181102032728-5e5cf60278f6/go.mod h1: github.com/ianlancetaylor/demangle v0.0.0-20200824232613-28f6c0f3b639/go.mod h1:aSSvb/t6k1mPoxDqO4vJh6VOCGPwU4O0C2/Eqndh1Sc= github.com/jbenet/go-context v0.0.0-20150711004518-d14ea06fba99 h1:BQSFePA1RWJOlocH6Fxy8MmwDt+yVQYULKfN0RoTN8A= github.com/jbenet/go-context v0.0.0-20150711004518-d14ea06fba99/go.mod h1:1lJo3i6rXxKeerYnT8Nvf0QmHCRC1n8sfWVwXF2Frvo= -github.com/jedib0t/go-pretty/v6 v6.4.6 h1:v6aG9h6Uby3IusSSEjHaZNXpHFhzqMmjXcPq1Rjl9Jw= -github.com/jedib0t/go-pretty/v6 v6.4.6/go.mod h1:Ndk3ase2CkQbXLLNf5QDHoYb6J9WtVfmHZu9n8rk2xs= -github.com/jfrog/build-info-go v1.9.9 h1:YMA9okHawBNL8SrCWzqULSf5M4W+YnWyUhmkWSjoXEE= -github.com/jfrog/build-info-go v1.9.9/go.mod h1:t31QRpH5xUJKw8XkQlAA+Aq7aanyS1rrzpcK8xSNVts= -github.com/jfrog/froggit-go v1.13.4 h1:+pHq3iNkKFvojXCJ74sDV+UsV4Thsi03dsu36jkS7Rc= -github.com/jfrog/froggit-go v1.13.4/go.mod h1:0jRAaZZusaFFnITosmx6CA60SKryuoaCasJyUrP/c1s= +github.com/jedib0t/go-pretty/v6 v6.4.7 h1:lwiTJr1DEkAgzljsUsORmWsVn5MQjt1BPJdPCtJ6KXE= +github.com/jedib0t/go-pretty/v6 v6.4.7/go.mod h1:Ndk3ase2CkQbXLLNf5QDHoYb6J9WtVfmHZu9n8rk2xs= +github.com/jfrog/build-info-go v1.9.10 h1:uXnDLVxpqxoAMpXcki00QaBB+M2BoGMMpHODPkmmYOY= +github.com/jfrog/build-info-go v1.9.10/go.mod h1:ujJ8XQZMdT2tMkLSMJNyDd1pCY+duwHdjV+9or9FLIg= +github.com/jfrog/froggit-go v1.14.0 h1:WdbCgar/zMrmq5EIremVosLEbxEsOcpTtyhyeYZPbNE= +github.com/jfrog/froggit-go v1.14.0/go.mod h1:0jRAaZZusaFFnITosmx6CA60SKryuoaCasJyUrP/c1s= github.com/jfrog/gofrog v1.3.0 h1:o4zgsBZE4QyDbz2M7D4K6fXPTBJht+8lE87mS9bw7Gk= github.com/jfrog/gofrog v1.3.0/go.mod h1:IFMc+V/yf7rA5WZ74CSbXe+Lgf0iApEQLxRZVzKRUR0= -github.com/jfrog/jfrog-cli-core/v2 v2.41.4 h1:+V35NN+UaKl6ZFSjAyZFZ4VijCgsORnGsHug02DROdE= -github.com/jfrog/jfrog-cli-core/v2 v2.41.4/go.mod h1:Mi3WFUzG2CU6tlLpGsMNRaKkhH/tIMuci4tjnPZ9S3M= -github.com/jfrog/jfrog-client-go v1.31.6 h1:uWuyT4BDm9s5ES6oDTBny9Gl6yf8iKFjcbmHSHQZrDc= -github.com/jfrog/jfrog-client-go v1.31.6/go.mod h1:icb00ZJN/mMMNkQduHDkzpqsXH9Flwi3f3COYexq3Nc= +github.com/jfrog/jfrog-cli-core/v2 v2.41.6 h1:wnHfeO4/7MOqyGKN1I5RaXHpKPNKwYAUMEuOHlwjV0U= +github.com/jfrog/jfrog-cli-core/v2 v2.41.6/go.mod h1:HCMfdtCy2B81EF8YiQlsfbG3CsLk/VeqoWGNYoSUz8Q= +github.com/jfrog/jfrog-client-go v1.32.1 h1:RQmuPSLsF5222vZJzwkgHSZMMJF83ExS7SwIvh4P+H8= +github.com/jfrog/jfrog-client-go v1.32.1/go.mod h1:362+oa7uTTYurzBs1L0dmUTlLo7uhpAU/pwM5Zb9clg= github.com/jordan-wright/email v4.0.1-0.20210109023952-943e75fe5223+incompatible h1:jdpOPRN1zP63Td1hDQbZW73xKmzDvZHzVdNYxhnTMDA= github.com/jordan-wright/email v4.0.1-0.20210109023952-943e75fe5223+incompatible/go.mod h1:1c7szIrayyPPB/987hsnvNzLushdWf4o/79s3P08L8A= github.com/jstemmer/go-junit-report v0.0.0-20190106144839-af01ea7f8024/go.mod h1:6v2b51hI/fHJwM22ozAgKL4VKDeJcHhJFhtBdhmNjmU= @@ -994,6 +997,7 @@ github.com/rivo/uniseg v0.2.0/go.mod h1:J6wj4VEh+S6ZtnVlnTBMWIodfgj8LQOQFoIToxlJ github.com/rivo/uniseg v0.4.3 h1:utMvzDsuh3suAEnhH0RdHmoPbU648o6CvXxTx4SBMOw= github.com/rivo/uniseg v0.4.3/go.mod h1:FN3SvrM+Zdj16jyLfmOkMNblXMcoc8DfTHruCPUcx88= github.com/rogpeppe/fastuuid v1.2.0/go.mod h1:jVj6XXZzXRy/MSR5jhDC/2q6DgLz+nrA6LYCDYWNEvQ= +github.com/rogpeppe/go-charset v0.0.0-20180617210344-2471d30d28b4/go.mod h1:qgYeAmZ5ZIpBWTGllZSQnw97Dj+woV0toclVaRGI8pc= github.com/rogpeppe/go-internal v1.3.0/go.mod h1:M8bDsm7K2OlrFYOpmOWEs/qY81heoFRclV5y23lUDJ4= github.com/rogpeppe/go-internal v1.6.1/go.mod h1:xXDCJY+GAPziupqXw64V24skbSoqbTEfhy4qGm1nDQc= github.com/rogpeppe/go-internal v1.9.0 h1:73kH8U+JUqXU8lRuOHeVHaa/SZPifC7BkcraZVejAe8= @@ -1039,6 +1043,7 @@ github.com/stretchr/testify v1.8.4 h1:CcVxjf3Q8PM0mHUKJCdn+eZZtm5yQwehR5yeSVQQcU 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/terminalstatic/go-xsd-validate v0.1.5 h1:RqpJnf6HGE2CB/lZB1A8BYguk8uRtcvYAPLCF15qguo= github.com/ulikunitz/xz v0.5.8/go.mod h1:nbz6k7qbPmH4IRqmfOplQw/tblSgqTqBwxkY0oWt/14= github.com/ulikunitz/xz v0.5.9 h1:RsKRIA2MO8x56wkkcd3LbtcE/uMszhb6DpRf+3uwa3I= github.com/ulikunitz/xz v0.5.9/go.mod h1:nbz6k7qbPmH4IRqmfOplQw/tblSgqTqBwxkY0oWt/14= @@ -1104,8 +1109,8 @@ golang.org/x/crypto v0.3.1-0.20221117191849-2c476679df9a/go.mod h1:hebNnKkNXi2Uz golang.org/x/crypto v0.7.0/go.mod h1:pYwdfH91IfpZVANVyUOhSIPZaFoJGxTFbZhFTx+dXZU= golang.org/x/crypto v0.9.0/go.mod h1:yrmDGqONDYtNj3tH8X9dzUun2m2lzPa9ngI6/RUPGR0= golang.org/x/crypto v0.11.0/go.mod h1:xgJhtzW8F9jGdVFWZESrid1U1bjeNy4zgy5cRr/CIio= -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.13.0 h1:mvySKfSWJ+UKUii46M40LOvyWfN0s2U+46/jDd0e6Ck= +golang.org/x/crypto v0.13.0/go.mod h1:y6Z2r+Rw4iayiXXAIxJIDAJ1zMW4yaTpebo8fPOliYc= golang.org/x/exp v0.0.0-20180321215751-8460e604b9de/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA= golang.org/x/exp v0.0.0-20180807140117-3d87b88a115f/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA= golang.org/x/exp v0.0.0-20190121172915-509febef88a4/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA= @@ -1121,8 +1126,8 @@ golang.org/x/exp v0.0.0-20200119233911-0405dc783f0a/go.mod h1:2RIsYlXP63K8oxa1u0 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-20220827204233-334a2380cb91/go.mod h1:cyybsKvd6eL0RnXn6p/Grxp8F5bW7iYuBgsNCOHpMYE= -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-20230905200255-921286631fa9 h1:GoHiUyI/Tp2nVkLI2mCxVkOjsbSXD66ic0XW0js0R9g= +golang.org/x/exp v0.0.0-20230905200255-921286631fa9/go.mod h1:S2oDrQGGwySpoQPVqRShND87VCbxmc6bL1Yd2oYrm6k= golang.org/x/image v0.0.0-20180708004352-c73c2afc3b81/go.mod h1:ux5Hcp/YLpHSI86hEcLt0YII63i6oz57MZXIpbrjZUs= 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= @@ -1228,8 +1233,8 @@ golang.org/x/net v0.8.0/go.mod h1:QVkue5JL9kW//ek3r6jTKnTFis1tRmNAW2P1shuFdJc= golang.org/x/net v0.9.0/go.mod h1:d48xBJpPfHeWQsugry2m+kC02ZBRGRgulfHnEXEuWns= golang.org/x/net v0.10.0/go.mod h1:0qNGK6F8kojg2nk9dLZ2mShWaEBan6FAoqfSigmmuDg= golang.org/x/net v0.12.0/go.mod h1:zEVYFnQC7m/vmpQFELhcD1EWkZlX69l4oqgmer6hfKA= -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.15.0 h1:ugBLEUaxABaB5AJqW9enI0ACdci2RUd4eP51NTBvuJ8= +golang.org/x/net v0.15.0/go.mod h1:idbUs1IY1+zTqbi8yxTbhexhEEk5ur9LInksu6HrEpk= 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= @@ -1376,8 +1381,8 @@ golang.org/x/sys v0.6.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.7.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.8.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.10.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.12.0 h1:CM0HF96J0hcLAwsHPJZjfdNzs0gftsLfgKt57wWHJ0o= +golang.org/x/sys v0.12.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.1.0/go.mod h1:jbD1KX2456YbFQfuXm/mYQcufACuNUgVhRMnK/tPxf8= @@ -1389,8 +1394,8 @@ golang.org/x/term v0.6.0/go.mod h1:m6U89DPEgQRMq3DNkDClhWw02AUbt2daBVO4cn4Hv9U= golang.org/x/term v0.7.0/go.mod h1:P32HKFT3hSsZrRxla30E9HqToFYAQPCMs/zFMBUFqPY= golang.org/x/term v0.8.0/go.mod h1:xPskH00ivmX89bAKVGSKKtLOWNx2+17Eiy94tnKShWo= golang.org/x/term v0.10.0/go.mod h1:lpqdcUyK/oCiQxvxVrppt5ggO2KCZ5QblwqPnfZ6d5o= -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.12.0 h1:/ZfYdc3zq+q02Rv9vGqTeSItdzZTSNDmfTi0mBAuidU= +golang.org/x/term v0.12.0/go.mod h1:owVbMEjm3cBLCHdkQu9b1opXd4ETQWc3BhuQGKgXgvU= 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= @@ -1408,8 +1413,8 @@ 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.9.0/go.mod h1:e1OnstbJyHTd6l/uOt8jFFHp6TRDWZR/bV3emEE/zU8= golang.org/x/text v0.11.0/go.mod h1:TvPlkZtksWOMsz7fbANvkp4WM8x/WCo/om8BMLbz+aE= -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= @@ -1478,8 +1483,8 @@ golang.org/x/tools v0.1.12/go.mod h1:hNGJHUnrk76NpqgfD5Aqm5Crs+Hm0VOH/i9J2+nxYbc golang.org/x/tools v0.3.0/go.mod h1:/rWhSS2+zyEVwoJf8YAX6L2f0ntZ7Kn/mGgAWcipA5k= golang.org/x/tools v0.6.0/go.mod h1:Xwgl3UAJ/d3gWutnCtw505GrjyAbvKui8lOU390QaIU= golang.org/x/tools v0.7.0/go.mod h1:4pg6aUX35JBAogB10C9AtvVL+qowtN4pT3CGSQex14s= -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.13.0 h1:Iey4qkscZuv0VvIt8E0neZjtPVQFSc870HQ448QgEmQ= +golang.org/x/tools v0.13.0/go.mod h1:HvlwmtVNQAhOuCjW7xxvovg8wbNq7LwfXh/k7wXUl58= golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= golang.org/x/xerrors v0.0.0-20191011141410-1b5146add898/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= diff --git a/packagehandlers/commonpackagehandler.go b/packagehandlers/commonpackagehandler.go index f7f6e45af..7d9129616 100644 --- a/packagehandlers/commonpackagehandler.go +++ b/packagehandlers/commonpackagehandler.go @@ -30,6 +30,8 @@ func GetCompatiblePackageHandler(vulnDetails *utils.VulnerabilityDetails, detail handler = &PythonPackageHandler{pipRequirementsFile: details.PipRequirementsFile} case coreutils.Maven: handler = &MavenPackageHandler{depsRepo: details.DepsRepo, ServerDetails: details.ServerDetails} + case coreutils.Nuget: + handler = &NugetPackageHandler{} default: handler = &UnsupportedPackageHandler{} } @@ -44,18 +46,27 @@ func (cph *CommonPackageHandler) UpdateDependency(vulnDetails *utils.Vulnerabili impactedPackage := strings.ToLower(vulnDetails.ImpactedDependencyName) commandArgs := []string{installationCommand} commandArgs = append(commandArgs, extraArgs...) - operator := vulnDetails.Technology.GetPackageOperator() - fixedPackage := impactedPackage + operator + vulnDetails.SuggestedFixedVersion - commandArgs = append(commandArgs, fixedPackage) - return runPackageMangerCommand(vulnDetails.Technology.GetExecCommandName(), commandArgs) + versionOperator := vulnDetails.Technology.GetPackageVersionOperator() + fixedPackageArgs := getFixedPackage(impactedPackage, versionOperator, vulnDetails.SuggestedFixedVersion) + commandArgs = append(commandArgs, fixedPackageArgs...) + return runPackageMangerCommand(vulnDetails.Technology.GetExecCommandName(), vulnDetails.Technology.ToString(), commandArgs) } -func runPackageMangerCommand(commandName string, commandArgs []string) error { +func runPackageMangerCommand(commandName string, techName string, commandArgs []string) error { fullCommand := commandName + " " + strings.Join(commandArgs, " ") log.Debug(fmt.Sprintf("Running '%s'", fullCommand)) output, err := exec.Command(commandName, commandArgs...).CombinedOutput() // #nosec G204 if err != nil { - return fmt.Errorf("%s command failed: %s\n%s", fullCommand, err.Error(), output) + return fmt.Errorf("failed to update %s dependency: '%s' command failed: %s\n%s", techName, fullCommand, err.Error(), output) } return nil } + +// Returns the updated package and version as it should be run in the update command: +// If the package manager expects a single string (example: @) it returns []string{@} +// If the command args suppose to be seperated by spaces (example: -v ) it returns []string{, "-v", } +func getFixedPackage(impactedPackage string, versionOperator string, suggestedFixedVersion string) (fixedPackageArgs []string) { + fixedPackageString := strings.TrimSpace(impactedPackage) + versionOperator + strings.TrimSpace(suggestedFixedVersion) + fixedPackageArgs = strings.Split(fixedPackageString, " ") + return +} diff --git a/packagehandlers/mavenpackagehandler.go b/packagehandlers/mavenpackagehandler.go index 68552da08..09006d0e6 100644 --- a/packagehandlers/mavenpackagehandler.go +++ b/packagehandlers/mavenpackagehandler.go @@ -11,7 +11,7 @@ import ( rtutils "github.com/jfrog/jfrog-cli-core/v2/artifactory/utils" "github.com/jfrog/jfrog-cli-core/v2/utils/config" mvnutils "github.com/jfrog/jfrog-cli-core/v2/utils/mvn" - "github.com/jfrog/jfrog-cli-core/v2/xray/audit/java" + "github.com/jfrog/jfrog-cli-core/v2/xray/commands/audit/sca/java" "github.com/jfrog/jfrog-client-go/utils/log" "golang.org/x/exp/slices" "io" diff --git a/packagehandlers/nugetpackagehandler.go b/packagehandlers/nugetpackagehandler.go new file mode 100644 index 000000000..90a0fdeed --- /dev/null +++ b/packagehandlers/nugetpackagehandler.go @@ -0,0 +1,27 @@ +package packagehandlers + +import ( + "github.com/jfrog/frogbot/utils" +) + +const dotnetPackageUpgradeExtraArg = "package" + +type NugetPackageHandler struct { + CommonPackageHandler +} + +func (nph *NugetPackageHandler) UpdateDependency(vulnDetails *utils.VulnerabilityDetails) error { + if vulnDetails.IsDirectDependency { + return nph.updateDirectDependency(vulnDetails) + } + + return &utils.ErrUnsupportedFix{ + PackageName: vulnDetails.ImpactedDependencyName, + FixedVersion: vulnDetails.SuggestedFixedVersion, + ErrorType: utils.IndirectDependencyFixNotSupported, + } +} + +func (nph *NugetPackageHandler) updateDirectDependency(vulnDetails *utils.VulnerabilityDetails) (err error) { + return nph.CommonPackageHandler.UpdateDependency(vulnDetails, vulnDetails.Technology.GetPackageInstallationCommand(), dotnetPackageUpgradeExtraArg) +} diff --git a/packagehandlers/packagehandlers_test.go b/packagehandlers/packagehandlers_test.go index 9f9bfd219..a3dc10539 100644 --- a/packagehandlers/packagehandlers_test.go +++ b/packagehandlers/packagehandlers_test.go @@ -3,10 +3,10 @@ package packagehandlers import ( "fmt" testdatautils "github.com/jfrog/build-info-go/build/testdata" + biutils "github.com/jfrog/build-info-go/utils" "github.com/jfrog/frogbot/utils" "github.com/jfrog/jfrog-cli-core/v2/utils/coreutils" "github.com/jfrog/jfrog-cli-core/v2/xray/formats" - "github.com/jfrog/jfrog-client-go/utils/io/fileutils" "github.com/stretchr/testify/assert" "os" "path/filepath" @@ -17,140 +17,229 @@ import ( ) type dependencyFixTest struct { - vulnDetails *utils.VulnerabilityDetails - fixSupported bool - specificTechVersion string + vulnDetails *utils.VulnerabilityDetails + scanDetails *utils.ScanDetails + fixSupported bool + specificTechVersion string + uniqueChecksExtraArgs []string } -type pythonIndirectDependencies struct { - dependencyFixTest - requirementsPath string -} - -const requirementsFile = "oslo.config>=1.12.1,<1.13\noslo.utils<5.0,>=4.0.0\nparamiko==2.7.2\npasslib<=1.7.4\nprance>=0.9.0\nprompt-toolkit~=1.0.15\npyinotify>0.9.6\nPyJWT>1.7.1\nurllib3 > 1.1.9, < 1.5.*" +const ( + requirementsFile = "oslo.config>=1.12.1,<1.13\noslo.utils<5.0,>=4.0.0\nparamiko==2.7.2\npasslib<=1.7.4\nprance>=0.9.0\nprompt-toolkit~=1.0.15\npyinotify>0.9.6\nPyJWT>1.7.1\nurllib3 > 1.1.9, < 1.5.*" + GoPackageDescriptor = "go.mod" +) type pipPackageRegexTest struct { packageName string expectedRequirement string } -// Go -func TestGoPackageHandler_UpdateDependency(t *testing.T) { - goPackageHandler := GoPackageHandler{} - testcases := []dependencyFixTest{ +func TestUpdateDependency(t *testing.T) { + testCases := [][]dependencyFixTest{ + // Go test cases { - vulnDetails: &utils.VulnerabilityDetails{ - SuggestedFixedVersion: "0.0.0-20201216223049-8b5274cf687f", - IsDirectDependency: false, - VulnerabilityOrViolationRow: formats.VulnerabilityOrViolationRow{Technology: coreutils.Go, ImpactedDependencyName: "golang.org/x/crypto"}, + { + vulnDetails: &utils.VulnerabilityDetails{ + SuggestedFixedVersion: "0.0.0-20201216223049-8b5274cf687f", + IsDirectDependency: false, + VulnerabilityOrViolationRow: formats.VulnerabilityOrViolationRow{Technology: coreutils.Go, ImpactedDependencyName: "golang.org/x/crypto"}, + }, + fixSupported: true, + uniqueChecksExtraArgs: []string{GoPackageDescriptor}, + }, + { + vulnDetails: &utils.VulnerabilityDetails{ + SuggestedFixedVersion: "1.7.7", + IsDirectDependency: true, + VulnerabilityOrViolationRow: formats.VulnerabilityOrViolationRow{Technology: coreutils.Go, ImpactedDependencyName: "github.com/gin-gonic/gin"}, + }, + fixSupported: true, + uniqueChecksExtraArgs: []string{GoPackageDescriptor}, + }, + { + vulnDetails: &utils.VulnerabilityDetails{ + SuggestedFixedVersion: "1.3.0", + IsDirectDependency: true, + VulnerabilityOrViolationRow: formats.VulnerabilityOrViolationRow{Technology: coreutils.Go, ImpactedDependencyName: "github.com/google/uuid"}, + }, + fixSupported: true, + uniqueChecksExtraArgs: []string{GoPackageDescriptor}, }, - fixSupported: true, }, + + // Python test cases (includes pip, pipenv, poetry) { - vulnDetails: &utils.VulnerabilityDetails{ - SuggestedFixedVersion: "1.7.7", - IsDirectDependency: true, - VulnerabilityOrViolationRow: formats.VulnerabilityOrViolationRow{Technology: coreutils.Go, ImpactedDependencyName: "github.com/gin-gonic/gin"}, + { + vulnDetails: &utils.VulnerabilityDetails{ + SuggestedFixedVersion: "1.25.9", + VulnerabilityOrViolationRow: formats.VulnerabilityOrViolationRow{Technology: coreutils.Pip, ImpactedDependencyName: "urllib3"}, + }, + scanDetails: &utils.ScanDetails{Project: &utils.Project{PipRequirementsFile: "requirements.txt"}}, + fixSupported: false, + }, + { + vulnDetails: &utils.VulnerabilityDetails{ + SuggestedFixedVersion: "1.25.9", + VulnerabilityOrViolationRow: formats.VulnerabilityOrViolationRow{Technology: coreutils.Poetry, ImpactedDependencyName: "urllib3"}, + }, + scanDetails: &utils.ScanDetails{Project: &utils.Project{PipRequirementsFile: "pyproejct.toml"}}, + fixSupported: false, + }, + { + vulnDetails: &utils.VulnerabilityDetails{ + SuggestedFixedVersion: "1.25.9", + VulnerabilityOrViolationRow: formats.VulnerabilityOrViolationRow{Technology: coreutils.Pipenv, ImpactedDependencyName: "urllib3"}, + }, + scanDetails: &utils.ScanDetails{Project: &utils.Project{PipRequirementsFile: "Pipfile"}}, + fixSupported: false, + }, + { + vulnDetails: &utils.VulnerabilityDetails{ + SuggestedFixedVersion: "2.4.0", + VulnerabilityOrViolationRow: formats.VulnerabilityOrViolationRow{Technology: coreutils.Pip, ImpactedDependencyName: "pyjwt"}, + IsDirectDependency: true, + }, + scanDetails: &utils.ScanDetails{Project: &utils.Project{PipRequirementsFile: "requirements.txt"}}, + fixSupported: true, + }, + { + vulnDetails: &utils.VulnerabilityDetails{ + SuggestedFixedVersion: "2.4.0", + VulnerabilityOrViolationRow: formats.VulnerabilityOrViolationRow{Technology: coreutils.Pip, ImpactedDependencyName: "Pyjwt"}, + IsDirectDependency: true}, + scanDetails: &utils.ScanDetails{Project: &utils.Project{PipRequirementsFile: "requirements.txt"}}, + fixSupported: true, + }, + { + vulnDetails: &utils.VulnerabilityDetails{ + SuggestedFixedVersion: "2.4.0", + VulnerabilityOrViolationRow: formats.VulnerabilityOrViolationRow{Technology: coreutils.Pip, ImpactedDependencyName: "pyjwt"}, + IsDirectDependency: true}, + scanDetails: &utils.ScanDetails{Project: &utils.Project{PipRequirementsFile: "setup.py"}}, + fixSupported: true, + }, + { + vulnDetails: &utils.VulnerabilityDetails{ + SuggestedFixedVersion: "2.4.0", + VulnerabilityOrViolationRow: formats.VulnerabilityOrViolationRow{Technology: coreutils.Poetry, ImpactedDependencyName: "pyjwt"}, + IsDirectDependency: true}, + scanDetails: &utils.ScanDetails{Project: &utils.Project{PipRequirementsFile: "pyproject.toml"}}, + fixSupported: true, }, - fixSupported: true, }, + + // Npm test cases { - vulnDetails: &utils.VulnerabilityDetails{ - SuggestedFixedVersion: "1.3.0", - IsDirectDependency: true, - VulnerabilityOrViolationRow: formats.VulnerabilityOrViolationRow{Technology: coreutils.Go, ImpactedDependencyName: "github.com/google/uuid"}, + { + vulnDetails: &utils.VulnerabilityDetails{ + SuggestedFixedVersion: "0.8.4", + VulnerabilityOrViolationRow: formats.VulnerabilityOrViolationRow{Technology: coreutils.Npm, ImpactedDependencyName: "mpath"}, + }, + fixSupported: false, + }, + { + vulnDetails: &utils.VulnerabilityDetails{ + SuggestedFixedVersion: "3.0.2", + IsDirectDependency: true, + VulnerabilityOrViolationRow: formats.VulnerabilityOrViolationRow{Technology: coreutils.Npm, ImpactedDependencyName: "minimatch"}, + }, + fixSupported: true, }, - fixSupported: true, }, - } - for _, test := range testcases { - t.Run(test.vulnDetails.ImpactedDependencyName+" direct:"+strconv.FormatBool(test.vulnDetails.IsDirectDependency), func(t *testing.T) { - testDataDir := getTestDataDir(t, test.vulnDetails.IsDirectDependency) - cleanup := createTempDirAndChdir(t, testDataDir, test.vulnDetails.Technology.ToString()) - defer cleanup() - err := goPackageHandler.UpdateDependency(test.vulnDetails) - if test.fixSupported { - assert.NoError(t, err) - assertFixVersionInPackageDescriptor(t, test, "go.mod") - } else { - assert.Error(t, err, "Expected error to occur") - assert.IsType(t, &utils.ErrUnsupportedFix{}, err, "Expected unsupported fix error") - } - }) - } -} + // Yarn test cases + { + { + // This test case directs to non-existing directory. It only checks if the dependency update is blocked if the vulnerable dependency is not a direct dependency + vulnDetails: &utils.VulnerabilityDetails{ + SuggestedFixedVersion: "1.2.6", + IsDirectDependency: false, + VulnerabilityOrViolationRow: formats.VulnerabilityOrViolationRow{Technology: coreutils.Yarn, ImpactedDependencyName: "minimist"}, + }, + fixSupported: false, + }, + { + vulnDetails: &utils.VulnerabilityDetails{ + SuggestedFixedVersion: "1.2.6", + IsDirectDependency: true, + VulnerabilityOrViolationRow: formats.VulnerabilityOrViolationRow{Technology: coreutils.Yarn, ImpactedDependencyName: "minimist"}, + }, + fixSupported: true, + specificTechVersion: "1", + }, + { + vulnDetails: &utils.VulnerabilityDetails{ + SuggestedFixedVersion: "1.2.6", + IsDirectDependency: true, + VulnerabilityOrViolationRow: formats.VulnerabilityOrViolationRow{Technology: coreutils.Yarn, ImpactedDependencyName: "minimist"}, + }, + fixSupported: true, + specificTechVersion: "2", + }, + }, -// Python, includes pip,pipenv, poetry -func TestPythonPackageHandler_UpdateDependency(t *testing.T) { - testcases := []pythonIndirectDependencies{ - {dependencyFixTest: dependencyFixTest{ - vulnDetails: &utils.VulnerabilityDetails{ - SuggestedFixedVersion: "1.25.9", - VulnerabilityOrViolationRow: formats.VulnerabilityOrViolationRow{Technology: coreutils.Pip, ImpactedDependencyName: "urllib3"}}}, - requirementsPath: "requirements.txt"}, - {dependencyFixTest: dependencyFixTest{ - vulnDetails: &utils.VulnerabilityDetails{ - SuggestedFixedVersion: "1.25.9", - VulnerabilityOrViolationRow: formats.VulnerabilityOrViolationRow{Technology: coreutils.Poetry, ImpactedDependencyName: "urllib3"}}}, - requirementsPath: "pyproejct.toml"}, - {dependencyFixTest: dependencyFixTest{ - vulnDetails: &utils.VulnerabilityDetails{ - SuggestedFixedVersion: "1.25.9", - VulnerabilityOrViolationRow: formats.VulnerabilityOrViolationRow{Technology: coreutils.Pipenv, ImpactedDependencyName: "urllib3"}}}, - requirementsPath: "Pipfile"}, - {dependencyFixTest: dependencyFixTest{ - vulnDetails: &utils.VulnerabilityDetails{ - SuggestedFixedVersion: "2.4.0", - VulnerabilityOrViolationRow: formats.VulnerabilityOrViolationRow{Technology: coreutils.Pip, ImpactedDependencyName: "pyjwt"}, - IsDirectDependency: true}, - fixSupported: true}, - requirementsPath: "requirements.txt"}, - {dependencyFixTest: dependencyFixTest{ - vulnDetails: &utils.VulnerabilityDetails{ - SuggestedFixedVersion: "2.4.0", - VulnerabilityOrViolationRow: formats.VulnerabilityOrViolationRow{Technology: coreutils.Pip, ImpactedDependencyName: "Pyjwt"}, - IsDirectDependency: true}, - fixSupported: true}, - requirementsPath: "requirements.txt"}, - {dependencyFixTest: dependencyFixTest{ - vulnDetails: &utils.VulnerabilityDetails{ - SuggestedFixedVersion: "2.4.0", - VulnerabilityOrViolationRow: formats.VulnerabilityOrViolationRow{Technology: coreutils.Pip, ImpactedDependencyName: "pyjwt"}, - IsDirectDependency: true}, - fixSupported: true}, - requirementsPath: "setup.py"}, - {dependencyFixTest: dependencyFixTest{ - vulnDetails: &utils.VulnerabilityDetails{ - SuggestedFixedVersion: "2.4.0", - VulnerabilityOrViolationRow: formats.VulnerabilityOrViolationRow{Technology: coreutils.Poetry, ImpactedDependencyName: "pyjwt"}, - IsDirectDependency: true}, - fixSupported: true}, - requirementsPath: "pyproject.toml"}, - {dependencyFixTest: dependencyFixTest{ - vulnDetails: &utils.VulnerabilityDetails{ - SuggestedFixedVersion: "2.4.0", - VulnerabilityOrViolationRow: formats.VulnerabilityOrViolationRow{Technology: coreutils.Poetry, ImpactedDependencyName: "pyjwt"}, - IsDirectDependency: true}, - fixSupported: true}, - requirementsPath: "pyproject.toml"}, + // Maven test cases + { + { + vulnDetails: &utils.VulnerabilityDetails{ + SuggestedFixedVersion: "2.7", + VulnerabilityOrViolationRow: formats.VulnerabilityOrViolationRow{Technology: coreutils.Maven, ImpactedDependencyName: "commons-io:commons-io"}, + IsDirectDependency: true}, + scanDetails: &utils.ScanDetails{Project: &utils.Project{DepsRepo: ""}, ServerDetails: nil}, + fixSupported: true, + }, + { + vulnDetails: &utils.VulnerabilityDetails{ + SuggestedFixedVersion: "4.3.20", + VulnerabilityOrViolationRow: formats.VulnerabilityOrViolationRow{Technology: coreutils.Maven, ImpactedDependencyName: "org.springframework:spring-core"}, + IsDirectDependency: false}, + scanDetails: &utils.ScanDetails{Project: &utils.Project{DepsRepo: ""}, ServerDetails: nil}, + fixSupported: false, + }, + }, + + // NuGet test cases + { + { + // This test case directs to non-existing directory. It only checks if the dependency update is blocked if the vulnerable dependency is not a direct dependency + vulnDetails: &utils.VulnerabilityDetails{ + SuggestedFixedVersion: "1.1.1", + IsDirectDependency: false, + VulnerabilityOrViolationRow: formats.VulnerabilityOrViolationRow{Technology: coreutils.Nuget, ImpactedDependencyName: "snappier"}, + }, + fixSupported: false, + }, + { + vulnDetails: &utils.VulnerabilityDetails{ + SuggestedFixedVersion: "1.1.1", + IsDirectDependency: true, + VulnerabilityOrViolationRow: formats.VulnerabilityOrViolationRow{Technology: coreutils.Nuget, ImpactedDependencyName: "snappier"}, + }, + fixSupported: true, + }, + }, } - for _, test := range testcases { - t.Run(test.vulnDetails.ImpactedDependencyName+" direct:"+strconv.FormatBool(test.vulnDetails.IsDirectDependency), func(t *testing.T) { - testDataDir := getTestDataDir(t, test.vulnDetails.IsDirectDependency) - pythonPackageHandler := GetCompatiblePackageHandler(test.vulnDetails, &utils.ScanDetails{ - Project: &utils.Project{PipRequirementsFile: test.requirementsPath}}) - cleanup := createTempDirAndChdir(t, testDataDir, test.vulnDetails.Technology.ToString()) - defer cleanup() - err := pythonPackageHandler.UpdateDependency(test.vulnDetails) - if !test.fixSupported { - assert.Error(t, err, "Expected error to occur") - assert.IsType(t, &utils.ErrUnsupportedFix{}, err, "Expected unsupported fix error") - } else { - assert.NoError(t, err) - } - }) + + for _, testBatch := range testCases { + for _, test := range testBatch { + packageHandler := GetCompatiblePackageHandler(test.vulnDetails, test.scanDetails) + t.Run(fmt.Sprintf("%s:%s direct:%s", test.vulnDetails.Technology.ToString()+test.specificTechVersion, test.vulnDetails.ImpactedDependencyName, strconv.FormatBool(test.vulnDetails.IsDirectDependency)), + func(t *testing.T) { + testDataDir := getTestDataDir(t, test.vulnDetails.IsDirectDependency) + cleanup := createTempDirAndChdir(t, testDataDir, test.vulnDetails.Technology.ToString()+test.specificTechVersion) + defer cleanup() + err := packageHandler.UpdateDependency(test.vulnDetails) + if test.fixSupported { + assert.NoError(t, err) + uniquePackageManagerChecks(t, test) + } else { + assert.Error(t, err) + assert.IsType(t, &utils.ErrUnsupportedFix{}, err, "Expected unsupported fix error") + } + }) + } } + } func TestPipPackageRegex(t *testing.T) { @@ -174,123 +263,6 @@ func TestPipPackageRegex(t *testing.T) { } } -// Npm -func TestNpmPackageHandler_UpdateDependency(t *testing.T) { - npmPackageHandler := &NpmPackageHandler{} - testcases := []dependencyFixTest{ - { - vulnDetails: &utils.VulnerabilityDetails{ - SuggestedFixedVersion: "0.8.4", - VulnerabilityOrViolationRow: formats.VulnerabilityOrViolationRow{Technology: coreutils.Npm, ImpactedDependencyName: "mpath"}, - }, - fixSupported: false, - }, - { - vulnDetails: &utils.VulnerabilityDetails{ - SuggestedFixedVersion: "3.0.2", - IsDirectDependency: true, - VulnerabilityOrViolationRow: formats.VulnerabilityOrViolationRow{Technology: coreutils.Npm, ImpactedDependencyName: "minimatch"}, - }, - fixSupported: true, - }, - } - for _, test := range testcases { - t.Run(test.vulnDetails.ImpactedDependencyName+" direct:"+strconv.FormatBool(test.vulnDetails.IsDirectDependency), func(t *testing.T) { - testDataDir := getTestDataDir(t, test.vulnDetails.IsDirectDependency) - cleanup := createTempDirAndChdir(t, testDataDir, test.vulnDetails.Technology.ToString()) - defer cleanup() - err := npmPackageHandler.UpdateDependency(test.vulnDetails) - if !test.fixSupported { - assert.Error(t, err, "Expected error to occur") - assert.IsType(t, &utils.ErrUnsupportedFix{}, err, "Expected unsupported fix error") - } else { - assert.NoError(t, err) - } - }) - } -} - -// Yarn -func TestYarnPackageHandler_UpdateDependency(t *testing.T) { - yarnPackageHandler := &YarnPackageHandler{} - testcases := []dependencyFixTest{ - { - // This test case directs to non-existing directory. It only checks if the dependency update is blocked if the vulnerable dependency is not a direct dependency - vulnDetails: &utils.VulnerabilityDetails{ - SuggestedFixedVersion: "1.2.6", - IsDirectDependency: false, - VulnerabilityOrViolationRow: formats.VulnerabilityOrViolationRow{Technology: coreutils.Yarn, ImpactedDependencyName: "minimist"}, - }, - fixSupported: false, - }, - { - vulnDetails: &utils.VulnerabilityDetails{ - SuggestedFixedVersion: "1.2.6", - IsDirectDependency: true, - VulnerabilityOrViolationRow: formats.VulnerabilityOrViolationRow{Technology: coreutils.Yarn, ImpactedDependencyName: "minimist"}, - }, - fixSupported: true, - specificTechVersion: "1", - }, - { - vulnDetails: &utils.VulnerabilityDetails{ - SuggestedFixedVersion: "1.2.6", - IsDirectDependency: true, - VulnerabilityOrViolationRow: formats.VulnerabilityOrViolationRow{Technology: coreutils.Yarn, ImpactedDependencyName: "minimist"}, - }, - fixSupported: true, - specificTechVersion: "2", - }, - } - for _, test := range testcases { - - t.Run(test.vulnDetails.ImpactedDependencyName+" direct:"+strconv.FormatBool(test.vulnDetails.IsDirectDependency)+",yarn"+test.specificTechVersion, - func(t *testing.T) { - testDataDir := getTestDataDir(t, test.vulnDetails.IsDirectDependency) - cleanup := createTempDirAndChdir(t, testDataDir, test.vulnDetails.Technology.ToString()+test.specificTechVersion) - defer cleanup() - err := yarnPackageHandler.UpdateDependency(test.vulnDetails) - if test.fixSupported { - assert.NoError(t, err) - } else { - assert.Error(t, err, "Expected error to occur") - assert.IsType(t, &utils.ErrUnsupportedFix{}, err, "Expected unsupported fix error") - } - }) - } -} - -// Maven -func TestMavenPackageHandler_UpdateDependency(t *testing.T) { - tests := []dependencyFixTest{ - {vulnDetails: &utils.VulnerabilityDetails{ - SuggestedFixedVersion: "2.7", - VulnerabilityOrViolationRow: formats.VulnerabilityOrViolationRow{Technology: coreutils.Maven, ImpactedDependencyName: "commons-io:commons-io"}, - IsDirectDependency: true}, - fixSupported: true}, - {vulnDetails: &utils.VulnerabilityDetails{ - SuggestedFixedVersion: "4.3.20", - VulnerabilityOrViolationRow: formats.VulnerabilityOrViolationRow{Technology: coreutils.Maven, ImpactedDependencyName: "org.springframework:spring-core"}, - IsDirectDependency: false}, - fixSupported: false}, - } - for _, test := range tests { - t.Run(test.vulnDetails.ImpactedDependencyName+" direct:"+strconv.FormatBool(test.vulnDetails.IsDirectDependency), func(t *testing.T) { - testDataDir := getTestDataDir(t, test.vulnDetails.IsDirectDependency) - cleanup := createTempDirAndChdir(t, testDataDir, test.vulnDetails.Technology.ToString()) - defer cleanup() - mavenPackageHandler := MavenPackageHandler{} - err := mavenPackageHandler.UpdateDependency(test.vulnDetails) - if !test.fixSupported { - assert.Error(t, err, "Expected error to occur") - assert.IsType(t, &utils.ErrUnsupportedFix{}, err, "Expected unsupported fix error") - } else { - assert.NoError(t, err) - } - }) - } -} - // Maven utils functions func TestGetDependenciesFromPomXmlSingleDependency(t *testing.T) { testCases := []string{` @@ -468,7 +440,7 @@ func TestMavenGavReader(t *testing.T) { assert.NoError(t, err) tmpDir, err := os.MkdirTemp("", "") assert.NoError(t, err) - assert.NoError(t, fileutils.CopyDir(filepath.Join("..", "testdata", "projects", "maven"), tmpDir, true, nil)) + assert.NoError(t, biutils.CopyDir(filepath.Join("..", "testdata", "projects", "maven"), tmpDir, true, nil)) assert.NoError(t, os.Chdir(tmpDir)) defer func() { assert.NoError(t, os.Chdir(currDir)) @@ -508,7 +480,7 @@ func TestUpdatePackageVersion(t *testing.T) { assert.NoError(t, err) tmpDir, err := os.MkdirTemp("", "") assert.NoError(t, err) - assert.NoError(t, fileutils.CopyDir(testProjectPath, tmpDir, true, nil)) + assert.NoError(t, biutils.CopyDir(testProjectPath, tmpDir, true, nil)) assert.NoError(t, os.Chdir(tmpDir)) defer func() { assert.NoError(t, os.Chdir(currDir)) @@ -539,7 +511,7 @@ func TestUpdatePropertiesVersion(t *testing.T) { assert.NoError(t, err) tmpDir, err := os.MkdirTemp("", "") assert.NoError(t, err) - assert.NoError(t, fileutils.CopyDir(testProjectPath, tmpDir, true, nil)) + assert.NoError(t, biutils.CopyDir(testProjectPath, tmpDir, true, nil)) assert.NoError(t, os.Chdir(tmpDir)) defer func() { assert.NoError(t, os.Chdir(currDir)) @@ -587,3 +559,42 @@ func assertFixVersionInPackageDescriptor(t *testing.T, test dependencyFixTest, p assert.NotContains(t, string(file), test.vulnDetails) } } + +// This function is intended to add unique checks for specific package managers +func uniquePackageManagerChecks(t *testing.T, test dependencyFixTest) { + technology := test.vulnDetails.Technology + extraArgs := test.uniqueChecksExtraArgs + switch technology { + case coreutils.Go: + packageDescriptor := extraArgs[0] + assertFixVersionInPackageDescriptor(t, test, packageDescriptor) + default: + } +} + +func TestGetFixedPackage(t *testing.T) { + var testcases = []struct { + impactedPackage string + versionOperator string + suggestedFixedVersion string + expectedOutput []string + }{ + { + impactedPackage: "snappier", + versionOperator: " -v ", + suggestedFixedVersion: "1.1.1", + expectedOutput: []string{"snappier", "-v", "1.1.1"}, + }, + { + impactedPackage: "json", + versionOperator: "@", + suggestedFixedVersion: "10.0.0", + expectedOutput: []string{"json@10.0.0"}, + }, + } + + for _, test := range testcases { + fixedPackageArgs := getFixedPackage(test.impactedPackage, test.versionOperator, test.suggestedFixedVersion) + assert.Equal(t, test.expectedOutput, fixedPackageArgs) + } +} diff --git a/packagehandlers/pythonpackagehandler.go b/packagehandlers/pythonpackagehandler.go index 576f0ed0b..de3a03e16 100644 --- a/packagehandlers/pythonpackagehandler.go +++ b/packagehandlers/pythonpackagehandler.go @@ -56,7 +56,7 @@ func (py *PythonPackageHandler) handlePoetry(vulnDetails *utils.VulnerabilityDet return } // Update Poetry lock file as well - return runPackageMangerCommand(coreutils.Poetry.GetExecCommandName(), []string{"update"}) + return runPackageMangerCommand(coreutils.Poetry.GetExecCommandName(), coreutils.Poetry.ToString(), []string{"update"}) } func (py *PythonPackageHandler) handlePip(vulnDetails *utils.VulnerabilityDetails) (err error) { diff --git a/scanpullrequest/scanallpullrequests_test.go b/scanpullrequest/scanallpullrequests_test.go index 87e14a308..f2b78f9c8 100644 --- a/scanpullrequest/scanallpullrequests_test.go +++ b/scanpullrequest/scanallpullrequests_test.go @@ -3,18 +3,17 @@ package scanpullrequest import ( "context" "fmt" + "github.com/golang/mock/gomock" + biutils "github.com/jfrog/build-info-go/utils" + "github.com/jfrog/frogbot/testdata" "github.com/jfrog/frogbot/utils" "github.com/jfrog/frogbot/utils/outputwriter" + "github.com/jfrog/froggit-go/vcsclient" "github.com/jfrog/froggit-go/vcsutils" "github.com/stretchr/testify/assert" "path/filepath" "testing" "time" - - "github.com/golang/mock/gomock" - "github.com/jfrog/frogbot/testdata" - "github.com/jfrog/froggit-go/vcsclient" - "github.com/jfrog/jfrog-client-go/utils/io/fileutils" ) var gitParams = &utils.Repository{ @@ -141,13 +140,13 @@ func TestScanAllPullRequestsMultiRepo(t *testing.T) { err := scanAllPullRequestsCmd.Run(configAggregator, client) if assert.NoError(t, err) { assert.Len(t, frogbotMessages, 4) - expectedMessage := "
\n\n[![](https://raw.githubusercontent.com/jfrog/frogbot/master/resources/v2/vulnerabilitiesBannerPR.png)](https://github.com/jfrog/frogbot#readme)\n\n
\n\n\n## 📦 Vulnerable Dependencies \n\n### ✍️ Summary\n\n
\n\n| SEVERITY | CONTEXTUAL ANALYSIS | DIRECT DEPENDENCIES | IMPACTED DEPENDENCY | FIXED VERSIONS |\n| :---------------------: | :----------------------------------: | :----------------------------------: | :-----------------------------------: | :---------------------------------: | \n| ![](https://raw.githubusercontent.com/jfrog/frogbot/master/resources/v2/notApplicableCritical.png)
Critical | Not Applicable | minimist:1.2.5 | minimist:1.2.5 | [0.2.4]

[1.2.6] |\n\n
\n\n## 👇 Details\n\n\n\n\n- **Severity** 💀 Critical\n- **Contextual Analysis:** Not Applicable\n- **Package Name:** minimist\n- **Current Version:** 1.2.5\n- **Fixed Versions:** [0.2.4],[1.2.6]\n- **CVE:** CVE-2021-44906\n\n**Description:**\n\n[Minimist](https://github.com/substack/minimist) is a simple and very popular argument parser. It is used by more than 14 million by Mar 2022. This package developers stopped developing it since April 2020 and its community released a [newer version](https://github.com/meszaros-lajos-gyorgy/minimist-lite) supported by the community.\n\n\nAn incomplete fix for [CVE-2020-7598](https://nvd.nist.gov/vuln/detail/CVE-2020-7598) partially blocked prototype pollution attacks. Researchers discovered that it does not check for constructor functions which means they can be overridden. This behavior can be triggered easily when using it insecurely (which is the common usage). For example:\n```\nvar argv = parse(['--_.concat.constructor.prototype.y', '123']);\nt.equal((function(){}).foo, undefined);\nt.equal(argv.y, undefined);\n```\nIn this example, `prototype.y` is assigned with `123` which will be derived to every newly created object. \n\nThis vulnerability can be triggered when the attacker-controlled input is parsed using Minimist without any validation. As always with prototype pollution, the impact depends on the code that follows the attack, but denial of service is almost always guaranteed.\n\n**Remediation:**\n\n##### Development mitigations\n\nAdd the `Object.freeze(Object.prototype);` directive once at the beginning of your main JS source code file (ex. `index.js`), preferably after all your `require` directives. This will prevent any changes to the prototype object, thus completely negating prototype pollution attacks.\n\n\n\n\n
\n\n[JFrog Frogbot](https://github.com/jfrog/frogbot#readme)\n\n
\n" + expectedMessage := "
\n\n[![](https://raw.githubusercontent.com/jfrog/frogbot/master/resources/v2/vulnerabilitiesBannerPR.png)](https://github.com/jfrog/frogbot#readme)\n\n
\n\n\n## 📦 Vulnerable Dependencies \n\n### ✍️ Summary\n\n
\n\n| SEVERITY | CONTEXTUAL ANALYSIS | DIRECT DEPENDENCIES | IMPACTED DEPENDENCY | FIXED VERSIONS |\n| :---------------------: | :----------------------------------: | :----------------------------------: | :-----------------------------------: | :---------------------------------: | \n| ![](https://raw.githubusercontent.com/jfrog/frogbot/master/resources/v2/notApplicableCritical.png)
Critical | Not Applicable | minimist:1.2.5 | minimist:1.2.5 | [0.2.4]

[1.2.6] |\n\n
\n\n## 👇 Details\n\n\n\n\n- **Severity** 💀 Critical\n- **Contextual Analysis:** Not Applicable\n- **Package Name:** minimist\n- **Current Version:** 1.2.5\n- **CVE:** CVE-2021-44906\n- **Fixed Versions:** [0.2.4],[1.2.6]\n\n**Description:**\n\n[Minimist](https://github.com/substack/minimist) is a simple and very popular argument parser. It is used by more than 14 million by Mar 2022. This package developers stopped developing it since April 2020 and its community released a [newer version](https://github.com/meszaros-lajos-gyorgy/minimist-lite) supported by the community.\n\n\nAn incomplete fix for [CVE-2020-7598](https://nvd.nist.gov/vuln/detail/CVE-2020-7598) partially blocked prototype pollution attacks. Researchers discovered that it does not check for constructor functions which means they can be overridden. This behavior can be triggered easily when using it insecurely (which is the common usage). For example:\n```\nvar argv = parse(['--_.concat.constructor.prototype.y', '123']);\nt.equal((function(){}).foo, undefined);\nt.equal(argv.y, undefined);\n```\nIn this example, `prototype.y` is assigned with `123` which will be derived to every newly created object. \n\nThis vulnerability can be triggered when the attacker-controlled input is parsed using Minimist without any validation. As always with prototype pollution, the impact depends on the code that follows the attack, but denial of service is almost always guaranteed.\n\n**Remediation:**\n\n##### Development mitigations\n\nAdd the `Object.freeze(Object.prototype);` directive once at the beginning of your main JS source code file (ex. `index.js`), preferably after all your `require` directives. This will prevent any changes to the prototype object, thus completely negating prototype pollution attacks.\n\n\n\n\n---\n
\n\n[🐸 JFrog Frogbot](https://github.com/jfrog/frogbot#readme)\n\n
\n" assert.Equal(t, expectedMessage, frogbotMessages[0]) - expectedMessage = "
\n\n[![](https://raw.githubusercontent.com/jfrog/frogbot/master/resources/v2/noVulnerabilityBannerPR.png)](https://github.com/jfrog/frogbot#readme)\n\n
\n\n\n
\n\n[JFrog Frogbot](https://github.com/jfrog/frogbot#readme)\n\n
\n" + expectedMessage = "
\n\n[![](https://raw.githubusercontent.com/jfrog/frogbot/master/resources/v2/noVulnerabilityBannerPR.png)](https://github.com/jfrog/frogbot#readme)\n\n
\n\n\n---\n
\n\n[🐸 JFrog Frogbot](https://github.com/jfrog/frogbot#readme)\n\n
\n" assert.Equal(t, expectedMessage, frogbotMessages[1]) - expectedMessage = "
\n\n[![](https://raw.githubusercontent.com/jfrog/frogbot/master/resources/v2/vulnerabilitiesBannerPR.png)](https://github.com/jfrog/frogbot#readme)\n\n
\n\n\n## 📦 Vulnerable Dependencies \n\n### ✍️ Summary\n\n
\n\n| SEVERITY | CONTEXTUAL ANALYSIS | DIRECT DEPENDENCIES | IMPACTED DEPENDENCY | FIXED VERSIONS |\n| :---------------------: | :----------------------------------: | :----------------------------------: | :-----------------------------------: | :---------------------------------: | \n| ![](https://raw.githubusercontent.com/jfrog/frogbot/master/resources/v2/applicableHighSeverity.png)
High | Undetermined | pip-example:1.2.3 | pyjwt:1.7.1 | [2.4.0] |\n\n
\n\n## 👇 Details\n\n\n\n\n- **Severity** 🔥 High\n- **Contextual Analysis:** Undetermined\n- **Package Name:** pyjwt\n- **Current Version:** 1.7.1\n- **Fixed Version:** [2.4.0]\n- **CVE:** CVE-2022-29217\n\n**Description:**\n\n[PyJWT](https://pypi.org/project/PyJWT) is a Python implementation of the RFC 7519 standard (JSON Web Tokens). [JSON Web Tokens](https://jwt.io/) are an open, industry standard method for representing claims securely between two parties. A JWT comes with an inline signature that is meant to be verified by the receiving application. JWT supports multiple standard algorithms, and the algorithm itself is **specified in the JWT token itself**.\n\nThe PyJWT library uses the signature-verification algorithm that is specified in the JWT token (that is completely attacker-controlled), however - it requires the validating application to pass an `algorithms` kwarg that specifies the expected algorithms in order to avoid key confusion. Unfortunately - a non-default value `algorithms=jwt.algorithms.get_default_algorithms()` exists that allows all algorithms.\nThe PyJWT library also tries to mitigate key confusions in this case, by making sure that public keys are not used as an HMAC secret. For example, HMAC secrets that begin with `-----BEGIN PUBLIC KEY-----` are rejected when encoding a JWT.\n\nIt has been discovered that due to missing key-type checks, in cases where -\n1. The vulnerable application expects to receive a JWT signed with an Elliptic-Curve key (one of the algorithms `ES256`, `ES384`, `ES512`, `EdDSA`)\n2. The vulnerable application decodes the JWT token using the non-default kwarg `algorithms=jwt.algorithms.get_default_algorithms()` (or alternatively, `algorithms` contain both an HMAC-based algorithm and an EC-based algorithm)\n\nAn attacker can create an HMAC-signed (ex. `HS256`) JWT token, using the (well-known!) EC public key as the HMAC key. The validating application will accept this JWT token as a valid token.\n\nFor example, an application might have planned to validate an `EdDSA`-signed token that was generated as follows -\n```python\n# Making a good jwt token that should work by signing it with the private key\nencoded_good = jwt.encode({\"test\": 1234}, priv_key_bytes, algorithm=\"EdDSA\")\n```\nAn attacker in posession of the public key can generate an `HMAC`-signed token to confuse PyJWT - \n```python\n# Using HMAC with the public key to trick the receiver to think that the public key is a HMAC secret\nencoded_bad = jwt.encode({\"test\": 1234}, pub_key_bytes, algorithm=\"HS256\")\n```\n\nThe following vulnerable `decode` call will accept BOTH of the above tokens as valid - \n```\ndecoded = jwt.decode(encoded_good, pub_key_bytes, \nalgorithms=jwt.algorithms.get_default_algorithms())\n```\n\n**Remediation:**\n\n##### Development mitigations\n\nUse a specific algorithm instead of `jwt.algorithms.get_default_algorithms`.\nFor example, replace the following call - \n`jwt.decode(encoded_jwt, pub_key_bytes, algorithms=jwt.algorithms.get_default_algorithms())`\nWith -\n`jwt.decode(encoded_jwt, pub_key_bytes, algorithms=[\"ES256\"])`\n\n\n\n\n
\n\n[JFrog Frogbot](https://github.com/jfrog/frogbot#readme)\n\n
\n" + expectedMessage = "
\n\n[![](https://raw.githubusercontent.com/jfrog/frogbot/master/resources/v2/vulnerabilitiesBannerPR.png)](https://github.com/jfrog/frogbot#readme)\n\n
\n\n\n## 📦 Vulnerable Dependencies \n\n### ✍️ Summary\n\n
\n\n| SEVERITY | CONTEXTUAL ANALYSIS | DIRECT DEPENDENCIES | IMPACTED DEPENDENCY | FIXED VERSIONS |\n| :---------------------: | :----------------------------------: | :----------------------------------: | :-----------------------------------: | :---------------------------------: | \n| ![](https://raw.githubusercontent.com/jfrog/frogbot/master/resources/v2/applicableHighSeverity.png)
High | Undetermined | pip-example:1.2.3 | pyjwt:1.7.1 | [2.4.0] |\n\n
\n\n## 👇 Details\n\n\n\n\n- **Severity** 🔥 High\n- **Contextual Analysis:** Undetermined\n- **Package Name:** pyjwt\n- **Current Version:** 1.7.1\n- **CVE:** CVE-2022-29217\n- **Fixed Version:** [2.4.0]\n\n**Description:**\n\n[PyJWT](https://pypi.org/project/PyJWT) is a Python implementation of the RFC 7519 standard (JSON Web Tokens). [JSON Web Tokens](https://jwt.io/) are an open, industry standard method for representing claims securely between two parties. A JWT comes with an inline signature that is meant to be verified by the receiving application. JWT supports multiple standard algorithms, and the algorithm itself is **specified in the JWT token itself**.\n\nThe PyJWT library uses the signature-verification algorithm that is specified in the JWT token (that is completely attacker-controlled), however - it requires the validating application to pass an `algorithms` kwarg that specifies the expected algorithms in order to avoid key confusion. Unfortunately - a non-default value `algorithms=jwt.algorithms.get_default_algorithms()` exists that allows all algorithms.\nThe PyJWT library also tries to mitigate key confusions in this case, by making sure that public keys are not used as an HMAC secret. For example, HMAC secrets that begin with `-----BEGIN PUBLIC KEY-----` are rejected when encoding a JWT.\n\nIt has been discovered that due to missing key-type checks, in cases where -\n1. The vulnerable application expects to receive a JWT signed with an Elliptic-Curve key (one of the algorithms `ES256`, `ES384`, `ES512`, `EdDSA`)\n2. The vulnerable application decodes the JWT token using the non-default kwarg `algorithms=jwt.algorithms.get_default_algorithms()` (or alternatively, `algorithms` contain both an HMAC-based algorithm and an EC-based algorithm)\n\nAn attacker can create an HMAC-signed (ex. `HS256`) JWT token, using the (well-known!) EC public key as the HMAC key. The validating application will accept this JWT token as a valid token.\n\nFor example, an application might have planned to validate an `EdDSA`-signed token that was generated as follows -\n```python\n# Making a good jwt token that should work by signing it with the private key\nencoded_good = jwt.encode({\"test\": 1234}, priv_key_bytes, algorithm=\"EdDSA\")\n```\nAn attacker in posession of the public key can generate an `HMAC`-signed token to confuse PyJWT - \n```python\n# Using HMAC with the public key to trick the receiver to think that the public key is a HMAC secret\nencoded_bad = jwt.encode({\"test\": 1234}, pub_key_bytes, algorithm=\"HS256\")\n```\n\nThe following vulnerable `decode` call will accept BOTH of the above tokens as valid - \n```\ndecoded = jwt.decode(encoded_good, pub_key_bytes, \nalgorithms=jwt.algorithms.get_default_algorithms())\n```\n\n**Remediation:**\n\n##### Development mitigations\n\nUse a specific algorithm instead of `jwt.algorithms.get_default_algorithms`.\nFor example, replace the following call - \n`jwt.decode(encoded_jwt, pub_key_bytes, algorithms=jwt.algorithms.get_default_algorithms())`\nWith -\n`jwt.decode(encoded_jwt, pub_key_bytes, algorithms=[\"ES256\"])`\n\n\n\n\n---\n
\n\n[🐸 JFrog Frogbot](https://github.com/jfrog/frogbot#readme)\n\n
\n" assert.Equal(t, expectedMessage, frogbotMessages[2]) - expectedMessage = "
\n\n[![](https://raw.githubusercontent.com/jfrog/frogbot/master/resources/v2/noVulnerabilityBannerPR.png)](https://github.com/jfrog/frogbot#readme)\n\n
\n\n\n
\n\n[JFrog Frogbot](https://github.com/jfrog/frogbot#readme)\n\n
\n" + expectedMessage = "
\n\n[![](https://raw.githubusercontent.com/jfrog/frogbot/master/resources/v2/noVulnerabilityBannerPR.png)](https://github.com/jfrog/frogbot#readme)\n\n
\n\n\n---\n
\n\n[🐸 JFrog Frogbot](https://github.com/jfrog/frogbot#readme)\n\n
\n" assert.Equal(t, expectedMessage, frogbotMessages[3]) } } @@ -183,9 +182,9 @@ func TestScanAllPullRequests(t *testing.T) { err := scanAllPullRequestsCmd.Run(paramsAggregator, client) assert.NoError(t, err) assert.Len(t, frogbotMessages, 2) - expectedMessage := "**🚨 Frogbot scanned this pull request and found the below:**\n\n---\n## 📦 Vulnerable Dependencies\n---\n\n### ✍️ Summary \n\n| SEVERITY | CONTEXTUAL ANALYSIS | DIRECT DEPENDENCIES | IMPACTED DEPENDENCY | FIXED VERSIONS |\n| :---------------------: | :----------------------------------: | :----------------------------------: | :-----------------------------------: | :---------------------------------: | \n| Critical | Not Applicable | minimist:1.2.5 | minimist:1.2.5 | [0.2.4], [1.2.6] |\n\n---\n### 👇 Details\n---\n\n\n#### minimist 1.2.5\n\n\n- **Severity** 💀 Critical\n- **Contextual Analysis:** Not Applicable\n- **Package Name:** minimist\n- **Current Version:** 1.2.5\n- **Fixed Versions:** [0.2.4],[1.2.6]\n- **CVE:** CVE-2021-44906\n\n**Description:**\n\n[Minimist](https://github.com/substack/minimist) is a simple and very popular argument parser. It is used by more than 14 million by Mar 2022. This package developers stopped developing it since April 2020 and its community released a [newer version](https://github.com/meszaros-lajos-gyorgy/minimist-lite) supported by the community.\n\n\nAn incomplete fix for [CVE-2020-7598](https://nvd.nist.gov/vuln/detail/CVE-2020-7598) partially blocked prototype pollution attacks. Researchers discovered that it does not check for constructor functions which means they can be overridden. This behavior can be triggered easily when using it insecurely (which is the common usage). For example:\n```\nvar argv = parse(['--_.concat.constructor.prototype.y', '123']);\nt.equal((function(){}).foo, undefined);\nt.equal(argv.y, undefined);\n```\nIn this example, `prototype.y` is assigned with `123` which will be derived to every newly created object. \n\nThis vulnerability can be triggered when the attacker-controlled input is parsed using Minimist without any validation. As always with prototype pollution, the impact depends on the code that follows the attack, but denial of service is almost always guaranteed.\n\n**Remediation:**\n\n##### Development mitigations\n\nAdd the `Object.freeze(Object.prototype);` directive once at the beginning of your main JS source code file (ex. `index.js`), preferably after all your `require` directives. This will prevent any changes to the prototype object, thus completely negating prototype pollution attacks.\n\n\n\n\n\n[JFrog Frogbot](https://github.com/jfrog/frogbot#readme)" + expectedMessage := "**🚨 Frogbot scanned this pull request and found the below:**\n\n---\n## 📦 Vulnerable Dependencies\n---\n\n### ✍️ Summary \n\n| SEVERITY | CONTEXTUAL ANALYSIS | DIRECT DEPENDENCIES | IMPACTED DEPENDENCY | FIXED VERSIONS |\n| :---------------------: | :----------------------------------: | :----------------------------------: | :-----------------------------------: | :---------------------------------: | \n| Critical | Not Applicable | minimist:1.2.5 | minimist:1.2.5 | [0.2.4], [1.2.6] |\n\n---\n### 👇 Details\n---\n\n\n#### [ CVE-2021-44906 ] minimist 1.2.5\n\n\n- **Severity** 💀 Critical\n- **Contextual Analysis:** Not Applicable\n- **Package Name:** minimist\n- **Current Version:** 1.2.5\n- **CVE:** CVE-2021-44906\n- **Fixed Versions:** [0.2.4],[1.2.6]\n\n**Description:**\n\n[Minimist](https://github.com/substack/minimist) is a simple and very popular argument parser. It is used by more than 14 million by Mar 2022. This package developers stopped developing it since April 2020 and its community released a [newer version](https://github.com/meszaros-lajos-gyorgy/minimist-lite) supported by the community.\n\n\nAn incomplete fix for [CVE-2020-7598](https://nvd.nist.gov/vuln/detail/CVE-2020-7598) partially blocked prototype pollution attacks. Researchers discovered that it does not check for constructor functions which means they can be overridden. This behavior can be triggered easily when using it insecurely (which is the common usage). For example:\n```\nvar argv = parse(['--_.concat.constructor.prototype.y', '123']);\nt.equal((function(){}).foo, undefined);\nt.equal(argv.y, undefined);\n```\nIn this example, `prototype.y` is assigned with `123` which will be derived to every newly created object. \n\nThis vulnerability can be triggered when the attacker-controlled input is parsed using Minimist without any validation. As always with prototype pollution, the impact depends on the code that follows the attack, but denial of service is almost always guaranteed.\n\n**Remediation:**\n\n##### Development mitigations\n\nAdd the `Object.freeze(Object.prototype);` directive once at the beginning of your main JS source code file (ex. `index.js`), preferably after all your `require` directives. This will prevent any changes to the prototype object, thus completely negating prototype pollution attacks.\n\n\n\n\n\n[🐸 JFrog Frogbot](https://github.com/jfrog/frogbot#readme)" assert.Equal(t, expectedMessage, frogbotMessages[0]) - expectedMessage = "**👍 Frogbot scanned this pull request and found that it did not add vulnerable dependencies.** \n\n\n[JFrog Frogbot](https://github.com/jfrog/frogbot#readme)" + expectedMessage = "**👍 Frogbot scanned this pull request and found that it did not add vulnerable dependencies.** \n\n\n[🐸 JFrog Frogbot](https://github.com/jfrog/frogbot#readme)" assert.Equal(t, expectedMessage, frogbotMessages[1]) } @@ -199,6 +198,7 @@ func getMockClient(t *testing.T, frogbotMessages *[]string, mockParams ...MockPa client.EXPECT().ListOpenPullRequests(context.Background(), params.repoOwner, params.repoName).Return([]vcsclient.PullRequestInfo{{ID: 1, Source: sourceBranchInfo, Target: targetBranchInfo}, {ID: 2, Source: targetBranchInfo, Target: targetBranchInfo}}, nil) // Return empty comments slice so expect the code to scan both pull requests. client.EXPECT().ListPullRequestComments(context.Background(), params.repoOwner, params.repoName, gomock.Any()).Return([]vcsclient.CommentInfo{}, nil).AnyTimes() + client.EXPECT().ListPullRequestReviewComments(context.Background(), params.repoOwner, params.repoName, gomock.Any()).Return([]vcsclient.CommentInfo{}, nil).AnyTimes() // Copy test project according to the given branch name, instead of download it. client.EXPECT().DownloadRepository(context.Background(), params.repoOwner, params.repoName, gomock.Any(), gomock.Any()).DoAndReturn(fakeRepoDownload).AnyTimes() // Capture the result comment post @@ -206,9 +206,16 @@ func getMockClient(t *testing.T, frogbotMessages *[]string, mockParams ...MockPa *frogbotMessages = append(*frogbotMessages, content) return nil }).AnyTimes() + client.EXPECT().AddPullRequestReviewComments(context.Background(), params.repoOwner, params.repoName, gomock.Any(), gomock.Any()).DoAndReturn(func(_ context.Context, _, _, content string, _ int) error { + *frogbotMessages = append(*frogbotMessages, content) + return nil + }).AnyTimes() client.EXPECT().DeletePullRequestComment(context.Background(), params.repoOwner, params.repoName, gomock.Any(), gomock.Any()).Return(nil).AnyTimes() + client.EXPECT().DeletePullRequestReviewComments(context.Background(), params.repoOwner, params.repoName, gomock.Any(), gomock.Any()).Return(nil).AnyTimes() // Return private repositories visibility client.EXPECT().GetRepositoryInfo(context.Background(), gomock.Any(), gomock.Any()).Return(vcsclient.RepositoryInfo{RepositoryVisibility: vcsclient.Private}, nil).AnyTimes() + // Return latest commit info for XSC context. + client.EXPECT().GetLatestCommit(context.Background(), params.repoOwner, params.repoName, gomock.Any()).Return(vcsclient.CommitInfo{}, nil).AnyTimes() } return client } @@ -222,7 +229,7 @@ func fakeRepoDownload(_ context.Context, _, _, testProject, targetDir string) er if err != nil { return err } - return fileutils.CopyDir(sourceDir, targetDir, true, []string{}) + return biutils.CopyDir(sourceDir, targetDir, true, []string{}) } func CreateMockVcsClient(t *testing.T) *testdata.MockVcsClient { diff --git a/scanpullrequest/scanpullrequest.go b/scanpullrequest/scanpullrequest.go index 293b1e1de..757e817e9 100644 --- a/scanpullrequest/scanpullrequest.go +++ b/scanpullrequest/scanpullrequest.go @@ -4,17 +4,18 @@ import ( "context" "errors" "fmt" + "os" + "github.com/jfrog/frogbot/utils" "github.com/jfrog/frogbot/utils/outputwriter" "github.com/jfrog/froggit-go/vcsclient" "github.com/jfrog/froggit-go/vcsutils" "github.com/jfrog/gofrog/datastructures" - audit "github.com/jfrog/jfrog-cli-core/v2/xray/commands/audit/generic" + "github.com/jfrog/jfrog-cli-core/v2/xray/commands/audit" "github.com/jfrog/jfrog-cli-core/v2/xray/formats" xrayutils "github.com/jfrog/jfrog-cli-core/v2/xray/utils" "github.com/jfrog/jfrog-client-go/utils/log" "github.com/jfrog/jfrog-client-go/xray/services" - "os" ) const ( @@ -91,19 +92,19 @@ func scanPullRequest(repo *utils.Repository, client vcsclient.VcsClient) (err er log.Info("-----------------------------------------------------------") // Audit PR code - vulnerabilitiesRows, iacRows, secretsRows, err := auditPullRequest(repo, client, pullRequestDetails) + vulnerabilitiesRows, iacIssues, secretsIssues, sastIssues, err := auditPullRequest(repo, client, pullRequestDetails) if err != nil { return } - shouldSendExposedSecretsEmail := len(secretsRows) > 0 && repo.SmtpServer != "" + shouldSendExposedSecretsEmail := len(secretsIssues) > 0 && repo.SmtpServer != "" if shouldSendExposedSecretsEmail { prSourceDetails := pullRequestDetails.Source secretsEmailDetails := utils.NewSecretsEmailDetails( client, repo.GitProvider, prSourceDetails.Owner, prSourceDetails.Repository, prSourceDetails.Name, pullRequestDetails.URL, - secretsRows, repo.EmailDetails) + secretsIssues, repo.EmailDetails) if err = utils.AlertSecretsExposed(secretsEmailDetails); err != nil { return } @@ -115,23 +116,33 @@ func scanPullRequest(repo *utils.Repository, client vcsclient.VcsClient) (err er } // Create a pull request message - message := createPullRequestMessage(vulnerabilitiesRows, iacRows, repo.OutputWriter) + message := createPullRequestMessage(vulnerabilitiesRows, iacIssues, sastIssues, repo.OutputWriter) - // Add comment to the pull request + // Add SCA scan comment if err = client.AddPullRequestComment(context.Background(), repo.RepoOwner, repo.RepoName, message, int(pullRequestDetails.ID)); err != nil { err = errors.New("couldn't add pull request comment: " + err.Error()) return } + // Handle review comments at the pull request + if err = utils.AddReviewComments(repo, int(pullRequestDetails.ID), client, vulnerabilitiesRows, iacIssues, sastIssues); err != nil { + err = errors.New("couldn't add review comments: " + err.Error()) + return + } + // Fail the Frogbot task if a security issue is found and Frogbot isn't configured to avoid the failure. - if repo.FailOnSecurityIssues != nil && *repo.FailOnSecurityIssues && len(vulnerabilitiesRows) > 0 { + if repo.FailOnSecurityIssues != nil && *repo.FailOnSecurityIssues && isDetectedIssues(vulnerabilitiesRows, iacIssues, sastIssues) { err = errors.New(securityIssueFoundErr) } return } +func isDetectedIssues(vulnerabilitiesRows []formats.VulnerabilityOrViolationRow, iacRows []formats.SourceCodeRow, sastIssues []formats.SourceCodeRow) bool { + return len(vulnerabilitiesRows) > 0 || len(iacRows) > 0 || len(sastIssues) > 0 +} + // Downloads Pull Requests branches code and audits them -func auditPullRequest(repoConfig *utils.Repository, client vcsclient.VcsClient, pullRequestDetails vcsclient.PullRequestInfo) (vulnerabilitiesRows []formats.VulnerabilityOrViolationRow, iacRows []formats.IacSecretsRow, secretsRows []formats.IacSecretsRow, err error) { +func auditPullRequest(repoConfig *utils.Repository, client vcsclient.VcsClient, pullRequestDetails vcsclient.PullRequestInfo) (vulnerabilitiesRows []formats.VulnerabilityOrViolationRow, iacRows []formats.SourceCodeRow, secretsRows []formats.SourceCodeRow, sastRows []formats.SourceCodeRow, err error) { // Download source branch sourceBranchInfo := pullRequestDetails.Source sourceBranchWd, cleanupSource, err := utils.DownloadRepoToTempDir(client, sourceBranchInfo.Owner, sourceBranchInfo.Repository, sourceBranchInfo.Name) @@ -185,6 +196,7 @@ func auditPullRequest(repoConfig *utils.Repository, client vcsclient.VcsClient, vulnerabilitiesRows = append(vulnerabilitiesRows, allIssuesRows...) iacRows = append(iacRows, xrayutils.PrepareIacs(sourceScanResults.IacScanResults)...) secretsRows = append(secretsRows, xrayutils.PrepareSecrets(sourceScanResults.SecretsScanResults)...) + sastRows = append(sastRows, xrayutils.PrepareSast(sourceScanResults.SastScanResults)...) continue } @@ -192,61 +204,103 @@ func auditPullRequest(repoConfig *utils.Repository, client vcsclient.VcsClient, var targetResults *audit.Results workingDirs = utils.GetFullPathWorkingDirs(scanDetails.Project.WorkingDirs, targetBranchWd) targetResults, err = scanDetails.RunInstallAndAudit(workingDirs...) + if err != nil { return } // Get new issues var newVulnerabilities []formats.VulnerabilityOrViolationRow - var newIacs, newSecrets []formats.IacSecretsRow - if newVulnerabilities, newIacs, newSecrets, err = getNewIssues(targetResults, sourceResults); err != nil { + var newIacs, newSecrets, newSast []formats.SourceCodeRow + if newVulnerabilities, newIacs, newSecrets, newSast, err = getNewIssues(targetResults, sourceResults); err != nil { return } vulnerabilitiesRows = append(vulnerabilitiesRows, newVulnerabilities...) iacRows = append(iacRows, newIacs...) secretsRows = append(secretsRows, newSecrets...) + sastRows = append(sastRows, newSast...) } + + convertPathsToRelative(vulnerabilitiesRows, iacRows, secretsRows, sastRows, sourceBranchWd, targetBranchWd) return } -func getNewIssues(targetResults, sourceResults *audit.Results) ([]formats.VulnerabilityOrViolationRow, []formats.IacSecretsRow, []formats.IacSecretsRow, error) { +func convertPathsToRelative(vulnerabilitiesRows []formats.VulnerabilityOrViolationRow, iacRows []formats.SourceCodeRow, secretsRows []formats.SourceCodeRow, sastRows []formats.SourceCodeRow, sourceWd, targetWd string) { + for _, row := range vulnerabilitiesRows { + for _, cve := range row.Cves { + if cve.Applicability != nil { + for i := range cve.Applicability.Evidence { + cve.Applicability.Evidence[i].File = xrayutils.ExtractRelativePath(cve.Applicability.Evidence[i].File, sourceWd) + cve.Applicability.Evidence[i].File = xrayutils.ExtractRelativePath(cve.Applicability.Evidence[i].File, targetWd) + } + } + } + } + for i := range iacRows { + iacRows[i].Location.File = xrayutils.ExtractRelativePath(iacRows[i].Location.File, sourceWd) + iacRows[i].Location.File = xrayutils.ExtractRelativePath(iacRows[i].Location.File, targetWd) + } + for i := range secretsRows { + secretsRows[i].Location.File = xrayutils.ExtractRelativePath(secretsRows[i].Location.File, sourceWd) + secretsRows[i].Location.File = xrayutils.ExtractRelativePath(secretsRows[i].Location.File, targetWd) + } + for i := range sastRows { + sastRows[i].Location.File = xrayutils.ExtractRelativePath(sastRows[i].Location.File, sourceWd) + sastRows[i].Location.File = xrayutils.ExtractRelativePath(sastRows[i].Location.File, targetWd) + for f := range sastRows[i].CodeFlow { + for l := range sastRows[i].CodeFlow[f] { + sastRows[i].CodeFlow[f][l].File = xrayutils.ExtractRelativePath(sastRows[i].CodeFlow[f][l].File, sourceWd) + sastRows[i].CodeFlow[f][l].File = xrayutils.ExtractRelativePath(sastRows[i].CodeFlow[f][l].File, targetWd) + } + } + } +} + +func getNewIssues(targetResults, sourceResults *audit.Results) ([]formats.VulnerabilityOrViolationRow, []formats.SourceCodeRow, []formats.SourceCodeRow, []formats.SourceCodeRow, error) { var newVulnerabilities []formats.VulnerabilityOrViolationRow var err error if len(sourceResults.ExtendedScanResults.XrayResults) > 0 { if newVulnerabilities, err = createNewVulnerabilitiesRows(targetResults, sourceResults); err != nil { - return nil, nil, nil, err + return nil, nil, nil, nil, err } } - var newIacs []formats.IacSecretsRow + var newIacs []formats.SourceCodeRow if len(sourceResults.ExtendedScanResults.IacScanResults) > 0 { targetIacRows := xrayutils.PrepareIacs(targetResults.ExtendedScanResults.IacScanResults) sourceIacRows := xrayutils.PrepareIacs(sourceResults.ExtendedScanResults.IacScanResults) - newIacs = createNewIacOrSecretsRows(targetIacRows, sourceIacRows) + newIacs = createNewSourceCodeRows(targetIacRows, sourceIacRows) } - var newSecrets []formats.IacSecretsRow + var newSecrets []formats.SourceCodeRow if len(sourceResults.ExtendedScanResults.SecretsScanResults) > 0 { - targetSecretsRows := xrayutils.PrepareSecrets(targetResults.ExtendedScanResults.SecretsScanResults) - sourceSecretsRows := xrayutils.PrepareSecrets(sourceResults.ExtendedScanResults.SecretsScanResults) - newSecrets = createNewIacOrSecretsRows(targetSecretsRows, sourceSecretsRows) + targetSecretsRows := xrayutils.PrepareIacs(targetResults.ExtendedScanResults.SecretsScanResults) + sourceSecretsRows := xrayutils.PrepareIacs(sourceResults.ExtendedScanResults.SecretsScanResults) + newSecrets = createNewSourceCodeRows(targetSecretsRows, sourceSecretsRows) + } + + var newSast []formats.SourceCodeRow + if len(targetResults.ExtendedScanResults.SastScanResults) > 0 { + targetSastRows := xrayutils.PrepareSast(targetResults.ExtendedScanResults.SastScanResults) + sourceSastRows := xrayutils.PrepareSast(sourceResults.ExtendedScanResults.SastScanResults) + newSast = createNewSourceCodeRows(targetSastRows, sourceSastRows) } - return newVulnerabilities, newIacs, newSecrets, nil + return newVulnerabilities, newIacs, newSecrets, newSast, nil } -func createNewIacOrSecretsRows(targetResults, sourceResults []formats.IacSecretsRow) []formats.IacSecretsRow { - targetIacOrSecretsVulnerabilitiesKeys := datastructures.MakeSet[string]() +func createNewSourceCodeRows(targetResults, sourceResults []formats.SourceCodeRow) []formats.SourceCodeRow { + targetSourceCodeVulnerabilitiesKeys := datastructures.MakeSet[string]() for _, row := range targetResults { - targetIacOrSecretsVulnerabilitiesKeys.Add(row.File + row.Text) + targetSourceCodeVulnerabilitiesKeys.Add(row.File + row.Snippet) } - var addedIacOrSecretsVulnerabilities []formats.IacSecretsRow + var addedSourceCodeVulnerabilities []formats.SourceCodeRow for _, row := range sourceResults { - if !targetIacOrSecretsVulnerabilitiesKeys.Exists(row.File + row.Text) { - addedIacOrSecretsVulnerabilities = append(addedIacOrSecretsVulnerabilities, row) + if !targetSourceCodeVulnerabilitiesKeys.Exists(row.File + row.Snippet) { + addedSourceCodeVulnerabilities = append(addedSourceCodeVulnerabilities, row) } } - return addedIacOrSecretsVulnerabilities + return addedSourceCodeVulnerabilities } // Create vulnerabilities rows. The rows should contain only the new issues added by this PR @@ -338,11 +392,11 @@ func getNewVulnerabilities(targetScan, sourceScan services.ScanResponse, auditRe return } -func createPullRequestMessage(vulnerabilitiesRows []formats.VulnerabilityOrViolationRow, iacRows []formats.IacSecretsRow, writer outputwriter.OutputWriter) string { - if len(vulnerabilitiesRows) == 0 && len(iacRows) == 0 { +func createPullRequestMessage(vulnerabilitiesRows []formats.VulnerabilityOrViolationRow, iacIssues, sastIssues []formats.SourceCodeRow, writer outputwriter.OutputWriter) string { + if !isDetectedIssues(vulnerabilitiesRows, iacIssues, sastIssues) { return writer.NoVulnerabilitiesTitle() + writer.UntitledForJasMsg() + writer.Footer() } - return writer.VulnerabilitiesTitle(true) + writer.VulnerabilitiesContent(vulnerabilitiesRows) + writer.IacContent(iacRows) + writer.UntitledForJasMsg() + writer.Footer() + return writer.VulnerabilitiesTitle(true) + writer.VulnerabilitiesContent(vulnerabilitiesRows) + writer.UntitledForJasMsg() + writer.Footer() } func deleteExistingPullRequestComment(repository *utils.Repository, client vcsclient.VcsClient) error { diff --git a/scanpullrequest/scanpullrequest_test.go b/scanpullrequest/scanpullrequest_test.go index 6a729d19d..32b2b62c0 100644 --- a/scanpullrequest/scanpullrequest_test.go +++ b/scanpullrequest/scanpullrequest_test.go @@ -5,27 +5,29 @@ import ( "context" "errors" "fmt" + "io" + "net/http" + "net/http/httptest" + "os" + "path/filepath" + "strings" + "testing" + "time" + "github.com/jfrog/frogbot/utils" "github.com/jfrog/frogbot/utils/outputwriter" "github.com/jfrog/froggit-go/vcsclient" "github.com/jfrog/froggit-go/vcsutils" coreconfig "github.com/jfrog/jfrog-cli-core/v2/utils/config" "github.com/jfrog/jfrog-cli-core/v2/utils/coreutils" - audit "github.com/jfrog/jfrog-cli-core/v2/xray/commands/audit/generic" + "github.com/jfrog/jfrog-cli-core/v2/xray/commands/audit" "github.com/jfrog/jfrog-cli-core/v2/xray/formats" xrayutils "github.com/jfrog/jfrog-cli-core/v2/xray/utils" "github.com/jfrog/jfrog-client-go/utils/io/fileutils" "github.com/jfrog/jfrog-client-go/utils/log" "github.com/jfrog/jfrog-client-go/xray/services" + "github.com/owenrumney/go-sarif/v2/sarif" "github.com/stretchr/testify/assert" - "io" - "net/http" - "net/http/httptest" - "os" - "path/filepath" - "strings" - "testing" - "time" ) const ( @@ -267,7 +269,7 @@ func TestGetNewVulnerabilities(t *testing.T) { IssueId: "XRAY-2", Severity: "low", ImpactedDependencyName: "component-C", - Cves: []formats.CveRow{{Id: "CVE-2023-4321"}}, + Cves: []formats.CveRow{{Id: "CVE-2023-4321", Applicability: &formats.Applicability{Status: "Applicable", Evidence: []formats.Evidence{{Location: formats.Location{File: "file1", StartLine: 1, StartColumn: 10}}}}}}, Technology: coreutils.Yarn, }, { @@ -275,7 +277,7 @@ func TestGetNewVulnerabilities(t *testing.T) { Applicable: "Applicable", IssueId: "XRAY-2", Severity: "low", - Cves: []formats.CveRow{{Id: "CVE-2023-4321"}}, + Cves: []formats.CveRow{{Id: "CVE-2023-4321", Applicability: &formats.Applicability{Status: "Applicable", Evidence: []formats.Evidence{{Location: formats.Location{File: "file1", StartLine: 1, StartColumn: 10}}}}}}, ImpactedDependencyName: "component-D", Technology: coreutils.Yarn, }, @@ -283,8 +285,16 @@ func TestGetNewVulnerabilities(t *testing.T) { // Run createNewIssuesRows and make sure that only the XRAY-2 vulnerability exists in the results rows, err := createNewVulnerabilitiesRows( - &audit.Results{ExtendedScanResults: &xrayutils.ExtendedScanResults{XrayResults: []services.ScanResponse{previousScan}, EntitledForJas: true, ApplicabilityScanResults: map[string]string{"CVE-2023-4321": "Applicable"}}}, - &audit.Results{ExtendedScanResults: &xrayutils.ExtendedScanResults{XrayResults: []services.ScanResponse{currentScan}, EntitledForJas: true, ApplicabilityScanResults: map[string]string{"CVE-2023-4321": "Applicable"}}}, + &audit.Results{ExtendedScanResults: &xrayutils.ExtendedScanResults{XrayResults: []services.ScanResponse{previousScan}, EntitledForJas: true, ApplicabilityScanResults: []*sarif.Run{sarif.NewRunWithInformationURI("", "").WithResults([]*sarif.Result{ + sarif.NewRuleResult("applic_CVE-2023-4321").WithLocations([]*sarif.Location{ + sarif.NewLocationWithPhysicalLocation(sarif.NewPhysicalLocation().WithArtifactLocation(sarif.NewArtifactLocation().WithUri("file1")).WithRegion(sarif.NewRegion().WithStartLine(1).WithStartColumn(10))), + }), + })}}}, + &audit.Results{ExtendedScanResults: &xrayutils.ExtendedScanResults{XrayResults: []services.ScanResponse{currentScan}, EntitledForJas: true, ApplicabilityScanResults: []*sarif.Run{sarif.NewRunWithInformationURI("", "").WithResults([]*sarif.Result{ + sarif.NewRuleResult("applic_CVE-2023-4321").WithLocations([]*sarif.Location{ + sarif.NewLocationWithPhysicalLocation(sarif.NewPhysicalLocation().WithArtifactLocation(sarif.NewArtifactLocation().WithUri("file1")).WithRegion(sarif.NewRegion().WithStartLine(1).WithStartColumn(10))), + }), + })}}}, ) assert.NoError(t, err) assert.Len(t, rows, 2) @@ -379,7 +389,7 @@ func TestGetNewVulnerabilitiesCaseNoNewVulnerabilities(t *testing.T) { func TestCreatePullRequestMessageNoVulnerabilities(t *testing.T) { vulnerabilities := []formats.VulnerabilityOrViolationRow{} - message := createPullRequestMessage(vulnerabilities, nil, &outputwriter.StandardOutput{}) + message := createPullRequestMessage(vulnerabilities, nil, nil, &outputwriter.StandardOutput{}) expectedMessageByte, err := os.ReadFile(filepath.Join("..", "testdata", "messages", "novulnerabilities.md")) assert.NoError(t, err) @@ -388,7 +398,7 @@ func TestCreatePullRequestMessageNoVulnerabilities(t *testing.T) { outputWriter := &outputwriter.StandardOutput{} outputWriter.SetVcsProvider(vcsutils.GitLab) - message = createPullRequestMessage(vulnerabilities, nil, outputWriter) + message = createPullRequestMessage(vulnerabilities, nil, nil, outputWriter) expectedMessageByte, err = os.ReadFile(filepath.Join("..", "testdata", "messages", "novulnerabilitiesMR.md")) assert.NoError(t, err) @@ -440,32 +450,16 @@ func TestCreatePullRequestMessage(t *testing.T) { Cves: []formats.CveRow{{Id: "CVE-2022-26652"}}, }, } - iac := []formats.IacSecretsRow{ - { - Severity: "Low", - File: "test.js", - LineColumn: "1:20", - Text: "kms_key_id='' was detected", - Type: "aws_cloudtrail_encrypt", - }, - { - Severity: "High", - File: "test2.js", - LineColumn: "4:30", - Text: "Deprecated TLS version was detected", - Type: "aws_cloudfront_tls_version", - }, - } writerOutput := &outputwriter.StandardOutput{} writerOutput.SetJasOutputFlags(true, true) - message := createPullRequestMessage(vulnerabilities, iac, writerOutput) + message := createPullRequestMessage(vulnerabilities, nil, nil, writerOutput) - expectedMessage := "
\n\n[![](https://raw.githubusercontent.com/jfrog/frogbot/master/resources/v2/vulnerabilitiesBannerPR.png)](https://github.com/jfrog/frogbot#readme)\n\n
\n\n\n## 📦 Vulnerable Dependencies \n\n### ✍️ Summary\n\n
\n\n| SEVERITY | CONTEXTUAL ANALYSIS | DIRECT DEPENDENCIES | IMPACTED DEPENDENCY | FIXED VERSIONS |\n| :---------------------: | :----------------------------------: | :----------------------------------: | :-----------------------------------: | :---------------------------------: | \n| ![](https://raw.githubusercontent.com/jfrog/frogbot/master/resources/v2/applicableHighSeverity.png)
High | Undetermined | github.com/nats-io/nats-streaming-server:v0.21.0 | github.com/nats-io/nats-streaming-server:v0.21.0 | [0.24.1] |\n| ![](https://raw.githubusercontent.com/jfrog/frogbot/master/resources/v2/applicableHighSeverity.png)
High | Undetermined | github.com/mholt/archiver/v3:v3.5.1 | github.com/mholt/archiver/v3:v3.5.1 | |\n| ![](https://raw.githubusercontent.com/jfrog/frogbot/master/resources/v2/applicableMediumSeverity.png)
Medium | Undetermined | github.com/nats-io/nats-streaming-server:v0.21.0 | github.com/nats-io/nats-streaming-server:v0.21.0 | [0.24.3] |\n\n
\n\n## 👇 Details\n\n\n
\n github.com/nats-io/nats-streaming-server v0.21.0 \n
\n\n- **Severity** 🔥 High\n- **Contextual Analysis:** Undetermined\n- **Package Name:** github.com/nats-io/nats-streaming-server\n- **Current Version:** v0.21.0\n- **Fixed Version:** [0.24.1]\n- **CVE:** CVE-2022-24450\n\n\n
\n\n\n
\n github.com/mholt/archiver/v3 v3.5.1 \n
\n\n- **Severity** 🔥 High\n- **Contextual Analysis:** Undetermined\n- **Package Name:** github.com/mholt/archiver/v3\n- **Current Version:** v3.5.1\n\n\n
\n\n\n
\n github.com/nats-io/nats-streaming-server v0.21.0 \n
\n\n- **Severity** 🎃 Medium\n- **Contextual Analysis:** Undetermined\n- **Package Name:** github.com/nats-io/nats-streaming-server\n- **Current Version:** v0.21.0\n- **Fixed Version:** [0.24.3]\n- **CVE:** CVE-2022-26652\n\n\n
\n\n\n## 🛠️ Infrastructure as Code \n\n
\n\n\n| SEVERITY | FILE | LINE:COLUMN | FINDING |\n| :---------------------: | :----------------------------------: | :-----------------------------------: | :---------------------------------: | \n| ![](https://raw.githubusercontent.com/jfrog/frogbot/master/resources/v2/applicableLowSeverity.png)
Low | test.js | 1:20 | kms_key_id='' was detected |\n| ![](https://raw.githubusercontent.com/jfrog/frogbot/master/resources/v2/applicableHighSeverity.png)
High | test2.js | 4:30 | Deprecated TLS version was detected |\n\n
\n\n\n
\n\n[JFrog Frogbot](https://github.com/jfrog/frogbot#readme)\n\n
\n" + expectedMessage := "
\n\n[![](https://raw.githubusercontent.com/jfrog/frogbot/master/resources/v2/vulnerabilitiesBannerPR.png)](https://github.com/jfrog/frogbot#readme)\n\n
\n\n\n## 📦 Vulnerable Dependencies \n\n### ✍️ Summary\n\n
\n\n| SEVERITY | CONTEXTUAL ANALYSIS | DIRECT DEPENDENCIES | IMPACTED DEPENDENCY | FIXED VERSIONS |\n| :---------------------: | :----------------------------------: | :----------------------------------: | :-----------------------------------: | :---------------------------------: | \n| ![](https://raw.githubusercontent.com/jfrog/frogbot/master/resources/v2/applicableHighSeverity.png)
High | Undetermined | github.com/nats-io/nats-streaming-server:v0.21.0 | github.com/nats-io/nats-streaming-server:v0.21.0 | [0.24.1] |\n| ![](https://raw.githubusercontent.com/jfrog/frogbot/master/resources/v2/applicableHighSeverity.png)
High | Undetermined | github.com/mholt/archiver/v3:v3.5.1 | github.com/mholt/archiver/v3:v3.5.1 | |\n| ![](https://raw.githubusercontent.com/jfrog/frogbot/master/resources/v2/applicableMediumSeverity.png)
Medium | Undetermined | github.com/nats-io/nats-streaming-server:v0.21.0 | github.com/nats-io/nats-streaming-server:v0.21.0 | [0.24.3] |\n\n
\n\n## 👇 Details\n\n\n
\n [ CVE-2022-24450 ] github.com/nats-io/nats-streaming-server v0.21.0 \n
\n\n- **Severity** 🔥 High\n- **Contextual Analysis:** Undetermined\n- **Package Name:** github.com/nats-io/nats-streaming-server\n- **Current Version:** v0.21.0\n- **CVE:** CVE-2022-24450\n- **Fixed Version:** [0.24.1]\n\n\n
\n\n\n
\n github.com/mholt/archiver/v3 v3.5.1 \n
\n\n- **Severity** 🔥 High\n- **Contextual Analysis:** Undetermined\n- **Package Name:** github.com/mholt/archiver/v3\n- **Current Version:** v3.5.1\n\n\n
\n\n\n
\n [ CVE-2022-26652 ] github.com/nats-io/nats-streaming-server v0.21.0 \n
\n\n- **Severity** 🎃 Medium\n- **Contextual Analysis:** Undetermined\n- **Package Name:** github.com/nats-io/nats-streaming-server\n- **Current Version:** v0.21.0\n- **CVE:** CVE-2022-26652\n- **Fixed Version:** [0.24.3]\n\n\n
\n\n\n---\n
\n\n[🐸 JFrog Frogbot](https://github.com/jfrog/frogbot#readme)\n\n
\n" assert.Equal(t, expectedMessage, message) writerOutput.SetVcsProvider(vcsutils.GitLab) - message = createPullRequestMessage(vulnerabilities, iac, writerOutput) - expectedMessage = "
\n\n[![](https://raw.githubusercontent.com/jfrog/frogbot/master/resources/v2/vulnerabilitiesBannerMR.png)](https://github.com/jfrog/frogbot#readme)\n\n
\n\n\n## 📦 Vulnerable Dependencies \n\n### ✍️ Summary\n\n
\n\n| SEVERITY | CONTEXTUAL ANALYSIS | DIRECT DEPENDENCIES | IMPACTED DEPENDENCY | FIXED VERSIONS |\n| :---------------------: | :----------------------------------: | :----------------------------------: | :-----------------------------------: | :---------------------------------: | \n| ![](https://raw.githubusercontent.com/jfrog/frogbot/master/resources/v2/applicableHighSeverity.png)
High | Undetermined | github.com/nats-io/nats-streaming-server:v0.21.0 | github.com/nats-io/nats-streaming-server:v0.21.0 | [0.24.1] |\n| ![](https://raw.githubusercontent.com/jfrog/frogbot/master/resources/v2/applicableHighSeverity.png)
High | Undetermined | github.com/mholt/archiver/v3:v3.5.1 | github.com/mholt/archiver/v3:v3.5.1 | |\n| ![](https://raw.githubusercontent.com/jfrog/frogbot/master/resources/v2/applicableMediumSeverity.png)
Medium | Undetermined | github.com/nats-io/nats-streaming-server:v0.21.0 | github.com/nats-io/nats-streaming-server:v0.21.0 | [0.24.3] |\n\n
\n\n## 👇 Details\n\n\n
\n github.com/nats-io/nats-streaming-server v0.21.0 \n
\n\n- **Severity** 🔥 High\n- **Contextual Analysis:** Undetermined\n- **Package Name:** github.com/nats-io/nats-streaming-server\n- **Current Version:** v0.21.0\n- **Fixed Version:** [0.24.1]\n- **CVE:** CVE-2022-24450\n\n\n
\n\n\n
\n github.com/mholt/archiver/v3 v3.5.1 \n
\n\n- **Severity** 🔥 High\n- **Contextual Analysis:** Undetermined\n- **Package Name:** github.com/mholt/archiver/v3\n- **Current Version:** v3.5.1\n\n\n
\n\n\n
\n github.com/nats-io/nats-streaming-server v0.21.0 \n
\n\n- **Severity** 🎃 Medium\n- **Contextual Analysis:** Undetermined\n- **Package Name:** github.com/nats-io/nats-streaming-server\n- **Current Version:** v0.21.0\n- **Fixed Version:** [0.24.3]\n- **CVE:** CVE-2022-26652\n\n\n
\n\n\n## 🛠️ Infrastructure as Code \n\n
\n\n\n| SEVERITY | FILE | LINE:COLUMN | FINDING |\n| :---------------------: | :----------------------------------: | :-----------------------------------: | :---------------------------------: | \n| ![](https://raw.githubusercontent.com/jfrog/frogbot/master/resources/v2/applicableLowSeverity.png)
Low | test.js | 1:20 | kms_key_id='' was detected |\n| ![](https://raw.githubusercontent.com/jfrog/frogbot/master/resources/v2/applicableHighSeverity.png)
High | test2.js | 4:30 | Deprecated TLS version was detected |\n\n
\n\n\n
\n\n[JFrog Frogbot](https://github.com/jfrog/frogbot#readme)\n\n
\n" + message = createPullRequestMessage(vulnerabilities, nil, nil, writerOutput) + expectedMessage = "
\n\n[![](https://raw.githubusercontent.com/jfrog/frogbot/master/resources/v2/vulnerabilitiesBannerMR.png)](https://github.com/jfrog/frogbot#readme)\n\n
\n\n\n## 📦 Vulnerable Dependencies \n\n### ✍️ Summary\n\n
\n\n| SEVERITY | CONTEXTUAL ANALYSIS | DIRECT DEPENDENCIES | IMPACTED DEPENDENCY | FIXED VERSIONS |\n| :---------------------: | :----------------------------------: | :----------------------------------: | :-----------------------------------: | :---------------------------------: | \n| ![](https://raw.githubusercontent.com/jfrog/frogbot/master/resources/v2/applicableHighSeverity.png)
High | Undetermined | github.com/nats-io/nats-streaming-server:v0.21.0 | github.com/nats-io/nats-streaming-server:v0.21.0 | [0.24.1] |\n| ![](https://raw.githubusercontent.com/jfrog/frogbot/master/resources/v2/applicableHighSeverity.png)
High | Undetermined | github.com/mholt/archiver/v3:v3.5.1 | github.com/mholt/archiver/v3:v3.5.1 | |\n| ![](https://raw.githubusercontent.com/jfrog/frogbot/master/resources/v2/applicableMediumSeverity.png)
Medium | Undetermined | github.com/nats-io/nats-streaming-server:v0.21.0 | github.com/nats-io/nats-streaming-server:v0.21.0 | [0.24.3] |\n\n
\n\n## 👇 Details\n\n\n
\n [ CVE-2022-24450 ] github.com/nats-io/nats-streaming-server v0.21.0 \n
\n\n- **Severity** 🔥 High\n- **Contextual Analysis:** Undetermined\n- **Package Name:** github.com/nats-io/nats-streaming-server\n- **Current Version:** v0.21.0\n- **CVE:** CVE-2022-24450\n- **Fixed Version:** [0.24.1]\n\n\n
\n\n\n
\n github.com/mholt/archiver/v3 v3.5.1 \n
\n\n- **Severity** 🔥 High\n- **Contextual Analysis:** Undetermined\n- **Package Name:** github.com/mholt/archiver/v3\n- **Current Version:** v3.5.1\n\n\n
\n\n\n
\n [ CVE-2022-26652 ] github.com/nats-io/nats-streaming-server v0.21.0 \n
\n\n- **Severity** 🎃 Medium\n- **Contextual Analysis:** Undetermined\n- **Package Name:** github.com/nats-io/nats-streaming-server\n- **Current Version:** v0.21.0\n- **CVE:** CVE-2022-26652\n- **Fixed Version:** [0.24.3]\n\n\n
\n\n\n---\n
\n\n[🐸 JFrog Frogbot](https://github.com/jfrog/frogbot#readme)\n\n
\n" assert.Equal(t, expectedMessage, message) } @@ -706,6 +700,11 @@ func createGitLabHandler(t *testing.T, projectName string) http.HandlerFunc { jsonResponse := `{"id": 3,"visibility": "private","ssh_url_to_repo": "git@example.com:diaspora/diaspora-project-site.git","http_url_to_repo": "https://example.com/diaspora/diaspora-project-site.git"}` _, err := w.Write([]byte(jsonResponse)) assert.NoError(t, err) + case r.RequestURI == fmt.Sprintf("/api/v4/projects/jfrog%s/merge_requests/133/discussions", "%2F"+projectName): + discussions, err := os.ReadFile(filepath.Join("..", "list_merge_request_discussion_items.json")) + assert.NoError(t, err) + _, err = w.Write(discussions) + assert.NoError(t, err) } } } @@ -713,69 +712,75 @@ func createGitLabHandler(t *testing.T, projectName string) http.HandlerFunc { func TestCreateNewIacRows(t *testing.T) { testCases := []struct { name string - targetIacResults []xrayutils.IacOrSecretResult - sourceIacResults []xrayutils.IacOrSecretResult - expectedAddedIacVulnerabilities []formats.IacSecretsRow + targetIacResults []*sarif.Result + sourceIacResults []*sarif.Result + expectedAddedIacVulnerabilities []formats.SourceCodeRow }{ { name: "No vulnerabilities in source IaC results", - targetIacResults: []xrayutils.IacOrSecretResult{ - { - Severity: "High", - File: "file1", - LineColumn: "1:10", - Text: "aws violation", - }, + targetIacResults: []*sarif.Result{ + sarif.NewRuleResult("").WithLevel(xrayutils.ConvertToSarifLevel("high")).WithLocations([]*sarif.Location{ + sarif.NewLocationWithPhysicalLocation(sarif.NewPhysicalLocation(). + WithArtifactLocation(sarif.NewArtifactLocation().WithUri("file1")). + WithRegion(sarif.NewRegion().WithStartLine(1).WithStartColumn(10).WithSnippet(sarif.NewArtifactContent().WithText("aws violation"))), + ), + }), }, - sourceIacResults: []xrayutils.IacOrSecretResult{}, - expectedAddedIacVulnerabilities: []formats.IacSecretsRow{}, + sourceIacResults: []*sarif.Result{}, + expectedAddedIacVulnerabilities: []formats.SourceCodeRow{}, }, { name: "No vulnerabilities in target IaC results", - targetIacResults: []xrayutils.IacOrSecretResult{}, - sourceIacResults: []xrayutils.IacOrSecretResult{ - { - Severity: "High", - File: "file1", - LineColumn: "1:10", - Text: "aws violation", - }, + targetIacResults: []*sarif.Result{}, + sourceIacResults: []*sarif.Result{ + sarif.NewRuleResult("").WithLevel(xrayutils.ConvertToSarifLevel("high")).WithLocations([]*sarif.Location{ + sarif.NewLocationWithPhysicalLocation(sarif.NewPhysicalLocation(). + WithArtifactLocation(sarif.NewArtifactLocation().WithUri("file1")). + WithRegion(sarif.NewRegion().WithStartLine(1).WithStartColumn(10).WithSnippet(sarif.NewArtifactContent().WithText("aws violation"))), + ), + }), }, - expectedAddedIacVulnerabilities: []formats.IacSecretsRow{ + expectedAddedIacVulnerabilities: []formats.SourceCodeRow{ { - Severity: "High", - File: "file1", - LineColumn: "1:10", - Text: "aws violation", - SeverityNumValue: 10, + Severity: "High", + Location: formats.Location{ + File: "file1", + StartLine: 1, + StartColumn: 10, + Snippet: "aws violation", + }, + SeverityNumValue: 13, }, }, }, { name: "Some new vulnerabilities in source IaC results", - targetIacResults: []xrayutils.IacOrSecretResult{ - { - Severity: "High", - File: "file1", - LineColumn: "1:10", - Text: "aws violation", - }, + targetIacResults: []*sarif.Result{ + sarif.NewRuleResult("").WithLevel(xrayutils.ConvertToSarifLevel("high")).WithLocations([]*sarif.Location{ + sarif.NewLocationWithPhysicalLocation(sarif.NewPhysicalLocation(). + WithArtifactLocation(sarif.NewArtifactLocation().WithUri("file1")). + WithRegion(sarif.NewRegion().WithStartLine(1).WithStartColumn(10).WithSnippet(sarif.NewArtifactContent().WithText("aws violation"))), + ), + }), }, - sourceIacResults: []xrayutils.IacOrSecretResult{ - { - Severity: "Medium", - File: "file2", - LineColumn: "2:5", - Text: "gcp violation", - }, + sourceIacResults: []*sarif.Result{ + sarif.NewRuleResult("").WithLevel(xrayutils.ConvertToSarifLevel("medium")).WithLocations([]*sarif.Location{ + sarif.NewLocationWithPhysicalLocation(sarif.NewPhysicalLocation(). + WithArtifactLocation(sarif.NewArtifactLocation().WithUri("file2")). + WithRegion(sarif.NewRegion().WithStartLine(2).WithStartColumn(5).WithSnippet(sarif.NewArtifactContent().WithText("gcp violation"))), + ), + }), }, - expectedAddedIacVulnerabilities: []formats.IacSecretsRow{ + expectedAddedIacVulnerabilities: []formats.SourceCodeRow{ { Severity: "Medium", - SeverityNumValue: 8, - File: "file2", - LineColumn: "2:5", - Text: "gcp violation", + SeverityNumValue: 11, + Location: formats.Location{ + File: "file2", + StartLine: 2, + StartColumn: 5, + Snippet: "gcp violation", + }, }, }, }, @@ -783,9 +788,9 @@ func TestCreateNewIacRows(t *testing.T) { for _, tc := range testCases { t.Run(tc.name, func(t *testing.T) { - targetIacRows := xrayutils.PrepareSecrets(tc.targetIacResults) - sourceIacRows := xrayutils.PrepareSecrets(tc.sourceIacResults) - addedIacVulnerabilities := createNewIacOrSecretsRows(targetIacRows, sourceIacRows) + targetIacRows := xrayutils.PrepareIacs([]*sarif.Run{sarif.NewRunWithInformationURI("", "").WithResults(tc.targetIacResults)}) + sourceIacRows := xrayutils.PrepareIacs([]*sarif.Run{sarif.NewRunWithInformationURI("", "").WithResults(tc.sourceIacResults)}) + addedIacVulnerabilities := createNewSourceCodeRows(targetIacRows, sourceIacRows) assert.ElementsMatch(t, tc.expectedAddedIacVulnerabilities, addedIacVulnerabilities) }) } @@ -794,75 +799,77 @@ func TestCreateNewIacRows(t *testing.T) { func TestCreateNewSecretRows(t *testing.T) { testCases := []struct { name string - targetSecretsResults []xrayutils.IacOrSecretResult - sourceSecretsResults []xrayutils.IacOrSecretResult - expectedAddedSecretsVulnerabilities []formats.IacSecretsRow + targetSecretsResults []*sarif.Result + sourceSecretsResults []*sarif.Result + expectedAddedSecretsVulnerabilities []formats.SourceCodeRow }{ { name: "No vulnerabilities in source secrets results", - targetSecretsResults: []xrayutils.IacOrSecretResult{ - { - Severity: "High", - File: "file1", - LineColumn: "1:10", - Type: "Secret", - Text: "Sensitive information", - }, + targetSecretsResults: []*sarif.Result{ + sarif.NewRuleResult("").WithMessage(sarif.NewTextMessage("Secret")).WithLevel(xrayutils.ConvertToSarifLevel("high")).WithLocations([]*sarif.Location{ + sarif.NewLocationWithPhysicalLocation(sarif.NewPhysicalLocation(). + WithArtifactLocation(sarif.NewArtifactLocation().WithUri("file1")). + WithRegion(sarif.NewRegion().WithStartLine(1).WithStartColumn(10).WithSnippet(sarif.NewArtifactContent().WithText("Sensitive information"))), + ), + }), }, - sourceSecretsResults: []xrayutils.IacOrSecretResult{}, - expectedAddedSecretsVulnerabilities: []formats.IacSecretsRow{}, + sourceSecretsResults: []*sarif.Result{}, + expectedAddedSecretsVulnerabilities: []formats.SourceCodeRow{}, }, { name: "No vulnerabilities in target secrets results", - targetSecretsResults: []xrayutils.IacOrSecretResult{}, - sourceSecretsResults: []xrayutils.IacOrSecretResult{ - { - Severity: "High", - File: "file1", - LineColumn: "1:10", - Type: "Secret", - Text: "Sensitive information", - }, + targetSecretsResults: []*sarif.Result{}, + sourceSecretsResults: []*sarif.Result{ + sarif.NewRuleResult("").WithMessage(sarif.NewTextMessage("Secret")).WithLevel(xrayutils.ConvertToSarifLevel("high")).WithLocations([]*sarif.Location{ + sarif.NewLocationWithPhysicalLocation(sarif.NewPhysicalLocation(). + WithArtifactLocation(sarif.NewArtifactLocation().WithUri("file1")). + WithRegion(sarif.NewRegion().WithStartLine(1).WithStartColumn(10).WithSnippet(sarif.NewArtifactContent().WithText("Sensitive information"))), + ), + }), }, - expectedAddedSecretsVulnerabilities: []formats.IacSecretsRow{ + expectedAddedSecretsVulnerabilities: []formats.SourceCodeRow{ { - Severity: "High", - File: "file1", - LineColumn: "1:10", - Type: "Secret", - Text: "Sensitive information", - SeverityNumValue: 10, + Severity: "High", + Finding: "Secret", + Location: formats.Location{ + File: "file1", + StartLine: 1, + StartColumn: 10, + Snippet: "Sensitive information", + }, + SeverityNumValue: 13, }, }, }, { name: "Some new vulnerabilities in source secrets results", - targetSecretsResults: []xrayutils.IacOrSecretResult{ - { - Severity: "High", - File: "file1", - LineColumn: "1:10", - Type: "Secret", - Text: "Sensitive information", - }, + targetSecretsResults: []*sarif.Result{ + sarif.NewRuleResult("").WithMessage(sarif.NewTextMessage("Secret")).WithLevel(xrayutils.ConvertToSarifLevel("high")).WithLocations([]*sarif.Location{ + sarif.NewLocationWithPhysicalLocation(sarif.NewPhysicalLocation(). + WithArtifactLocation(sarif.NewArtifactLocation().WithUri("file1")). + WithRegion(sarif.NewRegion().WithStartLine(1).WithStartColumn(10).WithSnippet(sarif.NewArtifactContent().WithText("Sensitive information"))), + ), + }), }, - sourceSecretsResults: []xrayutils.IacOrSecretResult{ - { - Severity: "Medium", - File: "file2", - LineColumn: "2:5", - Type: "Secret", - Text: "Confidential data", - }, + sourceSecretsResults: []*sarif.Result{ + sarif.NewRuleResult("").WithMessage(sarif.NewTextMessage("Secret")).WithLevel(xrayutils.ConvertToSarifLevel("medium")).WithLocations([]*sarif.Location{ + sarif.NewLocationWithPhysicalLocation(sarif.NewPhysicalLocation(). + WithArtifactLocation(sarif.NewArtifactLocation().WithUri("file2")). + WithRegion(sarif.NewRegion().WithStartLine(2).WithStartColumn(5).WithSnippet(sarif.NewArtifactContent().WithText("Confidential data"))), + ), + }), }, - expectedAddedSecretsVulnerabilities: []formats.IacSecretsRow{ + expectedAddedSecretsVulnerabilities: []formats.SourceCodeRow{ { Severity: "Medium", - SeverityNumValue: 8, - File: "file2", - LineColumn: "2:5", - Text: "Confidential data", - Type: "Secret", + Finding: "Secret", + SeverityNumValue: 11, + Location: formats.Location{ + File: "file2", + StartLine: 2, + StartColumn: 5, + Snippet: "Confidential data", + }, }, }, }, @@ -870,9 +877,9 @@ func TestCreateNewSecretRows(t *testing.T) { for _, tc := range testCases { t.Run(tc.name, func(t *testing.T) { - targetSecretsRows := xrayutils.PrepareSecrets(tc.targetSecretsResults) - sourceSecretsRows := xrayutils.PrepareSecrets(tc.sourceSecretsResults) - addedSecretsVulnerabilities := createNewIacOrSecretsRows(targetSecretsRows, sourceSecretsRows) + targetSecretsRows := xrayutils.PrepareSecrets([]*sarif.Run{sarif.NewRunWithInformationURI("", "").WithResults(tc.targetSecretsResults)}) + sourceSecretsRows := xrayutils.PrepareSecrets([]*sarif.Run{sarif.NewRunWithInformationURI("", "").WithResults(tc.sourceSecretsResults)}) + addedSecretsVulnerabilities := createNewSourceCodeRows(targetSecretsRows, sourceSecretsRows) assert.ElementsMatch(t, tc.expectedAddedSecretsVulnerabilities, addedSecretsVulnerabilities) }) } diff --git a/scanrepository/scanrepository.go b/scanrepository/scanrepository.go index 28f6c88cb..8537e9a97 100644 --- a/scanrepository/scanrepository.go +++ b/scanrepository/scanrepository.go @@ -4,6 +4,10 @@ import ( "context" "errors" "fmt" + "path/filepath" + "regexp" + "strings" + "github.com/jfrog/frogbot/packagehandlers" "github.com/jfrog/frogbot/utils" "github.com/jfrog/frogbot/utils/outputwriter" @@ -11,16 +15,13 @@ import ( "github.com/jfrog/froggit-go/vcsutils" "github.com/jfrog/gofrog/version" "github.com/jfrog/jfrog-cli-core/v2/utils/coreutils" - audit "github.com/jfrog/jfrog-cli-core/v2/xray/commands/audit/generic" + "github.com/jfrog/jfrog-cli-core/v2/xray/commands/audit" "github.com/jfrog/jfrog-cli-core/v2/xray/formats" xrayutils "github.com/jfrog/jfrog-cli-core/v2/xray/utils" "github.com/jfrog/jfrog-client-go/utils/io/fileutils" "github.com/jfrog/jfrog-client-go/utils/log" "golang.org/x/exp/maps" "golang.org/x/exp/slices" - "path/filepath" - "regexp" - "strings" ) type ScanRepositoryCmd struct { @@ -30,8 +31,8 @@ type ScanRepositoryCmd struct { dryRun bool // When dryRun is enabled, dryRunRepoPath specifies the repository local path to clone dryRunRepoPath string - // The details of the current scan - details *utils.ScanDetails + // The scanDetails of the current scan + scanDetails *utils.ScanDetails // The base working directory baseWd string // The git client the command performs git operations with @@ -57,6 +58,7 @@ func (cfp *ScanRepositoryCmd) scanAndFixRepository(repository *utils.Repository, if err = cfp.setCommandPrerequisites(repository, branch, client); err != nil { return } + cfp.scanDetails.SetXscGitInfoContext(branch, repository.Project, client) if err = cfp.scanAndFixBranch(repository); err != nil { return } @@ -78,7 +80,7 @@ func (cfp *ScanRepositoryCmd) scanAndFixBranch(repository *utils.Repository) (er err = errors.Join(err, restoreBaseDir(), fileutils.RemoveTempDir(clonedRepoDir)) }() for i := range repository.Projects { - cfp.details.Project = &repository.Projects[i] + cfp.scanDetails.Project = &repository.Projects[i] cfp.projectTech = "" if err = cfp.scanAndFixProject(repository); err != nil { return @@ -88,45 +90,51 @@ func (cfp *ScanRepositoryCmd) scanAndFixBranch(repository *utils.Repository) (er } func (cfp *ScanRepositoryCmd) setCommandPrerequisites(repository *utils.Repository, branch string, client vcsclient.VcsClient) (err error) { - cfp.details = utils.NewScanDetails(client, &repository.Server, &repository.Git). + + cfp.scanDetails = utils.NewScanDetails(client, &repository.Server, &repository.Git). SetXrayGraphScanParams(repository.Watches, repository.JFrogProjectKey). SetFailOnInstallationErrors(*repository.FailOnSecurityIssues). SetBaseBranch(branch). SetFixableOnly(repository.FixableOnly). SetMinSeverity(repository.MinSeverity) + cfp.aggregateFixes = repository.Git.AggregateFixes cfp.OutputWriter = outputwriter.GetCompatibleOutputWriter(repository.GitProvider) - repositoryInfo, err := client.GetRepositoryInfo(context.Background(), cfp.details.RepoOwner, cfp.details.RepoName) + repositoryInfo, err := client.GetRepositoryInfo(context.Background(), cfp.scanDetails.RepoOwner, cfp.scanDetails.RepoName) if err != nil { return } - remoteHttpsGitUrl := repositoryInfo.CloneInfo.HTTP + cfp.scanDetails.Git.RepositoryCloneUrl = repositoryInfo.CloneInfo.HTTP cfp.gitManager, err = utils.NewGitManager(). - SetAuth(cfp.details.Username, cfp.details.Token). + SetAuth(cfp.scanDetails.Username, cfp.scanDetails.Token). SetDryRun(cfp.dryRun, cfp.dryRunRepoPath). - SetRemoteGitUrl(remoteHttpsGitUrl) + SetRemoteGitUrl(cfp.scanDetails.Git.RepositoryCloneUrl) if err != nil { return } - _, err = cfp.gitManager.SetGitParams(cfp.details.Git) + _, err = cfp.gitManager.SetGitParams(cfp.scanDetails.Git) return } func (cfp *ScanRepositoryCmd) scanAndFixProject(repository *utils.Repository) error { var fixNeeded bool // A map that contains the full project paths as a keys - // The value is a map of vulnerable package names -> the details of the vulnerable packages. - // That means we have a map of all the vulnerabilities that were found in a specific folder, along with their full details. + // The value is a map of vulnerable package names -> the scanDetails of the vulnerable packages. + // That means we have a map of all the vulnerabilities that were found in a specific folder, along with their full scanDetails. vulnerabilitiesByPathMap := make(map[string]map[string]*utils.VulnerabilityDetails) - projectFullPathWorkingDirs := utils.GetFullPathWorkingDirs(cfp.details.Project.WorkingDirs, cfp.baseWd) + projectFullPathWorkingDirs := utils.GetFullPathWorkingDirs(cfp.scanDetails.Project.WorkingDirs, cfp.baseWd) for _, fullPathWd := range projectFullPathWorkingDirs { scanResults, err := cfp.scan(fullPathWd) if err != nil { return err } - if err = utils.UploadScanToGitProvider(scanResults, repository, cfp.details.BaseBranch(), cfp.details.Client()); err != nil { - log.Warn(err) + if repository.GitProvider.String() == vcsutils.GitHub.String() { + // Uploads Sarif results to GitHub in order to view the scan in the code scanning UI + // Currently available on GitHub only + if err = utils.UploadSarifResultsToGithubSecurityTab(scanResults, repository, cfp.scanDetails.BaseBranch(), cfp.scanDetails.Client()); err != nil { + log.Warn(err) + } } // Prepare the vulnerabilities map for each working dir path @@ -148,7 +156,7 @@ func (cfp *ScanRepositoryCmd) scanAndFixProject(repository *utils.Repository) er // Audit the dependencies of the current commit. func (cfp *ScanRepositoryCmd) scan(currentWorkingDir string) (*audit.Results, error) { // Audit commit code - auditResults, err := cfp.details.RunInstallAndAudit(currentWorkingDir) + auditResults, err := cfp.scanDetails.RunInstallAndAudit(currentWorkingDir) if err != nil { return nil, err } @@ -167,7 +175,7 @@ func (cfp *ScanRepositoryCmd) getVulnerabilitiesMap(scanResults *xrayutils.Exten // Nothing to fix, return if len(vulnerabilitiesMap) == 0 { - log.Info("Didn't find vulnerable dependencies with existing fix versions for", cfp.details.RepoName) + log.Info("Didn't find vulnerable dependencies with existing fix versions for", cfp.scanDetails.RepoName) } return vulnerabilitiesMap, nil } @@ -211,7 +219,7 @@ func (cfp *ScanRepositoryCmd) fixProjectVulnerabilities(fullProjectPath string, } // After fixing the current vulnerability, checkout to the base branch to start fixing the next vulnerability - if e := cfp.gitManager.Checkout(cfp.details.BaseBranch()); e != nil { + if e := cfp.gitManager.Checkout(cfp.scanDetails.BaseBranch()); e != nil { err = errors.Join(err, cfp.handleUpdatePackageErrors(e)) return } @@ -279,7 +287,7 @@ func (cfp *ScanRepositoryCmd) handleUpdatePackageErrors(err error) error { func (cfp *ScanRepositoryCmd) fixSinglePackageAndCreatePR(vulnDetails *utils.VulnerabilityDetails) (err error) { fixVersion := vulnDetails.SuggestedFixedVersion log.Debug("Attempting to fix", vulnDetails.ImpactedDependencyName, "with", fixVersion) - fixBranchName, err := cfp.gitManager.GenerateFixBranchName(cfp.details.BaseBranch(), vulnDetails.ImpactedDependencyName, fixVersion) + fixBranchName, err := cfp.gitManager.GenerateFixBranchName(cfp.scanDetails.BaseBranch(), vulnDetails.ImpactedDependencyName, fixVersion) if err != nil { return } @@ -325,8 +333,8 @@ func (cfp *ScanRepositoryCmd) openFixingPullRequest(fixBranchName string, vulnDe if err != nil { return } - log.Debug("Creating Pull Request form:", fixBranchName, " to:", cfp.details.BaseBranch()) - return cfp.details.Client().CreatePullRequest(context.Background(), cfp.details.RepoOwner, cfp.details.RepoName, fixBranchName, cfp.details.BaseBranch(), pullRequestTitle, prBody) + log.Debug("Creating Pull Request form:", fixBranchName, " to:", cfp.scanDetails.BaseBranch()) + return cfp.scanDetails.Client().CreatePullRequest(context.Background(), cfp.scanDetails.RepoOwner, cfp.scanDetails.RepoName, fixBranchName, cfp.scanDetails.BaseBranch(), pullRequestTitle, prBody) } // openAggregatedPullRequest handles the opening or updating of a pull request when the aggregate mode is active. @@ -344,11 +352,11 @@ func (cfp *ScanRepositoryCmd) openAggregatedPullRequest(fixBranchName string, pu return } if pullRequestInfo == nil { - log.Info("Creating Pull Request from:", fixBranchName, "to:", cfp.details.BaseBranch()) - return cfp.details.Client().CreatePullRequest(context.Background(), cfp.details.RepoOwner, cfp.details.RepoName, fixBranchName, cfp.details.BaseBranch(), pullRequestTitle, prBody) + log.Info("Creating Pull Request from:", fixBranchName, "to:", cfp.scanDetails.BaseBranch()) + return cfp.scanDetails.Client().CreatePullRequest(context.Background(), cfp.scanDetails.RepoOwner, cfp.scanDetails.RepoName, fixBranchName, cfp.scanDetails.BaseBranch(), pullRequestTitle, prBody) } - log.Info("Updating Pull Request from:", fixBranchName, "to:", cfp.details.BaseBranch()) - return cfp.details.Client().UpdatePullRequest(context.Background(), cfp.details.RepoOwner, cfp.details.RepoName, pullRequestTitle, prBody, "", int(pullRequestInfo.ID), vcsutils.Open) + log.Info("Updating Pull Request from:", fixBranchName, "to:", cfp.scanDetails.BaseBranch()) + return cfp.scanDetails.Client().UpdatePullRequest(context.Background(), cfp.scanDetails.RepoOwner, cfp.scanDetails.RepoName, pullRequestTitle, prBody, "", int(pullRequestInfo.ID), vcsutils.Open) } func (cfp *ScanRepositoryCmd) preparePullRequestDetails(vulnerabilitiesDetails ...*utils.VulnerabilityDetails) (string, string, error) { @@ -373,7 +381,7 @@ func (cfp *ScanRepositoryCmd) preparePullRequestDetails(vulnerabilitiesDetails . func (cfp *ScanRepositoryCmd) cloneRepositoryAndCheckoutToBranch() (tempWd string, restoreDir func() error, err error) { if cfp.dryRun { - tempWd = filepath.Join(cfp.dryRunRepoPath, cfp.details.RepoName) + tempWd = filepath.Join(cfp.dryRunRepoPath, cfp.scanDetails.RepoName) } else { // Create temp working directory if tempWd, err = fileutils.CreateTempDir(); err != nil { @@ -383,7 +391,7 @@ func (cfp *ScanRepositoryCmd) cloneRepositoryAndCheckoutToBranch() (tempWd strin log.Debug("Created temp working directory:", tempWd) // Clone the content of the repo to the new working directory - if err = cfp.gitManager.Clone(tempWd, cfp.details.BaseBranch()); err != nil { + if err = cfp.gitManager.Clone(tempWd, cfp.scanDetails.BaseBranch()); err != nil { return } @@ -466,7 +474,7 @@ func (cfp *ScanRepositoryCmd) updatePackageToFixedVersion(vulnDetails *utils.Vul handler := cfp.handlers[vulnDetails.Technology] if handler == nil { - handler = packagehandlers.GetCompatiblePackageHandler(vulnDetails, cfp.details) + handler = packagehandlers.GetCompatiblePackageHandler(vulnDetails, cfp.scanDetails) cfp.handlers[vulnDetails.Technology] = handler } else if _, unsupported := handler.(*packagehandlers.UnsupportedPackageHandler); unsupported { return @@ -492,7 +500,7 @@ func (cfp *ScanRepositoryCmd) getRemoteBranchScanHash(prBody string) string { } func (cfp *ScanRepositoryCmd) getOpenPullRequestBySourceBranch(branchName string) (prInfo *vcsclient.PullRequestInfo, err error) { - list, err := cfp.details.Client().ListOpenPullRequestsWithBody(context.Background(), cfp.details.RepoOwner, cfp.details.RepoName) + list, err := cfp.scanDetails.Client().ListOpenPullRequestsWithBody(context.Background(), cfp.scanDetails.RepoOwner, cfp.scanDetails.RepoName) if err != nil { return } @@ -537,7 +545,7 @@ func (cfp *ScanRepositoryCmd) aggregateFixAndOpenPullRequest(vulnerabilitiesMap } } log.Info("-----------------------------------------------------------------") - if e := cfp.gitManager.Checkout(cfp.details.BaseBranch()); e != nil { + if e := cfp.gitManager.Checkout(cfp.scanDetails.BaseBranch()); e != nil { err = errors.Join(err, e) } return @@ -582,10 +590,10 @@ func getMinimalFixVersion(impactedPackageVersion string, fixVersions []string) s // 1.0 --> 1.0 ≤ x // (,1.0] --> x ≤ 1.0 -// (,1.0) --> x < 1.0 +// (,1.0) --> x < 1.0 // [1.0] --> x == 1.0 -// (1.0,) --> 1.0 < x -// (1.0, 2.0) --> 1.0 < x < 2.0 +// (1.0,) --> 1.0 >= x +// (1.0, 2.0) --> 1.0 < x < 2.0 // [1.0, 2.0] --> 1.0 ≤ x ≤ 2.0 func parseVersionChangeString(fixVersion string) string { latestVersion := strings.Split(fixVersion, ",")[0] diff --git a/scanrepository/scanrepository_test.go b/scanrepository/scanrepository_test.go index b25a4a2a5..7f28dc724 100644 --- a/scanrepository/scanrepository_test.go +++ b/scanrepository/scanrepository_test.go @@ -4,6 +4,7 @@ import ( "errors" "fmt" "github.com/google/go-github/v45/github" + biutils "github.com/jfrog/build-info-go/utils" "github.com/jfrog/frogbot/utils" "github.com/jfrog/frogbot/utils/outputwriter" "github.com/jfrog/froggit-go/vcsclient" @@ -56,8 +57,8 @@ var testPackagesData = []struct { commandArgs: []string{"install"}, }, { - packageType: coreutils.Dotnet.ToString(), - commandName: "dotnet", + packageType: coreutils.Nuget.ToString(), + commandName: "nuget", commandArgs: []string{"restore"}, }, { @@ -372,7 +373,7 @@ func TestPackageTypeFromScan(t *testing.T) { err = fileutils.RemoveTempDir(tmpDir) }() assert.NoError(t, err) - assert.NoError(t, fileutils.CopyDir(projectPath, tmpDir, true, nil)) + assert.NoError(t, biutils.CopyDir(projectPath, tmpDir, true, nil)) if pkg.packageType == coreutils.Gradle.ToString() { assert.NoError(t, os.Chmod(filepath.Join(tmpDir, "gradlew"), 0777)) assert.NoError(t, os.Chmod(filepath.Join(tmpDir, "gradlew.bat"), 0777)) @@ -390,7 +391,7 @@ func TestPackageTypeFromScan(t *testing.T) { Project: &frogbotParams.Projects[0], ServerDetails: &frogbotParams.Server, } - testScan.details = &scanSetup + testScan.scanDetails = &scanSetup scanResponse, err := testScan.scan(tmpDir) assert.NoError(t, err) verifyTechnologyNaming(t, scanResponse.ExtendedScanResults.XrayResults, pkg.packageType) @@ -672,7 +673,7 @@ func TestPreparePullRequestDetails(t *testing.T) { SuggestedFixedVersion: "1.0.0", }, } - expectedPrBody := "
\n\n[![](https://raw.githubusercontent.com/jfrog/frogbot/master/resources/v2/vulnerabilitiesFixBannerPR.png)](https://github.com/jfrog/frogbot#readme)\n\n
\n\n\n\n## 📦 Vulnerable Dependencies \n\n### ✍️ Summary\n\n
\n\n\n| SEVERITY | DIRECT DEPENDENCIES | IMPACTED DEPENDENCY | FIXED VERSIONS |\n| :---------------------: | :----------------------------------: | :-----------------------------------: | :---------------------------------: | \n| ![](https://raw.githubusercontent.com/jfrog/frogbot/master/resources/v2/applicableHighSeverity.png)
High | | package1:1.0.0 | 1.0.0

2.0.0 |\n\n
\n\n## 👇 Details\n\n\n\n\n- **Severity** 🔥 High\n- **Package Name:** package1\n- **Current Version:** 1.0.0\n- **Fixed Versions:** 1.0.0,2.0.0\n- **CVE:** CVE-2022-1234\n\n**Description:**\n\nsummary\n\n\n\n\n---\n\n
\n\n**Frogbot** also supports **Contextual Analysis, Secret Detection and IaC Vulnerabilities Scanning**. This features are included as part of the [JFrog Advanced Security](https://jfrog.com/xray/) package, which isn't enabled on your system.\n\n
\n\n
\n\n[JFrog Frogbot](https://github.com/jfrog/frogbot#readme)\n\n
\n" + expectedPrBody := "
\n\n[![](https://raw.githubusercontent.com/jfrog/frogbot/master/resources/v2/vulnerabilitiesFixBannerPR.png)](https://github.com/jfrog/frogbot#readme)\n\n
\n\n\n\n## 📦 Vulnerable Dependencies \n\n### ✍️ Summary\n\n
\n\n\n| SEVERITY | DIRECT DEPENDENCIES | IMPACTED DEPENDENCY | FIXED VERSIONS |\n| :---------------------: | :----------------------------------: | :-----------------------------------: | :---------------------------------: | \n| ![](https://raw.githubusercontent.com/jfrog/frogbot/master/resources/v2/applicableHighSeverity.png)
High | | package1:1.0.0 | 1.0.0

2.0.0 |\n\n
\n\n## 👇 Details\n\n\n\n\n- **Severity** 🔥 High\n- **Package Name:** package1\n- **Current Version:** 1.0.0\n- **CVE:** CVE-2022-1234\n- **Fixed Versions:** 1.0.0,2.0.0\n\n**Description:**\n\nsummary\n\n\n\n\n---\n\n
\n\n**Frogbot** also supports **Contextual Analysis, Secret Detection and IaC Vulnerabilities Scanning**. This features are included as part of the [JFrog Advanced Security](https://jfrog.com/xray/) package, which isn't enabled on your system.\n\n
\n\n---\n
\n\n[🐸 JFrog Frogbot](https://github.com/jfrog/frogbot#readme)\n\n
\n" prTitle, prBody, err := cfp.preparePullRequestDetails(vulnerabilities...) assert.NoError(t, err) assert.Equal(t, "[🐸 Frogbot] Update version of package1 to 1.0.0", prTitle) @@ -689,13 +690,13 @@ func TestPreparePullRequestDetails(t *testing.T) { SuggestedFixedVersion: "2.0.0", }) cfp.aggregateFixes = true - expectedPrBody = "
\n\n[![](https://raw.githubusercontent.com/jfrog/frogbot/master/resources/v2/vulnerabilitiesFixBannerPR.png)](https://github.com/jfrog/frogbot#readme)\n\n
\n\n\n\n## 📦 Vulnerable Dependencies \n\n### ✍️ Summary\n\n
\n\n\n| SEVERITY | DIRECT DEPENDENCIES | IMPACTED DEPENDENCY | FIXED VERSIONS |\n| :---------------------: | :----------------------------------: | :-----------------------------------: | :---------------------------------: | \n| ![](https://raw.githubusercontent.com/jfrog/frogbot/master/resources/v2/applicableHighSeverity.png)
High | | package1:1.0.0 | 1.0.0

2.0.0 |\n| ![](https://raw.githubusercontent.com/jfrog/frogbot/master/resources/v2/applicableCriticalSeverity.png)
Critical | | package2:2.0.0 | 2.0.0

3.0.0 |\n\n
\n\n## 👇 Details\n\n\n
\n package1 1.0.0 \n
\n\n- **Severity** 🔥 High\n- **Package Name:** package1\n- **Current Version:** 1.0.0\n- **Fixed Versions:** 1.0.0,2.0.0\n- **CVE:** CVE-2022-1234\n\n**Description:**\n\nsummary\n\n\n\n
\n\n\n
\n package2 2.0.0 \n
\n\n- **Severity** 💀 Critical\n- **Package Name:** package2\n- **Current Version:** 2.0.0\n- **Fixed Versions:** 2.0.0,3.0.0\n- **CVE:** CVE-2022-4321\n\n**Description:**\n\nsummary\n\n\n\n
\n\n\n---\n\n
\n\n**Frogbot** also supports **Contextual Analysis, Secret Detection and IaC Vulnerabilities Scanning**. This features are included as part of the [JFrog Advanced Security](https://jfrog.com/xray/) package, which isn't enabled on your system.\n\n
\n\n
\n\n[JFrog Frogbot](https://github.com/jfrog/frogbot#readme)\n\n
\n\n[comment]: <> (Checksum: bec823edaceb5d0478b789798e819bde)\n" + expectedPrBody = "
\n\n[![](https://raw.githubusercontent.com/jfrog/frogbot/master/resources/v2/vulnerabilitiesFixBannerPR.png)](https://github.com/jfrog/frogbot#readme)\n\n
\n\n\n\n## 📦 Vulnerable Dependencies \n\n### ✍️ Summary\n\n
\n\n\n| SEVERITY | DIRECT DEPENDENCIES | IMPACTED DEPENDENCY | FIXED VERSIONS |\n| :---------------------: | :----------------------------------: | :-----------------------------------: | :---------------------------------: | \n| ![](https://raw.githubusercontent.com/jfrog/frogbot/master/resources/v2/applicableHighSeverity.png)
High | | package1:1.0.0 | 1.0.0

2.0.0 |\n| ![](https://raw.githubusercontent.com/jfrog/frogbot/master/resources/v2/applicableCriticalSeverity.png)
Critical | | package2:2.0.0 | 2.0.0

3.0.0 |\n\n
\n\n## 👇 Details\n\n\n
\n [ CVE-2022-1234 ] package1 1.0.0 \n
\n\n- **Severity** 🔥 High\n- **Package Name:** package1\n- **Current Version:** 1.0.0\n- **CVE:** CVE-2022-1234\n- **Fixed Versions:** 1.0.0,2.0.0\n\n**Description:**\n\nsummary\n\n\n\n
\n\n\n
\n [ CVE-2022-4321 ] package2 2.0.0 \n
\n\n- **Severity** 💀 Critical\n- **Package Name:** package2\n- **Current Version:** 2.0.0\n- **CVE:** CVE-2022-4321\n- **Fixed Versions:** 2.0.0,3.0.0\n\n**Description:**\n\nsummary\n\n\n\n
\n\n\n---\n\n
\n\n**Frogbot** also supports **Contextual Analysis, Secret Detection and IaC Vulnerabilities Scanning**. This features are included as part of the [JFrog Advanced Security](https://jfrog.com/xray/) package, which isn't enabled on your system.\n\n
\n\n---\n
\n\n[🐸 JFrog Frogbot](https://github.com/jfrog/frogbot#readme)\n\n
\n\n[comment]: <> (Checksum: bec823edaceb5d0478b789798e819bde)\n" prTitle, prBody, err = cfp.preparePullRequestDetails(vulnerabilities...) assert.NoError(t, err) assert.Equal(t, outputwriter.GetAggregatedPullRequestTitle(""), prTitle) assert.Equal(t, expectedPrBody, prBody) cfp.OutputWriter = &outputwriter.SimplifiedOutput{} - expectedPrBody = "**🚨 This automated pull request was created by Frogbot and fixes the below:**\n\n\n---\n## 📦 Vulnerable Dependencies\n---\n\n### ✍️ Summary \n\n\n| SEVERITY | DIRECT DEPENDENCIES | IMPACTED DEPENDENCY | FIXED VERSIONS |\n| :---------------------: | :----------------------------------: | :-----------------------------------: | :---------------------------------: | \n| High | | package1:1.0.0 | 1.0.0, 2.0.0 |\n| Critical | | package2:2.0.0 | 2.0.0, 3.0.0 |\n\n---\n### 👇 Details\n---\n\n\n#### package1 1.0.0\n\n\n- **Severity** 🔥 High\n- **Package Name:** package1\n- **Current Version:** 1.0.0\n- **Fixed Versions:** 1.0.0,2.0.0\n- **CVE:** CVE-2022-1234\n\n**Description:**\n\nsummary\n\n\n\n\n#### package2 2.0.0\n\n\n- **Severity** 💀 Critical\n- **Package Name:** package2\n- **Current Version:** 2.0.0\n- **Fixed Versions:** 2.0.0,3.0.0\n- **CVE:** CVE-2022-4321\n\n**Description:**\n\nsummary\n\n\n\n\n---\n\n\n**Frogbot** also supports **Contextual Analysis, Secret Detection and IaC Vulnerabilities Scanning**. This features are included as part of the [JFrog Advanced Security](https://jfrog.com/xray/) package, which isn't enabled on your system.\n\n[JFrog Frogbot](https://github.com/jfrog/frogbot#readme)\n[comment]: <> (Checksum: bec823edaceb5d0478b789798e819bde)\n" + expectedPrBody = "**🚨 This automated pull request was created by Frogbot and fixes the below:**\n\n\n---\n## 📦 Vulnerable Dependencies\n---\n\n### ✍️ Summary \n\n\n| SEVERITY | DIRECT DEPENDENCIES | IMPACTED DEPENDENCY | FIXED VERSIONS |\n| :---------------------: | :----------------------------------: | :-----------------------------------: | :---------------------------------: | \n| High | | package1:1.0.0 | 1.0.0, 2.0.0 |\n| Critical | | package2:2.0.0 | 2.0.0, 3.0.0 |\n\n---\n### 👇 Details\n---\n\n\n#### [ CVE-2022-1234 ] package1 1.0.0\n\n\n- **Severity** 🔥 High\n- **Package Name:** package1\n- **Current Version:** 1.0.0\n- **CVE:** CVE-2022-1234\n- **Fixed Versions:** 1.0.0,2.0.0\n\n**Description:**\n\nsummary\n\n\n\n\n#### [ CVE-2022-4321 ] package2 2.0.0\n\n\n- **Severity** 💀 Critical\n- **Package Name:** package2\n- **Current Version:** 2.0.0\n- **CVE:** CVE-2022-4321\n- **Fixed Versions:** 2.0.0,3.0.0\n\n**Description:**\n\nsummary\n\n\n\n\n---\n\n\n**Frogbot** also supports **Contextual Analysis, Secret Detection and IaC Vulnerabilities Scanning**. This features are included as part of the [JFrog Advanced Security](https://jfrog.com/xray/) package, which isn't enabled on your system.\n\n[🐸 JFrog Frogbot](https://github.com/jfrog/frogbot#readme)\n[comment]: <> (Checksum: bec823edaceb5d0478b789798e819bde)\n" prTitle, prBody, err = cfp.preparePullRequestDetails(vulnerabilities...) assert.NoError(t, err) assert.Equal(t, outputwriter.GetAggregatedPullRequestTitle(""), prTitle) diff --git a/testdata/config/frogbot-config-clean-test-proj.yml b/testdata/config/frogbot-config-clean-test-proj.yml old mode 100644 new mode 100755 diff --git a/testdata/config/frogbot-config-empty-scan.yml b/testdata/config/frogbot-config-empty-scan.yml old mode 100644 new mode 100755 diff --git a/testdata/config/frogbot-config-multi-dir-test-proj-no-fail.yml b/testdata/config/frogbot-config-multi-dir-test-proj-no-fail.yml old mode 100644 new mode 100755 diff --git a/testdata/config/frogbot-config-multi-dir-test-proj.yml b/testdata/config/frogbot-config-multi-dir-test-proj.yml old mode 100644 new mode 100755 diff --git a/testdata/config/frogbot-config-scan-multiple-repositories.yml b/testdata/config/frogbot-config-scan-multiple-repositories.yml old mode 100644 new mode 100755 diff --git a/testdata/config/frogbot-config-test-params-merge.yml b/testdata/config/frogbot-config-test-params-merge.yml old mode 100644 new mode 100755 diff --git a/testdata/config/frogbot-config-test-params.yml b/testdata/config/frogbot-config-test-params.yml old mode 100644 new mode 100755 diff --git a/testdata/config/frogbot-config-test-proj-no-fail.yml b/testdata/config/frogbot-config-test-proj-no-fail.yml old mode 100644 new mode 100755 diff --git a/testdata/config/frogbot-config-test-proj-no-vul.yml b/testdata/config/frogbot-config-test-proj-no-vul.yml old mode 100644 new mode 100755 diff --git a/testdata/config/frogbot-config-test-proj-subdir.yml b/testdata/config/frogbot-config-test-proj-subdir.yml old mode 100644 new mode 100755 diff --git a/testdata/config/frogbot-config-test-proj.yml b/testdata/config/frogbot-config-test-proj.yml old mode 100644 new mode 100755 diff --git a/testdata/config/frogbot-config-test-unmarshal.yml b/testdata/config/frogbot-config-test-unmarshal.yml old mode 100644 new mode 100755 diff --git a/testdata/indirect-projects/go/go.mod b/testdata/indirect-projects/go/go.mod old mode 100644 new mode 100755 diff --git a/testdata/indirect-projects/go/go.sum b/testdata/indirect-projects/go/go.sum old mode 100644 new mode 100755 diff --git a/testdata/indirect-projects/go/main.go b/testdata/indirect-projects/go/main.go old mode 100644 new mode 100755 diff --git a/testdata/indirect-projects/maven/pom.xml b/testdata/indirect-projects/maven/pom.xml old mode 100644 new mode 100755 diff --git a/testdata/indirect-projects/pip/requirements.txt b/testdata/indirect-projects/pip/requirements.txt old mode 100644 new mode 100755 diff --git a/testdata/indirect-projects/pip/setup.py b/testdata/indirect-projects/pip/setup.py old mode 100644 new mode 100755 diff --git a/testdata/indirect-projects/pipenv/Pipfile b/testdata/indirect-projects/pipenv/Pipfile old mode 100644 new mode 100755 diff --git a/testdata/indirect-projects/pipenv/Pipfile.lock b/testdata/indirect-projects/pipenv/Pipfile.lock old mode 100644 new mode 100755 diff --git a/testdata/indirect-projects/poetry/pyproject.toml b/testdata/indirect-projects/poetry/pyproject.toml old mode 100644 new mode 100755 diff --git a/testdata/messages/novulnerabilities.md b/testdata/messages/novulnerabilities.md old mode 100644 new mode 100755 index 59a0c4b1a..75684cf4b --- a/testdata/messages/novulnerabilities.md +++ b/testdata/messages/novulnerabilities.md @@ -11,8 +11,9 @@ +---
-[JFrog Frogbot](https://github.com/jfrog/frogbot#readme) +[🐸 JFrog Frogbot](https://github.com/jfrog/frogbot#readme)
diff --git a/testdata/messages/novulnerabilitiesMR.md b/testdata/messages/novulnerabilitiesMR.md old mode 100644 new mode 100755 index ccd40cc6f..11fd4a0c8 --- a/testdata/messages/novulnerabilitiesMR.md +++ b/testdata/messages/novulnerabilitiesMR.md @@ -11,8 +11,9 @@ +---
-[JFrog Frogbot](https://github.com/jfrog/frogbot#readme) +[🐸 JFrog Frogbot](https://github.com/jfrog/frogbot#readme)
diff --git a/testdata/packagehandlers/pom.xml b/testdata/packagehandlers/pom.xml old mode 100644 new mode 100755 diff --git a/testdata/projects/dotnet/Program.cs b/testdata/projects/dotnet/Program.cs old mode 100644 new mode 100755 diff --git a/testdata/projects/dotnet/dotnet.csproj b/testdata/projects/dotnet/dotnet.csproj old mode 100644 new mode 100755 diff --git a/testdata/projects/go/go.mod b/testdata/projects/go/go.mod old mode 100644 new mode 100755 diff --git a/testdata/projects/go/go.sum b/testdata/projects/go/go.sum old mode 100644 new mode 100755 diff --git a/testdata/projects/go/hello.go b/testdata/projects/go/hello.go old mode 100644 new mode 100755 diff --git a/testdata/projects/gradle/build.gradle b/testdata/projects/gradle/build.gradle old mode 100644 new mode 100755 diff --git a/testdata/projects/gradle/gradle/wrapper/gradle-wrapper.jar b/testdata/projects/gradle/gradle/wrapper/gradle-wrapper.jar old mode 100644 new mode 100755 diff --git a/testdata/projects/gradle/gradle/wrapper/gradle-wrapper.properties b/testdata/projects/gradle/gradle/wrapper/gradle-wrapper.properties old mode 100644 new mode 100755 diff --git a/testdata/projects/gradle/gradlew.bat b/testdata/projects/gradle/gradlew.bat old mode 100644 new mode 100755 diff --git a/testdata/projects/gradle/src/main/template/Main.java b/testdata/projects/gradle/src/main/template/Main.java old mode 100644 new mode 100755 diff --git a/testdata/projects/maven/multi1/pom.xml b/testdata/projects/maven/multi1/pom.xml old mode 100644 new mode 100755 diff --git a/testdata/projects/maven/pom.xml b/testdata/projects/maven/pom.xml old mode 100644 new mode 100755 diff --git a/testdata/projects/npm/package.json b/testdata/projects/npm/package.json old mode 100644 new mode 100755 diff --git a/testdata/projects/nuget/Program.cs b/testdata/projects/nuget/Program.cs new file mode 100755 index 000000000..3751555cb --- /dev/null +++ b/testdata/projects/nuget/Program.cs @@ -0,0 +1,2 @@ +// See https://aka.ms/new-console-template for more information +Console.WriteLine("Hello, World!"); diff --git a/testdata/projects/nuget/nuget.csproj b/testdata/projects/nuget/nuget.csproj new file mode 100755 index 000000000..46690096f --- /dev/null +++ b/testdata/projects/nuget/nuget.csproj @@ -0,0 +1,14 @@ + + + + Exe + net6.0 + enable + enable + + + + + + + diff --git a/testdata/projects/nuget/obj/nuget.csproj.nuget.dgspec.json b/testdata/projects/nuget/obj/nuget.csproj.nuget.dgspec.json new file mode 100755 index 000000000..b7a47c776 --- /dev/null +++ b/testdata/projects/nuget/obj/nuget.csproj.nuget.dgspec.json @@ -0,0 +1,67 @@ +{ + "format": 1, + "restore": { + "/Users/erant/Desktop/jfrog/frogbot/testdata/projects/nuget/nuget.csproj": {} + }, + "projects": { + "/Users/erant/Desktop/jfrog/frogbot/testdata/projects/nuget/nuget.csproj": { + "version": "1.0.0", + "restore": { + "projectUniqueName": "/Users/erant/Desktop/jfrog/frogbot/testdata/projects/nuget/nuget.csproj", + "projectName": "nuget", + "projectPath": "/Users/erant/Desktop/jfrog/frogbot/testdata/projects/nuget/nuget.csproj", + "packagesPath": "/Users/erant/.nuget/packages/", + "outputPath": "/Users/erant/Desktop/jfrog/frogbot/testdata/projects/nuget/obj/", + "projectStyle": "PackageReference", + "configFilePaths": [ + "/Users/erant/.nuget/NuGet/NuGet.Config" + ], + "originalTargetFrameworks": [ + "net6.0" + ], + "sources": { + "https://api.nuget.org/v3/index.json": {} + }, + "frameworks": { + "net6.0": { + "targetAlias": "net6.0", + "projectReferences": {} + } + }, + "warningProperties": { + "warnAsError": [ + "NU1605" + ] + } + }, + "frameworks": { + "net6.0": { + "targetAlias": "net6.0", + "dependencies": { + "snappier": { + "target": "Package", + "version": "[1.1.0, )" + } + }, + "imports": [ + "net461", + "net462", + "net47", + "net471", + "net472", + "net48", + "net481" + ], + "assetTargetFallback": true, + "warn": true, + "frameworkReferences": { + "Microsoft.NETCore.App": { + "privateAssets": "all" + } + }, + "runtimeIdentifierGraphPath": "/usr/local/share/dotnet/sdk/6.0.413/RuntimeIdentifierGraph.json" + } + } + } + } +} \ No newline at end of file diff --git a/testdata/projects/nuget/obj/nuget.csproj.nuget.g.props b/testdata/projects/nuget/obj/nuget.csproj.nuget.g.props new file mode 100755 index 000000000..19fb1b881 --- /dev/null +++ b/testdata/projects/nuget/obj/nuget.csproj.nuget.g.props @@ -0,0 +1,15 @@ + + + + True + NuGet + $(MSBuildThisFileDirectory)project.assets.json + /Users/erant/.nuget/packages/ + /Users/erant/.nuget/packages/ + PackageReference + 6.3.3 + + + + + \ No newline at end of file diff --git a/testdata/projects/nuget/obj/nuget.csproj.nuget.g.targets b/testdata/projects/nuget/obj/nuget.csproj.nuget.g.targets new file mode 100755 index 000000000..3dc06ef3c --- /dev/null +++ b/testdata/projects/nuget/obj/nuget.csproj.nuget.g.targets @@ -0,0 +1,2 @@ + + \ No newline at end of file diff --git a/testdata/projects/nuget/obj/project.assets.json b/testdata/projects/nuget/obj/project.assets.json new file mode 100755 index 000000000..5d6c6fd27 --- /dev/null +++ b/testdata/projects/nuget/obj/project.assets.json @@ -0,0 +1,107 @@ +{ + "version": 3, + "targets": { + "net6.0": { + "Snappier/1.1.0": { + "type": "package", + "compile": { + "lib/net6.0/Snappier.dll": { + "related": ".xml" + } + }, + "runtime": { + "lib/net6.0/Snappier.dll": { + "related": ".xml" + } + } + } + } + }, + "libraries": { + "Snappier/1.1.0": { + "sha512": "ws78FoXdwY5rTwoSqpdl9HFpgSU4ih0byThG2+s+Bm1VSfyhkQxOdrV0aWQ1R1MptA8EwAcrnn3Mh0GkFSssxA==", + "type": "package", + "path": "snappier/1.1.0", + "files": [ + ".nupkg.metadata", + ".signature.p7s", + "COPYING.txt", + "lib/net6.0/Snappier.dll", + "lib/net6.0/Snappier.xml", + "lib/net7.0/Snappier.dll", + "lib/net7.0/Snappier.xml", + "lib/netstandard2.0/Snappier.dll", + "lib/netstandard2.0/Snappier.xml", + "snappier.1.1.0.nupkg.sha512", + "snappier.nuspec" + ] + } + }, + "projectFileDependencyGroups": { + "net6.0": [ + "snappier >= 1.1.0" + ] + }, + "packageFolders": { + "/Users/erant/.nuget/packages/": {} + }, + "project": { + "version": "1.0.0", + "restore": { + "projectUniqueName": "/Users/erant/Desktop/jfrog/frogbot/testdata/projects/nuget/nuget.csproj", + "projectName": "nuget", + "projectPath": "/Users/erant/Desktop/jfrog/frogbot/testdata/projects/nuget/nuget.csproj", + "packagesPath": "/Users/erant/.nuget/packages/", + "outputPath": "/Users/erant/Desktop/jfrog/frogbot/testdata/projects/nuget/obj/", + "projectStyle": "PackageReference", + "configFilePaths": [ + "/Users/erant/.nuget/NuGet/NuGet.Config" + ], + "originalTargetFrameworks": [ + "net6.0" + ], + "sources": { + "https://api.nuget.org/v3/index.json": {} + }, + "frameworks": { + "net6.0": { + "targetAlias": "net6.0", + "projectReferences": {} + } + }, + "warningProperties": { + "warnAsError": [ + "NU1605" + ] + } + }, + "frameworks": { + "net6.0": { + "targetAlias": "net6.0", + "dependencies": { + "snappier": { + "target": "Package", + "version": "[1.1.0, )" + } + }, + "imports": [ + "net461", + "net462", + "net47", + "net471", + "net472", + "net48", + "net481" + ], + "assetTargetFallback": true, + "warn": true, + "frameworkReferences": { + "Microsoft.NETCore.App": { + "privateAssets": "all" + } + }, + "runtimeIdentifierGraphPath": "/usr/local/share/dotnet/sdk/6.0.413/RuntimeIdentifierGraph.json" + } + } + } +} \ No newline at end of file diff --git a/testdata/projects/nuget/obj/project.nuget.cache b/testdata/projects/nuget/obj/project.nuget.cache new file mode 100755 index 000000000..a9cc31f45 --- /dev/null +++ b/testdata/projects/nuget/obj/project.nuget.cache @@ -0,0 +1,10 @@ +{ + "version": 2, + "dgSpecHash": "nTBoiK3A4Eb9Rzc+BEh0EkdFq1PwStuNqJCXUlbyFbNFnkHXugywHgL1MTIaC7Oyvh8RbNmtYvR+gL5D4nhoEg==", + "success": true, + "projectFilePath": "/Users/erant/Desktop/jfrog/frogbot/testdata/projects/nuget/nuget.csproj", + "expectedPackageFiles": [ + "/Users/erant/.nuget/packages/snappier/1.1.0/snappier.1.1.0.nupkg.sha512" + ], + "logs": [] +} \ No newline at end of file diff --git a/testdata/projects/pip/requirements.txt b/testdata/projects/pip/requirements.txt old mode 100644 new mode 100755 diff --git a/testdata/projects/pip/setup.py b/testdata/projects/pip/setup.py old mode 100644 new mode 100755 diff --git a/testdata/projects/pipenv/Pipfile b/testdata/projects/pipenv/Pipfile old mode 100644 new mode 100755 diff --git a/testdata/projects/pipenv/Pipfile.lock b/testdata/projects/pipenv/Pipfile.lock old mode 100644 new mode 100755 diff --git a/testdata/projects/poetry/pyproject.toml b/testdata/projects/poetry/pyproject.toml old mode 100644 new mode 100755 diff --git a/testdata/projects/yarn1/.yarn/install-state.gz b/testdata/projects/yarn1/.yarn/install-state.gz old mode 100644 new mode 100755 diff --git a/testdata/projects/yarn1/.yarnrc.yml b/testdata/projects/yarn1/.yarnrc.yml old mode 100644 new mode 100755 diff --git a/testdata/projects/yarn1/package.json b/testdata/projects/yarn1/package.json old mode 100644 new mode 100755 diff --git a/testdata/projects/yarn1/yarn.lock b/testdata/projects/yarn1/yarn.lock old mode 100644 new mode 100755 diff --git a/testdata/projects/yarn2/.yarn/install-state.gz b/testdata/projects/yarn2/.yarn/install-state.gz old mode 100644 new mode 100755 diff --git a/testdata/projects/yarn2/.yarnrc.yml b/testdata/projects/yarn2/.yarnrc.yml old mode 100644 new mode 100755 diff --git a/testdata/projects/yarn2/package.json b/testdata/projects/yarn2/package.json old mode 100644 new mode 100755 diff --git a/testdata/projects/yarn2/yarn.lock b/testdata/projects/yarn2/yarn.lock old mode 100644 new mode 100755 diff --git a/testdata/scanallpullrequests/test-proj-pip-with-vulnerability/setup.py b/testdata/scanallpullrequests/test-proj-pip-with-vulnerability/setup.py old mode 100644 new mode 100755 diff --git a/testdata/scanallpullrequests/test-proj-pip/setup.py b/testdata/scanallpullrequests/test-proj-pip/setup.py old mode 100644 new mode 100755 diff --git a/testdata/scanallpullrequests/test-proj-with-vulnerability/package.json b/testdata/scanallpullrequests/test-proj-with-vulnerability/package.json old mode 100644 new mode 100755 diff --git a/testdata/scanallpullrequests/test-proj/package.json b/testdata/scanallpullrequests/test-proj/package.json old mode 100644 new mode 100755 diff --git a/testdata/scanmultiplerepositories/mvn-repo/pom.xml b/testdata/scanmultiplerepositories/mvn-repo/pom.xml old mode 100644 new mode 100755 diff --git a/testdata/scanmultiplerepositories/npm-repo/package-lock.json b/testdata/scanmultiplerepositories/npm-repo/package-lock.json old mode 100644 new mode 100755 diff --git a/testdata/scanmultiplerepositories/npm-repo/package.json b/testdata/scanmultiplerepositories/npm-repo/package.json old mode 100644 new mode 100755 diff --git a/testdata/scanmultiplerepositories/pip-repo/requirements.txt b/testdata/scanmultiplerepositories/pip-repo/requirements.txt old mode 100644 new mode 100755 diff --git a/testdata/scanpullrequest/clean-test-proj/sourceBranch.gz b/testdata/scanpullrequest/clean-test-proj/sourceBranch.gz old mode 100644 new mode 100755 diff --git a/testdata/scanpullrequest/clean-test-proj/targetBranch.gz b/testdata/scanpullrequest/clean-test-proj/targetBranch.gz old mode 100644 new mode 100755 diff --git a/testdata/scanpullrequest/commits.json b/testdata/scanpullrequest/commits.json old mode 100644 new mode 100755 diff --git a/testdata/scanpullrequest/expectedPullRequestDetailsResponse.json b/testdata/scanpullrequest/expectedPullRequestDetailsResponse.json old mode 100644 new mode 100755 diff --git a/testdata/scanpullrequest/expectedResponse.json b/testdata/scanpullrequest/expectedResponse.json old mode 100644 new mode 100755 index 0761b730c..c5222cb76 --- a/testdata/scanpullrequest/expectedResponse.json +++ b/testdata/scanpullrequest/expectedResponse.json @@ -1,3 +1,3 @@ { - "body": "\u003cdiv align='center'\u003e\n\n[![](https://raw.githubusercontent.com/jfrog/frogbot/master/resources/v2/vulnerabilitiesBannerPR.png)](https://github.com/jfrog/frogbot#readme)\n\n\u003c/div\u003e\n\n\n## 📦 Vulnerable Dependencies \n\n### ✍️ Summary\n\n\u003cdiv align=\"center\"\u003e\n\n| SEVERITY | CONTEXTUAL ANALYSIS | DIRECT DEPENDENCIES | IMPACTED DEPENDENCY | FIXED VERSIONS |\n| :---------------------: | :----------------------------------: | :----------------------------------: | :-----------------------------------: | :---------------------------------: | \n| ![](https://raw.githubusercontent.com/jfrog/frogbot/master/resources/v2/notApplicableCritical.png)\u003cbr\u003eCritical | Not Applicable | minimist:1.2.5 | minimist:1.2.5 | [0.2.4]\u003cbr\u003e\u003cbr\u003e[1.2.6] |\n\n\u003c/div\u003e\n\n## 👇 Details\n\n\n\n\n- **Severity** 💀 Critical\n- **Contextual Analysis:** Not Applicable\n- **Package Name:** minimist\n- **Current Version:** 1.2.5\n- **Fixed Versions:** [0.2.4],[1.2.6]\n- **CVE:** CVE-2021-44906\n\n**Description:**\n\n[Minimist](https://github.com/substack/minimist) is a simple and very popular argument parser. It is used by more than 14 million by Mar 2022. This package developers stopped developing it since April 2020 and its community released a [newer version](https://github.com/meszaros-lajos-gyorgy/minimist-lite) supported by the community.\n\n\nAn incomplete fix for [CVE-2020-7598](https://nvd.nist.gov/vuln/detail/CVE-2020-7598) partially blocked prototype pollution attacks. Researchers discovered that it does not check for constructor functions which means they can be overridden. This behavior can be triggered easily when using it insecurely (which is the common usage). For example:\n```\nvar argv = parse(['--_.concat.constructor.prototype.y', '123']);\nt.equal((function(){}).foo, undefined);\nt.equal(argv.y, undefined);\n```\nIn this example, `prototype.y` is assigned with `123` which will be derived to every newly created object. \n\nThis vulnerability can be triggered when the attacker-controlled input is parsed using Minimist without any validation. As always with prototype pollution, the impact depends on the code that follows the attack, but denial of service is almost always guaranteed.\n\n**Remediation:**\n\n##### Development mitigations\n\nAdd the `Object.freeze(Object.prototype);` directive once at the beginning of your main JS source code file (ex. `index.js`), preferably after all your `require` directives. This will prevent any changes to the prototype object, thus completely negating prototype pollution attacks.\n\n\n\n\n\u003cdiv align=\"center\"\u003e\n\n[JFrog Frogbot](https://github.com/jfrog/frogbot#readme)\n\n\u003c/div\u003e\n" + "body": "\u003cdiv align='center'\u003e\n\n[![](https://raw.githubusercontent.com/jfrog/frogbot/master/resources/v2/vulnerabilitiesBannerPR.png)](https://github.com/jfrog/frogbot#readme)\n\n\u003c/div\u003e\n\n\n## 📦 Vulnerable Dependencies \n\n### ✍️ Summary\n\n\u003cdiv align=\"center\"\u003e\n\n| SEVERITY | CONTEXTUAL ANALYSIS | DIRECT DEPENDENCIES | IMPACTED DEPENDENCY | FIXED VERSIONS |\n| :---------------------: | :----------------------------------: | :----------------------------------: | :-----------------------------------: | :---------------------------------: | \n| ![](https://raw.githubusercontent.com/jfrog/frogbot/master/resources/v2/notApplicableCritical.png)\u003cbr\u003eCritical | Not Applicable | minimist:1.2.5 | minimist:1.2.5 | [0.2.4]\u003cbr\u003e\u003cbr\u003e[1.2.6] |\n\n\u003c/div\u003e\n\n## 👇 Details\n\n\n\n\n- **Severity** 💀 Critical\n- **Contextual Analysis:** Not Applicable\n- **Package Name:** minimist\n- **Current Version:** 1.2.5\n- **CVE:** CVE-2021-44906\n- **Fixed Versions:** [0.2.4],[1.2.6]\n\n**Description:**\n\n[Minimist](https://github.com/substack/minimist) is a simple and very popular argument parser. It is used by more than 14 million by Mar 2022. This package developers stopped developing it since April 2020 and its community released a [newer version](https://github.com/meszaros-lajos-gyorgy/minimist-lite) supported by the community.\n\n\nAn incomplete fix for [CVE-2020-7598](https://nvd.nist.gov/vuln/detail/CVE-2020-7598) partially blocked prototype pollution attacks. Researchers discovered that it does not check for constructor functions which means they can be overridden. This behavior can be triggered easily when using it insecurely (which is the common usage). For example:\n```\nvar argv = parse(['--_.concat.constructor.prototype.y', '123']);\nt.equal((function(){}).foo, undefined);\nt.equal(argv.y, undefined);\n```\nIn this example, `prototype.y` is assigned with `123` which will be derived to every newly created object. \n\nThis vulnerability can be triggered when the attacker-controlled input is parsed using Minimist without any validation. As always with prototype pollution, the impact depends on the code that follows the attack, but denial of service is almost always guaranteed.\n\n**Remediation:**\n\n##### Development mitigations\n\nAdd the `Object.freeze(Object.prototype);` directive once at the beginning of your main JS source code file (ex. `index.js`), preferably after all your `require` directives. This will prevent any changes to the prototype object, thus completely negating prototype pollution attacks.\n\n\n\n\n---\n\u003cdiv align=\"center\"\u003e\n\n[🐸 JFrog Frogbot](https://github.com/jfrog/frogbot#readme)\n\n\u003c/div\u003e\n" } \ No newline at end of file diff --git a/testdata/scanpullrequest/expectedResponseMultiDir.json b/testdata/scanpullrequest/expectedResponseMultiDir.json old mode 100644 new mode 100755 index 2c57f1d2d..5bcebfce2 --- a/testdata/scanpullrequest/expectedResponseMultiDir.json +++ b/testdata/scanpullrequest/expectedResponseMultiDir.json @@ -1,3 +1,3 @@ { - "body": "\u003cdiv align='center'\u003e\n\n[![](https://raw.githubusercontent.com/jfrog/frogbot/master/resources/v2/vulnerabilitiesBannerPR.png)](https://github.com/jfrog/frogbot#readme)\n\n\u003c/div\u003e\n\n\n## 📦 Vulnerable Dependencies \n\n### ✍️ Summary\n\n\u003cdiv align=\"center\"\u003e\n\n| SEVERITY | CONTEXTUAL ANALYSIS | DIRECT DEPENDENCIES | IMPACTED DEPENDENCY | FIXED VERSIONS |\n| :---------------------: | :----------------------------------: | :----------------------------------: | :-----------------------------------: | :---------------------------------: | \n| ![](https://raw.githubusercontent.com/jfrog/frogbot/master/resources/v2/notApplicableHigh.png)\u003cbr\u003e High | Not Applicable | minimatch:3.0.4 | minimatch:3.0.4 | [3.0.5] |\n| ![](https://raw.githubusercontent.com/jfrog/frogbot/master/resources/v2/applicableHighSeverity.png)\u003cbr\u003e High | Undetermined | pyjwt:1.7.1 | pyjwt:1.7.1 | [2.4.0] |\n\n\u003c/div\u003e\n\n## 👇 Details\n\n\n\u003cdetails\u003e\n\u003csummary\u003e \u003cb\u003eminimatch 3.0.4\u003c/b\u003e \u003c/summary\u003e\n\u003cbr\u003e\n\n- **Severity** 🔥 High\n- **Contextual Analysis:** Not Applicable\n- **Package Name:** minimatch\n- **Current Version:** 3.0.4\n- **Fixed Version:** [3.0.5]\n- **CVE:** CVE-2022-3517\n\n**Description:**\n\nA vulnerability was found in the minimatch package. This flaw allows a Regular Expression Denial of Service (ReDoS) when calling the braceExpand function with specific arguments, resulting in a Denial of Service.\n\n\n\n\u003c/details\u003e\n\n\n\u003cdetails\u003e\n\u003csummary\u003e \u003cb\u003epyjwt 1.7.1\u003c/b\u003e \u003c/summary\u003e\n\u003cbr\u003e\n\n- **Severity** 🔥 High\n- **Contextual Analysis:** Undetermined\n- **Package Name:** pyjwt\n- **Current Version:** 1.7.1\n- **Fixed Version:** [2.4.0]\n- **CVE:** CVE-2022-29217\n\n**Description:**\n\n[PyJWT](https://pypi.org/project/PyJWT) is a Python implementation of the RFC 7519 standard (JSON Web Tokens). [JSON Web Tokens](https://jwt.io/) are an open, industry standard method for representing claims securely between two parties. A JWT comes with an inline signature that is meant to be verified by the receiving application. JWT supports multiple standard algorithms, and the algorithm itself is **specified in the JWT token itself**.\n\nThe PyJWT library uses the signature-verification algorithm that is specified in the JWT token (that is completely attacker-controlled), however - it requires the validating application to pass an `algorithms` kwarg that specifies the expected algorithms in order to avoid key confusion. Unfortunately - a non-default value `algorithms=jwt.algorithms.get_default_algorithms()` exists that allows all algorithms.\nThe PyJWT library also tries to mitigate key confusions in this case, by making sure that public keys are not used as an HMAC secret. For example, HMAC secrets that begin with `-----BEGIN PUBLIC KEY-----` are rejected when encoding a JWT.\n\nIt has been discovered that due to missing key-type checks, in cases where -\n1. The vulnerable application expects to receive a JWT signed with an Elliptic-Curve key (one of the algorithms `ES256`, `ES384`, `ES512`, `EdDSA`)\n2. The vulnerable application decodes the JWT token using the non-default kwarg `algorithms=jwt.algorithms.get_default_algorithms()` (or alternatively, `algorithms` contain both an HMAC-based algorithm and an EC-based algorithm)\n\nAn attacker can create an HMAC-signed (ex. `HS256`) JWT token, using the (well-known!) EC public key as the HMAC key. The validating application will accept this JWT token as a valid token.\n\nFor example, an application might have planned to validate an `EdDSA`-signed token that was generated as follows -\n```python\n# Making a good jwt token that should work by signing it with the private key\nencoded_good = jwt.encode({\"test\": 1234}, priv_key_bytes, algorithm=\"EdDSA\")\n```\nAn attacker in posession of the public key can generate an `HMAC`-signed token to confuse PyJWT - \n```python\n# Using HMAC with the public key to trick the receiver to think that the public key is a HMAC secret\nencoded_bad = jwt.encode({\"test\": 1234}, pub_key_bytes, algorithm=\"HS256\")\n```\n\nThe following vulnerable `decode` call will accept BOTH of the above tokens as valid - \n```\ndecoded = jwt.decode(encoded_good, pub_key_bytes, \nalgorithms=jwt.algorithms.get_default_algorithms())\n```\n\n**Remediation:**\n\n##### Development mitigations\n\nUse a specific algorithm instead of `jwt.algorithms.get_default_algorithms`.\nFor example, replace the following call - \n`jwt.decode(encoded_jwt, pub_key_bytes, algorithms=jwt.algorithms.get_default_algorithms())`\nWith -\n`jwt.decode(encoded_jwt, pub_key_bytes, algorithms=[\"ES256\"])`\n\n\n\n\u003c/details\u003e\n\n\n\u003cdiv align=\"center\"\u003e\n\n[JFrog Frogbot](https://github.com/jfrog/frogbot#readme)\n\n\u003c/div\u003e\n" + "body": "\u003cdiv align='center'\u003e\n\n[![](https://raw.githubusercontent.com/jfrog/frogbot/master/resources/v2/vulnerabilitiesBannerPR.png)](https://github.com/jfrog/frogbot#readme)\n\n\u003c/div\u003e\n\n\n## 📦 Vulnerable Dependencies \n\n### ✍️ Summary\n\n\u003cdiv align=\"center\"\u003e\n\n| SEVERITY | CONTEXTUAL ANALYSIS | DIRECT DEPENDENCIES | IMPACTED DEPENDENCY | FIXED VERSIONS |\n| :---------------------: | :----------------------------------: | :----------------------------------: | :-----------------------------------: | :---------------------------------: | \n| ![](https://raw.githubusercontent.com/jfrog/frogbot/master/resources/v2/notApplicableHigh.png)\u003cbr\u003e High | Not Applicable | minimatch:3.0.4 | minimatch:3.0.4 | [3.0.5] |\n| ![](https://raw.githubusercontent.com/jfrog/frogbot/master/resources/v2/applicableHighSeverity.png)\u003cbr\u003e High | Undetermined | pyjwt:1.7.1 | pyjwt:1.7.1 | [2.4.0] |\n\n\u003c/div\u003e\n\n## 👇 Details\n\n\n\u003cdetails\u003e\n\u003csummary\u003e \u003cb\u003e[ CVE-2022-3517 ] minimatch 3.0.4\u003c/b\u003e \u003c/summary\u003e\n\u003cbr\u003e\n\n- **Severity** 🔥 High\n- **Contextual Analysis:** Not Applicable\n- **Package Name:** minimatch\n- **Current Version:** 3.0.4\n- **CVE:** CVE-2022-3517\n- **Fixed Version:** [3.0.5]\n\n**Description:**\n\nA vulnerability was found in the minimatch package. This flaw allows a Regular Expression Denial of Service (ReDoS) when calling the braceExpand function with specific arguments, resulting in a Denial of Service.\n\n\n\n\u003c/details\u003e\n\n\n\u003cdetails\u003e\n\u003csummary\u003e \u003cb\u003e[ CVE-2022-29217 ] pyjwt 1.7.1\u003c/b\u003e \u003c/summary\u003e\n\u003cbr\u003e\n\n- **Severity** 🔥 High\n- **Contextual Analysis:** Undetermined\n- **Package Name:** pyjwt\n- **Current Version:** 1.7.1\n- **CVE:** CVE-2022-29217\n- **Fixed Version:** [2.4.0]\n\n**Description:**\n\n[PyJWT](https://pypi.org/project/PyJWT) is a Python implementation of the RFC 7519 standard (JSON Web Tokens). [JSON Web Tokens](https://jwt.io/) are an open, industry standard method for representing claims securely between two parties. A JWT comes with an inline signature that is meant to be verified by the receiving application. JWT supports multiple standard algorithms, and the algorithm itself is **specified in the JWT token itself**.\n\nThe PyJWT library uses the signature-verification algorithm that is specified in the JWT token (that is completely attacker-controlled), however - it requires the validating application to pass an `algorithms` kwarg that specifies the expected algorithms in order to avoid key confusion. Unfortunately - a non-default value `algorithms=jwt.algorithms.get_default_algorithms()` exists that allows all algorithms.\nThe PyJWT library also tries to mitigate key confusions in this case, by making sure that public keys are not used as an HMAC secret. For example, HMAC secrets that begin with `-----BEGIN PUBLIC KEY-----` are rejected when encoding a JWT.\n\nIt has been discovered that due to missing key-type checks, in cases where -\n1. The vulnerable application expects to receive a JWT signed with an Elliptic-Curve key (one of the algorithms `ES256`, `ES384`, `ES512`, `EdDSA`)\n2. The vulnerable application decodes the JWT token using the non-default kwarg `algorithms=jwt.algorithms.get_default_algorithms()` (or alternatively, `algorithms` contain both an HMAC-based algorithm and an EC-based algorithm)\n\nAn attacker can create an HMAC-signed (ex. `HS256`) JWT token, using the (well-known!) EC public key as the HMAC key. The validating application will accept this JWT token as a valid token.\n\nFor example, an application might have planned to validate an `EdDSA`-signed token that was generated as follows -\n```python\n# Making a good jwt token that should work by signing it with the private key\nencoded_good = jwt.encode({\"test\": 1234}, priv_key_bytes, algorithm=\"EdDSA\")\n```\nAn attacker in posession of the public key can generate an `HMAC`-signed token to confuse PyJWT - \n```python\n# Using HMAC with the public key to trick the receiver to think that the public key is a HMAC secret\nencoded_bad = jwt.encode({\"test\": 1234}, pub_key_bytes, algorithm=\"HS256\")\n```\n\nThe following vulnerable `decode` call will accept BOTH of the above tokens as valid - \n```\ndecoded = jwt.decode(encoded_good, pub_key_bytes, \nalgorithms=jwt.algorithms.get_default_algorithms())\n```\n\n**Remediation:**\n\n##### Development mitigations\n\nUse a specific algorithm instead of `jwt.algorithms.get_default_algorithms`.\nFor example, replace the following call - \n`jwt.decode(encoded_jwt, pub_key_bytes, algorithms=jwt.algorithms.get_default_algorithms())`\nWith -\n`jwt.decode(encoded_jwt, pub_key_bytes, algorithms=[\"ES256\"])`\n\n\n\n\u003c/details\u003e\n\n\n---\n\u003cdiv align=\"center\"\u003e\n\n[🐸 JFrog Frogbot](https://github.com/jfrog/frogbot#readme)\n\n\u003c/div\u003e\n" } \ No newline at end of file diff --git a/testdata/scanpullrequest/expectedResponsePip.json b/testdata/scanpullrequest/expectedResponsePip.json old mode 100644 new mode 100755 diff --git a/testdata/scanpullrequest/list_merge_request_discussion_items.json b/testdata/scanpullrequest/list_merge_request_discussion_items.json new file mode 100644 index 000000000..dc5c5d9a5 --- /dev/null +++ b/testdata/scanpullrequest/list_merge_request_discussion_items.json @@ -0,0 +1,87 @@ +[ + { + "id": "6a9c1750b37d513a43987b574953fceb50b03ce7", + "individual_note": false, + "notes": [ + { + "id": 1126, + "type": "DiscussionNote", + "body": "discussion text", + "attachment": null, + "author": { + "id": 1, + "name": "root", + "username": "root", + "state": "active", + "avatar_url": "https://www.gravatar.com/avatar/00afb8fb6ab07c3ee3e9c1f38777e2f4?s=80&d=identicon", + "web_url": "http://localhost:3000/root" + }, + "created_at": "2018-03-03T21:54:39.668Z", + "updated_at": "2018-03-03T21:54:39.668Z", + "system": false, + "noteable_id": 3, + "noteable_type": "Merge request", + "project_id": 5, + "noteable_iid": null, + "resolved": false, + "resolvable": true, + "resolved_by": null, + "resolved_at": null + }, + { + "id": 1129, + "type": "DiscussionNote", + "body": "reply to the discussion", + "attachment": null, + "author": { + "id": 1, + "name": "root", + "username": "root", + "state": "active", + "avatar_url": "https://www.gravatar.com/avatar/00afb8fb6ab07c3ee3e9c1f38777e2f4?s=80&d=identicon", + "web_url": "http://localhost:3000/root" + }, + "created_at": "2018-03-04T13:38:02.127Z", + "updated_at": "2018-03-04T13:38:02.127Z", + "system": false, + "noteable_id": 3, + "noteable_type": "Merge request", + "project_id": 5, + "noteable_iid": null, + "resolved": false, + "resolvable": true, + "resolved_by": null + } + ] + }, + { + "id": "87805b7c09016a7058e91bdbe7b29d1f284a39e6", + "individual_note": true, + "notes": [ + { + "id": 1128, + "type": null, + "body": "a single comment", + "attachment": null, + "author": { + "id": 1, + "name": "root", + "username": "root", + "state": "active", + "avatar_url": "https://www.gravatar.com/avatar/00afb8fb6ab07c3ee3e9c1f38777e2f4?s=80&d=identicon", + "web_url": "http://localhost:3000/root" + }, + "created_at": "2018-03-04T09:17:22.520Z", + "updated_at": "2018-03-04T09:17:22.520Z", + "system": false, + "noteable_id": 3, + "noteable_type": "Merge request", + "project_id": 5, + "noteable_iid": null, + "resolved": false, + "resolvable": true, + "resolved_by": null + } + ] + } +] \ No newline at end of file diff --git a/testdata/scanpullrequest/multi-dir-test-proj/sourceBranch.gz b/testdata/scanpullrequest/multi-dir-test-proj/sourceBranch.gz old mode 100644 new mode 100755 diff --git a/testdata/scanpullrequest/multi-dir-test-proj/sub1/package-lock.json b/testdata/scanpullrequest/multi-dir-test-proj/sub1/package-lock.json old mode 100644 new mode 100755 diff --git a/testdata/scanpullrequest/multi-dir-test-proj/targetBranch.gz b/testdata/scanpullrequest/multi-dir-test-proj/targetBranch.gz old mode 100644 new mode 100755 diff --git a/testdata/scanpullrequest/test-proj-pip/sourceBranch.gz b/testdata/scanpullrequest/test-proj-pip/sourceBranch.gz old mode 100644 new mode 100755 diff --git a/testdata/scanpullrequest/test-proj-pip/targetBranch.gz b/testdata/scanpullrequest/test-proj-pip/targetBranch.gz old mode 100644 new mode 100755 diff --git a/testdata/scanpullrequest/test-proj-subdir/sourceBranch.gz b/testdata/scanpullrequest/test-proj-subdir/sourceBranch.gz old mode 100644 new mode 100755 diff --git a/testdata/scanpullrequest/test-proj-subdir/targetBranch.gz b/testdata/scanpullrequest/test-proj-subdir/targetBranch.gz old mode 100644 new mode 100755 diff --git a/testdata/scanpullrequest/test-proj/sourceBranch.gz b/testdata/scanpullrequest/test-proj/sourceBranch.gz old mode 100644 new mode 100755 diff --git a/testdata/scanpullrequest/test-proj/targetBranch.gz b/testdata/scanpullrequest/test-proj/targetBranch.gz old mode 100644 new mode 100755 diff --git a/testdata/scanrepository/aggregate-pr-lifecycle/aggregate-dont-update-pr/package-lock.json b/testdata/scanrepository/aggregate-pr-lifecycle/aggregate-dont-update-pr/package-lock.json old mode 100644 new mode 100755 diff --git a/testdata/scanrepository/aggregate-pr-lifecycle/aggregate-dont-update-pr/package.json b/testdata/scanrepository/aggregate-pr-lifecycle/aggregate-dont-update-pr/package.json old mode 100644 new mode 100755 diff --git a/testdata/scanrepository/aggregate-pr-lifecycle/aggregate-update-pr/package-lock.json b/testdata/scanrepository/aggregate-pr-lifecycle/aggregate-update-pr/package-lock.json old mode 100644 new mode 100755 diff --git a/testdata/scanrepository/aggregate-pr-lifecycle/aggregate-update-pr/package.json b/testdata/scanrepository/aggregate-pr-lifecycle/aggregate-update-pr/package.json old mode 100644 new mode 100755 diff --git a/testdata/scanrepository/cmd/aggregate-cant-fix/setup.py b/testdata/scanrepository/cmd/aggregate-cant-fix/setup.py old mode 100644 new mode 100755 diff --git a/testdata/scanrepository/cmd/aggregate-multi-dir/.frogbot/frogbot-config.yml b/testdata/scanrepository/cmd/aggregate-multi-dir/.frogbot/frogbot-config.yml old mode 100644 new mode 100755 diff --git a/testdata/scanrepository/cmd/aggregate-multi-dir/npm1/package.json b/testdata/scanrepository/cmd/aggregate-multi-dir/npm1/package.json old mode 100644 new mode 100755 diff --git a/testdata/scanrepository/cmd/aggregate-multi-dir/npm2/package.json b/testdata/scanrepository/cmd/aggregate-multi-dir/npm2/package.json old mode 100644 new mode 100755 diff --git a/testdata/scanrepository/cmd/aggregate-multi-project/.frogbot/frogbot-config.yml b/testdata/scanrepository/cmd/aggregate-multi-project/.frogbot/frogbot-config.yml old mode 100644 new mode 100755 diff --git a/testdata/scanrepository/cmd/aggregate-multi-project/npm/package.json b/testdata/scanrepository/cmd/aggregate-multi-project/npm/package.json old mode 100644 new mode 100755 diff --git a/testdata/scanrepository/cmd/aggregate-multi-project/pip/requirements.txt b/testdata/scanrepository/cmd/aggregate-multi-project/pip/requirements.txt old mode 100644 new mode 100755 diff --git a/testdata/scanrepository/cmd/aggregate-no-vul/package-lock.json b/testdata/scanrepository/cmd/aggregate-no-vul/package-lock.json old mode 100644 new mode 100755 diff --git a/testdata/scanrepository/cmd/aggregate-no-vul/package.json b/testdata/scanrepository/cmd/aggregate-no-vul/package.json old mode 100644 new mode 100755 diff --git a/testdata/scanrepository/cmd/aggregate/package-lock.json b/testdata/scanrepository/cmd/aggregate/package-lock.json old mode 100644 new mode 100755 diff --git a/testdata/scanrepository/cmd/aggregate/package.json b/testdata/scanrepository/cmd/aggregate/package.json old mode 100644 new mode 100755 diff --git a/testdata/scanrepository/cmd/non-aggregate/package-lock.json b/testdata/scanrepository/cmd/non-aggregate/package-lock.json old mode 100644 new mode 100755 diff --git a/testdata/scanrepository/cmd/non-aggregate/package.json b/testdata/scanrepository/cmd/non-aggregate/package.json old mode 100644 new mode 100755 diff --git a/utils/depsutil.go b/utils/depsutil.go index 15c12a95b..6e5fe1cea 100644 --- a/utils/depsutil.go +++ b/utils/depsutil.go @@ -65,7 +65,7 @@ func resolveYarnDependencies(scanSetup *ScanDetails) (output []byte, err error) return } - restoreYarnrcFunc, err := rtutils.BackupFile(filepath.Join(currWd, yarn.YarnrcFileName), filepath.Join(currWd, yarn.YarnrcBackupFileName)) + restoreYarnrcFunc, err := rtutils.BackupFile(filepath.Join(currWd, yarn.YarnrcFileName), yarn.YarnrcBackupFileName) if err != nil { return nil, err } diff --git a/utils/depsutil_test.go b/utils/depsutil_test.go index d0baa6089..cf80488a2 100644 --- a/utils/depsutil_test.go +++ b/utils/depsutil_test.go @@ -2,6 +2,7 @@ package utils import ( "fmt" + biutils "github.com/jfrog/build-info-go/utils" "github.com/jfrog/jfrog-cli-core/v2/utils/config" "github.com/jfrog/jfrog-client-go/artifactory/services" "github.com/jfrog/jfrog-client-go/auth" @@ -20,7 +21,7 @@ func setTestEnvironment(t *testing.T, project string, server *config.ServerDetai tmpDir, err := fileutils.CreateTempDir() assert.NoError(t, err) sourceDir := filepath.Join("..", "testdata", "projects", project) - assert.NoError(t, fileutils.CopyDir(sourceDir, tmpDir, true, nil)) + assert.NoError(t, biutils.CopyDir(sourceDir, tmpDir, true, nil)) restoreDir, err := Chdir(tmpDir) assert.NoError(t, err) deleteRemoteRepoFunc, repoKey := createRemoteRepo(t, project, server) diff --git a/utils/email.go b/utils/email.go index 8de1ef898..0e437dc13 100644 --- a/utils/email.go +++ b/utils/email.go @@ -20,14 +20,14 @@ type SecretsEmailDetails struct { branch string repoName string repoOwner string - detectedSecrets []formats.IacSecretsRow + detectedSecrets []formats.SourceCodeRow pullRequestLink string EmailDetails } func NewSecretsEmailDetails(gitClient vcsclient.VcsClient, gitProvider vcsutils.VcsProvider, repoOwner, repoName, branch, pullRequestLink string, - detectedSecrets []formats.IacSecretsRow, emailDetails EmailDetails) *SecretsEmailDetails { + detectedSecrets []formats.SourceCodeRow, emailDetails EmailDetails) *SecretsEmailDetails { return &SecretsEmailDetails{gitClient: gitClient, gitProvider: gitProvider, repoOwner: repoOwner, repoName: repoName, branch: branch, pullRequestLink: pullRequestLink, detectedSecrets: detectedSecrets, EmailDetails: emailDetails} @@ -45,18 +45,19 @@ func AlertSecretsExposed(secretsDetails *SecretsEmailDetails) (err error) { emailDetails := secretsDetails.EmailDetails emailContent := getSecretsEmailContent(secretsDetails.detectedSecrets, secretsDetails.gitProvider, secretsDetails.pullRequestLink) sender := fmt.Sprintf("JFrog Frogbot <%s>", emailDetails.SmtpUser) - subject := outputwriter.FrogbotTitlePrefix + " detected potential secrets" + subject := outputwriter.FrogbotTitlePrefix + " Potential secrets detected" return sendEmail(sender, subject, emailContent, emailDetails) } -func getSecretsEmailContent(secrets []formats.IacSecretsRow, gitProvider vcsutils.VcsProvider, pullRequestLink string) string { +func getSecretsEmailContent(secrets []formats.SourceCodeRow, gitProvider vcsutils.VcsProvider, pullRequestLink string) string { var tableContent strings.Builder for _, secret := range secrets { tableContent.WriteString( fmt.Sprintf(outputwriter.SecretsEmailTableRow, secret.File, - secret.LineColumn, - secret.Text)) + secret.StartLine, + secret.StartColumn, + secret.Snippet)) } pullOrMergeRequest := "pull request" if gitProvider == vcsutils.GitLab { diff --git a/utils/email_test.go b/utils/email_test.go index 7dac03b49..3dcc360f2 100644 --- a/utils/email_test.go +++ b/utils/email_test.go @@ -12,24 +12,28 @@ import ( ) func TestGetSecretsEmailContent(t *testing.T) { - secrets := []formats.IacSecretsRow{ - {Severity: "High", File: "/config.yaml", LineColumn: "12:30", Text: "pass*****"}, - {Severity: "Medium", File: "/server-conf.json", LineColumn: "15:20", Text: "pass*****"}, + secrets := []formats.SourceCodeRow{ + {Severity: "High", + Location: formats.Location{ + File: "/config.yaml", StartLine: 12, StartColumn: 30, Snippet: "pass*****"}, + }, + {Severity: "Medium", Location: formats.Location{ + File: "/server-conf.json", StartLine: 15, StartColumn: 20, Snippet: "pass*****"}}, } // Test for results including the "Pull Request" keyword - expected := "\n\n\n\n Frogbot Secret Detection\n \n\n\n\t
\n\t\tThe following potential exposed secrets in your pull request have been detected by Frogbot\n\t\t
\n\t\t\n \n \n \n \n \n \n \n \n \n\t\t\t\t\n\t\t\t\t\t\n\t\t\t\t\t\n\t\t\t\t\t\n\t\t\t\t\n\t\t\t\t\n\t\t\t\t\t\n\t\t\t\t\t\n\t\t\t\t\t\n\t\t\t\t\n \n
FILELINE:COLUMNSECRET
/config.yaml 12:30 pass*****
/server-conf.json 15:20 pass*****
\n\t\t
\n\t\tTo make Frogbot ignore the lines with the potential secrets, add a comment above the line which includes the jfrog-ignore keyword.\t\n\t\t
\n\t
\n\n" + expected := "\n\n\n\n Frogbot Secret Detection\n \n\n\n\t
\n\t\tThe following potential exposed secrets in your pull request have been detected by Frogbot\n\t\t
\n\t\t\n \n \n \n \n \n \n \n \n \n\t\t\t\t\n\t\t\t\t\t\n\t\t\t\t\t\n\t\t\t\t\t\n\t\t\t\t\n\t\t\t\t\n\t\t\t\t\t\n\t\t\t\t\t\n\t\t\t\t\t\n\t\t\t\t\n \n
FILELINE:COLUMNSECRET
/config.yaml 12:30 pass*****
/server-conf.json 15:20 pass*****
\n\t\t
\n\t\tNOTE: If you'd like Frogbot to ignore the lines with the potential secrets, add a comment that includes the jfrog-ignore keyword above the lines with the secrets.\t\n\t\t
\n\t
\n\n" actualContent := getSecretsEmailContent(secrets, vcsutils.GitHub, "https://github.com/owner/repo/pullrequest/1") assert.Equal(t, expected, actualContent) // Test for results including the "Merge Request" keyword - expected = "\n\n\n\n Frogbot Secret Detection\n \n\n\n\t
\n\t\tThe following potential exposed secrets in your merge request have been detected by Frogbot\n\t\t
\n\t\t\n \n \n \n \n \n \n \n \n \n\t\t\t\t\n\t\t\t\t\t\n\t\t\t\t\t\n\t\t\t\t\t\n\t\t\t\t\n\t\t\t\t\n\t\t\t\t\t\n\t\t\t\t\t\n\t\t\t\t\t\n\t\t\t\t\n \n
FILELINE:COLUMNSECRET
/config.yaml 12:30 pass*****
/server-conf.json 15:20 pass*****
\n\t\t
\n\t\tTo make Frogbot ignore the lines with the potential secrets, add a comment above the line which includes the jfrog-ignore keyword.\t\n\t\t
\n\t
\n\n" + expected = "\n\n\n\n Frogbot Secret Detection\n \n\n\n\t
\n\t\tThe following potential exposed secrets in your merge request have been detected by Frogbot\n\t\t
\n\t\t\n \n \n \n \n \n \n \n \n \n\t\t\t\t\n\t\t\t\t\t\n\t\t\t\t\t\n\t\t\t\t\t\n\t\t\t\t\n\t\t\t\t\n\t\t\t\t\t\n\t\t\t\t\t\n\t\t\t\t\t\n\t\t\t\t\n \n
FILELINE:COLUMNSECRET
/config.yaml 12:30 pass*****
/server-conf.json 15:20 pass*****
\n\t\t
\n\t\tNOTE: If you'd like Frogbot to ignore the lines with the potential secrets, add a comment that includes the jfrog-ignore keyword above the lines with the secrets.\t\n\t\t
\n\t
\n\n" actualContent = getSecretsEmailContent(secrets, vcsutils.GitLab, "https://github.com/owner/repo/pullrequest/1") assert.Equal(t, expected, actualContent) } func TestPrepareEmail(t *testing.T) { sender := "JFrog Frogbot " - subject := outputwriter.FrogbotTitlePrefix + " detected potential secrets" + subject := outputwriter.FrogbotTitlePrefix + " Potential secrets detected" content := "content" emailDetails := EmailDetails{EmailReceivers: []string{"receiver@jfrog.com"}} expectedEmailObject := &email.Email{ diff --git a/utils/outputwriter/outputwriter.go b/utils/outputwriter/outputwriter.go index 590d70329..31455ab0e 100644 --- a/utils/outputwriter/outputwriter.go +++ b/utils/outputwriter/outputwriter.go @@ -2,16 +2,17 @@ package outputwriter import ( "fmt" + "strings" + "github.com/jfrog/froggit-go/vcsutils" "github.com/jfrog/jfrog-cli-core/v2/utils/coreutils" "github.com/jfrog/jfrog-cli-core/v2/xray/formats" xrayutils "github.com/jfrog/jfrog-cli-core/v2/xray/utils" - "strings" ) const ( FrogbotTitlePrefix = "[🐸 Frogbot]" - CommentGeneratedByFrogbot = "[JFrog Frogbot](https://github.com/jfrog/frogbot#readme)" + CommentGeneratedByFrogbot = "[🐸 JFrog Frogbot](https://github.com/jfrog/frogbot#readme)" vulnerabilitiesTableHeader = "\n| SEVERITY | DIRECT DEPENDENCIES | IMPACTED DEPENDENCY | FIXED VERSIONS |\n| :---------------------: | :----------------------------------: | :-----------------------------------: | :---------------------------------: |" vulnerabilitiesTableHeaderWithContextualAnalysis = "| SEVERITY | CONTEXTUAL ANALYSIS | DIRECT DEPENDENCIES | IMPACTED DEPENDENCY | FIXED VERSIONS |\n| :---------------------: | :----------------------------------: | :----------------------------------: | :-----------------------------------: | :---------------------------------: |" iacTableHeader = "\n| SEVERITY | FILE | LINE:COLUMN | FINDING |\n| :---------------------: | :----------------------------------: | :-----------------------------------: | :---------------------------------: |" @@ -77,7 +78,7 @@ const (
- To make Frogbot ignore the lines with the potential secrets, add a comment above the line which includes the jfrog-ignore keyword. + NOTE: If you'd like Frogbot to ignore the lines with the potential secrets, add a comment that includes the jfrog-ignore keyword above the lines with the secrets.
@@ -86,7 +87,7 @@ const ( SecretsEmailTableRow = ` %s - %s + %d:%d %s ` ) @@ -98,15 +99,19 @@ type OutputWriter interface { NoVulnerabilitiesTitle() string VulnerabilitiesTitle(isComment bool) string VulnerabilitiesContent(vulnerabilities []formats.VulnerabilityOrViolationRow) string - IacContent(iacRows []formats.IacSecretsRow) string + IacTableContent(iacRows []formats.SourceCodeRow) string Footer() string Separator() string - FormattedSeverity(severity, applicability string) string + FormattedSeverity(severity, applicability string, addName bool) string IsFrogbotResultComment(comment string) bool SetJasOutputFlags(entitled, showCaColumn bool) VcsProvider() vcsutils.VcsProvider SetVcsProvider(provider vcsutils.VcsProvider) UntitledForJasMsg() string + + ApplicableCveReviewContent(severity, finding, fullDetails, cveDetails, remediation string) string + IacReviewContent(severity, finding, fullDetails string) string + SastReviewContent(severity, finding, fullDetails string, codeFlows [][]formats.Location) string } func GetCompatibleOutputWriter(provider vcsutils.VcsProvider) OutputWriter { @@ -123,10 +128,12 @@ type descriptionBullet struct { value string } -func createVulnerabilityDescription(vulnerability *formats.VulnerabilityOrViolationRow) string { - var cves []string - for _, cve := range vulnerability.Cves { - cves = append(cves, cve.Id) +func createVulnerabilityDescription(vulnerability *formats.VulnerabilityOrViolationRow, cves []string) string { + descriptionBullets := []descriptionBullet{ + {title: "**Severity**", value: fmt.Sprintf("%s %s", xrayutils.GetSeverity(vulnerability.Severity, xrayutils.Applicable).Emoji(), vulnerability.Severity)}, + {title: "**Contextual Analysis:**", value: vulnerability.Applicable}, + {title: "**Package Name:**", value: vulnerability.ImpactedDependencyName}, + {title: "**Current Version:**", value: vulnerability.ImpactedDependencyVersion}, } cvesTitle := "**CVE:**" @@ -139,13 +146,14 @@ func createVulnerabilityDescription(vulnerability *formats.VulnerabilityOrViolat fixedVersionsTitle = "**Fixed Versions:**" } - descriptionBullets := []descriptionBullet{ - {title: "**Severity**", value: fmt.Sprintf("%s %s", xrayutils.GetSeverity(vulnerability.Severity, xrayutils.ApplicableStringValue).Emoji(), vulnerability.Severity)}, - {title: "**Contextual Analysis:**", value: vulnerability.Applicable}, - {title: "**Package Name:**", value: vulnerability.ImpactedDependencyName}, - {title: "**Current Version:**", value: vulnerability.ImpactedDependencyVersion}, - {title: fixedVersionsTitle, value: strings.Join(vulnerability.FixedVersions, ",")}, - {title: cvesTitle, value: strings.Join(cves, ", ")}, + if len(cves) != 0 { + cveBullet := descriptionBullet{title: cvesTitle, value: strings.Join(cves, ",")} + descriptionBullets = append(descriptionBullets, cveBullet) + } + + if len(vulnerability.FixedVersions) != 0 { + fixedVersionBullet := descriptionBullet{title: fixedVersionsTitle, value: strings.Join(vulnerability.FixedVersions, ",")} + descriptionBullets = append(descriptionBullets, fixedVersionBullet) } var descriptionBuilder strings.Builder @@ -183,10 +191,10 @@ func getVulnerabilitiesTableContent(vulnerabilities []formats.VulnerabilityOrVio return tableContent } -func getIacTableContent(iacRows []formats.IacSecretsRow, writer OutputWriter) string { +func getIacTableContent(iacRows []formats.SourceCodeRow, writer OutputWriter) string { var tableContent string for _, iac := range iacRows { - tableContent += fmt.Sprintf("\n| %s | %s | %s | %s |", writer.FormattedSeverity(iac.Severity, xrayutils.ApplicableStringValue), iac.File, iac.LineColumn, iac.Text) + tableContent += fmt.Sprintf("\n| %s | %s | %s | %s |", writer.FormattedSeverity(iac.Severity, string(xrayutils.Applicable), true), iac.File, fmt.Sprintf("%d:%d", iac.StartLine, iac.StartColumn), iac.Snippet) } return tableContent } @@ -195,6 +203,31 @@ func MarkdownComment(text string) string { return fmt.Sprintf("\n[comment]: <> (%s)\n", text) } +func MarkAsQuote(s string) string { + return fmt.Sprintf("`%s`", s) +} + +func MarkAsCodeSnippet(snippet string) string { + return fmt.Sprintf("```\n%s\n```", snippet) +} + +func GetJasMarkdownDescription(severity, finding string) string { + headerRow := "| Severity | Finding |\n" + separatorRow := "| :--------------: | :---: |\n" + return headerRow + separatorRow + fmt.Sprintf("| %s | %s |", severity, finding) +} + +func GetLocationDescription(location formats.Location) string { + return fmt.Sprintf(` +Found issue with the following snippet +%s +at %s (line %d) +`, + MarkAsCodeSnippet(location.Snippet), + MarkAsQuote(location.File), + location.StartLine) +} + func GetAggregatedPullRequestTitle(tech coreutils.Technology) string { if tech.ToString() == "" { return FrogbotTitlePrefix + " Update dependencies" @@ -208,3 +241,20 @@ func getVulnerabilitiesTableHeader(showCaColumn bool) string { } return vulnerabilitiesTableHeader } + +func getCveIdSliceFromCveRows(cves []formats.CveRow) []string { + var cveIds []string + for _, cve := range cves { + if cve.Id != "" { + cveIds = append(cveIds, cve.Id) + } + } + return cveIds +} + +func getDescriptionBulletCveTitle(cves []string) string { + if len(cves) == 0 { + return "" + } + return fmt.Sprintf("[ %s ] ", strings.Join(cves, ",")) +} diff --git a/utils/outputwriter/simplifiedoutput.go b/utils/outputwriter/simplifiedoutput.go index 688692b5f..740c38c09 100644 --- a/utils/outputwriter/simplifiedoutput.go +++ b/utils/outputwriter/simplifiedoutput.go @@ -2,9 +2,10 @@ package outputwriter import ( "fmt" + "strings" + "github.com/jfrog/froggit-go/vcsutils" "github.com/jfrog/jfrog-cli-core/v2/xray/formats" - "strings" ) const ( @@ -19,7 +20,7 @@ type SimplifiedOutput struct { } func (smo *SimplifiedOutput) VulnerabilitiesTableRow(vulnerability formats.VulnerabilityOrViolationRow) string { - row := fmt.Sprintf("| %s | ", smo.FormattedSeverity(vulnerability.Severity, vulnerability.Applicable)) + row := fmt.Sprintf("| %s | ", smo.FormattedSeverity(vulnerability.Severity, vulnerability.Applicable, true)) directsRowFmt := directDependencyRow if smo.showCaColumn { row += vulnerability.Applicable + " |" @@ -93,21 +94,118 @@ func (smo *SimplifiedOutput) VulnerabilitiesContent(vulnerabilities []formats.Vu getVulnerabilitiesTableHeader(smo.showCaColumn), getVulnerabilitiesTableContent(vulnerabilities, smo))) for i := range vulnerabilities { + cves := getCveIdSliceFromCveRows(vulnerabilities[i].Cves) contentBuilder.WriteString(fmt.Sprintf(` -#### %s %s +#### %s%s %s %s `, + getDescriptionBulletCveTitle(cves), vulnerabilities[i].ImpactedDependencyName, vulnerabilities[i].ImpactedDependencyVersion, - createVulnerabilityDescription(&vulnerabilities[i]))) + createVulnerabilityDescription(&vulnerabilities[i], cves))) + } + + return contentBuilder.String() +} + +func (smo *SimplifiedOutput) ApplicableCveReviewContent(severity, finding, fullDetails, cveDetails, remediation string) string { + var contentBuilder strings.Builder + contentBuilder.WriteString(fmt.Sprintf(` +### 📦🔍 Contextual Analysis CVE Vulnerability + +%s + +#### Description + +%s + +#### CVE details + +%s + +`, + GetJasMarkdownDescription(smo.FormattedSeverity(severity, "Applicable", false), finding), + fullDetails, + cveDetails)) + + if len(remediation) > 0 { + contentBuilder.WriteString(fmt.Sprintf(` +#### Remediation + +%s + +`, + remediation)) } + return contentBuilder.String() +} + +func (smo *SimplifiedOutput) IacReviewContent(severity, finding, fullDetails string) string { + return fmt.Sprintf(` +### 🛠️ Infrastructure as Code Vulnerability + +%s +### 👇 Details + +%s + +`, + GetJasMarkdownDescription(smo.FormattedSeverity(severity, "Applicable", false), finding), + fullDetails) +} + +func (smo *SimplifiedOutput) SastReviewContent(severity, finding, fullDetails string, codeFlows [][]formats.Location) string { + var contentBuilder strings.Builder + contentBuilder.WriteString(fmt.Sprintf(` +### 🎯 Static Application Security Testing (SAST) Vulnerability + +%s + +--- +#### Full description + +%s + +--- +#### Code Flows + +`, + GetJasMarkdownDescription(smo.FormattedSeverity(severity, "Applicable", false), finding), + fullDetails, + )) + + if len(codeFlows) > 0 { + for _, flow := range codeFlows { + contentBuilder.WriteString(` + +--- +Vulnerable data flow analysis result: +`) + for _, location := range flow { + contentBuilder.WriteString(fmt.Sprintf(` +%s %s (at %s line %d) +`, + "↘️", + MarkAsQuote(location.Snippet), + location.File, + location.StartLine, + )) + } + contentBuilder.WriteString(` + +--- + +`, + ) + } + } return contentBuilder.String() } -func (smo *SimplifiedOutput) IacContent(iacRows []formats.IacSecretsRow) string { +func (smo *SimplifiedOutput) IacTableContent(iacRows []formats.SourceCodeRow) string { if len(iacRows) == 0 { return "" } @@ -130,7 +228,7 @@ func (smo *SimplifiedOutput) Separator() string { return ", " } -func (smo *SimplifiedOutput) FormattedSeverity(severity, _ string) string { +func (smo *SimplifiedOutput) FormattedSeverity(severity, _ string, _ bool) string { return severity } diff --git a/utils/outputwriter/simplifiedoutput_test.go b/utils/outputwriter/simplifiedoutput_test.go index 5e47b5a2b..d88dbe6ee 100644 --- a/utils/outputwriter/simplifiedoutput_test.go +++ b/utils/outputwriter/simplifiedoutput_test.go @@ -151,24 +151,26 @@ func TestSimplifiedOutput_VulnerabilitiesContent(t *testing.T) { --- -#### %s %s +#### %s %s %s %s -#### %s %s +#### %s %s %s %s `, getVulnerabilitiesTableHeader(false), getVulnerabilitiesTableContent(vulnerabilitiesRows, so), + fmt.Sprintf("[ %s ]", vulnerabilitiesRows[0].Cves[0].Id), vulnerabilitiesRows[0].ImpactedDependencyName, vulnerabilitiesRows[0].ImpactedDependencyVersion, - createVulnerabilityDescription(&vulnerabilitiesRows[0]), + createVulnerabilityDescription(&vulnerabilitiesRows[0], []string{vulnerabilitiesRows[0].Cves[0].Id}), + fmt.Sprintf("[ %s ]", vulnerabilitiesRows[1].Cves[0].Id), vulnerabilitiesRows[1].ImpactedDependencyName, vulnerabilitiesRows[1].ImpactedDependencyVersion, - createVulnerabilityDescription(&vulnerabilitiesRows[1]), + createVulnerabilityDescription(&vulnerabilitiesRows[1], []string{vulnerabilitiesRows[1].Cves[0].Id}), ) actualContent := so.VulnerabilitiesContent(vulnerabilitiesRows) @@ -201,7 +203,7 @@ func TestSimplifiedOutput_ContentWithContextualAnalysis(t *testing.T) { Severity: "Low", Components: []formats.ComponentRow{{Name: "Direct1", Version: "1.0.0"}, {Name: "Direct2", Version: "2.0.0"}}, FixedVersions: []string{"2.2.3"}, - Cves: []formats.CveRow{{Id: "CVE-2023-1234"}}, + Cves: []formats.CveRow{{Id: "CVE-2024-1234"}}, Applicable: "Not Applicable", Technology: coreutils.Poetry, }, @@ -221,24 +223,26 @@ func TestSimplifiedOutput_ContentWithContextualAnalysis(t *testing.T) { --- -#### %s %s +#### %s %s %s %s -#### %s %s +#### %s %s %s %s `, getVulnerabilitiesTableHeader(true), getVulnerabilitiesTableContent(vulnerabilitiesRows, so), + fmt.Sprintf("[ %s ]", "CVE-2023-1234"), vulnerabilitiesRows[0].ImpactedDependencyName, vulnerabilitiesRows[0].ImpactedDependencyVersion, - createVulnerabilityDescription(&vulnerabilitiesRows[0]), + createVulnerabilityDescription(&vulnerabilitiesRows[0], []string{"CVE-2023-1234"}), + fmt.Sprintf("[ %s ]", "CVE-2024-1234"), vulnerabilitiesRows[1].ImpactedDependencyName, vulnerabilitiesRows[1].ImpactedDependencyVersion, - createVulnerabilityDescription(&vulnerabilitiesRows[1]), + createVulnerabilityDescription(&vulnerabilitiesRows[1], []string{"CVE-2024-1234"}), ) actualContent := so.VulnerabilitiesContent(vulnerabilitiesRows) @@ -251,44 +255,52 @@ func TestSimplifiedOutput_ContentWithContextualAnalysis(t *testing.T) { func TestSimplifiedOutput_IacContent(t *testing.T) { testCases := []struct { name string - iacRows []formats.IacSecretsRow + iacRows []formats.SourceCodeRow expectedOutput string }{ { name: "Empty IAC rows", - iacRows: []formats.IacSecretsRow{}, + iacRows: []formats.SourceCodeRow{}, expectedOutput: "", }, { name: "Single IAC row", - iacRows: []formats.IacSecretsRow{ + iacRows: []formats.SourceCodeRow{ { Severity: "High", SeverityNumValue: 3, - File: "applicable/req_sw_terraform_azure_redis_auth.tf", - LineColumn: "11:1", - Text: "Missing Periodic patching was detected", - Type: "azure_redis_patch", + Location: formats.Location{ + File: "applicable/req_sw_terraform_azure_redis_auth.tf", + StartLine: 11, + StartColumn: 1, + Snippet: "Missing Periodic patching was detected", + }, }, }, expectedOutput: "\n## 🛠️ Infrastructure as Code \n\n\n| SEVERITY | FILE | LINE:COLUMN | FINDING |\n| :---------------------: | :----------------------------------: | :-----------------------------------: | :---------------------------------: | \n| High | applicable/req_sw_terraform_azure_redis_auth.tf | 11:1 | Missing Periodic patching was detected |\n\n", }, { name: "Multiple IAC rows", - iacRows: []formats.IacSecretsRow{ + iacRows: []formats.SourceCodeRow{ { Severity: "High", SeverityNumValue: 3, - File: "applicable/req_sw_terraform_azure_redis_patch.tf", - LineColumn: "11:1", - Text: "Missing redis firewall definition or start_ip=0.0.0.0 was detected, Missing redis firewall definition or start_ip=0.0.0.0 was detected", + Location: formats.Location{ + File: "applicable/req_sw_terraform_azure_redis_patch.tf", + StartLine: 11, + StartColumn: 1, + Snippet: "Missing redis firewall definition or start_ip=0.0.0.0 was detected, Missing redis firewall definition or start_ip=0.0.0.0 was detected", + }, }, { Severity: "High", SeverityNumValue: 3, - File: "applicable/req_sw_terraform_azure_redis_auth.tf", - LineColumn: "11:1", - Text: "Missing Periodic patching was detected", + Location: formats.Location{ + File: "applicable/req_sw_terraform_azure_redis_auth.tf", + StartLine: 11, + StartColumn: 1, + Snippet: "Missing Periodic patching was detected", + }, }, }, expectedOutput: "\n## 🛠️ Infrastructure as Code \n\n\n| SEVERITY | FILE | LINE:COLUMN | FINDING |\n| :---------------------: | :----------------------------------: | :-----------------------------------: | :---------------------------------: | \n| High | applicable/req_sw_terraform_azure_redis_patch.tf | 11:1 | Missing redis firewall definition or start_ip=0.0.0.0 was detected, Missing redis firewall definition or start_ip=0.0.0.0 was detected |\n| High | applicable/req_sw_terraform_azure_redis_auth.tf | 11:1 | Missing Periodic patching was detected |\n\n", @@ -298,7 +310,7 @@ func TestSimplifiedOutput_IacContent(t *testing.T) { writer := &SimplifiedOutput{} for _, tc := range testCases { t.Run(tc.name, func(t *testing.T) { - output := writer.IacContent(tc.iacRows) + output := writer.IacTableContent(tc.iacRows) assert.Equal(t, tc.expectedOutput, output) }) } @@ -307,46 +319,52 @@ func TestSimplifiedOutput_IacContent(t *testing.T) { func TestSimplifiedOutput_GetIacTableContent(t *testing.T) { testCases := []struct { name string - iacRows []formats.IacSecretsRow + iacRows []formats.SourceCodeRow expectedOutput string }{ { name: "Empty IAC rows", - iacRows: []formats.IacSecretsRow{}, + iacRows: []formats.SourceCodeRow{}, expectedOutput: "", }, { name: "Single IAC row", - iacRows: []formats.IacSecretsRow{ + iacRows: []formats.SourceCodeRow{ { Severity: "Medium", SeverityNumValue: 2, - File: "file1", - LineColumn: "1:10", - Text: "Public access to MySQL was detected", - Type: "azure_mysql_no_public", + Location: formats.Location{ + File: "file1", + StartLine: 1, + StartColumn: 10, + Snippet: "Public access to MySQL was detected", + }, }, }, expectedOutput: "\n| Medium | file1 | 1:10 | Public access to MySQL was detected |", }, { name: "Multiple IAC rows", - iacRows: []formats.IacSecretsRow{ + iacRows: []formats.SourceCodeRow{ { Severity: "High", SeverityNumValue: 3, - File: "file1", - LineColumn: "1:10", - Text: "Public access to MySQL was detected", - Type: "azure_mysql_no_public", + Location: formats.Location{ + File: "file1", + StartLine: 1, + StartColumn: 10, + Snippet: "Public access to MySQL was detected", + }, }, { Severity: "Medium", SeverityNumValue: 2, - File: "file2", - LineColumn: "2:5", - Text: "Public access to MySQL was detected", - Type: "azure_mysql_no_public", + Location: formats.Location{ + File: "file2", + StartLine: 2, + StartColumn: 5, + Snippet: "Public access to MySQL was detected", + }, }, }, expectedOutput: "\n| High | file1 | 1:10 | Public access to MySQL was detected |\n| Medium | file2 | 2:5 | Public access to MySQL was detected |", diff --git a/utils/outputwriter/standardoutput.go b/utils/outputwriter/standardoutput.go index c1833da1a..e61719dd3 100644 --- a/utils/outputwriter/standardoutput.go +++ b/utils/outputwriter/standardoutput.go @@ -2,9 +2,10 @@ package outputwriter import ( "fmt" + "strings" + "github.com/jfrog/froggit-go/vcsutils" "github.com/jfrog/jfrog-cli-core/v2/xray/formats" - "strings" ) type StandardOutput struct { @@ -19,7 +20,7 @@ func (so *StandardOutput) VulnerabilitiesTableRow(vulnerability formats.Vulnerab directDependencies.WriteString(fmt.Sprintf("%s:%s%s", dependency.Name, dependency.Version, so.Separator())) } - row := fmt.Sprintf("| %s | ", so.FormattedSeverity(vulnerability.Severity, vulnerability.Applicable)) + row := fmt.Sprintf("| %s | ", so.FormattedSeverity(vulnerability.Severity, vulnerability.Applicable, true)) if so.showCaColumn { row += vulnerability.Applicable + " | " } @@ -97,31 +98,168 @@ func (so *StandardOutput) VulnerabilitiesContent(vulnerabilities []formats.Vulne getVulnerabilitiesTableContent(vulnerabilities, so))) // Write details for each vulnerability for i := range vulnerabilities { + cves := getCveIdSliceFromCveRows(vulnerabilities[i].Cves) if len(vulnerabilities) == 1 { contentBuilder.WriteString(fmt.Sprintf(` %s -`, createVulnerabilityDescription(&vulnerabilities[i]))) +`, createVulnerabilityDescription(&vulnerabilities[i], cves))) break } contentBuilder.WriteString(fmt.Sprintf(`
- %s %s + %s%s %s
%s
`, + getDescriptionBulletCveTitle(cves), vulnerabilities[i].ImpactedDependencyName, vulnerabilities[i].ImpactedDependencyVersion, - createVulnerabilityDescription(&vulnerabilities[i]))) + createVulnerabilityDescription(&vulnerabilities[i], cves))) } return contentBuilder.String() } -func (so *StandardOutput) IacContent(iacRows []formats.IacSecretsRow) string { +func (so *StandardOutput) ApplicableCveReviewContent(severity, finding, fullDetails, cveDetails, remediation string) string { + var contentBuilder strings.Builder + contentBuilder.WriteString(fmt.Sprintf(` +
+ +### 📦🔍 Contextual Analysis CVE Vulnerability + +%s + +
+ +
+ Description +
+ +%s + +
+ +
+ CVE details +
+ +%s + +
+ +`, + GetJasMarkdownDescription(so.FormattedSeverity(severity, "Applicable", false), finding), + fullDetails, + cveDetails)) + + contentBuilder.WriteString(fmt.Sprintf(` +
+ Remediation +
+ +%s + +
+ +`, + remediation)) + return contentBuilder.String() +} + +func (so *StandardOutput) IacReviewContent(severity, finding, fullDetails string) string { + return fmt.Sprintf(` +
+ +### 🛠️ Infrastructure as Code Vulnerability + +%s + +
+ +
+ Full description +
+ +%s + +
+ +`, + GetJasMarkdownDescription(so.FormattedSeverity(severity, "Applicable", false), finding), + fullDetails) +} + +func (so *StandardOutput) SastReviewContent(severity, finding, fullDetails string, codeFlows [][]formats.Location) string { + var contentBuilder strings.Builder + contentBuilder.WriteString(fmt.Sprintf(` +
+ +### 🎯 Static Application Security Testing (SAST) Vulnerability + +%s + +
+ +
+ Full description +
+ +%s + +
+ +`, + GetJasMarkdownDescription(so.FormattedSeverity(severity, "Applicable", false), finding), + fullDetails, + )) + + if len(codeFlows) > 0 { + contentBuilder.WriteString(` + +
+Code Flows + +`) + for _, flow := range codeFlows { + contentBuilder.WriteString(` + +
+Vulnerable data flow analysis result +
+`) + for _, location := range flow { + contentBuilder.WriteString(fmt.Sprintf(` +%s %s (at %s line %d) +`, + "↘️", + MarkAsQuote(location.Snippet), + location.File, + location.StartLine, + )) + } + + contentBuilder.WriteString(` + +
+ +`, + ) + } + contentBuilder.WriteString(` + +
+ +`, + ) + } + return contentBuilder.String() +} + +func (so *StandardOutput) IacTableContent(iacRows []formats.SourceCodeRow) string { if len(iacRows) == 0 { return "" } @@ -142,6 +280,7 @@ func (so *StandardOutput) IacContent(iacRows []formats.IacSecretsRow) string { func (so *StandardOutput) Footer() string { return fmt.Sprintf(` +---
%s @@ -154,8 +293,12 @@ func (so *StandardOutput) Separator() string { return "

" } -func (so *StandardOutput) FormattedSeverity(severity, applicability string) string { - return fmt.Sprintf("%s%8s", getSeverityTag(IconName(severity), applicability), severity) +func (so *StandardOutput) FormattedSeverity(severity, applicability string, addName bool) string { + s := getSeverityTag(IconName(severity), applicability) + if addName { + s = fmt.Sprintf(s+"%8s", severity) + } + return s } func (so *StandardOutput) UntitledForJasMsg() string { diff --git a/utils/outputwriter/standardoutput_test.go b/utils/outputwriter/standardoutput_test.go index 312aa4b73..996db7d72 100644 --- a/utils/outputwriter/standardoutput_test.go +++ b/utils/outputwriter/standardoutput_test.go @@ -6,6 +6,7 @@ import ( "github.com/jfrog/jfrog-cli-core/v2/utils/coreutils" "github.com/jfrog/jfrog-cli-core/v2/xray/formats" "github.com/stretchr/testify/assert" + "strings" "testing" ) @@ -151,7 +152,7 @@ func TestStandardOutput_VulnerabilitiesContent(t *testing.T) {
- %s %s + %s%s %s
%s @@ -159,7 +160,7 @@ func TestStandardOutput_VulnerabilitiesContent(t *testing.T) {
- %s %s + %s%s %s
%s @@ -168,12 +169,14 @@ func TestStandardOutput_VulnerabilitiesContent(t *testing.T) { `, getVulnerabilitiesTableHeader(false), getVulnerabilitiesTableContent(vulnerabilitiesRows, so), + "", vulnerabilitiesRows[0].ImpactedDependencyName, vulnerabilitiesRows[0].ImpactedDependencyVersion, - createVulnerabilityDescription(&vulnerabilitiesRows[0]), + createVulnerabilityDescription(&vulnerabilitiesRows[0], nil), + "", vulnerabilitiesRows[1].ImpactedDependencyName, vulnerabilitiesRows[1].ImpactedDependencyVersion, - createVulnerabilityDescription(&vulnerabilitiesRows[1]), + createVulnerabilityDescription(&vulnerabilitiesRows[1], nil), ) actualContent := so.VulnerabilitiesContent(vulnerabilitiesRows) @@ -196,12 +199,14 @@ func TestStandardOutput_ContentWithContextualAnalysis(t *testing.T) { ImpactedDependencyVersion: "1.0.0", Applicable: "Applicable", Technology: coreutils.Pip, + Cves: []formats.CveRow{{Id: "CVE-2023-1234"}, {Id: "CVE-2023-4321"}}, }, { ImpactedDependencyName: "Dependency2", ImpactedDependencyVersion: "2.0.0", Applicable: "Not Applicable", Technology: coreutils.Pip, + Cves: []formats.CveRow{{Id: "CVE-2022-4321"}}, }, } @@ -221,7 +226,7 @@ func TestStandardOutput_ContentWithContextualAnalysis(t *testing.T) {
- %s %s + %s%s %s
%s @@ -229,7 +234,7 @@ func TestStandardOutput_ContentWithContextualAnalysis(t *testing.T) {
- %s %s + %s%s %s
%s @@ -238,12 +243,14 @@ func TestStandardOutput_ContentWithContextualAnalysis(t *testing.T) { `, getVulnerabilitiesTableHeader(true), getVulnerabilitiesTableContent(vulnerabilitiesRows, so), + fmt.Sprintf("[ %s ] ", strings.Join([]string{vulnerabilitiesRows[0].Cves[0].Id, vulnerabilitiesRows[0].Cves[1].Id}, ",")), vulnerabilitiesRows[0].ImpactedDependencyName, vulnerabilitiesRows[0].ImpactedDependencyVersion, - createVulnerabilityDescription(&vulnerabilitiesRows[0]), + createVulnerabilityDescription(&vulnerabilitiesRows[0], []string{vulnerabilitiesRows[0].Cves[0].Id, vulnerabilitiesRows[0].Cves[1].Id}), + fmt.Sprintf("[ %s ] ", strings.Join([]string{vulnerabilitiesRows[1].Cves[0].Id}, ",")), vulnerabilitiesRows[1].ImpactedDependencyName, vulnerabilitiesRows[1].ImpactedDependencyVersion, - createVulnerabilityDescription(&vulnerabilitiesRows[1]), + createVulnerabilityDescription(&vulnerabilitiesRows[1], []string{vulnerabilitiesRows[1].Cves[0].Id}), ) actualContent = so.VulnerabilitiesContent(vulnerabilitiesRows) @@ -256,43 +263,52 @@ func TestStandardOutput_ContentWithContextualAnalysis(t *testing.T) { func TestStandardOutput_IacContent(t *testing.T) { testCases := []struct { name string - iacRows []formats.IacSecretsRow + iacRows []formats.SourceCodeRow expectedOutput string }{ { name: "Empty IAC rows", - iacRows: []formats.IacSecretsRow{}, + iacRows: []formats.SourceCodeRow{}, expectedOutput: "", }, { name: "Single IAC row", - iacRows: []formats.IacSecretsRow{ + iacRows: []formats.SourceCodeRow{ { Severity: "High", SeverityNumValue: 3, - File: "applicable/req_sw_terraform_azure_redis_auth.tf", - LineColumn: "11:1", - Text: "Missing Periodic patching was detected", + Location: formats.Location{ + File: "applicable/req_sw_terraform_azure_redis_auth.tf", + StartLine: 11, + StartColumn: 1, + Snippet: "Missing Periodic patching was detected", + }, }, }, expectedOutput: "\n## 🛠️ Infrastructure as Code \n\n
\n\n\n| SEVERITY | FILE | LINE:COLUMN | FINDING |\n| :---------------------: | :----------------------------------: | :-----------------------------------: | :---------------------------------: | \n| ![](https://raw.githubusercontent.com/jfrog/frogbot/master/resources/v2/applicableHighSeverity.png)
High | applicable/req_sw_terraform_azure_redis_auth.tf | 11:1 | Missing Periodic patching was detected |\n\n
\n\n", }, { name: "Multiple IAC rows", - iacRows: []formats.IacSecretsRow{ + iacRows: []formats.SourceCodeRow{ { Severity: "High", SeverityNumValue: 3, - File: "applicable/req_sw_terraform_azure_redis_patch.tf", - LineColumn: "11:1", - Text: "Missing redis firewall definition or start_ip=0.0.0.0 was detected, Missing redis firewall definition or start_ip=0.0.0.0 was detected", + Location: formats.Location{ + File: "applicable/req_sw_terraform_azure_redis_patch.tf", + StartLine: 11, + StartColumn: 1, + Snippet: "Missing redis firewall definition or start_ip=0.0.0.0 was detected, Missing redis firewall definition or start_ip=0.0.0.0 was detected", + }, }, { Severity: "High", SeverityNumValue: 3, - File: "applicable/req_sw_terraform_azure_redis_auth.tf", - LineColumn: "11:1", - Text: "Missing Periodic patching was detected", + Location: formats.Location{ + File: "applicable/req_sw_terraform_azure_redis_auth.tf", + StartLine: 11, + StartColumn: 1, + Snippet: "Missing Periodic patching was detected", + }, }, }, expectedOutput: "\n## 🛠️ Infrastructure as Code \n\n
\n\n\n| SEVERITY | FILE | LINE:COLUMN | FINDING |\n| :---------------------: | :----------------------------------: | :-----------------------------------: | :---------------------------------: | \n| ![](https://raw.githubusercontent.com/jfrog/frogbot/master/resources/v2/applicableHighSeverity.png)
High | applicable/req_sw_terraform_azure_redis_patch.tf | 11:1 | Missing redis firewall definition or start_ip=0.0.0.0 was detected, Missing redis firewall definition or start_ip=0.0.0.0 was detected |\n| ![](https://raw.githubusercontent.com/jfrog/frogbot/master/resources/v2/applicableHighSeverity.png)
High | applicable/req_sw_terraform_azure_redis_auth.tf | 11:1 | Missing Periodic patching was detected |\n\n
\n\n", @@ -302,7 +318,7 @@ func TestStandardOutput_IacContent(t *testing.T) { writer := &StandardOutput{} for _, tc := range testCases { t.Run(tc.name, func(t *testing.T) { - output := writer.IacContent(tc.iacRows) + output := writer.IacTableContent(tc.iacRows) assert.Equal(t, tc.expectedOutput, output) }) } @@ -311,46 +327,52 @@ func TestStandardOutput_IacContent(t *testing.T) { func TestStandardOutput_GetIacTableContent(t *testing.T) { testCases := []struct { name string - iacRows []formats.IacSecretsRow + iacRows []formats.SourceCodeRow expectedOutput string }{ { name: "Empty IAC rows", - iacRows: []formats.IacSecretsRow{}, + iacRows: []formats.SourceCodeRow{}, expectedOutput: "", }, { name: "Single IAC row", - iacRows: []formats.IacSecretsRow{ + iacRows: []formats.SourceCodeRow{ { Severity: "Medium", SeverityNumValue: 2, - File: "file1", - LineColumn: "1:10", - Text: "Public access to MySQL was detected", - Type: "azure_mysql_no_public", + Location: formats.Location{ + File: "file1", + StartLine: 1, + StartColumn: 10, + Snippet: "Public access to MySQL was detected", + }, }, }, expectedOutput: "\n| ![](https://raw.githubusercontent.com/jfrog/frogbot/master/resources/v2/applicableMediumSeverity.png)
Medium | file1 | 1:10 | Public access to MySQL was detected |", }, { name: "Multiple IAC rows", - iacRows: []formats.IacSecretsRow{ + iacRows: []formats.SourceCodeRow{ { Severity: "High", SeverityNumValue: 3, - File: "file1", - LineColumn: "1:10", - Text: "Public access to MySQL was detected", - Type: "azure_mysql_no_public", + Location: formats.Location{ + File: "file1", + StartLine: 1, + StartColumn: 10, + Snippet: "Public access to MySQL was detected", + }, }, { Severity: "Medium", SeverityNumValue: 2, - File: "file2", - LineColumn: "2:5", - Text: "Public access to MySQL was detected", - Type: "azure_mysql_no_public", + Location: formats.Location{ + File: "file2", + StartLine: 2, + StartColumn: 5, + Snippet: "Public access to MySQL was detected", + }, }, }, expectedOutput: "\n| ![](https://raw.githubusercontent.com/jfrog/frogbot/master/resources/v2/applicableHighSeverity.png)
High | file1 | 1:10 | Public access to MySQL was detected |\n| ![](https://raw.githubusercontent.com/jfrog/frogbot/master/resources/v2/applicableMediumSeverity.png)
Medium | file2 | 2:5 | Public access to MySQL was detected |", diff --git a/utils/params.go b/utils/params.go index 6440e76a3..3d5dfa6c8 100644 --- a/utils/params.go +++ b/utils/params.go @@ -232,6 +232,7 @@ type Git struct { EmailAuthor string `yaml:"emailAuthor,omitempty"` AggregateFixes bool `yaml:"aggregateFixes,omitempty"` PullRequestDetails vcsclient.PullRequestInfo + RepositoryCloneUrl string } func (g *Git) setDefaultsIfNeeded(gitParamsFromEnv *Git) (err error) { diff --git a/utils/reviewcomment.go b/utils/reviewcomment.go new file mode 100644 index 000000000..b0c5a28d6 --- /dev/null +++ b/utils/reviewcomment.go @@ -0,0 +1,194 @@ +package utils + +import ( + "context" + "errors" + "strings" + + "github.com/jfrog/frogbot/utils/outputwriter" + "github.com/jfrog/froggit-go/vcsclient" + "github.com/jfrog/jfrog-cli-core/v2/xray/formats" + "github.com/jfrog/jfrog-client-go/utils/log" +) + +type ReviewCommentType string + +type ReviewComment struct { + Location formats.Location + CommentInfo vcsclient.PullRequestComment + Type ReviewCommentType +} + +const ( + ApplicableComment ReviewCommentType = "Applicable" + IacComment ReviewCommentType = "Iac" + SastComment ReviewCommentType = "Sast" + + CommentId = "FrogbotReviewComment" +) + +func AddReviewComments(repo *Repository, pullRequestID int, client vcsclient.VcsClient, vulnerabilitiesRows []formats.VulnerabilityOrViolationRow, iacIssues, sastIssues []formats.SourceCodeRow) (err error) { + if err = deleteOldReviewComments(repo, pullRequestID, client); err != nil { + err = errors.New("couldn't delete pull request review comment: " + err.Error()) + return + } + if err = deleteOldFallbackComments(repo, pullRequestID, client); err != nil { + err = errors.New("couldn't delete pull request comment: " + err.Error()) + return + } + // Add review comments for the given data + commentsToAdd := getNewReviewComments(repo, vulnerabilitiesRows, iacIssues, sastIssues) + if len(commentsToAdd) > 0 { + for _, comment := range commentsToAdd { + if e := client.AddPullRequestReviewComments(context.Background(), repo.RepoOwner, repo.RepoName, pullRequestID, comment.CommentInfo); e != nil { + log.Debug("couldn't add pull request review comment, fallback to regular comment: " + e.Error()) + if err = client.AddPullRequestComment(context.Background(), repo.RepoOwner, repo.RepoName, getRegularCommentContent(comment), pullRequestID); err != nil { + err = errors.New("couldn't add pull request comment, fallback to comment: " + err.Error()) + return + } + } + } + } + return +} + +func deleteOldReviewComments(repo *Repository, pullRequestID int, client vcsclient.VcsClient) (err error) { + // Get all comments in PR + var existingComments []vcsclient.CommentInfo + if existingComments, err = client.ListPullRequestReviewComments(context.Background(), repo.RepoOwner, repo.RepoName, pullRequestID); err != nil { + err = errors.New("couldn't list existing review comments: " + err.Error()) + return + } + // Delete old review comments + if len(existingComments) > 0 { + if err = client.DeletePullRequestReviewComments(context.Background(), repo.RepoOwner, repo.RepoName, pullRequestID, getFrogbotReviewComments(existingComments)...); err != nil { + err = errors.New("couldn't delete pull request review comment: " + err.Error()) + return + } + } + return +} + +func deleteOldFallbackComments(repo *Repository, pullRequestID int, client vcsclient.VcsClient) (err error) { + // Get all comments in PR + existingComments, err := GetSortedPullRequestComments(client, repo.RepoOwner, repo.RepoName, pullRequestID) + if err != nil { + err = errors.New("couldn't list existing regular comments: " + err.Error()) + return + } + // Delete old review comments + if len(existingComments) > 0 { + for _, commentToDelete := range getFrogbotReviewComments(existingComments) { + if err = client.DeletePullRequestComment(context.Background(), repo.RepoOwner, repo.RepoName, pullRequestID, int(commentToDelete.ID)); err != nil { + err = errors.New("couldn't delete pull request review comment: " + err.Error()) + return + } + } + } + return +} + +func getFrogbotReviewComments(existingComments []vcsclient.CommentInfo) (reviewComments []vcsclient.CommentInfo) { + log.Debug("Delete old Frogbot review comments") + for _, comment := range existingComments { + if strings.Contains(comment.Content, outputwriter.CommentGeneratedByFrogbot) || strings.Contains(comment.Content, CommentId) { + log.Debug("Deleting comment id:", comment.ID) + reviewComments = append(reviewComments, comment) + } + } + return +} + +func getRegularCommentContent(comment ReviewComment) string { + content := outputwriter.MarkdownComment(CommentId) + return content + outputwriter.GetLocationDescription(comment.Location) + comment.CommentInfo.Content +} + +func getNewReviewComments(repo *Repository, vulnerabilitiesRows []formats.VulnerabilityOrViolationRow, iacIssues, sastIssues []formats.SourceCodeRow) (commentsToAdd []ReviewComment) { + writer := repo.OutputWriter + + for _, vulnerability := range vulnerabilitiesRows { + for _, cve := range vulnerability.Cves { + if cve.Applicability != nil { + for _, evidence := range cve.Applicability.Evidence { + commentsToAdd = append(commentsToAdd, generateReviewComment(ApplicableComment, evidence.Location, generateApplicabilityReviewContent(evidence, cve, vulnerability, writer))) + } + } + } + } + for _, iac := range iacIssues { + commentsToAdd = append(commentsToAdd, generateReviewComment(IacComment, iac.Location, generateReviewCommentContent(IacComment, iac, writer))) + } + + for _, sast := range sastIssues { + commentsToAdd = append(commentsToAdd, generateReviewComment(SastComment, sast.Location, generateReviewCommentContent(SastComment, sast, writer))) + } + return +} + +func generateReviewComment(commentType ReviewCommentType, location formats.Location, content string) (comment ReviewComment) { + return ReviewComment{ + Location: location, + CommentInfo: vcsclient.PullRequestComment{ + CommentInfo: vcsclient.CommentInfo{ + Content: content, + }, + PullRequestDiff: createPullRequestDiff(location), + }, + Type: commentType, + } + +} + +func generateApplicabilityReviewContent(issue formats.Evidence, relatedCve formats.CveRow, relatedVulnerability formats.VulnerabilityOrViolationRow, writer outputwriter.OutputWriter) (content string) { + remediation := "" + if relatedVulnerability.JfrogResearchInformation != nil { + remediation = relatedVulnerability.JfrogResearchInformation.Remediation + } + content += writer.ApplicableCveReviewContent( + relatedVulnerability.Severity, + issue.Reason, + relatedCve.Applicability.ScannerDescription, + relatedVulnerability.Summary, + remediation, + ) + content += writer.Footer() + return +} + +func generateReviewCommentContent(commentType ReviewCommentType, issue formats.SourceCodeRow, writer outputwriter.OutputWriter) (content string) { + switch commentType { + case IacComment: + content += writer.IacReviewContent( + issue.Severity, + issue.Finding, + issue.ScannerDescription, + ) + case SastComment: + content += writer.SastReviewContent( + issue.Severity, + issue.Finding, + issue.ScannerDescription, + issue.CodeFlow, + ) + } + + content += writer.Footer() + return +} + +func createPullRequestDiff(location formats.Location) vcsclient.PullRequestDiff { + return vcsclient.PullRequestDiff{ + OriginalFilePath: location.File, + OriginalStartLine: location.StartLine, + OriginalEndLine: location.EndLine, + OriginalStartColumn: location.StartColumn, + OriginalEndColumn: location.EndColumn, + + NewFilePath: location.File, + NewStartLine: location.StartLine, + NewEndLine: location.EndLine, + NewStartColumn: location.StartColumn, + NewEndColumn: location.EndColumn, + } +} diff --git a/utils/scandetails.go b/utils/scandetails.go index d9509a2f7..0026b8e26 100644 --- a/utils/scandetails.go +++ b/utils/scandetails.go @@ -1,14 +1,16 @@ package utils import ( + "context" "errors" "fmt" "github.com/jfrog/froggit-go/vcsclient" "github.com/jfrog/jfrog-cli-core/v2/utils/config" - audit "github.com/jfrog/jfrog-cli-core/v2/xray/commands/audit/generic" + "github.com/jfrog/jfrog-cli-core/v2/xray/commands/audit" xrayutils "github.com/jfrog/jfrog-cli-core/v2/xray/utils" "github.com/jfrog/jfrog-client-go/utils/log" "github.com/jfrog/jfrog-client-go/xray/services" + "os/exec" "path/filepath" "strings" @@ -119,18 +121,19 @@ func (sc *ScanDetails) RunInstallAndAudit(workDirs ...string) (auditResults *aud } } - graphBasicParams := (&xrayutils.GraphBasicParams{}). + auditBasicParams := (&xrayutils.AuditBasicParams{}). SetPipRequirementsFile(sc.PipRequirementsFile). SetUseWrapper(*sc.UseWrapper). SetDepsRepo(sc.DepsRepo). SetIgnoreConfigFile(true). SetServerDetails(sc.ServerDetails) + auditParams := audit.NewAuditParams(). SetXrayGraphScanParams(sc.XrayGraphScanParams). SetWorkingDirs(workDirs). SetMinSeverityFilter(sc.MinSeverityFilter()). SetFixableOnly(sc.FixableOnly()). - SetGraphBasicParams(graphBasicParams) + SetGraphBasicParams(auditBasicParams) auditResults, err = audit.RunAudit(auditParams) if auditResults != nil { @@ -170,6 +173,44 @@ func (sc *ScanDetails) runInstallCommand() ([]byte, error) { return MapTechToResolvingFunc[sc.InstallCommandName](sc) } +func (sc *ScanDetails) SetXscGitInfoContext(scannedBranch, gitProject string, client vcsclient.VcsClient) *ScanDetails { + XscGitInfoContext, err := sc.createGitInfoContext(scannedBranch, gitProject, client) + if err != nil { + log.Debug("failed trying to create GitInfoContext for Xsc with the following error: ", err.Error()) + return sc + } + sc.XscGitInfoContext = XscGitInfoContext + return sc +} + +// CreateGitInfoContext Creates GitInfoContext for XSC scans, this is optional. +// ScannedBranch - name of the branch we are scanning. +// GitProject - [Optional] relevant for azure repos and Bitbucket server. +// Client vscClient +func (sc *ScanDetails) createGitInfoContext(scannedBranch, gitProject string, client vcsclient.VcsClient) (gitInfo *services.XscGitInfoContext, err error) { + latestCommit, err := client.GetLatestCommit(context.Background(), sc.RepoOwner, sc.RepoName, scannedBranch) + if err != nil { + return nil, fmt.Errorf("failed getting latest commit, repository: %s, branch: %s. error: %s ", sc.RepoName, scannedBranch, err.Error()) + } + // In some VCS providers, there are no git projects, fallback to the repository owner. + if gitProject == "" { + gitProject = sc.RepoOwner + } + gitInfo = &services.XscGitInfoContext{ + // Use Clone URLs as Repo Url, on browsers it will redirect to repository URLS. + GitRepoUrl: sc.Git.RepositoryCloneUrl, + GitRepoName: sc.RepoName, + GitProvider: sc.GitProvider.String(), + GitProject: gitProject, + BranchName: scannedBranch, + LastCommit: latestCommit.Url, + CommitHash: latestCommit.Hash, + CommitMessage: latestCommit.Message, + CommitAuthor: latestCommit.AuthorName, + } + return +} + func GetFullPathWorkingDirs(workingDirs []string, baseWd string) []string { var fullPathWds []string if len(workingDirs) != 0 { diff --git a/utils/testsutils.go b/utils/testsutils.go index 45de1bf0f..2fe2a8822 100644 --- a/utils/testsutils.go +++ b/utils/testsutils.go @@ -5,6 +5,7 @@ import ( "github.com/go-git/go-git/v5" goGitConfig "github.com/go-git/go-git/v5/config" "github.com/go-git/go-git/v5/plumbing/object" + biutils "github.com/jfrog/build-info-go/utils" "github.com/jfrog/jfrog-cli-core/v2/utils/config" "github.com/jfrog/jfrog-client-go/utils/io/fileutils" "os" @@ -42,7 +43,7 @@ func PrepareTestEnvironment(t *testing.T, testDir string) (tmpDir string, restor // Copy project to a temporary directory tmpDir, err := fileutils.CreateTempDir() assert.NoError(t, err) - err = fileutils.CopyDir(filepath.Join("..", "testdata", testDir), tmpDir, true, []string{}) + err = biutils.CopyDir(filepath.Join("..", "testdata", testDir), tmpDir, true, []string{}) assert.NoError(t, err) restoreFunc = func() { assert.NoError(t, fileutils.RemoveTempDir(tmpDir)) diff --git a/utils/utils.go b/utils/utils.go index c39d0c475..a2b146213 100644 --- a/utils/utils.go +++ b/utils/utils.go @@ -12,17 +12,17 @@ import ( "strings" "github.com/jfrog/froggit-go/vcsclient" - "github.com/jfrog/froggit-go/vcsutils" "github.com/jfrog/gofrog/version" "github.com/jfrog/jfrog-cli-core/v2/common/commands" "github.com/jfrog/jfrog-cli-core/v2/utils/config" "github.com/jfrog/jfrog-cli-core/v2/utils/coreutils" "github.com/jfrog/jfrog-cli-core/v2/utils/usage" - audit "github.com/jfrog/jfrog-cli-core/v2/xray/commands/audit/generic" + "github.com/jfrog/jfrog-cli-core/v2/xray/commands/audit" "github.com/jfrog/jfrog-cli-core/v2/xray/formats" xrayutils "github.com/jfrog/jfrog-cli-core/v2/xray/utils" "github.com/jfrog/jfrog-client-go/utils/io/fileutils" "github.com/jfrog/jfrog-client-go/utils/log" + "github.com/owenrumney/go-sarif/v2/sarif" ) const ( @@ -43,6 +43,10 @@ const ( skipBuildToolDependencyMsg = "Skipping vulnerable package %s since it is not defined in your package descriptor file. " + "Update %s version to %s to fix this vulnerability." JfrogHomeDirEnv = "JFROG_CLI_HOME_DIR" + + // Sarif run output tool annotator + sarifToolName = "JFrog Frogbot" + sarifToolUrl = "https://github.com/jfrog/frogbot" ) var ( @@ -205,23 +209,60 @@ func VulnerabilityDetailsToMD5Hash(vulnerabilities ...formats.VulnerabilityOrVio return hex.EncodeToString(hash.Sum(nil)), nil } -// UploadScanToGitProvider uploads scan results to the relevant git provider in order to view the scan in the Git provider code scanning UI -func UploadScanToGitProvider(scanResults *audit.Results, repo *Repository, branch string, client vcsclient.VcsClient) error { - if repo.GitProvider.String() != vcsutils.GitHub.String() { - log.Debug("Upload Scan to " + repo.GitProvider.String() + " is currently unsupported.") - return nil - } - - scan, err := xrayutils.GenerateSarifFileFromScan(scanResults.ExtendedScanResults, scanResults.IsMultipleRootProject, true, "JFrog Frogbot", "https://github.com/jfrog/frogbot") +func UploadSarifResultsToGithubSecurityTab(scanResults *audit.Results, repo *Repository, branch string, client vcsclient.VcsClient) error { + report, err := GenerateFrogbotSarifReport(scanResults.ExtendedScanResults, scanResults.IsMultipleRootProject) if err != nil { return err } - _, err = client.UploadCodeScanning(context.Background(), repo.RepoOwner, repo.RepoName, branch, scan) + _, err = client.UploadCodeScanning(context.Background(), repo.RepoOwner, repo.RepoName, branch, report) if err != nil { return fmt.Errorf("upload code scanning for %s branch failed with: %s", branch, err.Error()) } log.Info("The complete scanning results have been uploaded to your Code Scanning alerts view") - return err + return nil +} + +func prepareRunsForGithubReport(runs []*sarif.Run) { + for _, run := range runs { + run.Tool.Driver.Name = sarifToolName + run.Tool.Driver.WithInformationURI(sarifToolUrl) + // Remove results without locations + results := []*sarif.Result{} + for _, result := range run.Results { + if len(result.Locations) == 0 { + continue + } + results = append(results, result) + } + run.Results = results + } + convertToRelativePath(runs) +} + +func convertToRelativePath(runs []*sarif.Run) { + for _, run := range runs { + for _, result := range run.Results { + for _, location := range result.Locations { + xrayutils.SetLocationFileName(location, xrayutils.GetRelativeLocationFileName(location, run.Invocations)) + } + for _, flows := range result.CodeFlows { + for _, flow := range flows.ThreadFlows { + for _, location := range flow.Locations { + xrayutils.SetLocationFileName(location.Location, xrayutils.GetRelativeLocationFileName(location.Location, run.Invocations)) + } + } + } + } + } +} + +func GenerateFrogbotSarifReport(extendedResults *xrayutils.ExtendedScanResults, isMultipleRoots bool) (string, error) { + prepareRunsForGithubReport(extendedResults.ApplicabilityScanResults) + prepareRunsForGithubReport(extendedResults.IacScanResults) + prepareRunsForGithubReport(extendedResults.SecretsScanResults) + prepareRunsForGithubReport(extendedResults.SastScanResults) + // Generate report from the data + return xrayutils.GenerateSarifContentFromResults(extendedResults, isMultipleRoots, false, true) } func DownloadRepoToTempDir(client vcsclient.VcsClient, repoOwner, repoName, branch string) (wd string, cleanup func() error, err error) {