diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml new file mode 100644 index 000000000..2897d5e1d --- /dev/null +++ b/.github/workflows/release.yml @@ -0,0 +1,277 @@ +name: Release Frogbot + +on: + workflow_dispatch: + inputs: + version: + description: 'Release version (e.g., 3.0.1)' + required: true + type: string + +# Required permissions +permissions: + contents: write + actions: read + +jobs: + release: + name: Release Frogbot v3 + runs-on: ubuntu-latest + + steps: + - name: Extract version from tag + id: version + run: | + if [ "${{ github.event_name }}" == "workflow_dispatch" ]; then + # Manual trigger: use input version + VERSION="${{ inputs.version }}" + # Add 'v' prefix if not present + if [[ ! "$VERSION" =~ ^v ]]; then + TAG="v$VERSION" + else + TAG="$VERSION" + VERSION="${VERSION#v}" + fi + + # Validate it's a v3.x.x version + if [[ ! "$TAG" =~ ^v3\.[0-9]+\.[0-9]+$ ]]; then + echo "❌ Error: Version must be v3.x.x format (e.g., v3.0.1)" + echo "Got: $TAG" + exit 1 + fi + else + # Release trigger: use release tag + TAG="${{ github.event.release.tag_name }}" + VERSION="${TAG#v}" + fi + echo "version=$VERSION" >> $GITHUB_OUTPUT + echo "tag=$TAG" >> $GITHUB_OUTPUT + echo "✅ Release version: $VERSION" + echo "✅ Release tag: $TAG" + + - name: Check if tag already exists + if: github.event_name == 'workflow_dispatch' + uses: actions/github-script@v7 + with: + script: | + const tag = '${{ steps.version.outputs.tag }}'; + + try { + // Check if tag exists + await github.rest.git.getRef({ + owner: context.repo.owner, + repo: context.repo.repo, + ref: `tags/${tag}` + }); + + // Tag exists - fail the workflow + core.setFailed(`❌ Tag ${tag} already exists! Please use a different version.`); + } catch (error) { + if (error.status === 404) { + // Tag doesn't exist - good to proceed + console.log(`✅ Tag ${tag} does not exist, proceeding with release`); + } else { + // Some other error + throw error; + } + } + + - name: Checkout code + uses: actions/checkout@v4 + with: + ref: ${{ github.event_name == 'workflow_dispatch' && github.ref || github.event.release.tag_name }} + fetch-depth: 0 + + - name: Set up Go + uses: actions/setup-go@v5 + with: + go-version-file: 'go.mod' + cache: true + + - name: Download JFrog CLI + run: | + curl -fL https://install-cli.jfrog.io | sh + # The install script already moves jf to /usr/local/bin/ + + - name: Configure JFrog CLI + env: + JF_URL: ${{ secrets.JF_URL }} + JF_ACCESS_TOKEN: ${{ secrets.JF_ACCESS_TOKEN }} + run: | + jf c rm --quiet || true + jf c add internal --url="$JF_URL" --access-token="$JF_ACCESS_TOKEN" + jf goc --repo-resolve ecosys-go-virtual + + - name: Generate mocks + run: go generate ./... + + - name: Run Frogbot scan before release + uses: jfrog/frogbot@v2 + env: + JF_URL: ${{ secrets.JF_URL }} + JF_ACCESS_TOKEN: ${{ secrets.JF_ACCESS_TOKEN }} + JF_GIT_TOKEN: ${{ secrets.GITHUB_TOKEN }} + JF_GIT_BASE_BRANCH: ${{ github.ref_name }} + JF_FAIL: "true" + JF_SKIP_AUTOFIX: "true" + + - name: Set up Node.js for ActionØ + uses: actions/setup-node@v4 + with: + node-version: '16' + cache: 'npm' + cache-dependency-path: action/package-lock.json + + - name: Build GitHub Action + working-directory: action + run: | + npm ci --ignore-scripts + npm run compile + npm run format-check + npm test + + - name: Commit and update tag with compiled action + run: | + git config user.name "github-actions[bot]" + git config user.email "github-actions[bot]@users.noreply.github.com" + git add action/lib/ + + # Check if there are changes + CHANGES=false + if ! git diff --staged --quiet; then + echo "Action files changed, committing..." + git commit -m "Build action for ${{ steps.version.outputs.tag }}" + CHANGES=true + else + echo "No changes to action files" + fi + + # For manual triggers, always create/update the tag + # For release triggers, update the tag only if there were changes + if [ "${{ github.event_name }}" == "workflow_dispatch" ] || [ "$CHANGES" = "true" ]; then + echo "Creating/updating tag ${{ steps.version.outputs.tag }}..." + git tag -f ${{ steps.version.outputs.tag }} + git push origin ${{ steps.version.outputs.tag }} --force + echo "Tag ${{ steps.version.outputs.tag }} created/updated" + fi + + - name: Update GitHub Action major version tag (v3) + run: | + # Update v3 tag to point to the latest v3.x.x release + git tag -f v3 + git push origin v3 --force + echo "Updated v3 tag to ${{ steps.version.outputs.tag }}" + + - name: Build and upload binaries (parallel) + env: + VERSION: ${{ steps.version.outputs.tag }} + JFROG_CLI_BUILD_NAME: ecosystem-frogbot-release + JFROG_CLI_BUILD_NUMBER: ${{ github.run_number }} + JFROG_CLI_BUILD_PROJECT: ecosys + run: | + env -i PATH=$PATH HOME=$HOME \ + JFROG_CLI_BUILD_NAME=$JFROG_CLI_BUILD_NAME \ + JFROG_CLI_BUILD_NUMBER=$JFROG_CLI_BUILD_NUMBER \ + JFROG_CLI_BUILD_PROJECT=$JFROG_CLI_BUILD_PROJECT \ + CI=true \ + release/buildAndUpload.sh "${{ steps.version.outputs.version }}" + + - name: Publish build info + env: + JFROG_CLI_BUILD_NAME: ecosystem-frogbot-release + JFROG_CLI_BUILD_NUMBER: ${{ github.run_number }} + JFROG_CLI_BUILD_PROJECT: ecosys + run: | + jf rt bag + jf rt bce + jf rt bp + + - name: Create and distribute release bundle + env: + VERSION: ${{ steps.version.outputs.version }} + run: | + jf ds rbc ecosystem-frogbot $VERSION \ + --spec="release/specs/frogbot-rbc-spec.json" \ + --spec-vars="VERSION=$VERSION" \ + --sign + jf ds rbd ecosystem-frogbot $VERSION \ + --site="releases.jfrog.io" \ + --sync + + - name: Create GitHub Release + if: github.event_name == 'workflow_dispatch' + uses: actions/github-script@v7 + with: + script: | + const tag = '${{ steps.version.outputs.tag }}'; + + console.log(`Creating release for tag ${tag}`); + + // The tag was already created and pushed in the previous step + // Now create the release with auto-generated notes + // Note: make_latest is set to false so this doesn't become the "Latest" release + const release = await github.rest.repos.createRelease({ + owner: context.repo.owner, + repo: context.repo.repo, + tag_name: tag, + name: `Release ${tag}`, + generate_release_notes: true, + draft: false, + prerelease: false, + make_latest: false + }); + + console.log(`✅ Created release ${release.data.id} for ${tag}`); + console.log(`📦 Release URL: ${release.data.html_url}`); + console.log(`ℹ️ This release is NOT marked as "Latest"`); + + - name: Cleanup JFrog config + if: always() + run: jf c rm --quiet || true + + # On failure: delete release and tag + - name: Delete release on failure + if: failure() + uses: actions/github-script@v7 + with: + script: | + const tag = '${{ steps.version.outputs.tag }}'; + + console.log(`Workflow failed, cleaning up tag ${tag}`); + + try { + // Try to find and delete the release + const releases = await github.rest.repos.listReleases({ + owner: context.repo.owner, + repo: context.repo.repo + }); + + const release = releases.data.find(r => r.tag_name === tag); + if (release) { + console.log(`Deleting release ${release.id}`); + await github.rest.repos.deleteRelease({ + owner: context.repo.owner, + repo: context.repo.repo, + release_id: release.id + }); + console.log('Release deleted'); + } else { + console.log('No release found to delete'); + } + + // Delete the tag + try { + await github.rest.git.deleteRef({ + owner: context.repo.owner, + repo: context.repo.repo, + ref: `tags/${tag}` + }); + console.log('Tag deleted'); + } catch (error) { + console.log(`Tag deletion failed or tag doesn't exist: ${error.message}`); + } + } catch (error) { + console.error(`Cleanup failed: ${error.message}`); + // Don't fail the workflow if cleanup fails + } + diff --git a/action/node_modules/@kwsites/file-exists/dist/test/__mocks__/fs.d.ts b/action/node_modules/@kwsites/file-exists/dist/test/__mocks__/fs.d.ts new file mode 100644 index 000000000..ab80d332b --- /dev/null +++ b/action/node_modules/@kwsites/file-exists/dist/test/__mocks__/fs.d.ts @@ -0,0 +1,7 @@ +export declare function statSync(...args: any[]): any; +export declare function addStatSyncMock(fn: any): void; +export declare function assertMocksUsed(): void; +declare const mockFs: { + statSync: typeof statSync; +}; +export default mockFs; diff --git a/action/node_modules/@kwsites/file-exists/dist/test/__mocks__/fs.js b/action/node_modules/@kwsites/file-exists/dist/test/__mocks__/fs.js new file mode 100644 index 000000000..cd46c71ca --- /dev/null +++ b/action/node_modules/@kwsites/file-exists/dist/test/__mocks__/fs.js @@ -0,0 +1,26 @@ +"use strict"; +Object.defineProperty(exports, "__esModule", { value: true }); +let statSyncMocks = []; +function statSync(...args) { + const mock = statSyncMocks.shift(); + if (typeof mock !== 'function') { + throw new Error(`fs.statSync called without configuring a mock`); + } + return mock(...args); +} +exports.statSync = statSync; +function addStatSyncMock(fn) { + statSyncMocks.push(fn); +} +exports.addStatSyncMock = addStatSyncMock; +function assertMocksUsed() { + if (statSyncMocks.length) { + throw new Error(`fs.afterEach: statSync has ${statSyncMocks.length} unused mocks`); + } +} +exports.assertMocksUsed = assertMocksUsed; +const mockFs = { + statSync, +}; +exports.default = mockFs; +//# sourceMappingURL=fs.js.map \ No newline at end of file diff --git a/action/node_modules/@kwsites/file-exists/dist/test/__mocks__/fs.js.map b/action/node_modules/@kwsites/file-exists/dist/test/__mocks__/fs.js.map new file mode 100644 index 000000000..e2a3d1688 --- /dev/null +++ b/action/node_modules/@kwsites/file-exists/dist/test/__mocks__/fs.js.map @@ -0,0 +1 @@ +{"version":3,"file":"fs.js","sourceRoot":"","sources":["../../../test/__mocks__/fs.ts"],"names":[],"mappings":";;AACA,IAAI,aAAa,GAAU,EAAE,CAAC;AAE9B,SAAgB,QAAQ,CAAC,GAAG,IAAW;IACpC,MAAO,IAAI,GAAG,aAAa,CAAC,KAAK,EAAE,CAAC;IACpC,IAAI,OAAO,IAAI,KAAK,UAAU,EAAE;QAC7B,MAAM,IAAI,KAAK,CAAC,+CAA+C,CAAC,CAAC;KACnE;IAED,OAAO,IAAI,CAAC,GAAG,IAAI,CAAC,CAAC;AACxB,CAAC;AAPD,4BAOC;AAED,SAAgB,eAAe,CAAC,EAAO;IACpC,aAAa,CAAC,IAAI,CAAC,EAAE,CAAC,CAAC;AAC1B,CAAC;AAFD,0CAEC;AAED,SAAgB,eAAe;IAC5B,IAAI,aAAa,CAAC,MAAM,EAAE;QACvB,MAAM,IAAI,KAAK,CAAC,8BAA8B,aAAa,CAAC,MAAM,eAAe,CAAC,CAAC;KACrF;AACJ,CAAC;AAJD,0CAIC;AAED,MAAM,MAAM,GAAG;IACZ,QAAQ;CACV,CAAA;AAED,kBAAe,MAAM,CAAC"} \ No newline at end of file diff --git a/action/test/main.spec.ts b/action/test/main.spec.ts index 9e4be4ef2..78db35020 100644 --- a/action/test/main.spec.ts +++ b/action/test/main.spec.ts @@ -17,7 +17,12 @@ describe('Frogbot Action Tests', () => { describe('Frogbot URL Tests', () => { const myOs: jest.Mocked = os as any; let cases: string[][] = [ - ['win32' as NodeJS.Platform, 'amd64', 'jfrog.exe', 'https://releases.jfrog.io/artifactory/frogbot/v1/1.2.3/frogbot-windows-amd64/jfrog.exe',], + [ + 'win32' as NodeJS.Platform, + 'amd64', + 'jfrog.exe', + 'https://releases.jfrog.io/artifactory/frogbot/v1/1.2.3/frogbot-windows-amd64/jfrog.exe', + ], ['darwin' as NodeJS.Platform, 'amd64', 'jfrog', 'https://releases.jfrog.io/artifactory/frogbot/v1/1.2.3/frogbot-mac-386/jfrog'], ['darwin' as NodeJS.Platform, 'arm64', 'jfrog', 'https://releases.jfrog.io/artifactory/frogbot/v1/1.2.3/frogbot-mac-arm64/jfrog'], ['linux' as NodeJS.Platform, 'amd64', 'jfrog', 'https://releases.jfrog.io/artifactory/frogbot/v1/1.2.3/frogbot-linux-amd64/jfrog'], diff --git a/go.mod b/go.mod index 145e718cd..adc67d943 100644 --- a/go.mod +++ b/go.mod @@ -1,25 +1,25 @@ module github.com/jfrog/frogbot/v2 -go 1.24.6 +go 1.25.4 require ( github.com/CycloneDX/cyclonedx-go v0.9.3 github.com/go-git/go-git/v5 v5.16.3 github.com/golang/mock v1.6.0 github.com/google/go-github/v45 v45.2.0 - github.com/jfrog/build-info-go v1.12.0 - github.com/jfrog/froggit-go v1.20.4 + github.com/jfrog/build-info-go v1.12.4 + github.com/jfrog/froggit-go v1.20.6 github.com/jfrog/gofrog v1.7.6 - github.com/jfrog/jfrog-cli-artifactory v0.7.3-0.20251021143342-49bab7f38cec + github.com/jfrog/jfrog-cli-artifactory v0.7.3-0.20251118100843-ac34330a70d3 github.com/jfrog/jfrog-cli-core/v2 v2.60.1-0.20251023084247-a56afca52451 - github.com/jfrog/jfrog-cli-security v1.21.9 - github.com/jfrog/jfrog-client-go v1.55.1-0.20251023073119-78f187c9afbf + github.com/jfrog/jfrog-cli-security v1.22.0 + github.com/jfrog/jfrog-client-go v1.55.1-0.20251119183924-d765eb708cec github.com/jordan-wright/email v4.0.1-0.20210109023952-943e75fe5223+incompatible github.com/owenrumney/go-sarif/v3 v3.2.3 github.com/stretchr/testify v1.11.1 - github.com/urfave/cli/v2 v2.27.4 + github.com/urfave/cli/v2 v2.27.7 github.com/xeipuuv/gojsonschema v1.2.0 - golang.org/x/exp v0.0.0-20250911091902-df9299821621 + golang.org/x/exp v0.0.0-20251023183803-a4bb9ffd2546 gopkg.in/yaml.v2 v2.4.0 gopkg.in/yaml.v3 v3.0.1 ) @@ -40,7 +40,7 @@ require ( github.com/dsnet/compress v0.0.2-0.20210315054119-f66993602bf5 // indirect github.com/emirpasic/gods v1.18.1 // indirect github.com/fatih/color v1.16.0 // indirect - github.com/forPelevin/gomoji v1.4.0 // indirect + github.com/forPelevin/gomoji v1.4.1 // indirect github.com/fsnotify/fsnotify v1.9.0 // indirect github.com/gfleury/go-bitbucket-v1 v0.0.0-20230825095122-9bc1711434ab // indirect github.com/go-git/gcfg v1.5.1-0.20230307220236-3a3c6141e376 // indirect @@ -107,15 +107,15 @@ require ( github.com/xeipuuv/gojsonreference v0.0.0-20180127040603-bd5ef7bd5415 // indirect github.com/xi2/xz v0.0.0-20171230120015-48954b6210f8 // indirect github.com/xo/terminfo v0.0.0-20220910002029-abceb7e1c41e // indirect - github.com/xrash/smetrics v0.0.0-20240521201337-686a1a2994c1 // indirect + github.com/xrash/smetrics v0.0.0-20250705151800-55b8f293f342 // indirect go.yaml.in/yaml/v3 v3.0.4 // indirect golang.org/x/crypto v0.43.0 // indirect - golang.org/x/mod v0.28.0 // indirect + golang.org/x/mod v0.29.0 // indirect golang.org/x/net v0.45.0 // indirect golang.org/x/oauth2 v0.31.0 // indirect - golang.org/x/sync v0.17.0 // indirect - golang.org/x/sys v0.37.0 // indirect - golang.org/x/term v0.36.0 // indirect + golang.org/x/sync v0.18.0 // indirect + golang.org/x/sys v0.38.0 // indirect + golang.org/x/term v0.37.0 // indirect golang.org/x/text v0.30.0 // indirect golang.org/x/time v0.12.0 // indirect google.golang.org/genproto/googleapis/rpc v0.0.0-20241223144023-3abc09e42ca8 // indirect diff --git a/go.sum b/go.sum index 81e7c3c86..316062643 100644 --- a/go.sum +++ b/go.sum @@ -59,8 +59,8 @@ github.com/emirpasic/gods v1.18.1/go.mod h1:8tpGGwCnJ5H4r6BWwaV6OrWmMoPhUl5jm/FM github.com/fatih/color v1.13.0/go.mod h1:kLAiJbzzSOZDVNGyDpeOxJ47H46qBXwg5ILebYFFOfk= github.com/fatih/color v1.16.0 h1:zmkK9Ngbjj+K0yRhTVONQh1p/HknKYSlNT+vZCzyokM= github.com/fatih/color v1.16.0/go.mod h1:fL2Sau1YI5c0pdGEVCbKQbLXB6edEj1ZgiY4NijnWvE= -github.com/forPelevin/gomoji v1.4.0 h1:RwrT+GimxEtFnGqq4ep1upwR54J5FP84aVAYJA+p8BQ= -github.com/forPelevin/gomoji v1.4.0/go.mod h1:mM6GtmCgpoQP2usDArc6GjbXrti5+FffolyQfGgPboQ= +github.com/forPelevin/gomoji v1.4.1 h1:7U+Bl8o6RV/dOQz7coQFWj/jX6Ram6/cWFOuFDEPEUo= +github.com/forPelevin/gomoji v1.4.1/go.mod h1:mM6GtmCgpoQP2usDArc6GjbXrti5+FffolyQfGgPboQ= github.com/frankban/quicktest v1.14.6 h1:7Xjx+VpznH+oBnejlPUj8oUpdxnVs4f8XU8WnHkI4W8= github.com/frankban/quicktest v1.14.6/go.mod h1:4ptaffx2x8+WTWXmUCuVU6aPUX1/Mz7zb5vbUoiM6w0= github.com/fsnotify/fsnotify v1.9.0 h1:2Ml+OJNzbYCTzsxtv8vKSFD9PbJjmhYF14k/jKC7S9k= @@ -128,22 +128,22 @@ github.com/jedib0t/go-pretty/v6 v6.6.8 h1:JnnzQeRz2bACBobIaa/r+nqjvws4yEhcmaZ4n1 github.com/jedib0t/go-pretty/v6 v6.6.8/go.mod h1:YwC5CE4fJ1HFUDeivSV1r//AmANFHyqczZk+U6BDALU= github.com/jfrog/archiver/v3 v3.6.1 h1:LOxnkw9pOn45DzCbZNFV6K0+6dCsQ0L8mR3ZcujO5eI= github.com/jfrog/archiver/v3 v3.6.1/go.mod h1:VgR+3WZS4N+i9FaDwLZbq+jeU4B4zctXL+gL4EMzfLw= -github.com/jfrog/build-info-go v1.12.0 h1:/abBQdIxrkYjOwO79sIL0p+XPnMCCtKhiWToHKXXqHg= -github.com/jfrog/build-info-go v1.12.0/go.mod h1:szdz9+WzB7+7PGnILLUgyY+OF5qD5geBT7UGNIxibyw= -github.com/jfrog/froggit-go v1.20.4 h1:N9XkNV00HNjpI8p6xXlF9DrWmvE9hz3z2XRDAYJDweQ= -github.com/jfrog/froggit-go v1.20.4/go.mod h1:obSG1SlsWjktkuqmKtpq7MNTTL63e0ot+ucTnlOMV88= +github.com/jfrog/build-info-go v1.12.4 h1:eoHoJDOF7Rx2gAOAXEAjbEIpnH+4y5ha2quQT48Py3Q= +github.com/jfrog/build-info-go v1.12.4/go.mod h1:NEJwH1HxzhtWuiT8eR/anbjT0A3OyLBWpZZrDJs+hWQ= +github.com/jfrog/froggit-go v1.20.6 h1:Xp7+LlEh0m1KGrQstb+u0aGfjRUtv1eh9xQBV3571jQ= +github.com/jfrog/froggit-go v1.20.6/go.mod h1:obSG1SlsWjktkuqmKtpq7MNTTL63e0ot+ucTnlOMV88= github.com/jfrog/gofrog v1.7.6 h1:QmfAiRzVyaI7JYGsB7cxfAJePAZTzFz0gRWZSE27c6s= github.com/jfrog/gofrog v1.7.6/go.mod h1:ntr1txqNOZtHplmaNd7rS4f8jpA5Apx8em70oYEe7+4= github.com/jfrog/jfrog-apps-config v1.0.1 h1:mtv6k7g8A8BVhlHGlSveapqf4mJfonwvXYLipdsOFMY= github.com/jfrog/jfrog-apps-config v1.0.1/go.mod h1:8AIIr1oY9JuH5dylz2S6f8Ym2MaadPLR6noCBO4C22w= -github.com/jfrog/jfrog-cli-artifactory v0.7.3-0.20251021143342-49bab7f38cec h1:iB5bXWKvzNejqyUgqxKf8YNj+DBx1suf2r2KzI03wkU= -github.com/jfrog/jfrog-cli-artifactory v0.7.3-0.20251021143342-49bab7f38cec/go.mod h1:JE/35+kU8cBET4I4iuNcVBvhm8SF64DAmGgtHRzf5Do= +github.com/jfrog/jfrog-cli-artifactory v0.7.3-0.20251118100843-ac34330a70d3 h1:sIjwBWBmyb7UEqP0IhQ22CWOedOPlNetyHzECS3sUyA= +github.com/jfrog/jfrog-cli-artifactory v0.7.3-0.20251118100843-ac34330a70d3/go.mod h1:3hLZrM2xT+PkIevIGret4x1xDFTaVoNu3h374QnrKyc= github.com/jfrog/jfrog-cli-core/v2 v2.60.1-0.20251023084247-a56afca52451 h1:Q0PY8VSOVsfvXzKiUnn+Rv7Ynf901QW6Wn1CbWpHBD0= github.com/jfrog/jfrog-cli-core/v2 v2.60.1-0.20251023084247-a56afca52451/go.mod h1:UOeOwEEmRIi57cRwghN5OBVoqkJieYQQfLpeqw8Yv38= -github.com/jfrog/jfrog-cli-security v1.21.9 h1:QSWADhVE0OYa0SPIc2YhV6QVtuhO6Y+K8rjGHE3MgeY= -github.com/jfrog/jfrog-cli-security v1.21.9/go.mod h1:XoJ6YBf/TrwkqC/r9+x/WKDIwnZogTF6+2wos1PJBUs= -github.com/jfrog/jfrog-client-go v1.55.1-0.20251023073119-78f187c9afbf h1:Ld+lGdCauixqWbkwK+wJn3QbPPBRgY35KgY+MxgrgCg= -github.com/jfrog/jfrog-client-go v1.55.1-0.20251023073119-78f187c9afbf/go.mod h1:jrODQbAbCt97F24d/0bYpqpdc0PFMuBxNJOTfTdW+Fk= +github.com/jfrog/jfrog-cli-security v1.22.0 h1:KNovA+BA1IpE0c0jI6jWF33fOimgylR9a7T84ZmgNJI= +github.com/jfrog/jfrog-cli-security v1.22.0/go.mod h1:uoACrGyWZViNPU0STC0fF38bVKtNXjm3hzWW/DKI0DY= +github.com/jfrog/jfrog-client-go v1.55.1-0.20251119183924-d765eb708cec h1:tNfeGi/2FuxIUSi8urFZuqa33grynAHwLRH6iIK/DB0= +github.com/jfrog/jfrog-client-go v1.55.1-0.20251119183924-d765eb708cec/go.mod h1:ureS+L3wNs0qYUBSwH8C9PjwnraTX9ibZu7JkaqjO/E= github.com/jhump/protoreflect v1.15.1 h1:HUMERORf3I3ZdX05WaQ6MIpd/NJ434hTp5YiKgfCL6c= github.com/jhump/protoreflect v1.15.1/go.mod h1:jD/2GMKKE6OqX8qTjhADU1e6DShO+gavG9e0Q693nKo= github.com/jordan-wright/email v4.0.1-0.20210109023952-943e75fe5223+incompatible h1:jdpOPRN1zP63Td1hDQbZW73xKmzDvZHzVdNYxhnTMDA= @@ -206,8 +206,8 @@ github.com/nwaples/rardecode v1.1.3 h1:cWCaZwfM5H7nAD6PyEdcVnczzV8i/JtotnyW/dD9l github.com/nwaples/rardecode v1.1.3/go.mod h1:5DzqNKiOdpKKBH87u8VlvAnPZMXcGRhxWkRpHbbfGS0= github.com/oklog/run v1.0.0 h1:Ru7dDtJNOyC66gQ5dQmaCa0qIsAUFY3sFpK1Xk8igrw= github.com/oklog/run v1.0.0/go.mod h1:dlhp/R75TPv97u0XWUtDeV/lRKWPKSdTuV0TZvrmrQA= -github.com/onsi/gomega v1.34.1 h1:EUMJIKUjM8sKjYbtxQI9A4z2o+rruxnzNvpknOXie6k= -github.com/onsi/gomega v1.34.1/go.mod h1:kU1QgUvBDLXBJq618Xvm2LUX6rSAfRaFRTcdOeDLwwY= +github.com/onsi/gomega v1.38.2 h1:eZCjf2xjZAqe+LeWvKb5weQ+NcPwX84kqJ0cZNxok2A= +github.com/onsi/gomega v1.38.2/go.mod h1:W2MJcYxRGV63b418Ai34Ud0hEdTVXq9NW9+Sx6uXf3k= github.com/owenrumney/go-sarif/v3 v3.2.3 h1:n6mdX5ugKwCrZInvBsf6WumXmpAe3mbmQXgkXlIq34U= github.com/owenrumney/go-sarif/v3 v3.2.3/go.mod h1:1bV7t8SZg7pX41spaDkEUs8/yEjzk9JapztMoX1XNjg= github.com/package-url/packageurl-go v0.1.3 h1:4juMED3hHiz0set3Vq3KeQ75KD1avthoXLtmE3I0PLs= @@ -278,8 +278,8 @@ github.com/ulikunitz/xz v0.5.15 h1:9DNdB5s+SgV3bQ2ApL10xRc35ck0DuIX/isZvIk+ubY= github.com/ulikunitz/xz v0.5.15/go.mod h1:nbz6k7qbPmH4IRqmfOplQw/tblSgqTqBwxkY0oWt/14= github.com/urfave/cli v1.22.17 h1:SYzXoiPfQjHBbkYxbew5prZHS1TOLT3ierW8SYLqtVQ= github.com/urfave/cli v1.22.17/go.mod h1:b0ht0aqgH/6pBYzzxURyrM4xXNgsoT/n2ZzwQiEhNVo= -github.com/urfave/cli/v2 v2.27.4 h1:o1owoI+02Eb+K107p27wEX9Bb8eqIoZCfLXloLUSWJ8= -github.com/urfave/cli/v2 v2.27.4/go.mod h1:m4QzxcD2qpra4z7WhzEGn74WZLViBnMpb1ToCAKdGRQ= +github.com/urfave/cli/v2 v2.27.7 h1:bH59vdhbjLv3LAvIu6gd0usJHgoTTPhCFib8qqOwXYU= +github.com/urfave/cli/v2 v2.27.7/go.mod h1:CyNAG/xg+iAOg0N4MPGZqVmv2rCoP267496AOXUZjA4= github.com/vbauerster/mpb/v8 v8.10.2 h1:2uBykSHAYHekE11YvJhKxYmLATKHAGorZwFlyNw4hHM= github.com/vbauerster/mpb/v8 v8.10.2/go.mod h1:+Ja4P92E3/CorSZgfDtK46D7AVbDqmBQRTmyTqPElo0= github.com/virtuald/go-ordered-json v0.0.0-20170621173500-b18e6e673d74 h1:JwtAtbp7r/7QSyGz8mKUbYJBg2+6Cd7OjM8o/GNOcVo= @@ -299,8 +299,8 @@ github.com/xi2/xz v0.0.0-20171230120015-48954b6210f8 h1:nIPpBwaJSVYIxUFsDv3M8ofm github.com/xi2/xz v0.0.0-20171230120015-48954b6210f8/go.mod h1:HUYIGzjTL3rfEspMxjDjgmT5uz5wzYJKVo23qUhYTos= github.com/xo/terminfo v0.0.0-20220910002029-abceb7e1c41e h1:JVG44RsyaB9T2KIHavMF/ppJZNG9ZpyihvCd0w101no= github.com/xo/terminfo v0.0.0-20220910002029-abceb7e1c41e/go.mod h1:RbqR21r5mrJuqunuUZ/Dhy/avygyECGrLceyNeo4LiM= -github.com/xrash/smetrics v0.0.0-20240521201337-686a1a2994c1 h1:gEOO8jv9F4OT7lGCjxCBTO/36wtF6j2nSip77qHd4x4= -github.com/xrash/smetrics v0.0.0-20240521201337-686a1a2994c1/go.mod h1:Ohn+xnUBiLI6FVj/9LpzZWtj1/D6lUovWYBkxHVV3aM= +github.com/xrash/smetrics v0.0.0-20250705151800-55b8f293f342 h1:FnBeRrxr7OU4VvAzt5X7s6266i6cSVkkFPS0TuXWbIg= +github.com/xrash/smetrics v0.0.0-20250705151800-55b8f293f342/go.mod h1:Ohn+xnUBiLI6FVj/9LpzZWtj1/D6lUovWYBkxHVV3aM= github.com/xyproto/randomstring v1.0.5 h1:YtlWPoRdgMu3NZtP45drfy1GKoojuR7hmRcnhZqKjWU= github.com/xyproto/randomstring v1.0.5/go.mod h1:rgmS5DeNXLivK7YprL0pY+lTuhNQW3iGxZ18UQApw/E= github.com/yuin/goldmark v1.3.5/go.mod h1:mwnBkeHKe2W/ZEtQ+71ViKU8L12m81fl3OWwC1Zlc8k= @@ -315,13 +315,13 @@ golang.org/x/crypto v0.19.0/go.mod h1:Iy9bg/ha4yyC70EfRS8jz+B6ybOBKMaSxLj6P6oBDf golang.org/x/crypto v0.22.0/go.mod h1:vr6Su+7cTlO45qkww3VDJlzDn0ctJvRgYbC2NvXHt+M= golang.org/x/crypto v0.43.0 h1:dduJYIi3A3KOfdGOHX8AVZ/jGiyPa3IbBozJ5kNuE04= golang.org/x/crypto v0.43.0/go.mod h1:BFbav4mRNlXJL4wNeejLpWxB7wMbc79PdRGhWKncxR0= -golang.org/x/exp v0.0.0-20250911091902-df9299821621 h1:2id6c1/gto0kaHYyrixvknJ8tUK/Qs5IsmBtrc+FtgU= -golang.org/x/exp v0.0.0-20250911091902-df9299821621/go.mod h1:TwQYMMnGpvZyc+JpB/UAuTNIsVJifOlSkrZkhcvpVUk= +golang.org/x/exp v0.0.0-20251023183803-a4bb9ffd2546 h1:mgKeJMpvi0yx/sU5GsxQ7p6s2wtOnGAHZWCHUM4KGzY= +golang.org/x/exp v0.0.0-20251023183803-a4bb9ffd2546/go.mod h1:j/pmGrbnkbPtQfxEe5D0VQhZC6qKbfKifgD0oM7sR70= golang.org/x/mod v0.4.2/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA= golang.org/x/mod v0.6.0-dev.0.20220419223038-86c51ed26bb4/go.mod h1:jJ57K6gSWd91VN4djpZkiMVwK6gcyfeH4XE8wZrZaV4= golang.org/x/mod v0.8.0/go.mod h1:iBbtSCu2XBx23ZKBPSOrRkjjQPZFPuis4dIYUhu/chs= -golang.org/x/mod v0.28.0 h1:gQBtGhjxykdjY9YhZpSlZIsbnaE2+PgjfLWUQTnoZ1U= -golang.org/x/mod v0.28.0/go.mod h1:yfB/L0NOf/kmEbXjzCPOx1iK1fRutOydrCMsqRhEBxI= +golang.org/x/mod v0.29.0 h1:HV8lRxZC4l2cr3Zq1LvtOsi/ThTgWnUk/y64QSs8GwA= +golang.org/x/mod v0.29.0/go.mod h1:NyhrlYXJ2H4eJiRy/WDBO6HMqZQ6q9nk4JzS3NuCK+w= golang.org/x/net v0.0.0-20180724234803-3673e40ba225/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= golang.org/x/net v0.0.0-20190108225652-1e06a53dbb7e/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= golang.org/x/net v0.0.0-20190404232315-eb5bcb51f2a3/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg= @@ -347,8 +347,8 @@ golang.org/x/sync v0.0.0-20190911185100-cd5d95a43a6e/go.mod h1:RxMgew5VJxzue5/jJ golang.org/x/sync v0.0.0-20210220032951-036812b2e83c/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sync v0.0.0-20220722155255-886fb9371eb4/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sync v0.1.0/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= -golang.org/x/sync v0.17.0 h1:l60nONMj9l5drqw6jlhIELNv9I0A4OFgRsG9k2oT9Ug= -golang.org/x/sync v0.17.0/go.mod h1:9KTHXmSnoGruLpwFjVSX0lNNA75CykiMECbovNTZqGI= +golang.org/x/sync v0.18.0 h1:kr88TuHDroi+UVf+0hZnirlk8o8T+4MrK6mr60WkH/I= +golang.org/x/sync v0.18.0/go.mod h1:9KTHXmSnoGruLpwFjVSX0lNNA75CykiMECbovNTZqGI= golang.org/x/sys v0.0.0-20181122145206-62eef0e2fa9b/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= golang.org/x/sys v0.0.0-20190222072716-a9d3bda3a223/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= @@ -380,16 +380,16 @@ golang.org/x/sys v0.6.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.8.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.17.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA= golang.org/x/sys v0.19.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA= -golang.org/x/sys v0.37.0 h1:fdNQudmxPjkdUTPnLn5mdQv7Zwvbvpaxqs831goi9kQ= -golang.org/x/sys v0.37.0/go.mod h1:OgkHotnGiDImocRcuBABYBEXf8A9a87e/uXjp9XT3ks= +golang.org/x/sys v0.38.0 h1:3yZWxaJjBmCWXqhN1qh02AkOnCQ1poK6oF+a7xWL6Gc= +golang.org/x/sys v0.38.0/go.mod h1:OgkHotnGiDImocRcuBABYBEXf8A9a87e/uXjp9XT3ks= 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.5.0/go.mod h1:jMB1sMXY+tzblOD4FWmEbocvup2/aLOaQEp7JmGp78k= golang.org/x/term v0.8.0/go.mod h1:xPskH00ivmX89bAKVGSKKtLOWNx2+17Eiy94tnKShWo= golang.org/x/term v0.17.0/go.mod h1:lLRBjIVuehSbZlaOtGMbcMncT+aqLLLmKrsjNrUguwk= golang.org/x/term v0.19.0/go.mod h1:2CuTdWZ7KHSQwUzKva0cbMg6q2DMI3Mmxp+gKJbskEk= -golang.org/x/term v0.36.0 h1:zMPR+aF8gfksFprF/Nc/rd1wRS1EI6nDBGyWAvDzx2Q= -golang.org/x/term v0.36.0/go.mod h1:Qu394IJq6V6dCBRgwqshf3mPF85AqzYEzofzRdZkWss= +golang.org/x/term v0.37.0 h1:8EGAD0qCmHYZg6J17DvsMy9/wJ7/D/4pV/wfnld5lTU= +golang.org/x/term v0.37.0/go.mod h1:5pB4lxRNYYVZuTLmy8oR2BH8dflOR+IbTYFD8fi3254= golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= golang.org/x/text v0.3.3/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ= golang.org/x/text v0.3.6/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ= @@ -406,8 +406,8 @@ golang.org/x/tools v0.0.0-20191119224855-298f0cb1881e/go.mod h1:b+2E5dAYhXwXZwtn golang.org/x/tools v0.1.1/go.mod h1:o0xws9oXOQQZyjljx8fwUC0k7L1pTE6eaCbjGeHmOkk= golang.org/x/tools v0.1.12/go.mod h1:hNGJHUnrk76NpqgfD5Aqm5Crs+Hm0VOH/i9J2+nxYbc= golang.org/x/tools v0.6.0/go.mod h1:Xwgl3UAJ/d3gWutnCtw505GrjyAbvKui8lOU390QaIU= -golang.org/x/tools v0.37.0 h1:DVSRzp7FwePZW356yEAChSdNcQo6Nsp+fex1SUW09lE= -golang.org/x/tools v0.37.0/go.mod h1:MBN5QPQtLMHVdvsbtarmTNukZDdgwdwlO5qGacAzF0w= +golang.org/x/tools v0.38.0 h1:Hx2Xv8hISq8Lm16jvBZ2VQf+RLmbd7wVUsALibYI/IQ= +golang.org/x/tools v0.38.0/go.mod h1:yEsQ/d/YK8cjh0L6rZlY8tgtlKiBNTL14pGDJPJpYQs= 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/pythonpackagehandler.go b/packagehandlers/pythonpackagehandler.go index 34c3c1727..3ff6569ac 100644 --- a/packagehandlers/pythonpackagehandler.go +++ b/packagehandlers/pythonpackagehandler.go @@ -64,23 +64,10 @@ func (py *PythonPackageHandler) handlePip(vulnDetails *utils.VulnerabilityDetail var fixedFile string // This function assumes that the version of the dependencies is statically pinned in the requirements file or inside the 'install_requires' array in the setup.py file fixedPackage := vulnDetails.ImpactedDependencyName + "==" + vulnDetails.SuggestedFixedVersion - if py.pipRequirementsFile == "" { - py.pipRequirementsFile = "setup.py" - } - wd, err := os.Getwd() + currentFile, err := py.tryGetRequirementFile() if err != nil { - return - } - fullPath := filepath.Join(wd, py.pipRequirementsFile) - if !strings.HasPrefix(filepath.Clean(fullPath), wd) { - return errors.New("wrong requirements file input") + return errors.New("failed to read requirements file: " + err.Error()) } - data, err := os.ReadFile(filepath.Clean(py.pipRequirementsFile)) - if err != nil { - return errors.New("an error occurred while attempting to read the requirements file:\n" + err.Error()) - } - currentFile := string(data) - // Check both original and lowered package name and replace to only one lowered result // This regex will match the impactedPackage with it's pinned version e.py. PyJWT==1.7.1 re := regexp.MustCompile(PythonPackageRegexPrefix + "(" + vulnDetails.ImpactedDependencyName + "|" + strings.ToLower(vulnDetails.ImpactedDependencyName) + ")" + PythonPackageRegexSuffix) @@ -95,3 +82,42 @@ func (py *PythonPackageHandler) handlePip(vulnDetails *utils.VulnerabilityDetail } return } + +func (py *PythonPackageHandler) tryGetRequirementFile() (string, error) { + if py.pipRequirementsFile != "" { + fileContent, err := py.tryReadRequirementFile(py.pipRequirementsFile) + if err != nil { + return "", err + } + return fileContent, nil + } else { + // if we don't have a value in py.pipRequirementsFile - we try first setup.py and then requirements.txt + py.pipRequirementsFile = "setup.py" + fileContent, err := py.tryReadRequirementFile(py.pipRequirementsFile) + if err != nil { + py.pipRequirementsFile = "requirements.txt" + fileContent, err = py.tryReadRequirementFile(py.pipRequirementsFile) + if err != nil { + return "", err + } + return fileContent, nil + } + return fileContent, nil + } +} + +func (py *PythonPackageHandler) tryReadRequirementFile(file string) (string, error) { + wd, err := os.Getwd() + if err != nil { + return "", err + } + fullPath := filepath.Join(wd, file) + if !strings.HasPrefix(filepath.Clean(fullPath), wd) { + return "", errors.New("wrong requirements file input: " + fullPath) + } + data, err := os.ReadFile(filepath.Clean(file)) + if err != nil { + return "", errors.New("an error occurred while attempting to read the requirements file:\n" + err.Error()) + } + return string(data), nil +} diff --git a/release/buildAndUpload.sh b/release/buildAndUpload.sh index 8f671fe7a..eb6e1e16f 100755 --- a/release/buildAndUpload.sh +++ b/release/buildAndUpload.sh @@ -14,7 +14,7 @@ build () { # Run verification after building plugin for the correct platform of this image. if [[ "$pkg" = "frogbot-linux-386" ]]; then - verifyVersionMatching + verifyVersionMatching "$exeName" fi } @@ -24,19 +24,26 @@ buildAndUpload () { goos="$2" goarch="$3" fileExtension="$4" - exeName="frogbot$fileExtension" + # Use unique filename during build to avoid parallel conflicts + uniqueExeName="${pkg}${fileExtension}" + finalExeName="frogbot$fileExtension" - build "$pkg" "$goos" "$goarch" "$exeName" + build "$pkg" "$goos" "$goarch" "$uniqueExeName" - destPath="$pkgPath/$version/$pkg/$exeName" - echo "Uploading $exeName to $destPath ..." - jf rt u "./$exeName" "$destPath" + destPath="$pkgPath/$version/$pkg/$finalExeName" + echo "Uploading $uniqueExeName to $destPath ..." + jf rt u "./$uniqueExeName" "$destPath" + + # Clean up the unique build file after upload + rm -f "./$uniqueExeName" } # Verify version provided in pipelines UI matches version in frogbot source code. +# Takes the executable name as parameter verifyVersionMatching () { + local exePath="$1" echo "Verifying provided version matches built version..." - res=$(eval "./frogbot -v") + res=$(eval "./$exePath -v") exitCode=$? if [[ $exitCode -ne 0 ]]; then echo "Error: Failed verifying version matches" @@ -55,19 +62,47 @@ verifyVersionMatching () { } version="$1" -pkgPath="ecosys-frogbot/v2" +# Extract major version (e.g., "3.1.1" -> "3") +majorVersion="${version%%.*}" +# Allow overriding repository name via environment variable +repoName="${FROGBOT_REPO_NAME:-ecosys-frogbot}" +pkgPath="${repoName}/v${majorVersion}" # Build and upload for every architecture. # Keep 'linux-386' first to prevent unnecessary uploads in case the built version doesn't match the provided one. +echo "Building linux-386 first for version verification..." buildAndUpload 'frogbot-linux-386' 'linux' '386' '' -buildAndUpload 'frogbot-linux-amd64' 'linux' 'amd64' '' -buildAndUpload 'frogbot-linux-s390x' 'linux' 's390x' '' -buildAndUpload 'frogbot-linux-arm64' 'linux' 'arm64' '' -buildAndUpload 'frogbot-linux-arm' 'linux' 'arm' '' -buildAndUpload 'frogbot-linux-ppc64' 'linux' 'ppc64' '' -buildAndUpload 'frogbot-linux-ppc64le' 'linux' 'ppc64le' '' -buildAndUpload 'frogbot-mac-386' 'darwin' 'amd64' '' -buildAndUpload 'frogbot-mac-arm64' 'darwin' 'arm64' '' -buildAndUpload 'frogbot-windows-amd64' 'windows' 'amd64' '.exe' + +# Build the rest in parallel for speed +echo "" +echo "Building remaining 9 platforms in parallel..." +pids=() + +buildAndUpload 'frogbot-linux-amd64' 'linux' 'amd64' '' & pids+=($!) +buildAndUpload 'frogbot-linux-s390x' 'linux' 's390x' '' & pids+=($!) +buildAndUpload 'frogbot-linux-arm64' 'linux' 'arm64' '' & pids+=($!) +buildAndUpload 'frogbot-linux-arm' 'linux' 'arm' '' & pids+=($!) +buildAndUpload 'frogbot-linux-ppc64' 'linux' 'ppc64' '' & pids+=($!) +buildAndUpload 'frogbot-linux-ppc64le' 'linux' 'ppc64le' '' & pids+=($!) +buildAndUpload 'frogbot-mac-386' 'darwin' 'amd64' '' & pids+=($!) +buildAndUpload 'frogbot-mac-arm64' 'darwin' 'arm64' '' & pids+=($!) +buildAndUpload 'frogbot-windows-amd64' 'windows' 'amd64' '.exe' & pids+=($!) + +# Wait for all background jobs and check for failures +echo "Waiting for all parallel builds to complete..." +failed=0 +for pid in "${pids[@]}"; do + wait $pid || failed=1 +done + +if [ $failed -eq 1 ]; then + echo "❌ One or more builds failed!" + exit 1 +fi + +echo "" +echo "✅ All builds completed successfully!" +echo "" jf rt u "./buildscripts/getFrogbot.sh" "$pkgPath/$version/" --flat + diff --git a/release/pipelines.yml b/release/pipelines.yml deleted file mode 100644 index c7618a1dd..000000000 --- a/release/pipelines.yml +++ /dev/null @@ -1,89 +0,0 @@ -resources: - - name: frogbotGit - type: GitRepo - configuration: - path: jfrog/frogbot - branches: - include: dev - gitProvider: il_automation - -pipelines: - - name: release_frogbot - configuration: - runtime: - type: image - image: - custom: - name: releases-docker.jfrog.io/jfrog-ecosystem-integration-env - tag: latest - environmentVariables: - readOnly: - NEXT_VERSION: 0.0.0 - - steps: - - name: Release - type: Bash - configuration: - inputResources: - - name: frogbotGit - trigger: false - integrations: - - name: il_automation - - name: ecosys_entplus_deployer - execution: - onExecute: - - cd $res_frogbotGit_resourcePath - - # Set env - - export CI=true - - export JFROG_CLI_BUILD_NAME=ecosystem-frogbotGit-release - - export JFROG_CLI_BUILD_NUMBER=$run_number - - export JFROG_CLI_BUILD_PROJECT=ecosys - - # Make sure version provided - - echo "Checking variables" - - test -n "$NEXT_VERSION" -a "$NEXT_VERSION" != "0.0.0" - - # Configure Git and merge from the dev - - git checkout master - - git remote set-url origin https://$int_il_automation_token@github.com/jfrog/frogbot.git - - git merge origin/dev - - git tag v${NEXT_VERSION} - - # Download JFrog CLI - - curl -fL https://install-cli.jfrog.io | sh - - jf c rm --quiet - - jf c add internal --url=$int_ecosys_entplus_deployer_url --user=$int_ecosys_entplus_deployer_user --password=$int_ecosys_entplus_deployer_apikey - - jf goc --repo-resolve ecosys-go-virtual - - # Generate mocks - - go generate ./... - - # Audit - - jf audit --fail=false - - # Build and upload - - > - env -i PATH=$PATH HOME=$HOME - JFROG_CLI_BUILD_NAME=$JFROG_CLI_BUILD_NAME - JFROG_CLI_BUILD_NUMBER=$JFROG_CLI_BUILD_NUMBER - JFROG_CLI_BUILD_PROJECT=$JFROG_CLI_BUILD_PROJECT - release/buildAndUpload.sh "$NEXT_VERSION" - - jf rt bag && jf rt bce - - jf rt bp - - # Distribute release bundle - - jf ds rbc ecosystem-frogbot $NEXT_VERSION --spec="release/specs/frogbot-rbc-spec.json" --spec-vars="VERSION=$NEXT_VERSION" --sign - - jf ds rbd ecosystem-frogbot $NEXT_VERSION --site="releases.jfrog.io" --sync - - # Push to master - - git clean -fd - - git push - - git push --tags - - # Merge changes to dev - - git checkout dev - - git merge origin/master - - git push - onComplete: - - jf c rm --quiet diff --git a/release/specs/frogbot-rbc-spec.json b/release/specs/frogbot-rbc-spec.json deleted file mode 100644 index ea24a35cd..000000000 --- a/release/specs/frogbot-rbc-spec.json +++ /dev/null @@ -1,8 +0,0 @@ -{ - "files": [ - { - "pattern": "ecosys-frogbot/(v2/${VERSION}/*)", - "target": "frogbot/{1}" - } - ] -} diff --git a/scanpullrequest/scanallpullrequests_test.go b/scanpullrequest/scanallpullrequests_test.go index 9fb31d07d..bd5410136 100644 --- a/scanpullrequest/scanallpullrequests_test.go +++ b/scanpullrequest/scanallpullrequests_test.go @@ -3,11 +3,12 @@ package scanpullrequest import ( "context" "fmt" - "github.com/jfrog/jfrog-cli-security/utils/xsc" "path/filepath" "testing" "time" + "github.com/jfrog/jfrog-cli-security/utils/xsc" + "github.com/golang/mock/gomock" biutils "github.com/jfrog/build-info-go/utils" "github.com/jfrog/frogbot/v2/testdata" diff --git a/scanpullrequest/scanpullrequest_test.go b/scanpullrequest/scanpullrequest_test.go index 5fdb46cab..0f99261c4 100644 --- a/scanpullrequest/scanpullrequest_test.go +++ b/scanpullrequest/scanpullrequest_test.go @@ -98,7 +98,7 @@ func TestScanResultsToIssuesCollection(t *testing.T) { Applicable: "Applicable", FixedVersions: []string{"1.2.3"}, ImpactedDependencyDetails: formats.ImpactedDependencyDetails{ - SeverityDetails: formats.SeverityDetails{Severity: "High", SeverityNumValue: 21}, + SeverityDetails: formats.SeverityDetails{Severity: "High", SeverityNumValue: 26}, ImpactedDependencyName: "Dep-1", }, Cves: []formats.CveRow{{Id: "CVE-2022-2122", Applicability: &formats.Applicability{Status: "Applicable", ScannerDescription: "rule-msg", Evidence: []formats.Evidence{{Reason: "result-msg", Location: formats.Location{File: "file1", StartLine: 1, StartColumn: 10, EndLine: 2, EndColumn: 11, Snippet: "snippet"}}}}}}, @@ -107,7 +107,7 @@ func TestScanResultsToIssuesCollection(t *testing.T) { Applicable: "Not Applicable", FixedVersions: []string{"1.2.2"}, ImpactedDependencyDetails: formats.ImpactedDependencyDetails{ - SeverityDetails: formats.SeverityDetails{Severity: "Low", SeverityNumValue: 2}, + SeverityDetails: formats.SeverityDetails{Severity: "Low", SeverityNumValue: 3}, ImpactedDependencyName: "Dep-2", }, Cves: []formats.CveRow{{Id: "CVE-2023-3122", Applicability: &formats.Applicability{Status: "Not Applicable", ScannerDescription: "rule-msg"}}}, @@ -117,7 +117,7 @@ func TestScanResultsToIssuesCollection(t *testing.T) { { SeverityDetails: formats.SeverityDetails{ Severity: "High", - SeverityNumValue: 21, + SeverityNumValue: 26, }, ScannerInfo: formats.ScannerInfo{ ScannerDescription: "rule-msg", @@ -138,7 +138,7 @@ func TestScanResultsToIssuesCollection(t *testing.T) { { SeverityDetails: formats.SeverityDetails{ Severity: "High", - SeverityNumValue: 21, + SeverityNumValue: 26, }, ScannerInfo: formats.ScannerInfo{ ScannerDescription: "rule-msg", @@ -159,7 +159,7 @@ func TestScanResultsToIssuesCollection(t *testing.T) { { SeverityDetails: formats.SeverityDetails{ Severity: "High", - SeverityNumValue: 21, + SeverityNumValue: 26, }, ScannerInfo: formats.ScannerInfo{ ScannerDescription: "rule-msg", @@ -183,7 +183,7 @@ func TestScanResultsToIssuesCollection(t *testing.T) { ImpactedDependencyDetails: formats.ImpactedDependencyDetails{ SeverityDetails: formats.SeverityDetails{ Severity: "Medium", - SeverityNumValue: 14, + SeverityNumValue: 19, }, ImpactedDependencyName: "Dep-1", }, diff --git a/testdata/messages/integration/test_proj_pip_with_vulnerability.md b/testdata/messages/integration/test_proj_pip_with_vulnerability.md index 00779aec8..b16086630 100644 --- a/testdata/messages/integration/test_proj_pip_with_vulnerability.md +++ b/testdata/messages/integration/test_proj_pip_with_vulnerability.md @@ -11,11 +11,11 @@ ## 📗 Scan Summary -- Frogbot scanned for vulnerabilities and found 1 issues +- Frogbot scanned for vulnerabilities and found 2 issues | Scan Category | Status | Security Issues | | --------------------- | :-----------------------------------: | ----------------------------------- | -| **Software Composition Analysis** | ✅ Done |
1 Issues Found 1 High
| +| **Software Composition Analysis** | ✅ Done |
2 Issues Found 2 High
| | **Contextual Analysis** | ✅ Done | - | | **Static Application Security Testing (SAST)** | ✅ Done | Not Found | | **Secrets** | ✅ Done | - | @@ -28,6 +28,7 @@ | Severity | ID | Contextual Analysis | Direct Dependencies | Impacted Dependency | Fixed Versions | | :---------------------: | :-----------------------------------: | :-----------------------------------: | :-----------------------------------: | :-----------------------------------: | :-----------------------------------: | | ![high](https://raw.githubusercontent.com/jfrog/frogbot/master/resources/v2/applicableHighSeverity.png)
High | CVE-2022-29217 | Not Covered | pyjwt:1.7.1 | pyjwt 1.7.1 | [2.4.0] | +| ![high (not applicable)](https://raw.githubusercontent.com/jfrog/frogbot/master/resources/v2/notApplicableHigh.png)
High | CVE-2025-45768 | Not Applicable | pyjwt:1.7.1 | pyjwt 1.7.1 | - | @@ -35,6 +36,7 @@ ### 🔖 Details +
[ CVE-2022-29217 ] pyjwt 1.7.1 ### Vulnerability Details | | | @@ -67,17 +69,7 @@ For example, an application might have planned to validate an `EdDSA`-signed tok # Making a good jwt token that should work by signing it with the private key encoded_good = jwt.encode({"test": 1234}, priv_key_bytes, algorithm="EdDSA") ``` -An attacker in posession of the public key can generate an `HMAC`-signed token to confuse PyJWT - -```python -# Using HMAC with the public key to trick the receiver to think that the public key is a HMAC secret -encoded_bad = jwt.encode({"test": 1234}, pub_key_bytes, algorithm="HS256") -``` - -The following vulnerable `decode` call will accept BOTH of the above tokens as valid - -``` -decoded = jwt.decode(encoded_good, pub_key_bytes, -algorithms=jwt.algorithms.get_default_algorithms()) -``` +A... **Remediation:** ##### Development mitigations @@ -87,7 +79,20 @@ For example, replace the following call - `jwt.decode(encoded_jwt, pub_key_bytes, algorithms=jwt.algorithms.get_default_algorithms())` With - `jwt.decode(encoded_jwt, pub_key_bytes, algorithms=["ES256"])` +
+ +
[ CVE-2025-45768 ] pyjwt 1.7.1 + +### Vulnerability Details +| | | +| --------------------- | :-----------------------------------: | +| **Contextual Analysis:** | Not Applicable | +| **Direct Dependencies:** | pyjwt:1.7.1 | +| **Impacted Dependency:** | pyjwt:1.7.1 | +| **Fixed Versions:** | - | +| **CVSS V3:** | 7.0 | +pyjwt v2.10.1 was discovered to contain weak encryption. NOTE: this is disputed by the Supplier because the key length is chosen by the application that uses the library (admittedly, library users may benefit from a minimum value and a mechanism for opting in to strict enforcement).
--- diff --git a/testdata/scanpullrequest/expected_response_multi_dir.md b/testdata/scanpullrequest/expected_response_multi_dir.md index e83ab7d5d..197a8aafb 100644 --- a/testdata/scanpullrequest/expected_response_multi_dir.md +++ b/testdata/scanpullrequest/expected_response_multi_dir.md @@ -11,11 +11,11 @@ ## 📗 Scan Summary -- Frogbot scanned for vulnerabilities and found 2 issues +- Frogbot scanned for vulnerabilities and found 3 issues | Scan Category | Status | Security Issues | | --------------------- | :-----------------------------------: | ----------------------------------- | -| **Software Composition Analysis** | ✅ Done |
2 Issues Found 2 High
| +| **Software Composition Analysis** | ✅ Done |
3 Issues Found 3 High
| | **Contextual Analysis** | ✅ Done | - | | **Static Application Security Testing (SAST)** | ✅ Done | Not Found | | **Secrets** | ✅ Done | - | @@ -29,6 +29,7 @@ | :---------------------: | :-----------------------------------: | :-----------------------------------: | :-----------------------------------: | :-----------------------------------: | :-----------------------------------: | | ![high (not applicable)](https://raw.githubusercontent.com/jfrog/frogbot/master/resources/v2/notApplicableHigh.png)
High | CVE-2022-3517 | Not Applicable | minimatch:3.0.4 | minimatch 3.0.4 | [3.0.5] | | ![high](https://raw.githubusercontent.com/jfrog/frogbot/master/resources/v2/applicableHighSeverity.png)
High | CVE-2022-29217 | Not Covered | pyjwt:1.7.1 | pyjwt 1.7.1 | [2.4.0] | +| ![high (not applicable)](https://raw.githubusercontent.com/jfrog/frogbot/master/resources/v2/notApplicableHigh.png)
High | CVE-2025-45768 | Not Applicable | pyjwt:1.7.1 | pyjwt 1.7.1 | - | @@ -82,17 +83,7 @@ For example, an application might have planned to validate an `EdDSA`-signed tok # Making a good jwt token that should work by signing it with the private key encoded_good = jwt.encode({"test": 1234}, priv_key_bytes, algorithm="EdDSA") ``` -An attacker in posession of the public key can generate an `HMAC`-signed token to confuse PyJWT - -```python -# Using HMAC with the public key to trick the receiver to think that the public key is a HMAC secret -encoded_bad = jwt.encode({"test": 1234}, pub_key_bytes, algorithm="HS256") -``` - -The following vulnerable `decode` call will accept BOTH of the above tokens as valid - -``` -decoded = jwt.decode(encoded_good, pub_key_bytes, -algorithms=jwt.algorithms.get_default_algorithms()) -``` +A... **Remediation:** ##### Development mitigations @@ -104,6 +95,19 @@ With - `jwt.decode(encoded_jwt, pub_key_bytes, algorithms=["ES256"])`
+
[ CVE-2025-45768 ] pyjwt 1.7.1 + +### Vulnerability Details +| | | +| --------------------- | :-----------------------------------: | +| **Contextual Analysis:** | Not Applicable | +| **Direct Dependencies:** | pyjwt:1.7.1 | +| **Impacted Dependency:** | pyjwt:1.7.1 | +| **Fixed Versions:** | - | +| **CVSS V3:** | 7.0 | + +pyjwt v2.10.1 was discovered to contain weak encryption. NOTE: this is disputed by the Supplier because the key length is chosen by the application that uses the library (admittedly, library users may benefit from a minimum value and a mechanism for opting in to strict enforcement).
+ ---
diff --git a/utils/consts.go b/utils/consts.go index 6c483a843..78fbc7b2c 100644 --- a/utils/consts.go +++ b/utils/consts.go @@ -11,6 +11,9 @@ const ( // Errors errUnsupportedMultiRepo = "multi repository configuration isn't supported. Only one repository configuration is allowed" + // MaxConcurrentScanners represents the maximum number of threads for running JFrog CLI scanners concurrently + MaxConcurrentScanners = 5 + // VCS providers params GitHub vcsProvider = "github" GitLab vcsProvider = "gitlab" diff --git a/utils/scandetails.go b/utils/scandetails.go index 291410085..2ae9cc408 100644 --- a/utils/scandetails.go +++ b/utils/scandetails.go @@ -182,6 +182,7 @@ func (sc *ScanDetails) RunInstallAndAudit(workDirs ...string) (auditResults *res SetDiffMode(sc.diffScan). SetResultsToCompare(sc.ResultsToCompare). SetMultiScanId(sc.MultiScanId). + SetThreads(MaxConcurrentScanners). SetStartTime(sc.StartTime) return audit.RunAudit(auditParams)