From 87f7462c1661be363603bb7a0cad1a1c75ba697e Mon Sep 17 00:00:00 2001 From: sandhi Date: Mon, 16 Feb 2026 12:21:49 +0530 Subject: [PATCH 01/12] Fix blackduck sca scan getting skipped Signed-off-by: sandhi --- .github/workflows/ci-main-pull-request.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/ci-main-pull-request.yml b/.github/workflows/ci-main-pull-request.yml index 3b99931..afca061 100644 --- a/.github/workflows/ci-main-pull-request.yml +++ b/.github/workflows/ci-main-pull-request.yml @@ -1315,7 +1315,7 @@ jobs: generate-sbom: name: 'Generating SBOM' # Create software bill-of-materials (SBOM) using SPDX format - if: ${{ inputs.generate-sbom == true }} + if: ${{ inputs.generate-sbom }} uses: chef/common-github-actions/.github/workflows/sbom.yml@main needs: ci-build secrets: inherit From 6a089ff8db098e2f5a7fbb286edba4ca37f1463a Mon Sep 17 00:00:00 2001 From: sandhi Date: Tue, 17 Feb 2026 11:03:25 +0530 Subject: [PATCH 02/12] Fix polaris scan Signed-off-by: sandhi --- .github/workflows/ci-main-pull-request.yml | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/.github/workflows/ci-main-pull-request.yml b/.github/workflows/ci-main-pull-request.yml index afca061..f97c2b0 100644 --- a/.github/workflows/ci-main-pull-request.yml +++ b/.github/workflows/ci-main-pull-request.yml @@ -1008,6 +1008,11 @@ jobs: runs-on: ubuntu-latest needs: checkout # TODO: fix set-application-version steps: + - name: Checkout repository + uses: actions/checkout@v6 + with: + fetch-depth: 0 + - name: Starting Black Duck Polaris scan run: | echo "Starting Polaris SAST scan" From 2c287a2f560895a3107d51255302e0c7b9ff2349 Mon Sep 17 00:00:00 2001 From: sandhi Date: Tue, 17 Feb 2026 15:06:50 +0530 Subject: [PATCH 03/12] Fail trivy for high and critical Signed-off-by: sandhi --- .github/workflows/ci-main-pull-request.yml | 8 +++++- .github/workflows/trivy.yml | 29 ++++++++++++++-------- 2 files changed, 25 insertions(+), 12 deletions(-) diff --git a/.github/workflows/ci-main-pull-request.yml b/.github/workflows/ci-main-pull-request.yml index f97c2b0..5ea220c 100644 --- a/.github/workflows/ci-main-pull-request.yml +++ b/.github/workflows/ci-main-pull-request.yml @@ -101,6 +101,11 @@ on: required: false type: boolean default: true + trivy-fail-on-high-critical: + description: 'Fail pipeline if Trivy finds HIGH or CRITICAL vulnerabilities' + required: false + type: boolean + default: true build: description: 'CI Build (language-specific)' @@ -711,10 +716,11 @@ jobs: run-trivy: name: 'Trivy scan' if: ${{ inputs.perform-trivy-scan }} - uses: chef/common-github-actions/.github/workflows/trivy.yml@main + uses: chef/common-github-actions/.github/workflows/trivy.yml@sandhi/fix-blackduc-sca needs: checkout with: version: ${{ inputs.version }} + fail-on-high-critical: ${{ inputs.trivy-fail-on-high-critical }} # run-srcclr: # if: ${{ inputs.perform-srcclr-scan == true }} diff --git a/.github/workflows/trivy.yml b/.github/workflows/trivy.yml index 8749e2f..6463cd6 100644 --- a/.github/workflows/trivy.yml +++ b/.github/workflows/trivy.yml @@ -15,6 +15,11 @@ on: required: false type: string default: '1.0.0' + fail-on-high-critical: + description: 'Fail the build if HIGH or CRITICAL vulnerabilities are found' + required: false + type: boolean + default: true jobs: trivy: @@ -67,14 +72,16 @@ jobs: # name: trivy-report-${{ github.event.repository.name }}-${{ github.ref_name }}-${{ inputs.version }}-$(date +'%Y%m%d')-text path: trivy-report.txt retention-days: 30 - # - name: Fail build on High/Criticial Vulnerabilities - # uses: aquasecurity/trivy-action@master - # with: - # scan-type: "fs" - # format: table - # scan-ref: . - # severity: HIGH,CRITICAL - # ignore-unfixed: true - # exit-code: 1 - # # On a subsequent call to the action we know trivy is already installed so can skip this - # skip-setup-trivy: true \ No newline at end of file + + - name: Fail build on High/Critical Vulnerabilities + if: ${{ inputs.fail-on-high-critical }} + uses: aquasecurity/trivy-action@master + with: + scan-type: "fs" + format: table + scan-ref: . + severity: HIGH,CRITICAL + ignore-unfixed: true + exit-code: 1 + # On a subsequent call to the action we know trivy is already installed so can skip this + skip-setup-trivy: true \ No newline at end of file From 99d9a964f9e5de17da4c3b75077cf571ab3549ba Mon Sep 17 00:00:00 2001 From: sandhi Date: Tue, 17 Feb 2026 16:38:54 +0530 Subject: [PATCH 04/12] Fix polaris scan Signed-off-by: sandhi --- .github/workflows/ci-main-pull-request.yml | 7 +------ 1 file changed, 1 insertion(+), 6 deletions(-) diff --git a/.github/workflows/ci-main-pull-request.yml b/.github/workflows/ci-main-pull-request.yml index 5ea220c..416237f 100644 --- a/.github/workflows/ci-main-pull-request.yml +++ b/.github/workflows/ci-main-pull-request.yml @@ -1012,13 +1012,8 @@ jobs: # chef-vault at https://polaris.blackduck.com/portfolio/portfolios/8b7ad6f7-6dcb-49ec-bded-bfc4f190d4f8/portfolio-items/fe369baf-11d2-4989-bcb7-045577856dcc/projects/2460eabd-d033-48a1-a378-6cadd49be6d1/tests/sast?branchId=a6d2c02a-05f8-4557-bfa1-c40e9337ee5d if: ${{ inputs.perform-blackduck-polaris == true }} runs-on: ubuntu-latest - needs: checkout # TODO: fix set-application-version + needs: ci-build steps: - - name: Checkout repository - uses: actions/checkout@v6 - with: - fetch-depth: 0 - - name: Starting Black Duck Polaris scan run: | echo "Starting Polaris SAST scan" From d72d1282c7c9f1d423e7b2063e2d5f84126c5276 Mon Sep 17 00:00:00 2001 From: sandhi Date: Tue, 17 Feb 2026 16:55:54 +0530 Subject: [PATCH 05/12] Fix for build pipeline Signed-off-by: sandhi --- .github/workflows/ci-main-pull-request.yml | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/.github/workflows/ci-main-pull-request.yml b/.github/workflows/ci-main-pull-request.yml index 416237f..eed968a 100644 --- a/.github/workflows/ci-main-pull-request.yml +++ b/.github/workflows/ci-main-pull-request.yml @@ -749,6 +749,11 @@ jobs: uses: actions/checkout@v6 with: fetch-depth: 0 + - name: Configure git for private Go modules + if: inputs.language == 'go' + env: + GOPRIVATE: ${{ inputs.go-private-modules }} + run: git config --global url."https://${{ secrets.GH_TOKEN }}@github.com/".insteadOf "https://github.com/" - name: 'Go build' if: ${{ inputs.language == 'go' && env.GA_BUILD_PROFILE == 'cli' }} continue-on-error: true @@ -1012,7 +1017,7 @@ jobs: # chef-vault at https://polaris.blackduck.com/portfolio/portfolios/8b7ad6f7-6dcb-49ec-bded-bfc4f190d4f8/portfolio-items/fe369baf-11d2-4989-bcb7-045577856dcc/projects/2460eabd-d033-48a1-a378-6cadd49be6d1/tests/sast?branchId=a6d2c02a-05f8-4557-bfa1-c40e9337ee5d if: ${{ inputs.perform-blackduck-polaris == true }} runs-on: ubuntu-latest - needs: ci-build + needs: checkout steps: - name: Starting Black Duck Polaris scan run: | From eb4b9bcc10fe1849db19a916c05efd2faeb0194d Mon Sep 17 00:00:00 2001 From: sandhi Date: Tue, 17 Feb 2026 17:16:05 +0530 Subject: [PATCH 06/12] Fix for polaris on 360 Signed-off-by: sandhi --- .github/workflows/ci-main-pull-request.yml | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/.github/workflows/ci-main-pull-request.yml b/.github/workflows/ci-main-pull-request.yml index eed968a..a60f5d4 100644 --- a/.github/workflows/ci-main-pull-request.yml +++ b/.github/workflows/ci-main-pull-request.yml @@ -1017,8 +1017,13 @@ jobs: # chef-vault at https://polaris.blackduck.com/portfolio/portfolios/8b7ad6f7-6dcb-49ec-bded-bfc4f190d4f8/portfolio-items/fe369baf-11d2-4989-bcb7-045577856dcc/projects/2460eabd-d033-48a1-a378-6cadd49be6d1/tests/sast?branchId=a6d2c02a-05f8-4557-bfa1-c40e9337ee5d if: ${{ inputs.perform-blackduck-polaris == true }} runs-on: ubuntu-latest - needs: checkout + needs: ci-build steps: + - name: Checkout repository + uses: actions/checkout@v6 + with: + fetch-depth: 0 + - name: Starting Black Duck Polaris scan run: | echo "Starting Polaris SAST scan" From 3b5942473e1264199a9fed697e6ae7011c215758 Mon Sep 17 00:00:00 2001 From: sandhi Date: Wed, 18 Feb 2026 10:44:18 +0530 Subject: [PATCH 07/12] Removing extra code Signed-off-by: sandhi --- .github/workflows/ci-main-pull-request.yml | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/.github/workflows/ci-main-pull-request.yml b/.github/workflows/ci-main-pull-request.yml index a60f5d4..1af7820 100644 --- a/.github/workflows/ci-main-pull-request.yml +++ b/.github/workflows/ci-main-pull-request.yml @@ -1017,7 +1017,7 @@ jobs: # chef-vault at https://polaris.blackduck.com/portfolio/portfolios/8b7ad6f7-6dcb-49ec-bded-bfc4f190d4f8/portfolio-items/fe369baf-11d2-4989-bcb7-045577856dcc/projects/2460eabd-d033-48a1-a378-6cadd49be6d1/tests/sast?branchId=a6d2c02a-05f8-4557-bfa1-c40e9337ee5d if: ${{ inputs.perform-blackduck-polaris == true }} runs-on: ubuntu-latest - needs: ci-build + needs: checkout # TODO: fix set-application-version steps: - name: Checkout repository uses: actions/checkout@v6 @@ -1085,7 +1085,7 @@ jobs: # polaris_upload_sarif_report: true # Mark build status if policy violating issues are found # mark_build_status: 'success' - continue-on-error: true + continue-on-error: false package-binary: name: 'Creating packaged binaries' @@ -1331,9 +1331,9 @@ jobs: generate-sbom: name: 'Generating SBOM' # Create software bill-of-materials (SBOM) using SPDX format - if: ${{ inputs.generate-sbom }} + if: ${{ inputs.generate-sbom == true }} uses: chef/common-github-actions/.github/workflows/sbom.yml@main - needs: ci-build + needs: checkout # TODO: fix set-application-version secrets: inherit with: version: ${{ inputs.version }} From 68026c2b0b41280ac6950bd3e6ebf430ca58f8ab Mon Sep 17 00:00:00 2001 From: sandhi Date: Wed, 18 Feb 2026 14:47:48 +0530 Subject: [PATCH 08/12] Added Erlang support Signed-off-by: sandhi --- .github/workflows/ci-main-pull-request.yml | 51 +++++++++++++++++++++- .github/workflows/sbom.yml | 11 ++++- 2 files changed, 59 insertions(+), 3 deletions(-) diff --git a/.github/workflows/ci-main-pull-request.yml b/.github/workflows/ci-main-pull-request.yml index 1af7820..0262254 100644 --- a/.github/workflows/ci-main-pull-request.yml +++ b/.github/workflows/ci-main-pull-request.yml @@ -1023,6 +1023,55 @@ jobs: uses: actions/checkout@v6 with: fetch-depth: 0 + + - name: Configure git for private Go modules + env: + GOPRIVATE: ${{ inputs.go-private-modules }} + run: git config --global url."https://${{ secrets.GH_TOKEN }}@github.com/".insteadOf "https://github.com/" + + - name: Install build tools for Erlang + if: inputs.language == 'erlang' + run: | + sudo apt-get update + sudo apt-get install -y build-essential + + - name: Set up Erlang/OTP and rebar3 + if: inputs.language == 'erlang' + uses: erlef/setup-beam@v1 + with: + otp-version: '25.3.2.16' + rebar3-version: '3.22.0' + + - name: Set up Ruby + if: inputs.language == 'ruby' + uses: ruby/setup-ruby@v1 + with: + ruby-version: '3.0' + bundler-cache: false + + - name: Create bundle stub for Erlang SAST scan + if: inputs.language == 'erlang' + working-directory: ${{ github.workspace }} + run: | + # Polaris scans Erlang source code for SAST - Ruby gems not needed + # System ruby-dev from apt provides Ruby runtime (already installed) + # Create bundle stub to skip gem installation during Polaris scan + echo "Creating bundle stub to bypass Ruby gem installation" + + # Create no-op bundle script + mkdir -p "$HOME/.polaris-stubs" + cat > "$HOME/.polaris-stubs/bundle" << 'EOF' + #!/bin/bash + # Stub: skips gem installation during SAST scan + echo "[STUB] Skipping bundle $@ - not needed for Erlang SAST" + exit 0 + EOF + chmod +x "$HOME/.polaris-stubs/bundle" + + # Prepend to PATH so stub is found before any system bundler + echo "$HOME/.polaris-stubs" >> $GITHUB_PATH + + echo "Bundle stub created and added to PATH" - name: Starting Black Duck Polaris scan run: | @@ -1332,7 +1381,7 @@ jobs: name: 'Generating SBOM' # Create software bill-of-materials (SBOM) using SPDX format if: ${{ inputs.generate-sbom == true }} - uses: chef/common-github-actions/.github/workflows/sbom.yml@main + uses: chef/common-github-actions/.github/workflows/sbom.yml@sandhi/fix-blackduc-sca needs: checkout # TODO: fix set-application-version secrets: inherit with: diff --git a/.github/workflows/sbom.yml b/.github/workflows/sbom.yml index c5cab7f..efe7624 100644 --- a/.github/workflows/sbom.yml +++ b/.github/workflows/sbom.yml @@ -203,12 +203,19 @@ jobs: uses: actions/checkout@v6 - name: Set up Ruby and run bundle install - if: ${{ inputs.language == 'ruby' }} # only run for Ruby projects where we need to generate Gemfile.lock at runtime, inputs.run-bundle-install == true + if: inputs.language == 'ruby' uses: ruby/setup-ruby@v1 with: - ruby-version: '3.4' + ruby-version: '3.4.2' bundler-cache: true + - name: Set up Erlang/OTP and rebar3 + if: inputs.language == 'erlang' + uses: erlef/setup-beam@v1 + with: + otp-version: '25.3.2.16' + rebar3-version: '3.22.0' + - name: Configure git for private Go modules if : ${{ inputs.go-private-modules != '' }} env: From 4ee8d09ec579f4ffe9d68e9c38ad44dde2da1d81 Mon Sep 17 00:00:00 2001 From: sandhi Date: Thu, 19 Feb 2026 11:19:23 +0530 Subject: [PATCH 09/12] Fixes for ruby code Signed-off-by: sandhi --- .github/workflows/ci-main-pull-request.yml | 27 +++++++++++----------- .github/workflows/sbom.yml | 14 ++++++++--- .github/workflows/trivy.yml | 2 +- 3 files changed, 26 insertions(+), 17 deletions(-) diff --git a/.github/workflows/ci-main-pull-request.yml b/.github/workflows/ci-main-pull-request.yml index 0262254..886cd08 100644 --- a/.github/workflows/ci-main-pull-request.yml +++ b/.github/workflows/ci-main-pull-request.yml @@ -105,7 +105,7 @@ on: description: 'Fail pipeline if Trivy finds HIGH or CRITICAL vulnerabilities' required: false type: boolean - default: true + default: false build: description: 'CI Build (language-specific)' @@ -169,15 +169,15 @@ on: type: string polaris-coverity-clean-command: # NEW IN 1.0.7 - description: 'Coverity clean command, typically done before build stage by language or here as param 1-liner like "mvn clean"' + description: 'Coverity clean command, typically done before build stage by language or here as param 1-liner like "mvn clean". Leave empty for buildless analysis (Ruby, Python, etc.)' required: false - default: 'go clean' + default: '' type: string polaris-coverity-build-command: # NEW IN 1.0.7 - description: 'Coverity build command, typically done in build stage by language or here as param 1-liner like "mvn clean install"' + description: 'Coverity build command, typically done in build stage by language or here as param 1-liner like "mvn clean install". Leave empty for buildless analysis (Ruby, Python, etc.)' required: false - default: 'go build' + default: '' type: string polaris-coverity-args: # NEW IN 1.0.7 @@ -374,6 +374,11 @@ on: required: false type: boolean default: false + ruby-app-directory: + description: 'Subdirectory containing Ruby Gemfile (e.g., "src/supermarket" for repos with non-root Gemfile location). Leave empty if Gemfile is in root.' + required: false + type: string + default: '' udf1: description: 'User defined flag 1' @@ -716,7 +721,7 @@ jobs: run-trivy: name: 'Trivy scan' if: ${{ inputs.perform-trivy-scan }} - uses: chef/common-github-actions/.github/workflows/trivy.yml@sandhi/fix-blackduc-sca + uses: chef/common-github-actions/.github/workflows/trivy.yml@main needs: checkout with: version: ${{ inputs.version }} @@ -1024,11 +1029,6 @@ jobs: with: fetch-depth: 0 - - name: Configure git for private Go modules - env: - GOPRIVATE: ${{ inputs.go-private-modules }} - run: git config --global url."https://${{ secrets.GH_TOKEN }}@github.com/".insteadOf "https://github.com/" - - name: Install build tools for Erlang if: inputs.language == 'erlang' run: | @@ -1046,7 +1046,7 @@ jobs: if: inputs.language == 'ruby' uses: ruby/setup-ruby@v1 with: - ruby-version: '3.0' + ruby-version: '3.4' bundler-cache: false - name: Create bundle stub for Erlang SAST scan @@ -1381,7 +1381,7 @@ jobs: name: 'Generating SBOM' # Create software bill-of-materials (SBOM) using SPDX format if: ${{ inputs.generate-sbom == true }} - uses: chef/common-github-actions/.github/workflows/sbom.yml@sandhi/fix-blackduc-sca + uses: chef/common-github-actions/.github/workflows/sbom.yml@main needs: checkout # TODO: fix set-application-version secrets: inherit with: @@ -1397,6 +1397,7 @@ jobs: blackduck-force-low-accuracy-mode: ${{ inputs.blackduck-force-low-accuracy-mode }} run-bundle-install: ${{ inputs.run-bundle-install }} # Passed to sbom.yml to generate Gemfile.lock at runtime language: ${{ inputs.language }} + ruby-app-directory: ${{ inputs.ruby-app-directory }} quality-dashboard: name: 'Reporting to quality dashboard' diff --git a/.github/workflows/sbom.yml b/.github/workflows/sbom.yml index efe7624..096916d 100644 --- a/.github/workflows/sbom.yml +++ b/.github/workflows/sbom.yml @@ -77,6 +77,11 @@ on: required: false type: string default: 'ruby' + ruby-app-directory: + description: 'Subdirectory containing Ruby Gemfile (e.g., "src/supermarket" for repos with non-root Gemfile location). Leave empty if Gemfile is in root.' + required: false + type: string + default: '' env: # Set the default SBOM filename prefix @@ -207,7 +212,8 @@ jobs: uses: ruby/setup-ruby@v1 with: ruby-version: '3.4.2' - bundler-cache: true + bundler-cache: false + working-directory: ${{ inputs.ruby-app-directory != '' && inputs.ruby-app-directory || '.' }} - name: Set up Erlang/OTP and rebar3 if: inputs.language == 'erlang' @@ -225,6 +231,7 @@ jobs: - name: generate Gemfile.lock if needed for Ruby projects if: ${{ inputs.run-bundle-install == true && inputs.language == 'ruby' }} continue-on-error: true + working-directory: ${{ inputs.ruby-app-directory != '' && inputs.ruby-app-directory || '.' }} run: | if [ ! -f Gemfile.lock ]; then bundle install @@ -235,7 +242,7 @@ jobs: uses: actions/upload-artifact@v4 continue-on-error: true with: - path: Gemfile.lock + path: ${{ inputs.ruby-app-directory != '' && format('{0}/Gemfile.lock', inputs.ruby-app-directory) || 'Gemfile.lock' }} name: ${{ github.event.repository.name }}-Gemfile-lock.txt - name: BlackDuck SCA scan @@ -249,7 +256,8 @@ jobs: with: blackducksca_url: ${{ secrets.BLACKDUCK_SBOM_URL }} # BLACKDUCK_URL, should be https://progresssoftware.app.blackduck.com/ blackducksca_token: ${{ secrets.BLACKDUCK_SCA_TOKEN }} # was BLACKDUCK_API_KEY - detect_args: ${{ inputs.blackduck-force-low-accuracy-mode == true && '--detect.excluded.detector.types=PIP --detect.accuracy.required=NONE' || '--detect.excluded.detector.types=PIP' }} + blackducksca_scan_full: true # Force INTELLIGENT scan mode for all branches (uploads results to server) + detect_args: ${{ inputs.ruby-app-directory != '' && format('{0} --detect.source.path={1}', inputs.blackduck-force-low-accuracy-mode == true && '--detect.excluded.detector.types=PIP --detect.accuracy.required=NONE' || '--detect.excluded.detector.types=PIP', inputs.ruby-app-directory) || (inputs.blackduck-force-low-accuracy-mode == true && '--detect.excluded.detector.types=PIP --detect.accuracy.required=NONE' || '--detect.excluded.detector.types=PIP') }} # blackducksca_scan_failure_severities: 'BLOCKER,CRITICAL' # ignore python per https://documentation.blackduck.com/bundle/detect/page/packagemgrs/python.html diff --git a/.github/workflows/trivy.yml b/.github/workflows/trivy.yml index 6463cd6..30d34a1 100644 --- a/.github/workflows/trivy.yml +++ b/.github/workflows/trivy.yml @@ -19,7 +19,7 @@ on: description: 'Fail the build if HIGH or CRITICAL vulnerabilities are found' required: false type: boolean - default: true + default: false jobs: trivy: From f9b16b79935218649147c647217b7dff46a419d4 Mon Sep 17 00:00:00 2001 From: sandhi Date: Tue, 24 Feb 2026 12:53:32 +0530 Subject: [PATCH 10/12] Fail on critical higj Signed-off-by: sandhi --- .github/workflows/ci-main-pull-request.yml | 224 ++++++++++++++------- .github/workflows/sbom.yml | 113 ++++++++++- .github/workflows/trivy.yml | 29 ++- .github/workflows/trufflehog.yml | 16 +- 4 files changed, 302 insertions(+), 80 deletions(-) diff --git a/.github/workflows/ci-main-pull-request.yml b/.github/workflows/ci-main-pull-request.yml index 886cd08..9d12336 100644 --- a/.github/workflows/ci-main-pull-request.yml +++ b/.github/workflows/ci-main-pull-request.yml @@ -96,17 +96,26 @@ on: required: false type: boolean default: true + fail-trufflehog-on-secrets-found: + description: 'Fail the pipeline if Trufflehog finds verified secrets' + required: false + type: boolean + default: true perform-trivy-scan: description: 'Perform Trivy scan' required: false type: boolean default: true - trivy-fail-on-high-critical: - description: 'Fail pipeline if Trivy finds HIGH or CRITICAL vulnerabilities' + trivy-fail-on-high: + description: 'Fail pipeline if Trivy finds HIGH vulnerabilities' + required: false + type: boolean + default: false + trivy-fail-on-critical: + description: 'Fail pipeline if Trivy finds CRITICAL vulnerabilities' required: false type: boolean default: false - build: description: 'CI Build (language-specific)' required: false @@ -209,6 +218,16 @@ on: required: false default: true type: boolean + polaris-fail-on-high: + description: 'Fail the pipeline if Polaris SAST scan finds HIGH vulnerabilities' + required: false + type: boolean + default: false + polaris-fail-on-critical: + description: 'Fail the pipeline if Polaris SAST scan finds CRITICAL vulnerabilities' + required: false + type: boolean + default: false perform-sonarqube-scan: description: 'Perform basic SonarQube scan' @@ -379,6 +398,21 @@ on: required: false type: string default: '' + blackduck-fail-on-blocker: + description: 'Fail the pipeline if BlackDuck SCA scan finds BLOCKER vulnerabilities' + required: false + type: boolean + default: false + blackduck-fail-on-critical: + description: 'Fail the pipeline if BlackDuck SCA scan finds CRITICAL vulnerabilities' + required: false + type: boolean + default: false + blackduck-fail-on-major: + description: 'Fail the pipeline if BlackDuck SCA scan finds MAJOR vulnerabilities' + required: false + type: boolean + default: false udf1: description: 'User defined flag 1' @@ -715,17 +749,20 @@ jobs: run-trufflehog: name: 'Trufflehog scan' if: ${{ inputs.perform-trufflehog-scan }} - uses: chef/common-github-actions/.github/workflows/trufflehog.yml@main + uses: chef/common-github-actions/.github/workflows/trufflehog.yml@sandhi/fix-blackduc-sca needs: checkout + with: + fail-trufflehog-on-secrets-found: ${{ inputs.fail-trufflehog-on-secrets-found }} run-trivy: name: 'Trivy scan' if: ${{ inputs.perform-trivy-scan }} - uses: chef/common-github-actions/.github/workflows/trivy.yml@main + uses: chef/common-github-actions/.github/workflows/trivy.yml@sandhi/fix-blackduc-sca needs: checkout with: version: ${{ inputs.version }} - fail-on-high-critical: ${{ inputs.trivy-fail-on-high-critical }} + trivy-fail-on-high: ${{ inputs.trivy-fail-on-high }} + trivy-fail-on-critical: ${{ inputs.trivy-fail-on-critical }} # run-srcclr: # if: ${{ inputs.perform-srcclr-scan == true }} @@ -939,73 +976,73 @@ jobs: # # A file, directory or wildcard pattern that describes what to upload # path: test/unittest/coverage.out - Sonar-public-repo: - name: 'PUBLIC Sonar SAST scan' - needs: ci-build - if: ${{ inputs.perform-sonarqube-scan == true && success() && inputs.visibility == 'public'}} - uses: chef/common-github-actions/.github/workflows/sonarqube-public-repo.yml@main - secrets: inherit - permissions: - id-token: write - contents: read - with: - perform-build: ${{ inputs.build }} # was ${{ inputs.perform-sonar-build }} - build-profile: ${{ inputs.build-profile }} - language: ${{ inputs.language }} - report-unit-test-coverage: ${{ inputs.report-unit-test-coverage }} - report-to-atlassian-dashboard: ${{ inputs.report-to-atlassian-dashboard }} - quality-product-name: ${{ inputs.quality-product-name }} - quality-sonar-app-name: ${{ inputs.quality-sonar-app-name }} - quality-testing-type: ${{ inputs.quality-testing-type }} - quality-service-name: ${{ inputs.quality-service-name }} - quality-junit-report: ${{ inputs.quality-junit-report }} - visibility: ${{ inputs.visibility }} - go-private-modules: ${{ inputs.go-private-modules }} - udf1: ${{ inputs.udf1 }} - udf2: ${{ inputs.udf2 }} - udf3: ${{ inputs.udf3 }} + # Sonar-public-repo: + # name: 'PUBLIC Sonar SAST scan' + # needs: ci-build + # if: ${{ inputs.perform-sonarqube-scan == true && success() && inputs.visibility == 'public'}} + # uses: chef/common-github-actions/.github/workflows/sonarqube-public-repo.yml@main + # secrets: inherit + # permissions: + # id-token: write + # contents: read + # with: + # perform-build: ${{ inputs.build }} # was ${{ inputs.perform-sonar-build }} + # build-profile: ${{ inputs.build-profile }} + # language: ${{ inputs.language }} + # report-unit-test-coverage: ${{ inputs.report-unit-test-coverage }} + # report-to-atlassian-dashboard: ${{ inputs.report-to-atlassian-dashboard }} + # quality-product-name: ${{ inputs.quality-product-name }} + # quality-sonar-app-name: ${{ inputs.quality-sonar-app-name }} + # quality-testing-type: ${{ inputs.quality-testing-type }} + # quality-service-name: ${{ inputs.quality-service-name }} + # quality-junit-report: ${{ inputs.quality-junit-report }} + # visibility: ${{ inputs.visibility }} + # go-private-modules: ${{ inputs.go-private-modules }} + # udf1: ${{ inputs.udf1 }} + # udf2: ${{ inputs.udf2 }} + # udf3: ${{ inputs.udf3 }} - Sonar-private-repo: - name: 'PRIVATE Sonar scan (inline)' - if: ${{ inputs.perform-sonarqube-scan == true && success() && inputs.visibility == 'private'}} - needs: ci-build - # was uses: chef/common-github-actions/.github/workflows/sonarqube-private-repo.yml@main - runs-on: ubuntu-latest - steps: - - name: SonarQube Scan - if: ${{ inputs.visibility == 'private' }} - uses: sonarsource/sonarqube-scan-action@v5.3.1 - continue-on-error: true - env: - SONAR_TOKEN: ${{ secrets.SONAR_TOKEN }} - SONAR_HOST_URL: ${{ secrets.SONAR_HOST_URL }} + # Sonar-private-repo: + # name: 'PRIVATE Sonar scan (inline)' + # if: ${{ inputs.perform-sonarqube-scan == true && success() && inputs.visibility == 'private'}} + # needs: ci-build + # # was uses: chef/common-github-actions/.github/workflows/sonarqube-private-repo.yml@main + # runs-on: ubuntu-latest + # steps: + # - name: SonarQube Scan + # if: ${{ inputs.visibility == 'private' }} + # uses: sonarsource/sonarqube-scan-action@v5.3.1 + # continue-on-error: true + # env: + # SONAR_TOKEN: ${{ secrets.SONAR_TOKEN }} + # SONAR_HOST_URL: ${{ secrets.SONAR_HOST_URL }} - Sonar-internal-repo: - name: 'INTERNAL Sonar scan' - if: ${{ inputs.perform-sonarqube-scan == true && inputs.visibility == 'internal'}} - # was if: ${{ inputs.perform-sonarqube-scan == true && success() && inputs.visibility == 'internal'}} - needs: ci-build - uses: chef/common-github-actions/.github/workflows/sonarqube-internal-repo.yml@main - secrets: inherit - permissions: - id-token: write - contents: read - with: - perform-build: ${{ inputs.build }} # was ${{ inputs.perform-sonar-build }} - build-profile: ${{ inputs.build-profile }} - language: ${{ inputs.language }} - report-unit-test-coverage: ${{ inputs.report-unit-test-coverage }} - report-to-atlassian-dashboard: ${{ inputs.report-to-atlassian-dashboard }} - quality-product-name: ${{ inputs.quality-product-name }} - quality-sonar-app-name: ${{ inputs.quality-sonar-app-name }} - quality-testing-type: ${{ inputs.quality-testing-type }} - quality-service-name: ${{ inputs.quality-service-name }} - quality-junit-report: ${{ inputs.quality-junit-report }} - visibility: ${{ inputs.visibility }} - go-private-modules: ${{ inputs.go-private-modules }} - udf1: ${{ inputs.udf1 }} - udf2: ${{ inputs.udf2 }} - udf3: ${{ inputs.udf3 }} + # Sonar-internal-repo: + # name: 'INTERNAL Sonar scan' + # if: ${{ inputs.perform-sonarqube-scan == true && inputs.visibility == 'internal'}} + # # was if: ${{ inputs.perform-sonarqube-scan == true && success() && inputs.visibility == 'internal'}} + # needs: ci-build + # uses: chef/common-github-actions/.github/workflows/sonarqube-internal-repo.yml@main + # secrets: inherit + # permissions: + # id-token: write + # contents: read + # with: + # perform-build: ${{ inputs.build }} # was ${{ inputs.perform-sonar-build }} + # build-profile: ${{ inputs.build-profile }} + # language: ${{ inputs.language }} + # report-unit-test-coverage: ${{ inputs.report-unit-test-coverage }} + # report-to-atlassian-dashboard: ${{ inputs.report-to-atlassian-dashboard }} + # quality-product-name: ${{ inputs.quality-product-name }} + # quality-sonar-app-name: ${{ inputs.quality-sonar-app-name }} + # quality-testing-type: ${{ inputs.quality-testing-type }} + # quality-service-name: ${{ inputs.quality-service-name }} + # quality-junit-report: ${{ inputs.quality-junit-report }} + # visibility: ${{ inputs.visibility }} + # go-private-modules: ${{ inputs.go-private-modules }} + # udf1: ${{ inputs.udf1 }} + # udf2: ${{ inputs.udf2 }} + # udf3: ${{ inputs.udf3 }} BlackDuck-Polaris-SAST: # branding: applied at action.yml level, not workflow, see https://docs.github.com/en/actions/reference/workflows-and-actions/metadata-syntax#branding @@ -1093,6 +1130,7 @@ jobs: fi - name: BlackDuck Polaris scan + id: polaris-scan uses: blackduck-inc/black-duck-security-scan@v2 # copied from uses: prgs-community/githubactions-securityscans/polaris@v0.5 in https://github.com/prgs-community/githubactions-securityscans/blob/main/polaris/README.md # uses: blackduck-inc/black-duck-security-scan@805cbd09e806b01907bbea0f990723c2bb85abe9 # 2.0.0 - Jan's version @@ -1135,6 +1173,45 @@ jobs: # Mark build status if policy violating issues are found # mark_build_status: 'success' continue-on-error: false + + - name: Check Polaris scan results and fail on HIGH or CRITICAL vulnerabilities + if: ${{ inputs.polaris-fail-on-high == true || inputs.polaris-fail-on-critical == true }} + run: | + echo "Checking Polaris SAST scan results..." + echo "Enforcement policy: HIGH=${{ inputs.polaris-fail-on-high }}, CRITICAL=${{ inputs.polaris-fail-on-critical }}" + + # Parse bridge.log for vulnerability counts + BRIDGE_LOG=".bridge/bridge.log" + + if [ ! -f "$BRIDGE_LOG" ]; then + echo "⚠️ Bridge log not found - failing as precaution" + exit 1 + fi + + # Extract vulnerability counts from log + HIGH_COUNT=$(grep -oP '"high":\s*\K\d+' "$BRIDGE_LOG" | tail -1 || echo 0) + CRITICAL_COUNT=$(grep -oP '"critical":\s*\K\d+' "$BRIDGE_LOG" | tail -1 || echo 0) + + echo "Found HIGH: $HIGH_COUNT, CRITICAL: $CRITICAL_COUNT" + + # Check for policy violations + SHOULD_FAIL=false + + if [ "${{ inputs.polaris-fail-on-critical }}" == "true" ] && [ "$CRITICAL_COUNT" -gt 0 ]; then + echo "❌ Found $CRITICAL_COUNT CRITICAL vulnerabilities (policy violation)" + SHOULD_FAIL=true + fi + + if [ "${{ inputs.polaris-fail-on-high }}" == "true" ] && [ "$HIGH_COUNT" -gt 0 ]; then + echo "❌ Found $HIGH_COUNT HIGH vulnerabilities (policy violation)" + SHOULD_FAIL=true + fi + + if [ "$SHOULD_FAIL" == "true" ]; then + exit 1 + else + echo "✅ No policy-violating vulnerabilities found" + fi package-binary: name: 'Creating packaged binaries' @@ -1381,7 +1458,7 @@ jobs: name: 'Generating SBOM' # Create software bill-of-materials (SBOM) using SPDX format if: ${{ inputs.generate-sbom == true }} - uses: chef/common-github-actions/.github/workflows/sbom.yml@main + uses: chef/common-github-actions/.github/workflows/sbom.yml@sandhi/fix-blackduc-sca needs: checkout # TODO: fix set-application-version secrets: inherit with: @@ -1398,6 +1475,9 @@ jobs: run-bundle-install: ${{ inputs.run-bundle-install }} # Passed to sbom.yml to generate Gemfile.lock at runtime language: ${{ inputs.language }} ruby-app-directory: ${{ inputs.ruby-app-directory }} + blackduck-fail-on-blocker: ${{ inputs.blackduck-fail-on-blocker }} + blackduck-fail-on-critical: ${{ inputs.blackduck-fail-on-critical }} + blackduck-fail-on-major: ${{ inputs.blackduck-fail-on-major }} quality-dashboard: name: 'Reporting to quality dashboard' diff --git a/.github/workflows/sbom.yml b/.github/workflows/sbom.yml index 096916d..432be9f 100644 --- a/.github/workflows/sbom.yml +++ b/.github/workflows/sbom.yml @@ -82,6 +82,21 @@ on: required: false type: string default: '' + blackduck-fail-on-blocker: + description: 'Fail the pipeline if BlackDuck SCA scan finds BLOCKER vulnerabilities' + required: false + type: boolean + default: false + blackduck-fail-on-critical: + description: 'Fail the pipeline if BlackDuck SCA scan finds CRITICAL vulnerabilities' + required: false + type: boolean + default: false + blackduck-fail-on-major: + description: 'Fail the pipeline if BlackDuck SCA scan finds MAJOR vulnerabilities' + required: false + type: boolean + default: false env: # Set the default SBOM filename prefix @@ -245,9 +260,57 @@ jobs: path: ${{ inputs.ruby-app-directory != '' && format('{0}/Gemfile.lock', inputs.ruby-app-directory) || 'Gemfile.lock' }} name: ${{ github.event.repository.name }}-Gemfile-lock.txt + - name: Construct BlackDuck detect arguments + id: detect-args + run: | + # Start with base arguments (always exclude PIP detector) + DETECT_ARGS="--detect.excluded.detector.types=PIP" + + # Add low accuracy mode if requested + if [[ "${{ inputs.blackduck-force-low-accuracy-mode }}" == "true" ]]; then + DETECT_ARGS="${DETECT_ARGS} --detect.accuracy.required=NONE" + fi + + # Add source path if ruby-app-directory is specified + if [[ -n "${{ inputs.ruby-app-directory }}" ]]; then + DETECT_ARGS="${DETECT_ARGS} --detect.source.path=${{ inputs.ruby-app-directory }}" + fi + + echo "DETECT_ARGS=${DETECT_ARGS}" >> $GITHUB_ENV + echo "Constructed detect_args: ${DETECT_ARGS}" + + - name: Construct BlackDuck failure severities + id: failure-severities + run: | + SEVERITIES="" + + if [[ "${{ inputs.blackduck-fail-on-blocker }}" == "true" ]]; then + SEVERITIES="BLOCKER" + fi + + if [[ "${{ inputs.blackduck-fail-on-critical }}" == "true" ]]; then + if [[ -n "$SEVERITIES" ]]; then + SEVERITIES="${SEVERITIES},CRITICAL" + else + SEVERITIES="CRITICAL" + fi + fi + + if [[ "${{ inputs.blackduck-fail-on-major }}" == "true" ]]; then + if [[ -n "$SEVERITIES" ]]; then + SEVERITIES="${SEVERITIES},MAJOR" + else + SEVERITIES="MAJOR" + fi + fi + + echo "FAILURE_SEVERITIES=${SEVERITIES}" >> $GITHUB_ENV + echo "Enforcement policy: ${SEVERITIES}" + - name: BlackDuck SCA scan + id: blackduck-scan uses: blackduck-inc/black-duck-security-scan@v2.1.1 - continue-on-error: true # Allow pipeline to continue even with policy violations + continue-on-error: false # Allow pipeline to continue even with policy violations env: GOPRIVATE: ${{ inputs.go-private-modules }} DETECT_PROJECT_GROUP_NAME: ${{ inputs.blackduck-project-group-name}} #'Chef-Agents' # , Chef, Chef-Agents, Chef-Automate, Chef-Chef360, Chef-Habitat, Chef-Infrastructure-Server, Chef-Shared-Services @@ -256,11 +319,55 @@ jobs: with: blackducksca_url: ${{ secrets.BLACKDUCK_SBOM_URL }} # BLACKDUCK_URL, should be https://progresssoftware.app.blackduck.com/ blackducksca_token: ${{ secrets.BLACKDUCK_SCA_TOKEN }} # was BLACKDUCK_API_KEY + blackducksca_scan_failure_severities: ${{ env.FAILURE_SEVERITIES }} blackducksca_scan_full: true # Force INTELLIGENT scan mode for all branches (uploads results to server) - detect_args: ${{ inputs.ruby-app-directory != '' && format('{0} --detect.source.path={1}', inputs.blackduck-force-low-accuracy-mode == true && '--detect.excluded.detector.types=PIP --detect.accuracy.required=NONE' || '--detect.excluded.detector.types=PIP', inputs.ruby-app-directory) || (inputs.blackduck-force-low-accuracy-mode == true && '--detect.excluded.detector.types=PIP --detect.accuracy.required=NONE' || '--detect.excluded.detector.types=PIP') }} - # blackducksca_scan_failure_severities: 'BLOCKER,CRITICAL' + detect_args: ${{ env.DETECT_ARGS }} # ignore python per https://documentation.blackduck.com/bundle/detect/page/packagemgrs/python.html + - name: Check BlackDuck SCA results and report violations + if: ${{ always() && (inputs.blackduck-fail-on-blocker == true || inputs.blackduck-fail-on-critical == true || inputs.blackduck-fail-on-major == true) }} + run: | + BRIDGE_LOG=".bridge/bridge.log" + + if [ ! -f "$BRIDGE_LOG" ]; then + echo "⚠️ Bridge log not found" + exit 0 + fi + + SEVERITY_LINE=$(grep "Policy Severity counts:" "$BRIDGE_LOG" || true) + + if [ -z "$SEVERITY_LINE" ]; then + echo "⚠️ Policy Severity counts line not found in bridge.log" + exit 0 + fi + + BLOCKER_COUNT=$(echo "$SEVERITY_LINE" | grep -oE '[0-9]+ match(es)? ha(s|ve) a severity level of BLOCKER' | grep -oE '^[0-9]+' || echo "0") + CRITICAL_COUNT=$(echo "$SEVERITY_LINE" | grep -oE '[0-9]+ match(es)? ha(s|ve) a severity level of CRITICAL' | grep -oE '^[0-9]+' || echo "0") + MAJOR_COUNT=$(echo "$SEVERITY_LINE" | grep -oE '[0-9]+ match(es)? ha(s|ve) a severity level of MAJOR' | grep -oE '^[0-9]+' || echo "0") + + echo "" + echo "============================================" + echo "BlackDuck SCA Policy Violation Summary" + echo "============================================" + echo "BLOCKER violations: $BLOCKER_COUNT" + echo "CRITICAL violations: $CRITICAL_COUNT" + echo "MAJOR violations: $MAJOR_COUNT" + echo "============================================" + + VIOLATIONS="" + [ "${{ inputs.blackduck-fail-on-blocker }}" == "true" ] && [ "$BLOCKER_COUNT" -gt 0 ] && VIOLATIONS="${VIOLATIONS}$BLOCKER_COUNT BLOCKER, " + [ "${{ inputs.blackduck-fail-on-critical }}" == "true" ] && [ "$CRITICAL_COUNT" -gt 0 ] && VIOLATIONS="${VIOLATIONS}$CRITICAL_COUNT CRITICAL, " + [ "${{ inputs.blackduck-fail-on-major }}" == "true" ] && [ "$MAJOR_COUNT" -gt 0 ] && VIOLATIONS="${VIOLATIONS}$MAJOR_COUNT MAJOR, " + + if [ -n "$VIOLATIONS" ]; then + echo "" + echo "❌ Vulnerabilities Found: ${VIOLATIONS%, }" + exit 1 + else + echo "" + echo "✅ No policy-violating vulnerabilities found" + fi + # original from https://github.com/progress-platform-services/common-github-actions/blob/main/.github/workflows/examples/ci-all-sbom-main.yml generate-msft-sbom: name: Generate MSFT SBOM diff --git a/.github/workflows/trivy.yml b/.github/workflows/trivy.yml index 30d34a1..ef96ade 100644 --- a/.github/workflows/trivy.yml +++ b/.github/workflows/trivy.yml @@ -15,8 +15,13 @@ on: required: false type: string default: '1.0.0' - fail-on-high-critical: - description: 'Fail the build if HIGH or CRITICAL vulnerabilities are found' + trivy-fail-on-high: + description: 'Fail the build if HIGH vulnerabilities are found' + required: false + type: boolean + default: false + trivy-fail-on-critical: + description: 'Fail the build if CRITICAL vulnerabilities are found' required: false type: boolean default: false @@ -73,14 +78,30 @@ jobs: path: trivy-report.txt retention-days: 30 + - name: Construct Trivy failure severities + id: failure-severities + run: | + SEVERITIES="" + + if [[ "${{ inputs.trivy-fail-on-high }}" == "true" ]]; then + SEVERITIES="HIGH" + fi + + if [[ "${{ inputs.trivy-fail-on-critical }}" == "true" ]]; then + SEVERITIES="${SEVERITIES},CRITICAL" + fi + + echo "FAILURE_SEVERITIES=${SEVERITIES}" >> $GITHUB_ENV + echo "Enforcement policy: ${SEVERITIES}" + - name: Fail build on High/Critical Vulnerabilities - if: ${{ inputs.fail-on-high-critical }} + if: ${{ inputs.trivy-fail-on-high || inputs.trivy-fail-on-critical }} uses: aquasecurity/trivy-action@master with: scan-type: "fs" format: table scan-ref: . - severity: HIGH,CRITICAL + severity: ${{ env.FAILURE_SEVERITIES }} ignore-unfixed: true exit-code: 1 # On a subsequent call to the action we know trivy is already installed so can skip this diff --git a/.github/workflows/trufflehog.yml b/.github/workflows/trufflehog.yml index c816344..ff201d4 100644 --- a/.github/workflows/trufflehog.yml +++ b/.github/workflows/trufflehog.yml @@ -4,6 +4,12 @@ name: Trufflehog secret scan on: workflow_call: + inputs: + fail-trufflehog-on-secrets-found: + description: 'Fail the pipeline if Trufflehog finds verified secrets' + required: false + type: boolean + default: true jobs: Trufflehog: @@ -14,11 +20,19 @@ jobs: with: fetch-depth: 0 + - name: Construct Trufflehog arguments + id: trufflehog-args + run: | + EXTRA_ARGS="--only-verified" + + echo "EXTRA_ARGS=${EXTRA_ARGS}" >> $GITHUB_ENV + echo "Trufflehog enforcement: fail-on-secrets=${{ inputs.fail-trufflehog-on-secrets-found }}" + - name: TruffleHog secret scan uses: trufflesecurity/trufflehog@main with: path: ./ - extra_args: --only-verified + extra_args: ${{ env.EXTRA_ARGS }} continue-on-error: false # --only-verified --fail --github-actions --results=verified,unknown --branch dev # TODO: use the GH_TOKEN --org=progress --token=ghp_xxxxx From ca2e00fa026252088dd12138335216c24fcf1860 Mon Sep 17 00:00:00 2001 From: sandhi Date: Tue, 24 Feb 2026 15:31:13 +0530 Subject: [PATCH 11/12] Grype support Signed-off-by: sandhi --- .github/workflows/ci-main-pull-request.yml | 105 ++++++++++++++++ .github/workflows/grype.yml | 136 +++++++++++++++++++++ .github/workflows/trufflehog.yml | 32 +++-- 3 files changed, 264 insertions(+), 9 deletions(-) create mode 100644 .github/workflows/grype.yml diff --git a/.github/workflows/ci-main-pull-request.yml b/.github/workflows/ci-main-pull-request.yml index 9d12336..b393a48 100644 --- a/.github/workflows/ci-main-pull-request.yml +++ b/.github/workflows/ci-main-pull-request.yml @@ -116,6 +116,21 @@ on: required: false type: boolean default: false + perform-grype-scan: + description: 'Perform Grype scan on source code' + required: false + type: boolean + default: false + grype-fail-on-high: + description: 'Fail pipeline if Grype finds HIGH vulnerabilities' + required: false + type: boolean + default: false + grype-fail-on-critical: + description: 'Fail pipeline if Grype finds CRITICAL vulnerabilities' + required: false + type: boolean + default: false build: description: 'CI Build (language-specific)' required: false @@ -763,6 +778,93 @@ jobs: version: ${{ inputs.version }} trivy-fail-on-high: ${{ inputs.trivy-fail-on-high }} trivy-fail-on-critical: ${{ inputs.trivy-fail-on-critical }} + + run-grype: + name: 'Grype scan' + if: ${{ inputs.perform-grype-scan }} + runs-on: ubuntu-latest + needs: checkout # TODO: fix set-application-version + steps: + - name: Checkout repository + uses: actions/checkout@v6 + with: + fetch-depth: 0 + + - name: Determine severity threshold + id: severity + run: | + if [ "${{ inputs.grype-fail-on-high }}" == "true" ]; then + echo "level=high" >> $GITHUB_OUTPUT + elif [ "${{ inputs.grype-fail-on-critical }}" == "true" ]; then + echo "level=critical" >> $GITHUB_OUTPUT + else + echo "level=none" >> $GITHUB_OUTPUT + fi + + - name: Run Grype scan on repo + id: scan + uses: anchore/scan-action@v3 + with: + path: . + fail-build: true + severity-cutoff: ${{ steps.severity.outputs.level }} + output-format: json + + + - name: Check Grype results and fail if vulnerabilities found + if: always() + run: | + JSON_FILE="./results.json" + + if [ ! -f "$JSON_FILE" ] || [ -z "$JSON_FILE" ]; then + echo "⚠️ Grype JSON output not found" + exit 0 + fi + + # Extract vulnerability counts using jq or grep fallback + if command -v jq &> /dev/null; then + CRITICAL_COUNT=$(jq '[.matches[]? | select(.vulnerability.severity == "Critical")] | length' "$JSON_FILE" 2>/dev/null || echo "0") + HIGH_COUNT=$(jq '[.matches[]? | select(.vulnerability.severity == "High")] | length' "$JSON_FILE" 2>/dev/null || echo "0") + else + CRITICAL_COUNT=$(grep -o '"severity":"Critical"' "$JSON_FILE" | wc -l | tr -d ' ' || echo "0") + HIGH_COUNT=$(grep -o '"severity":"High"' "$JSON_FILE" | wc -l | tr -d ' ' || echo "0") + fi + + echo "" + echo "============================================" + echo "Grype Security Scan Summary" + echo "============================================" + echo "CRITICAL vulnerabilities: $CRITICAL_COUNT" + echo "HIGH vulnerabilities: $HIGH_COUNT" + echo "============================================" + + VIOLATIONS="" + [ "${{ inputs.grype-fail-on-critical }}" == "true" ] && [ "$CRITICAL_COUNT" -gt 0 ] && VIOLATIONS="${VIOLATIONS}$CRITICAL_COUNT CRITICAL, " + [ "${{ inputs.grype-fail-on-high }}" == "true" ] && [ "$HIGH_COUNT" -gt 0 ] && VIOLATIONS="${VIOLATIONS}$HIGH_COUNT HIGH, " + + if [ -n "$VIOLATIONS" ]; then + echo "" + echo "❌ BUILD FAILED: Found ${VIOLATIONS%, }" + exit 1 + fi + + echo "" + echo "✅ No policy violations found" + + - name: Upload Grype scan results + if: always() + uses: actions/upload-artifact@v4 + with: + name: grype-results + path: ./results.json + retention-days: 30 + + # - name: Run Grype scan on repo + # uses: anchore/scan-action@v3 + # with: + # path: . + # fail-build: true + # severity-cutoff: high # run-srcclr: # if: ${{ inputs.perform-srcclr-scan == true }} @@ -1065,6 +1167,9 @@ jobs: uses: actions/checkout@v6 with: fetch-depth: 0 + + - name: Configure git for private + run: git config --global url."https://${{ secrets.GH_TOKEN }}@github.com/".insteadOf "https://github.com/" - name: Install build tools for Erlang if: inputs.language == 'erlang' diff --git a/.github/workflows/grype.yml b/.github/workflows/grype.yml new file mode 100644 index 0000000..36a225c --- /dev/null +++ b/.github/workflows/grype.yml @@ -0,0 +1,136 @@ +# grype.yml +# Grype security scan for source code vulnerabilities +# https://github.com/anchore/grype + +name: Grype security scan + +on: + workflow_call: + inputs: + fail-grype-on-high: + description: 'Fail the pipeline if Grype finds HIGH vulnerabilities' + required: false + type: boolean + default: false + fail-grype-on-critical: + description: 'Fail the pipeline if Grype finds CRITICAL vulnerabilities' + required: false + type: boolean + default: false + +jobs: + grype-scan: + name: Grype vulnerability scan + runs-on: ubuntu-latest + permissions: + id-token: write + contents: read + steps: + - name: Checkout code + uses: actions/checkout@v6 + with: + fetch-depth: 0 + + - name: Install Grype + run: | + curl -sSfL https://get.anchore.io/grype | sh -s -- -b /usr/local/bin + + - name: Login to Amazon ECR + id: ecr-login + continue-on-error: false + run: | + aws ecr get-login-password --region us-east-2 | docker login --username AWS --password-stdin 448877188565.dkr.ecr.us-east-2.amazonaws.com + echo "ecr_authenticated=true" >> $GITHUB_OUTPUT + + - name: Check for Dockerfile and build image if found + id: docker-build + continue-on-error: false + run: | + if [ -f "Dockerfile" ]; then + echo "Dockerfile found, attempting to build Docker image..." + if docker build -t grype-scan-image:latest .; then + echo "scan_target=grype-scan-image:latest" >> $GITHUB_OUTPUT + echo "scan_type=image" >> $GITHUB_OUTPUT + else + echo "Docker build failed, falling back to directory scan" + echo "scan_target=." >> $GITHUB_OUTPUT + echo "scan_type=dir" >> $GITHUB_OUTPUT + fi + else + echo "No Dockerfile found, will scan source directory" + echo "scan_target=." >> $GITHUB_OUTPUT + echo "scan_type=dir" >> $GITHUB_OUTPUT + fi + + - name: Run Grype scan + id: grype-scan + continue-on-error: true + run: | + SCAN_TARGET="${{ steps.docker-build.outputs.scan_target }}" + SCAN_TYPE="${{ steps.docker-build.outputs.scan_type }}" + SCAN_NAME="${{ github.repository }}" + + echo "Scan type: $SCAN_TYPE" + echo "Scan target: $SCAN_TARGET" + echo "Scan name: $SCAN_NAME" + + # Scan based on type + if [ "$SCAN_TYPE" == "image" ]; then + echo "Scanning Docker image..." + grype $SCAN_TARGET --name "$SCAN_NAME" --output json > grype-scan.json 2>&1 + grype $SCAN_TARGET --name "$SCAN_NAME" --output table > grype-scan.log 2>&1 || true + else + echo "Scanning source directory..." + grype dir:$SCAN_TARGET --name "$SCAN_NAME" --output json > grype-scan.json 2>&1 + grype dir:$SCAN_TARGET --name "$SCAN_NAME" --output table > grype-scan.log 2>&1 || true + fi + + - name: Check Grype results and fail if vulnerabilities found + if: ${{ always() && (inputs.fail-grype-on-high == true || inputs.fail-grype-on-critical == true) }} + run: | + JSON_FILE="grype-scan.json" + + if [ ! -f "$JSON_FILE" ]; then + echo "⚠️ Grype JSON output not found" + exit 0 + fi + + # Extract vulnerability counts by severity from JSON using jq + if command -v jq &> /dev/null; then + CRITICAL_COUNT=$(jq '[.matches[]? | select(.vulnerability.severity == "Critical")] | length' "$JSON_FILE" 2>/dev/null || echo "0") + HIGH_COUNT=$(jq '[.matches[]? | select(.vulnerability.severity == "High")] | length' "$JSON_FILE" 2>/dev/null || echo "0") + else + # Fallback to grep if jq not available + CRITICAL_COUNT=$(grep -o '"severity":"Critical"' "$JSON_FILE" | wc -l | tr -d ' ' || echo "0") + HIGH_COUNT=$(grep -o '"severity":"High"' "$JSON_FILE" | wc -l | tr -d ' ' || echo "0") + fi + + echo "" + echo "============================================" + echo "Grype Security Scan Summary" + echo "============================================" + echo "CRITICAL vulnerabilities: $CRITICAL_COUNT" + echo "HIGH vulnerabilities: $HIGH_COUNT" + echo "============================================" + + VIOLATIONS="" + [ "${{ inputs.fail-grype-on-critical }}" == "true" ] && [ "$CRITICAL_COUNT" -gt 0 ] && VIOLATIONS="${VIOLATIONS}$CRITICAL_COUNT CRITICAL, " + [ "${{ inputs.fail-grype-on-high }}" == "true" ] && [ "$HIGH_COUNT" -gt 0 ] && VIOLATIONS="${VIOLATIONS}$HIGH_COUNT HIGH, " + + if [ -n "$VIOLATIONS" ]; then + echo "" + echo "❌ BUILD FAILED: Found ${VIOLATIONS%, }" + exit 1 + else + echo "" + echo "✅ No policy-violating vulnerabilities found" + fi + + - name: Upload Grype scan results + if: always() + uses: actions/upload-artifact@v4 + with: + name: grype-scan-results + path: | + grype-scan.json + grype-scan.log diff --git a/.github/workflows/trufflehog.yml b/.github/workflows/trufflehog.yml index ff201d4..520d3c1 100644 --- a/.github/workflows/trufflehog.yml +++ b/.github/workflows/trufflehog.yml @@ -20,20 +20,34 @@ jobs: with: fetch-depth: 0 - - name: Construct Trufflehog arguments - id: trufflehog-args - run: | - EXTRA_ARGS="--only-verified" - - echo "EXTRA_ARGS=${EXTRA_ARGS}" >> $GITHUB_ENV - echo "Trufflehog enforcement: fail-on-secrets=${{ inputs.fail-trufflehog-on-secrets-found }}" - - name: TruffleHog secret scan + id: trufflehog-scan uses: trufflesecurity/trufflehog@main with: path: ./ - extra_args: ${{ env.EXTRA_ARGS }} + extra_args: --only-verified continue-on-error: false + + - name: Check results and fail if secrets found + if: ${{ always() && inputs.fail-trufflehog-on-secrets-found == true }} + run: | + # Parse the log output from the trufflehog action + LOG_OUTPUT="${{ steps.trufflehog-scan.outputs.stdout }}" + + # Extract verified_secrets count from JSON output + VERIFIED_COUNT=$(echo "$LOG_OUTPUT" | grep -oE '"verified_secrets":\s*[0-9]+' | grep -oE '[0-9]+' | tail -1 || echo "0") + + if [ "$VERIFIED_COUNT" -gt 0 ]; then + echo "" + echo "============================================" + echo "❌ Trufflehog Secret Scan Failed" + echo "============================================" + echo "Found $VERIFIED_COUNT verified secret(s)" + echo "============================================" + exit 1 + else + echo "✅ No verified secrets found" + fi # --only-verified --fail --github-actions --results=verified,unknown --branch dev # TODO: use the GH_TOKEN --org=progress --token=ghp_xxxxx From 8f14667007ba0d6f3a6197f1d1b5524ebb105534 Mon Sep 17 00:00:00 2001 From: sandhi Date: Tue, 24 Feb 2026 20:22:35 +0530 Subject: [PATCH 12/12] Adding changes for image grype Signed-off-by: sandhi --- .github/workflows/ci-main-pull-request.yml | 25 ++++ .github/workflows/grype.yml | 130 +++++++++++++-------- 2 files changed, 106 insertions(+), 49 deletions(-) diff --git a/.github/workflows/ci-main-pull-request.yml b/.github/workflows/ci-main-pull-request.yml index b393a48..fa7c647 100644 --- a/.github/workflows/ci-main-pull-request.yml +++ b/.github/workflows/ci-main-pull-request.yml @@ -131,6 +131,21 @@ on: required: false type: boolean default: false + perform-grype-image-scan: + description: 'Perform Grype scan on Docker image' + required: false + type: boolean + default: false + grype-image-fail-on-high: + description: 'Fail pipeline if Grype image scan finds HIGH vulnerabilities' + required: false + type: boolean + default: false + grype-image-fail-on-critical: + description: 'Fail pipeline if Grype image scan finds CRITICAL vulnerabilities' + required: false + type: boolean + default: false build: description: 'CI Build (language-specific)' required: false @@ -865,6 +880,16 @@ jobs: # path: . # fail-build: true # severity-cutoff: high + + run-grype-image: + name: 'Grype Docker image scan' + if: ${{ inputs.perform-grype-image-scan }} + uses: chef/common-github-actions/.github/workflows/grype.yml@sandhi/fix-blackduc-sca + needs: checkout + secrets: inherit + with: + fail-grype-on-high: ${{ inputs.grype-image-fail-on-high }} + fail-grype-on-critical: ${{ inputs.grype-image-fail-on-critical }} # run-srcclr: # if: ${{ inputs.perform-srcclr-scan == true }} diff --git a/.github/workflows/grype.yml b/.github/workflows/grype.yml index 36a225c..97c5f23 100644 --- a/.github/workflows/grype.yml +++ b/.github/workflows/grype.yml @@ -30,60 +30,97 @@ jobs: uses: actions/checkout@v6 with: fetch-depth: 0 + + - name: Configure git for private + env: + GOPRIVATE: ${{ inputs.go-private-modules }} + run: git config --global url."https://${{ secrets.GH_TOKEN }}@github.com/".insteadOf "https://github.com/" - name: Install Grype run: | curl -sSfL https://get.anchore.io/grype | sh -s -- -b /usr/local/bin - - name: Login to Amazon ECR - id: ecr-login - continue-on-error: false - run: | - aws ecr get-login-password --region us-east-2 | docker login --username AWS --password-stdin 448877188565.dkr.ecr.us-east-2.amazonaws.com - echo "ecr_authenticated=true" >> $GITHUB_OUTPUT + - name: Configure AWS credentials + uses: aws-actions/configure-aws-credentials@v4 + with: + aws-access-key-id: ${{ secrets.AWS_ACCESS_KEY_ID }} + aws-secret-access-key: ${{ secrets.AWS_SECRET_ACCESS_KEY }} + aws-session-token: ${{ secrets.AWS_SESSION_TOKEN }} + aws-region: us-east-2 - - name: Check for Dockerfile and build image if found - id: docker-build - continue-on-error: false - run: | - if [ -f "Dockerfile" ]; then - echo "Dockerfile found, attempting to build Docker image..." - if docker build -t grype-scan-image:latest .; then - echo "scan_target=grype-scan-image:latest" >> $GITHUB_OUTPUT - echo "scan_type=image" >> $GITHUB_OUTPUT - else - echo "Docker build failed, falling back to directory scan" - echo "scan_target=." >> $GITHUB_OUTPUT - echo "scan_type=dir" >> $GITHUB_OUTPUT - fi - else - echo "No Dockerfile found, will scan source directory" - echo "scan_target=." >> $GITHUB_OUTPUT - echo "scan_type=dir" >> $GITHUB_OUTPUT - fi + - name: Login to Amazon ECR + id: login-ecr + uses: aws-actions/amazon-ecr-login@v2 - - name: Run Grype scan + - name: Scan with Grype id: grype-scan - continue-on-error: true + env: + GITHUB_TOKEN: ${{ secrets.GH_TOKEN }} run: | - SCAN_TARGET="${{ steps.docker-build.outputs.scan_target }}" - SCAN_TYPE="${{ steps.docker-build.outputs.scan_type }}" SCAN_NAME="${{ github.repository }}" - echo "Scan type: $SCAN_TYPE" - echo "Scan target: $SCAN_TARGET" - echo "Scan name: $SCAN_NAME" + if [ ! -f "Dockerfile" ]; then + echo "❌ No Dockerfile found - this workflow requires a Dockerfile to scan Docker image" + exit 1 + fi + + echo "Building Docker image..." + REPO_NAME=$(basename $(pwd)) - # Scan based on type - if [ "$SCAN_TYPE" == "image" ]; then - echo "Scanning Docker image..." - grype $SCAN_TARGET --name "$SCAN_NAME" --output json > grype-scan.json 2>&1 - grype $SCAN_TARGET --name "$SCAN_NAME" --output table > grype-scan.log 2>&1 || true + # Strategy 1: Check for build-docker.sh script (e.g., dsm-erchef) + if [ -f "build-docker.sh" ]; then + echo "Found build-docker.sh script - using it to build images" + chmod +x build-docker.sh + GITHUB_TOKEN="${{ secrets.GH_TOKEN }}" ./build-docker.sh + + # Detect all images built (typically repo name or repo-name-init) + IMAGES=$(docker images --format "{{.Repository}}:{{.Tag}}" | grep -E "^${REPO_NAME}" | grep -v "^") + + if [ -z "$IMAGES" ]; then + echo "⚠️ No images found with prefix ${REPO_NAME} after build-docker.sh" + echo "Checking for any recently built images..." + IMAGES=$(docker images --format "{{.CreatedAt}}\t{{.Repository}}:{{.Tag}}" | sort -r | head -5 | cut -f2 | grep -v "^") + fi + # Strategy 2: Check for Makefile with compose-build target (e.g., chef-platform-user-accounts-service) + elif [ -f "Makefile" ] && grep -q "^compose-build:" Makefile; then + echo "Using Makefile compose-build target with GITHUB_TOKEN" + export GITHUB_TOKEN="${{ secrets.GH_TOKEN }}" + make compose-build + + echo "Detecting built images..." + docker compose images + + IMAGES=$(docker images --format "{{.Repository}}:{{.Tag}}" | grep "^${REPO_NAME}" | grep -v "^") + + if [ -z "$IMAGES" ]; then + echo "No images found with prefix ${REPO_NAME}, scanning all recent images" + IMAGES=$(docker images --format "{{.Repository}}:{{.Tag}}" | grep -v "^" | head -5) + fi + # Strategy 3: Fallback to standard docker build else - echo "Scanning source directory..." - grype dir:$SCAN_TARGET --name "$SCAN_NAME" --output json > grype-scan.json 2>&1 - grype dir:$SCAN_TARGET --name "$SCAN_NAME" --output table > grype-scan.log 2>&1 || true + echo "Using standard docker build with GITHUB_TOKEN build arg" + docker build --build-arg GITHUB_TOKEN="${{ secrets.GH_TOKEN }}" -t "${REPO_NAME}:latest" . + IMAGES="${REPO_NAME}:latest" + fi + + if [ -z "$IMAGES" ]; then + echo "❌ No Docker images found after build" + exit 1 fi + + echo "Found images to scan:" + echo "$IMAGES" + + # Scan all images and combine results into single files + > grype-scan.json # Initialize empty JSON file + > grype-scan.log # Initialize empty log file + + for IMAGE_NAME in $IMAGES; do + echo "" + echo "Scanning Docker image: $IMAGE_NAME" + grype "$IMAGE_NAME" --name "$SCAN_NAME-$(basename $IMAGE_NAME)" --output json >> grype-scan.json + grype "$IMAGE_NAME" --name "$SCAN_NAME-$(basename $IMAGE_NAME)" --output table >> grype-scan.log || true + done - name: Check Grype results and fail if vulnerabilities found if: ${{ always() && (inputs.fail-grype-on-high == true || inputs.fail-grype-on-critical == true) }} @@ -95,15 +132,10 @@ jobs: exit 0 fi - # Extract vulnerability counts by severity from JSON using jq - if command -v jq &> /dev/null; then - CRITICAL_COUNT=$(jq '[.matches[]? | select(.vulnerability.severity == "Critical")] | length' "$JSON_FILE" 2>/dev/null || echo "0") - HIGH_COUNT=$(jq '[.matches[]? | select(.vulnerability.severity == "High")] | length' "$JSON_FILE" 2>/dev/null || echo "0") - else - # Fallback to grep if jq not available - CRITICAL_COUNT=$(grep -o '"severity":"Critical"' "$JSON_FILE" | wc -l | tr -d ' ' || echo "0") - HIGH_COUNT=$(grep -o '"severity":"High"' "$JSON_FILE" | wc -l | tr -d ' ' || echo "0") - fi + # Extract vulnerability counts by severity from multiple JSON documents + # Use jq -s to slurp all JSON objects and combine matches + CRITICAL_COUNT=$(jq -s '[.[] | .matches[]? | select(.vulnerability.severity == "Critical")] | length' "$JSON_FILE" 2>/dev/null || echo "0") + HIGH_COUNT=$(jq -s '[.[] | .matches[]? | select(.vulnerability.severity == "High")] | length' "$JSON_FILE" 2>/dev/null || echo "0") echo "" echo "============================================"