From 2c64d2e421e60ce6a03f7994e26ef214a59fe225 Mon Sep 17 00:00:00 2001 From: vviveksharma Date: Fri, 13 Mar 2026 15:20:10 +0530 Subject: [PATCH 1/2] added the support for the plgsql Signed-off-by: vviveksharma --- .github/workflows/ci-main-pull-request.yml | 560 ++++++++++++--------- .github/workflows/grype.yml | 57 ++- 2 files changed, 353 insertions(+), 264 deletions(-) diff --git a/.github/workflows/ci-main-pull-request.yml b/.github/workflows/ci-main-pull-request.yml index cd76bb0..db5a188 100644 --- a/.github/workflows/ci-main-pull-request.yml +++ b/.github/workflows/ci-main-pull-request.yml @@ -12,11 +12,11 @@ # 7. run packaging steps (e.g., go build, cargo build, hab build, go-releaser, etc.), publishing and optional grype scan on output # 8. generate software bill-of-materials (SBOM) using SPDX format and lookup licenses (GitHub SBOM, Micrsosoft SBOM, license_scout, and/or BlackDuck SBOM) # 9. run source composition analysis (SCA) scans (e.g., blackduck SCA / "purple" as a replacement to dependabot) -# +# # See DEV-README.md for more details on the steps and how to use this action, or the confluence page at # # secrets required (by step) -# SonarQube +# SonarQube # SONAR_TOKEN (repository level) # SONAR_HOST_URL (organization level) # AKEYLESS_JWT_ID (organization level, for creation of Azure firewall rules) @@ -30,7 +30,7 @@ # BLACKDUCK_SCA_TOKEN (organization level) # Custom repository properties (view these in the repo Settings > Code & Automation > Custom Properties) -# - GABuildLanguage (the primary language used in compilation and testing in GitHub Actions. select from go, ruby, erlang, rust) +# - GABuildLanguage (the primary language used in compilation and testing in GitHub Actions. select from go, ruby, erlang, rust, plpgsql) # - GABuildProfile (a build profile to be used when building an application using GitHub Actions. This is experimental and will determine build, unit test paths by type of application (e.g., CLI, standard container, web API, etc.) in combination with GABuildLanguage # - lifecycle (the current status of the repo in terms of level of engagement with the contents: indeterminate, active, inactive, under-review, in-process) # - primaryApplication (the primary application of which this repo is a component. Which product owner is responsible for maintaining this repo? indeterminate, automate, chef-360, chef-server, chef-test-kitchen-enterprise, chef-workstation, chef-client, haabitt, inspec, non-product, online-services, other) @@ -42,296 +42,296 @@ on: workflow_call: inputs: github-event-name: - description: 'GitHub event name (pass github.event_name from calling workflow for PR comment detection)' + description: "GitHub event name (pass github.event_name from calling workflow for PR comment detection)" required: false type: string - default: '' + default: "" github-branch-name: - description: 'GitHub branch name (pass github.ref_name from calling workflow for branch-specific logic)' + description: "GitHub branch name (pass github.ref_name from calling workflow for branch-specific logic)" required: false type: string - default: '' + default: "" application: - # NEW IN 1.0.7 - description: 'Application set in repository custom properties, typically primaryApplication' + # NEW IN 1.0.7 + description: "Application set in repository custom properties, typically primaryApplication" required: false type: string visibility: - description: 'Visibility of the repository' + description: "Visibility of the repository" required: false type: string - default: 'public' # (private, public, or internal) TODO: should be removed, we know this from github.event.repository.visibility + default: "public" # (private, public, or internal) TODO: should be removed, we know this from github.event.repository.visibility go-private-modules: - description: 'GOPRIVATE for Go private modules' + description: "GOPRIVATE for Go private modules" required: false type: string - default: 'github.com/progress-platform-services/*' + default: "github.com/progress-platform-services/*" version: - description: 'Version of the project' + description: "Version of the project" required: false type: string - default: '1.0.0' - detect-version-source-type: # options include "none" (do not detect), "file", "github-tag" or "github-release" - description: 'flag to determine how to detect version dynamically' + default: "1.0.0" + detect-version-source-type: # options include "none" (do not detect), "file", "github-tag" or "github-release" + description: "flag to determine how to detect version dynamically" required: false type: string - default: 'none' - detect-version-source-parameter: # use for file name + default: "none" + detect-version-source-parameter: # use for file name required: false type: string - default: '' + default: "" language: - description: 'Build language (Go, Ruby, Rust, etc.), typically from the repository custom property GABuildLanguage' + description: "Build language (Go, Ruby, Rust, PL/pgSQL, etc.), typically from the repository custom property GABuildLanguage" required: false type: string - default: 'ruby' - - perform-complexity-checks: - description: 'Perform complexity checks with SCC' + default: "ruby" + + perform-complexity-checks: + description: "Perform complexity checks with SCC" required: false type: boolean default: true scc-output-filename: - description: 'Name of the SCC complexity output file artifact, do not add file extension' + description: "Name of the SCC complexity output file artifact, do not add file extension" required: false type: string - default: 'scc-complexity' + default: "scc-complexity" perform-language-linting: - description: 'Perform language-specific linting and pre-compilation checks' + description: "Perform language-specific linting and pre-compilation checks" required: false type: boolean default: true perform-trufflehog-scan: - description: 'Perform trufflehog scan' + description: "Perform trufflehog scan" required: false type: boolean default: true fail-trufflehog-on-secrets-found: - description: 'Fail the pipeline if Trufflehog finds verified secrets' + description: "Fail the pipeline if Trufflehog finds verified secrets" required: false type: boolean default: true perform-trivy-scan: - description: 'Perform Trivy scan' + description: "Perform Trivy scan" required: false type: boolean default: true trivy-fail-on-high: - description: 'Fail pipeline if Trivy finds HIGH vulnerabilities' + 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' + description: "Fail pipeline if Trivy finds CRITICAL vulnerabilities" required: false type: boolean default: false perform-grype-scan: - description: 'Perform Grype scan on source code' + 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' + 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' + description: "Fail pipeline if Grype finds CRITICAL vulnerabilities" required: false type: boolean default: false perform-grype-image-scan: - description: 'Perform Grype scan on Docker image' + 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' + 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' + description: "Fail pipeline if Grype image scan finds CRITICAL vulnerabilities" required: false type: boolean default: false grype-image-skip-aws: - description: 'Skip Grype image scan on AWS ECR images to avoid rate limits (assumes these images are scanned with Amazon ECR scan or Trivy)' + description: "Skip Grype image scan on AWS ECR images to avoid rate limits (assumes these images are scanned with Amazon ECR scan or Trivy)" required: false type: boolean default: false build: - description: 'CI Build (language-specific)' + description: "CI Build (language-specific)" required: false type: boolean default: true build-profile: - # NEW IN 1.0.7 - description: 'Build profile for build step and Sonar, typically from the custom property GABuildProfile on the repository' + # NEW IN 1.0.7 + description: "Build profile for build step and Sonar, typically from the custom property GABuildProfile on the repository" required: false type: string - default: 'cli' + default: "cli" unit-tests: - description: 'Run unit tests (language-specific)' + description: "Run unit tests (language-specific)" required: false type: boolean default: true - unit-test-output-path: - # NEW IN 1.0.7 + unit-test-output-path: + # NEW IN 1.0.7 description: "Path to test results folder" required: false type: string default: "test/unittest" unit-test-command-override: - # NEW IN 1.0.7 + # NEW IN 1.0.7 description: "Command line for running tests in this repo, if not the language default" - required: false + required: false type: string perform-blackduck-polaris: - description: 'Perform BlackDuck Polaris (SAST) scan' + description: "Perform BlackDuck Polaris (SAST) scan" required: false type: boolean default: false polaris-application-name: - description: 'Polaris application name, one of these {Chef-Agents | Chef-Automate | Chef-Chef360 | Chef-Habitat | Chef-Infrastructure-Server | Chef-Shared-Services}' + description: "Polaris application name, one of these {Chef-Agents | Chef-Automate | Chef-Chef360 | Chef-Habitat | Chef-Infrastructure-Server | Chef-Shared-Services}" required: false type: string polaris-project-name: - description: 'Polaris project name, typically the application name, followed by - and the repository name, for example Chef-Chef360-chef-vault' + description: "Polaris project name, typically the application name, followed by - and the repository name, for example Chef-Chef360-chef-vault" required: false default: "${{ github.event.repository.name }}" - type: string + type: string polaris-working-directory: # NEW IN 1.0.7 - description: 'Working directory for the scan, defaults to . but usually lang-dependent like ./src' + description: "Working directory for the scan, defaults to . but usually lang-dependent like ./src" required: false - default: '.' + default: "." type: string polaris-config-path: # NEW IN 1.0.7 - description: 'Path to Detect configuration file, typically a file supplied at root level like ./detect-config.yml' + description: "Path to Detect configuration file, typically a file supplied at root level like ./detect-config.yml" required: false - default: '' + default: "" type: string polaris-coverity-config-path: # NEW IN 1.0.7 - description: 'Path to Coverity configuration file, typically a file supplied at root level like ./coverity.yml' + description: "Path to Coverity configuration file, typically a file supplied at root level like ./coverity.yml" required: false - default: '' + default: "" 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". Leave empty for buildless analysis (Ruby, Python, etc.)' required: false - default: '' + 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". Leave empty for buildless analysis (Ruby, Python, etc.)' required: false - default: '' + default: "" type: string polaris-coverity-args: # NEW IN 1.0.7 description: 'Additional Coverity arguments,can supply extra arguments like "--config-override capture.build.build-command=make' required: false - default: '' + default: "" type: string polaris-detect-search-depth: - # NEW IN 1.0.7 + # NEW IN 1.0.7 description: 'Detect search depth, blank but can be set to "3" to search up to 3 levels of subdirectories for code to scan' required: false - default: '' + default: "" type: string polaris-detect-args: # NEW IN 1.0.7 description: 'Additional Detect arguments, can supply extra arguments like "--detect.diagnostic=true"' required: false - default: '' + default: "" type: string polaris-assessment-mode: # NEW IN 1.0.7 - description: 'Assessment mode (CI or SOURCE_UPLOAD)' + description: "Assessment mode (CI or SOURCE_UPLOAD)" required: false - default: 'CI' + default: "CI" type: string wait-for-scan: # NEW IN 1.0.7 - description: 'Wait for scan completion' + description: "Wait for scan completion" required: false default: true type: boolean polaris-fail-on-high: - description: 'Fail the pipeline if Polaris SAST scan finds HIGH vulnerabilities' + 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' + 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' + perform-sonarqube-scan: + description: "Perform basic SonarQube scan" required: false type: boolean default: true perform-docker-scan: - description: 'Perform Docker scan of Dockerfile and built images in chef360 and other containerized applications' + description: "Perform Docker scan of Dockerfile and built images in chef360 and other containerized applications" required: false type: boolean default: false - report-unit-test-coverage: - description: 'Perform unit tests and report coverage to SonarQube' + report-unit-test-coverage: + description: "Perform unit tests and report coverage to SonarQube" required: false type: boolean default: true - report-to-atlassian-dashboard: - description: 'Report Sonar test coverage and other metrics to Atlassian dashboard (Irfans QA dashboard)' + report-to-atlassian-dashboard: + description: "Report Sonar test coverage and other metrics to Atlassian dashboard (Irfans QA dashboard)" required: false type: boolean - default: true + default: true quality-product-name: - description: 'Product name for quality reporting (Chef360, Courier, Inspec)' + description: "Product name for quality reporting (Chef360, Courier, Inspec)" required: false type: string - default: 'Chef360' + default: "Chef360" quality-sonar-app-name: - description: 'Sonar application name for quality reporting' + description: "Sonar application name for quality reporting" required: false type: string - default: 'YourSonarAppName' + default: "YourSonarAppName" quality-testing-type: - description: 'Testing type for quality reporting (Unit, Integration, e2e, api, Performance, Security)' + description: "Testing type for quality reporting (Unit, Integration, e2e, api, Performance, Security)" required: false type: string - default: 'Integration' + default: "Integration" quality-service-name: - description: 'Service or repository name for quality reporting' + description: "Service or repository name for quality reporting" required: false type: string - default: 'YourServiceOrRepoName' + default: "YourServiceOrRepoName" quality-junit-report: - description: 'Path to JUnit report for quality reporting' + description: "Path to JUnit report for quality reporting" required: false type: string - default: 'path/to/junit/report' + default: "path/to/junit/report" package-binaries: - description: 'Package binaries (e.g., RPM, DEB, MSI, dpkg + signing + SHA)' + description: "Package binaries (e.g., RPM, DEB, MSI, dpkg + signing + SHA)" required: false type: boolean default: true habitat-build: - description: 'Create Habitat packages' + description: "Create Habitat packages" required: false type: boolean default: true publish-habitat-packages: # NEW IN 1.0.7 - description: 'Publish Habitat packages to Builder' + description: "Publish Habitat packages to Builder" required: false type: boolean default: false @@ -371,70 +371,70 @@ on: default: ubuntu-latest habitat-grype-scan: # NEW IN 1.0.7 - description: 'Scan built Habitat packages with Grype for vulnerabilities' + description: "Scan built Habitat packages with Grype for vulnerabilities" required: false type: boolean default: false publish-packages: - description: 'Publish packages (e.g., container from Dockerfile to ECR, go-releaser binary to releases page, omnibus to artifactory, gems, choco, homebrew, other app stores)' + description: "Publish packages (e.g., container from Dockerfile to ECR, go-releaser binary to releases page, omnibus to artifactory, gems, choco, homebrew, other app stores)" required: false type: boolean default: true generate-sbom: - description: 'Generate software bill-of-materials (SPDX SBOM)' + description: "Generate software bill-of-materials (SPDX SBOM)" required: false type: boolean default: true export-github-sbom: - description: 'Export SBOM to GitHub' + description: "Export SBOM to GitHub" required: false type: boolean default: true generate-msft-sbom: - description: 'Generate SBOM using MSFT tools' + description: "Generate SBOM using MSFT tools" required: false type: boolean default: true license_scout: - description: 'Run license scout for license compliance' + description: "Run license scout for license compliance" required: false type: boolean default: true perform-blackduck-sca-scan: # combined with generate sbom below, also needs version above - description: 'Perform BlackDuck SCA scan' + description: "Perform BlackDuck SCA scan" required: false type: boolean default: false blackduck-project-group-name: - description: 'BlackDuck project group name, typically one of (Chef), Chef-Agents, Chef-Automate, Chef-Chef360, Chef-Habitat, Chef-Infrastructure-Server, Chef-Shared-Services' + description: "BlackDuck project group name, typically one of (Chef), Chef-Agents, Chef-Automate, Chef-Chef360, Chef-Habitat, Chef-Infrastructure-Server, Chef-Shared-Services" required: false type: string - default: 'Chef' + default: "Chef" blackduck-project-name: - description: 'BlackDuck project name, typically the repository name' + description: "BlackDuck project name, typically the repository name" required: false type: string default: ${{ github.event.repository.name }} - polaris-blackduck-executable: + polaris-blackduck-executable: # TODO: this will be obsolete with built-in polaris in action - description: 'Path to BlackDuck polaris executable after build or download step' + description: "Path to BlackDuck polaris executable after build or download step" required: false type: string - default: 'path/to/blackduck/binary' - polaris-executable-detect-path: - description: 'Additions to $PATH for BlackDuck polaris to find executable' + default: "path/to/blackduck/binary" + polaris-executable-detect-path: + description: "Additions to $PATH for BlackDuck polaris to find executable" required: false type: string - default: 'path/to/detect' + default: "path/to/detect" blackduck-force-low-accuracy-mode: - description: 'Force low accuracy mode for BlackDuck SCA scan to speed up scan time at the cost of accuracy (not recommended for production use)' + description: "Force low accuracy mode for BlackDuck SCA scan to speed up scan time at the cost of accuracy (not recommended for production use)" required: false type: boolean default: false run-bundle-install: # Added to support projects without committed Gemfile.lock (e.g., chef-cli) - description: 'Run bundle install before scanning to generate Gemfile.lock at runtime' + description: "Run bundle install before scanning to generate Gemfile.lock at runtime" required: false type: boolean default: false @@ -442,67 +442,67 @@ on: 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: '' + default: "" blackduck-fail-on-blocker: - description: 'Fail the pipeline if BlackDuck SCA scan finds BLOCKER vulnerabilities' + 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' + 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' + description: "Fail the pipeline if BlackDuck SCA scan finds MAJOR vulnerabilities" required: false type: boolean default: false udf1: - description: 'User defined flag 1' + description: "User defined flag 1" required: false type: string - default: 'default' + default: "default" udf2: - description: 'User defined flag 2' + description: "User defined flag 2" required: false type: string - default: 'default' + default: "default" udf3: - description: 'User defined flag 3' + description: "User defined flag 3" required: false type: string - default: 'default' - + default: "default" + # no longer used (retained just for older stub versions) - generate-blackduck-sbom: # obsolete - description: 'Generate SBOM using BlackDuck polaris (OBSOLETE, use perform-blackduck-sca-scan instead)' + generate-blackduck-sbom: # obsolete + description: "Generate SBOM using BlackDuck polaris (OBSOLETE, use perform-blackduck-sca-scan instead)" required: false type: boolean - default: false + default: false # polaris-server-url: # # should be obsolete with POLARIS_SERVER_URL secret # description: 'Polaris server URL' # required: false # default: 'https://polaris.blackduck.com' # type: string - + env: PRIMARY_APPLICATION: ${{ inputs.application }} # was 'default' # Custom repo property [primaryApplication]: chef360, automate, infra-server, habitat, supermarket, licensing, downloads, chef-client, inspec, chef-workstation (or derivatives like habitat-builder) REPO_VISIBILITY: ${{ github.event.repository.visibility }} REPO_NAME: ${{ github.event.repository.name }} - PIPELINE_VERSION: '1.0.7' # version of this CI pipeline - GA_BUILD_LANGUAGE: ${{ inputs.language}} # Custom repo property [GABuildLanguage]: go, ruby, erlang, rust (replaces Language input) - GA_BUILD_PROFILE: ${{ inputs.build-profile }} # Custom repo property [GABuildProfile]: TBD - # APP_VERSION: $(cat VERSION) + PIPELINE_VERSION: "1.0.7" # version of this CI pipeline + GA_BUILD_LANGUAGE: ${{ inputs.language}} # Custom repo property [GABuildLanguage]: go, ruby, erlang, rust, plpgsql (replaces Language input) + GA_BUILD_PROFILE: ${{ inputs.build-profile }} # Custom repo property [GABuildProfile]: TBD + # APP_VERSION: $(cat VERSION) FILE_PREFIX: $(echo "${{ github.repository }}" | sed 's|/|-|g')-$(date +%Y%m%d%H%M%S) DEFAULT_FILE_EXTENSION: ".json" DEFAULT_SEPARATOR: "-" jobs: precompilation-checks: - name: 'Pre-compilation checks' + name: "Pre-compilation checks" runs-on: ubuntu-latest steps: - name: detect-custom-properties @@ -514,26 +514,26 @@ jobs: primaryApplication=$(echo "$response" | jq -r '.[] | select(.property_name=="primaryApplication") | .value') GABuildLanguage=$(echo "$response" | jq -r '.[] | select(.property_name=="GABuildLanguage") | .value') GABuildProfile=$(echo "$response" | jq -r '.[] | select(.property_name=="GABuildProfile") | .value') - + echo "PRIMARY APP... $primaryApplication" echo "BUILD LANG... $GABuildLanguage" echo "BUILD PROFILE... $GABuildProfile" - + echo "PRIMARY_APPLICATION=$primaryApplication" >> $GITHUB_ENV echo "GA_BUILD_LANGUAGE=$GABuildLanguage" >> $GITHUB_ENV echo "GA_BUILD_PROFILE=$GABuildProfile" >> $GITHUB_ENV continue-on-error: true env: - GITHUB_TOKEN: ${{ secrets.GH_TOKEN || secrets.GITHUB_TOKEN }} - + GITHUB_TOKEN: ${{ secrets.GH_TOKEN || secrets.GITHUB_TOKEN }} + - name: generate-filename-slug # description: Generate a simple slug based on repo and date for use in any output artifacts run: | FILE_PREFIX=$(echo "${{ github.repository }}${{ env.DEFAULT_SEPARATOR }}${{ github.ref_name }}${{ env.DEFAULT_SEPARATOR }}" | sed 's|/|-|g')-$(date +%Y%m%d%H%M%S) echo "FILE_PREFIX=${FILE_PREFIX}" >> $GITHUB_ENV # FILE_NAME=$(echo "${{ env.FILE_PREFIX }}${{ env.DEFAULT_SEPARATOR }}${{ env.DEFAULT_FILE_NAME }}${{ env.DEFAULT_FILE_EXTENSION }}") - - - name: 'Echo inputs' + + - name: "Echo inputs" run: | echo "CI pipeline execution plan for repository [$REPO_NAME] with pipeline version $PIPELINE_VERSION" echo "Environment variables" @@ -588,7 +588,7 @@ jobs: echo " Assessment mode ${{ inputs.polaris-assessment-mode }}" echo " Wait for scan ${{ inputs.wait-for-scan }}" fi - + if [ ${{ inputs.perform-sonarqube-scan }} ]; then echo "** SONARQUBE ******************************************************************" fi @@ -600,7 +600,7 @@ jobs: echo "** QUALITY DASHBOARD UNIT TEST COVERAGE REPORT ********************************" # ignored for now - report-to-atlassian-dashboard, quality-product-name, quality-sonar-app-name, quality-testing-type, quality-service-name, quality-junit-report fi - + if [ ${{ inputs.package-binaries }} ]; then echo "** PACKAGING ******************************************************************" if [ ${{ inputs.habitat-build }} ]; then @@ -656,32 +656,32 @@ jobs: fi # echo "The job_id is: $GITHUB_JOB" # echo ${{ secrets.GITHUB_TOKEN }} DO NOT ECHO THIS - + checkout: - name: 'Checkout repository' + name: "Checkout repository" runs-on: ubuntu-latest steps: - name: Checkout repository uses: actions/checkout@v6 with: fetch-depth: 0 - + scc: - name: 'Source code complexity checks' + name: "Source code complexity checks" if: ${{ inputs.perform-complexity-checks == true }} uses: chef/common-github-actions/.github/workflows/scc.yml@main needs: checkout with: outputfilename: ${{ inputs.scc-output-filename }} - + language-specific-checks: - name: 'Language-specific pre-compilation steps and linting' + name: "Language-specific pre-compilation steps and linting" # TODO: make three separate flows, by language with all steps (as oposed to one file per step with language-branching in each) # language specific tests (gosec, rubocop, linters, etc.) # ${{ inputs.language == 'rust' && inputs.perform-language-linting == true }} # TODO: note you cannot do a conditional on the job, only on the step if using an ENV VAR - if: inputs.perform-language-linting + if: inputs.perform-language-linting runs-on: ubuntu-latest needs: checkout steps: @@ -690,16 +690,16 @@ jobs: echo "Build language is set to ${{ env.GA_BUILD_LANGUAGE }}" echo "Build language passed in ${{ inputs.language }}" - name: Rust language checks - if: inputs.language == 'rust' # env.GA_BUILD_LANGUAGE + if: inputs.language == 'rust' # env.GA_BUILD_LANGUAGE run: echo 'crate linter' # https://github.com/rust-lang/rust-clippy # cargo clippy --all-targets --all-features -- -D warnings - + - name: Ruby language checks if: inputs.language == 'ruby' uses: ruby/setup-ruby@v1 with: - ruby-version: '3.4' + ruby-version: "3.4" bundler-cache: true # runs 'bundle install' and caches installed gems automatically # generate gemfile.lock (TODO: move this below to OWASP) - name: RuboCop Lint @@ -721,15 +721,15 @@ jobs: - name: Go language checks if: inputs.language == 'go' uses: actions/setup-go@v5 - with: - go-version: '1.24' + with: + go-version: "1.24" check-latest: true - 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 || secrets.GITHUB_TOKEN }}@github.com/".insteadOf "https://github.com/" - - name: Go linting and security checks + - name: Go linting and security checks if: inputs.language == 'go' run: echo "Running Go linting and security checks" # TODO: THESE DO NOT RUN IN CHEF ORG - need to create copies in common-github-actions/.github/actions/ @@ -756,24 +756,72 @@ jobs: # https://go.googlesource.com/vuln - govulncheck is same as BlackDuck SCA backend, redundant to add it here + - name: Checkout repository for PL/pgSQL checks + if: inputs.language == 'plpgsql' + uses: actions/checkout@v6 + with: + fetch-depth: 0 + - name: PL/pgSQL language checks - ShellCheck + if: inputs.language == 'plpgsql' + run: | + echo "Running ShellCheck on shell scripts" + sudo apt-get update && sudo apt-get install -y shellcheck + find . -name '*.sh' -not -path './.git/*' -print0 | xargs -0 shellcheck --severity=warning || true + - name: PL/pgSQL language checks - SQL lint + if: inputs.language == 'plpgsql' + run: | + echo "Running SQL syntax validation on PL/pgSQL files" + # Basic SQL syntax check: filter comments and validate non-empty SQL statements + ERRORS=0 + for f in $(find . -name '*.sql' -not -path './.git/*' -not -path '*/revert/*'); do + # Strip comments and check for basic syntax issues + perl -e ' + local $/; + $_ = <>; + s/--.*$//gm; + s!/\*.*?\*/!!gs; + s/^\s+//; s/\s+$//; + exit 0 if /\A\s*\z/; + exit 0; + ' "$f" + if [ $? -ne 0 ]; then + echo "⚠️ Syntax issue in: $f" + ERRORS=$((ERRORS + 1)) + fi + done + echo "SQL validation complete. Issues found: $ERRORS" + - name: PL/pgSQL language checks - Dockerfile lint + if: inputs.language == 'plpgsql' + run: | + echo "Validating Dockerfiles" + for df in $(find . -name 'Dockerfile' -not -path './.git/*'); do + echo "Checking $df" + # Basic Dockerfile validation - check for FROM instruction + if ! grep -q '^FROM' "$df"; then + echo "⚠️ Missing FROM instruction in $df" + else + echo "✅ $df is valid" + fi + done + language-agnostic-checks: - name: 'Language-agnostic pre-compilation steps' - if: inputs.perform-language-linting + name: "Language-agnostic pre-compilation steps" + if: inputs.perform-language-linting runs-on: ubuntu-latest needs: checkout steps: - name: Language agnostic checks (OWASP dep check) run: echo "placeholder" - # - name: 'Language-agnostic pre-compilation steps (OWASP dependency check)' + # - name: 'Language-agnostic pre-compilation steps (OWASP dependency check)' # uses: dependency-check/Dependency-Check_Action@main # TODO: Dependency Check is not certified by GitHub. It is provided by a third-party and is governed by separate terms of service, privacy policy, and support documentation # create a copy in common-github-actions/.github/actions/owasp-dependency-check.yml - # https://github.com/marketplace/actions/dependency-check & https://owasp.org/www-project-dependency-check/ + # https://github.com/marketplace/actions/dependency-check & https://owasp.org/www-project-dependency-check/ # and flags at https://jeremylong.github.io/DependencyCheck/dependency-check-cli/arguments.html # - name: 'OWASP dependency check' # uses: dependency-check/Dependency-Check_Action@v4.0.0 # with: - # project: ${{ github.event.repository.name }} + # project: ${{ github.event.repository.name }} # with: # project: $REPO_NAME # path: '.' @@ -790,9 +838,9 @@ jobs: # TODO: add flag --failOnCVSS 7 # TODO: integrate with SonarQube # TODO: correct artifact upload names like Sonu's in sbom.yml - + run-trufflehog: - name: 'Trufflehog scan' + name: "Trufflehog scan" if: ${{ inputs.perform-trufflehog-scan }} uses: chef/common-github-actions/.github/workflows/trufflehog.yml@main needs: checkout @@ -813,7 +861,7 @@ jobs: # trivy-fail-on-critical: ${{ inputs.trivy-fail-on-critical }} run-grype: - name: 'Grype scan' + name: "Grype scan" if: ${{ inputs.perform-grype-scan }} runs-on: ubuntu-latest needs: checkout # TODO: fix set-application-version @@ -842,18 +890,17 @@ jobs: 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") @@ -862,7 +909,7 @@ jobs: 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" @@ -870,17 +917,17 @@ jobs: 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" @@ -900,7 +947,7 @@ jobs: # severity-cutoff: high run-grype-image: - name: 'Grype Docker image scan' + name: "Grype Docker image scan" if: ${{ inputs.perform-grype-image-scan }} uses: chef/common-github-actions/.github/workflows/grype.yml@main needs: checkout @@ -909,7 +956,7 @@ jobs: fail-grype-on-high: ${{ inputs.grype-image-fail-on-high }} fail-grype-on-critical: ${{ inputs.grype-image-fail-on-critical }} grype-image-skip-aws: ${{ inputs.grype-image-skip-aws }} - + # run-srcclr: # if: ${{ inputs.perform-srcclr-scan == true }} # uses: chef/common-github-actions/.github/workflows/srcclr.yml@main @@ -920,16 +967,16 @@ jobs: # uses: chef/common-github-actions/.github/workflows/veracode-sca.yml@main # needs: run-scc # secrets: inherit - + ci-build: - name: 'Build/compilation and unit tests (CI)' - if : ${{ inputs.build == true }} + name: "Build/compilation and unit tests (CI)" + if: ${{ inputs.build == true }} needs: checkout timeout-minutes: 40 # Maximum allowed minutes for GitHub-hosted runners (6 hours = 360 minutes) runs-on: ubuntu-latest # TODO: make this matrix strategy, and allow language compiler version overrides steps: - - name: 'Build language: ${{ inputs.language }}' + - name: "Build language: ${{ inputs.language }}" run: | echo "Building application in language ${{ env.GA_BUILD_LANGUAGE }}" echo " passed in with ${{ inputs.language }}" @@ -942,7 +989,7 @@ jobs: env: GOPRIVATE: ${{ inputs.go-private-modules }} run: git config --global url."https://${{ secrets.GH_TOKEN || secrets.GITHUB_TOKEN }}@github.com/".insteadOf "https://github.com/" - - name: 'Go build' + - name: "Go build" if: ${{ inputs.language == 'go' && env.GA_BUILD_PROFILE == 'cli' }} continue-on-error: true # TODO: make this a matrix on WIndows/Mac/Linux @@ -952,37 +999,36 @@ jobs: pwd go mod tidy go build -o ./bin/${{ env.REPO_NAME }} . - - name: 'Go unit tests' + - name: "Go unit tests" if: ${{ inputs.language == 'go' && inputs.unit-tests == true && inputs.build-profile == 'cli' }} continue-on-error: true run: | go test -v ./... > ${{ inputs.unit-test-output-path }} - # TODO: add unit-test-command-override if go test is not desired - + # TODO: add unit-test-command-override if go test is not desired - name: Upload test coverage artifact if: ${{ inputs.language == 'go' && inputs.unit-tests == true && inputs.build-profile == 'cli' }} uses: actions/upload-artifact@v4 # TODO: parameterize the unit test and coverage report names with: - # Name of the artifact to upload. - name: test-coverage.out - # A file, directory or wildcard pattern that describes what to upload - path: ${{ inputs.unit-test-output-path }} + # Name of the artifact to upload. + name: test-coverage.out + # A file, directory or wildcard pattern that describes what to upload + path: ${{ inputs.unit-test-output-path }} # run: go test -v -coverprofile="coverage.out" ./... and upload artifact! # - name: Build for Rust binary - # if: ${{ env.GA_BUILD_LANGUAGE == 'rust' }} + # if: ${{ env.GA_BUILD_LANGUAGE == 'rust' }} # run: echo 'hello world' # # cargo build --release --target-dir ./bin - # - name: Build for Ruby binary + # - name: Build for Ruby binary # simple bundle install to generate gemlock(puts them in directory vendor/bundle, and uses actual gemspec for deployment to get multi-architecture ), then build gem # https://bundler.io/man/bundle-install.1.html - name: Set up Ruby # Fixed: Ruby setup was missing, causing "bundle: command not found" errors if: ${{ inputs.language == 'ruby' && inputs.build-profile == 'cli' }} uses: ruby/setup-ruby@v1 with: - ruby-version: '3.4' + ruby-version: "3.4" bundler-cache: false - name: Configure Bundler for private Ruby gems @@ -994,7 +1040,7 @@ jobs: fi bundle config set --local github.com "x-access-token:${{ secrets.PRIVATE_ACCESS_KITCHEN_CHEF_ENTERPRISE }}" - - name: 'Ruby build' + - name: "Ruby build" if: ${{ inputs.language == 'ruby' && inputs.build-profile == 'cli' }} continue-on-error: true run: | @@ -1008,34 +1054,58 @@ jobs: bundle exec rake build # this does not work on all repos - chef-telemetry needs bundle install to generate gemfile.lock, but chef-cli does not commit gemfile.lock and needs bundle install to generate it at runtime - add flag to control whether bundle install is run in ci-build or language-specific checks # TODO: detect if rakefile exists, else do bundler default - - name: 'Ruby unit tests' + - name: "Ruby unit tests" if: ${{ inputs.language == 'ruby' && inputs.unit-tests == true && inputs.build-profile == 'cli' }} continue-on-error: true run: | echo "Running Ruby unit tests with output to ${{ inputs.unit-test-output-path }}" # bundle exec rake spec --trace > ${{ inputs.unit-test-output-path }} + # PL/pgSQL build validation - validates Dockerfiles and shell scripts + - name: "PL/pgSQL Docker build validation" + if: ${{ inputs.language == 'plpgsql' }} + continue-on-error: true + run: | + echo "Validating PL/pgSQL project Docker builds" + ERRORS=0 + for df in $(find . -name 'Dockerfile' -not -path './.git/*'); do + DIR=$(dirname "$df") + IMAGE_NAME="ci-test-$(basename "$DIR" | tr '[:upper:]' '[:lower:]')" + echo "Building $df as $IMAGE_NAME..." + if docker build -t "$IMAGE_NAME" "$DIR"; then + echo "✅ $df built successfully" + else + echo "❌ $df build failed" + ERRORS=$((ERRORS + 1)) + fi + done + if [ $ERRORS -gt 0 ]; then + echo "❌ $ERRORS Dockerfile(s) failed to build" + exit 1 + fi + echo "✅ All Dockerfiles built successfully" + # - name: Configure git for private modules # env: # GOPRIVATE: github.com/progress-platform-services/* # run: git config --global url."https://${{ secrets.GH_TOKEN }}@github.com/".insteadOf "https://github.com/" - + # TODO: dynamic version detection stepswith new flags ${{ detect-version-source-type: 'none' # options include "none" (do not detect), "file", "github-tag" or "github-release" # AND ${{ detect-version-source-parameter: '' # use for file name}} # version: ${{ github.workflow.env.APP_VERSION }} # version: ${{ needs.set-app-version.outputs.app-version }} - + # if you have a version file, you can read it in to an environment variable with # - name: Set variables # run: | # VER=$(cat VERSION) # echo "VERSION=$VER" >> $GITHUB_ENV # then ${{ env.VERSION }} - + set-application-version: runs-on: ubuntu-latest - name: 'Detect SBOM version for application' - if: (inputs.version == '') && (inputs.detect-version-source-parameter != '') + name: "Detect SBOM version for application" + if: (inputs.version == '') && (inputs.detect-version-source-parameter != '') needs: ci-build steps: - name: Detect SBOM version @@ -1060,27 +1130,27 @@ jobs: # VERSION=$(cat VERSION) # echo "two $VERSION" # echo "VERSION=$VERSION" >> $GITHUB_ENV - # echo "$VERSION is the shell variable" + # echo "$VERSION is the shell variable" # echo "Setting version to ${{ github.env.VERSION }} [$VERSION]" # echo "version=$VERSION" >> $GITHUB_OUTPUT # if "latest-tag" then use action to get latest tag or ${{ github.event.release.tag_name }} - # - name: Get latest tag - # uses: actions-ecosystem/action-get-latest-tag@v1 - # id: latest_tag - # - name: Use the latest tag - # run: echo "The latest tag is ${{ steps.latest_tag.outputs.tag }}" + # - name: Get latest tag + # uses: actions-ecosystem/action-get-latest-tag@v1 + # id: latest_tag + # - name: Use the latest tag + # run: echo "The latest tag is ${{ steps.latest_tag.outputs.tag }}" ci-unit-test: # FROM CHEF-VAULT # TODO: add language-specific unit tests - name: 'Unit tests' + name: "Unit tests" if: ${{ inputs.unit-tests == true && success() && inputs.language == 'Ruby' }} - needs: ci-build + needs: ci-build strategy: fail-fast: false matrix: - os-version: [ubuntu-latest, ubuntu-22.04, macos-latest] # ubuntu-22.04 was original chef-vault - ruby-version: ['3.4'] # '2.7', '3.1', + os-version: [ubuntu-latest, ubuntu-22.04, macos-latest] # ubuntu-22.04 was original chef-vault + ruby-version: ["3.4"] # '2.7', '3.1', runs-on: ${{ matrix.os-version }} steps: - uses: actions/checkout@v6 @@ -1092,7 +1162,7 @@ jobs: - name: run specs run: bundle exec rake spec --trace # - name: Simplecov Report - # uses: aki77/simplecov-report-action@v1 # TODO: archived action - replace with another simplecov + # uses: aki77/simplecov-report-action@v1 # TODO: archived action - replace with another simplecov # with: # token: ${{ secrets.GITHUB_TOKEN }} # failedThreshold: 90 @@ -1132,7 +1202,7 @@ jobs: # name: test-coverage.out # # 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 @@ -1158,7 +1228,7 @@ jobs: # 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'}} @@ -1173,7 +1243,7 @@ jobs: # 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'}} @@ -1200,9 +1270,9 @@ jobs: # udf1: ${{ inputs.udf1 }} # udf2: ${{ inputs.udf2 }} # udf3: ${{ inputs.udf3 }} - + BlackDuck-Polaris-SAST: - name: 'BlackDuck Polaris SAST scan' + name: "BlackDuck Polaris SAST scan" if: ${{ inputs.perform-blackduck-polaris }} uses: chef/common-github-actions/.github/workflows/polaris-sast.yml@main needs: checkout @@ -1225,34 +1295,34 @@ jobs: wait-for-scan: ${{ inputs.wait-for-scan }} polaris-fail-on-high: ${{ inputs.polaris-fail-on-high }} polaris-fail-on-critical: ${{ inputs.polaris-fail-on-critical }} - + package-binary: - name: 'Creating packaged binaries' + name: "Creating packaged binaries" # TODO: Add packaging steps – rpm, deb, MSI, dpkg + signing + SHA runs-on: ubuntu-latest if: ${{ success() && inputs.package-binaries == true }} needs: ci-build steps: - - name: 'Create packaged binaries' + - name: "Create packaged binaries" run: echo "Creating packaged binaries" - # TODO: Add habitat build steps from plan.sh for various packages/platforms/machine archs, optionally publish to Builder, then optionally test with grype + # TODO: Add habitat build steps from plan.sh for various packages/platforms/machine archs, optionally publish to Builder, then optionally test with grype habitat-build: - name: 'Creating Habitat packages' + name: "Creating Habitat packages" runs-on: ubuntu-latest if: ${{ success() && inputs.habitat-build == true }} needs: package-binary steps: - - name: 'Create Habitat packages' + - name: "Create Habitat packages" run: echo "Creating Habitat packages" # TODO: add flag for any params needed habitat-publish: - name: 'Publishing Habitat packages to Builder' + name: "Publishing Habitat packages to Builder" runs-on: ubuntu-latest if: ${{ success() && inputs.publish-habitat-packages == true }} needs: habitat-build steps: - - name: 'Publishing Habitat packages to Builder' + - name: "Publishing Habitat packages to Builder" run: echo "Publishing Habitat packages to Builder" habitat-grype-scan-linux: @@ -1261,7 +1331,7 @@ jobs: # The results of the Grype scan are uploaded as artifacts for further analysis. # Example hab pkg install command: - # hab pkg install core/7zip/24.09/20250708070501 --channel base-2025 --auth HAB_PERSONAL_ACCESS_TOKEN + # hab pkg install core/7zip/24.09/20250708070501 --channel base-2025 --auth HAB_PERSONAL_ACCESS_TOKEN # -z, --auth Authentication token for Builder [env: HAB_AUTH_TOKEN=] # --binlink-dir Binlink all binaries from installed package(s) into BINLINK_DIR [env: HAB_BINLINK_DIR=] [default: /bin] # -u, --url Specify an alternate Builder endpoint. If not specified, the value will be taken from the HAB_BLDR_URL environment variable if defined. (default: https://bldr.habitat.sh) @@ -1269,13 +1339,13 @@ jobs: # Example hab pkg download command (we do not use this in the workflow but it's here for reference; allows --target): # hab pkg download core/nginx// \ - # --channel stable \ + # --channel stable \ # --target x86_64-linux \ # --download-directory /tmp/habitat_packages - name: 'Grype scan of Habitat packages' + name: "Grype scan of Habitat packages" runs-on: ubuntu-latest if: ${{ success() && inputs.habitat-grype-scan == true && inputs.publish-habitat-runner_os == 'ubuntu-latest' }} - # TODO: make this a matrix operation + # TODO: make this a matrix operation needs: habitat-publish steps: - name: Install Chef Habitat (MacOS and Linux) @@ -1286,10 +1356,10 @@ jobs: run: | # Add Habitat to PATH (for current session and future steps if needed, though install.sh usually handles symlinks) echo "/hab/bin" >> $GITHUB_PATH - + # Accept the license echo "HAB_LICENSE=accept-no-persist" >> $GITHUB_ENV - + # Create the necessary directory structure for license file sudo mkdir -p /hab/accepted-licenses/ sudo touch /hab/accepted-licenses/habitat @@ -1308,13 +1378,13 @@ jobs: if [ -n "${{ inputs.publish-habitat-hab_release }}" ]; then PACKAGE="${PACKAGE}/${{ inputs.publish-habitat-hab_release }}" fi - + INSTALL_CMD="sudo hab pkg install ${PACKAGE}" - + if [ -n "${{ inputs.publish-habitat-hab_channel }}" ]; then INSTALL_CMD="${INSTALL_CMD} --channel ${{ inputs.publish-habitat-hab_channel }}" fi - + AUTH_TOKEN="${{ inputs.publish-habitat-hab_auth_token }}" if [ -z "${AUTH_TOKEN}" ]; then AUTH_TOKEN="${{ secrets.HAB_PUBLIC_BLDR_PAT }}" @@ -1325,7 +1395,7 @@ jobs: # if [ -n "${AUTH_TOKEN}" ]; then # INSTALL_CMD="${INSTALL_CMD} --auth ${AUTH_TOKEN}" # fi - + echo "Installing: ${INSTALL_CMD}" eval ${INSTALL_CMD} @@ -1342,7 +1412,7 @@ jobs: echo $OUTPUT_FILE grype dir:$PKG_PATH --name ${{ inputs.publish-habitat-hab_package }} > $OUTPUT_FILE echo "OUTPUT_FILE=$OUTPUT_FILE" >> $GITHUB_ENV - + - name: Upload Grype Scan Results uses: actions/upload-artifact@v4 with: @@ -1350,10 +1420,10 @@ jobs: path: ${{ env.OUTPUT_FILE }} habitat-grype-scan-windows: - name: 'Grype scan of Habitat packages (Windows)' + name: "Grype scan of Habitat packages (Windows)" runs-on: windows-latest if: ${{ success() && inputs.habitat-grype-scan == true && inputs.publish-habitat-runner_os == 'windows-latest' }} - # TODO: make this a matrix operation + # TODO: make this a matrix operation needs: habitat-publish steps: - name: Install Chef Habitat (Windows) @@ -1371,19 +1441,19 @@ jobs: # Write-Host "three" # ls c:/hab # ls "~\bin\hab-1.6.1245-20250905141844-x86_64-windows" - + # Write-Host "end" hab --version - + - name: Configure Habitat (Windows) run: | # Accept the license echo "HAB_LICENSE=accept-no-persist" | Out-File -FilePath $env:GITHUB_ENV -Encoding utf8 -Append - + # Create the necessary directory structure for license file New-Item -ItemType Directory -Force -Path "C:\hab\accepted-licenses" New-Item -ItemType File -Force -Path "C:\hab\accepted-licenses\habitat" - + - name: Install Grype (Windows) continue-on-error: true run: | @@ -1393,16 +1463,16 @@ jobs: $grypeUrl = "https://github.com/anchore/grype/releases/download/$grypeVersion/grype_$($grypeVersion.TrimStart('v'))_windows_amd64.zip" $grypeZip = "$env:TEMP\grype.zip" $grypeDir = "$env:TEMP\grype" - + # Download Grype Invoke-WebRequest -Uri $grypeUrl -OutFile $grypeZip - + # Extract Grype Expand-Archive -Path $grypeZip -DestinationPath $grypeDir -Force - + # Add Grype to PATH for subsequent steps echo "$grypeDir" | Out-File -FilePath $env:GITHUB_PATH -Encoding utf8 -Append - + # Verify installation & "$grypeDir\grype.exe" version @@ -1415,13 +1485,13 @@ jobs: if ("${{ inputs.publish-habitat-hab_release }}" -ne "") { $Package = "${Package}/${{ inputs.publish-habitat-hab_release }}" } - + $InstallCmd = "hab pkg install ${Package}" - + if ("${{ inputs.publish-habitat-hab_channel }}" -ne "") { $InstallCmd = "${InstallCmd} --channel ${{ inputs.publish-habitat-hab_channel }}" } - + $AuthToken = "${{ inputs.publish-habitat-hab_auth_token }}" if ([string]::IsNullOrEmpty($AuthToken)) { $AuthToken = "${{ secrets.HAB_PUBLIC_BLDR_PAT }}" @@ -1432,7 +1502,7 @@ jobs: # if (-not [string]::IsNullOrEmpty($AuthToken)) { # $InstallCmd = "${InstallCmd} --auth ${AuthToken}" # } - + Write-Host "Installing: ${InstallCmd}" Invoke-Expression $InstallCmd @@ -1449,7 +1519,7 @@ jobs: Write-Host $OutputFile grype dir:$PkgPath --name ${{ inputs.publish-habitat-hab_package }} | Out-File -FilePath $OutputFile -Encoding utf8 echo "OUTPUT_FILE=$OutputFile" | Out-File -FilePath $env:GITHUB_ENV -Encoding utf8 -Append - + - name: Upload Grype Scan Results uses: actions/upload-artifact@v4 with: @@ -1457,18 +1527,18 @@ jobs: path: ${{ env.OUTPUT_FILE }} publish: - name: 'Publishing packages' - # TODO: Add binary publishing steps – container from Dockerfile to ECR, go-releaser binary to releases page, + name: "Publishing packages" + # TODO: Add binary publishing steps – container from Dockerfile to ECR, go-releaser binary to releases page, # omnibus to artifactory, gems, choco, homebrew, other app stores runs-on: ubuntu-latest if: ${{ success() && inputs.publish-packages == true }} needs: habitat-build steps: - - name: 'Publishing packages' + - name: "Publishing packages" run: echo "Publishing packages" - + generate-sbom: - name: 'Generating SBOM' + 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 @@ -1495,7 +1565,7 @@ jobs: blackduck-fail-on-major: ${{ inputs.blackduck-fail-on-major }} quality-dashboard: - name: 'Reporting to quality dashboard' + name: "Reporting to quality dashboard" needs: generate-sbom secrets: inherit permissions: diff --git a/.github/workflows/grype.yml b/.github/workflows/grype.yml index f9d5931..136c4be 100644 --- a/.github/workflows/grype.yml +++ b/.github/workflows/grype.yml @@ -8,17 +8,17 @@ on: workflow_call: inputs: fail-grype-on-high: - description: 'Fail the pipeline if Grype finds HIGH vulnerabilities' + 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' + description: "Fail the pipeline if Grype finds CRITICAL vulnerabilities" required: false type: boolean default: false grype-image-skip-aws: - description: 'Skip Grype image scan on AWS ECR images to avoid rate limits (assumes these images are scanned with Amazon ECR scan or Trivy)' + description: "Skip Grype image scan on AWS ECR images to avoid rate limits (assumes these images are scanned with Amazon ECR scan or Trivy)" required: false type: boolean default: false @@ -35,7 +35,7 @@ jobs: uses: actions/checkout@v6 with: fetch-depth: 0 - + - name: Configure git for private env: GOPRIVATE: ${{ inputs.go-private-modules }} @@ -65,26 +65,45 @@ jobs: GITHUB_TOKEN: ${{ secrets.GH_TOKEN }} run: | SCAN_NAME="${{ github.repository }}" - + 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)) - + # 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 + + # Snapshot image IDs before build to detect newly built images + BEFORE_IMAGES=$(docker images --format "{{.ID}}" | sort) + 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 "^") + # Detect newly built images by comparing before/after snapshots + AFTER_IMAGES=$(docker images --format "{{.ID}}" | sort) + NEW_IDS=$(comm -13 <(echo "$BEFORE_IMAGES") <(echo "$AFTER_IMAGES")) + + if [ -n "$NEW_IDS" ]; then + IMAGES="" + for ID in $NEW_IDS; do + IMG=$(docker images --format "{{.Repository}}:{{.Tag}}" --filter "id=$ID" | grep -v "^" | head -1) + [ -n "$IMG" ] && IMAGES="${IMAGES}${IMG}"$'\n' + done + IMAGES=$(echo "$IMAGES" | sed '/^$/d') + fi + + # Fallback: try matching by repo name prefix + if [ -z "$IMAGES" ]; then + IMAGES=$(docker images --format "{{.Repository}}:{{.Tag}}" | grep -E "^${REPO_NAME}" | grep -v "^") + fi if [ -z "$IMAGES" ]; then - echo "⚠️ No images found with prefix ${REPO_NAME} after build-docker.sh" + echo "⚠️ No images found 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 @@ -109,19 +128,19 @@ jobs: 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" @@ -133,17 +152,17 @@ jobs: 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 multiple JSON documents # Use jq -s to slurp, deduplicate by CVE+package+version, then count CRITICAL_COUNT=$(jq -s '[.[] | .matches[]? | select(.vulnerability.severity == "Critical")] | unique_by(.vulnerability.id + .artifact.name + .artifact.version) | length' "$JSON_FILE" 2>/dev/null || echo "0") HIGH_COUNT=$(jq -s '[.[] | .matches[]? | select(.vulnerability.severity == "High")] | unique_by(.vulnerability.id + .artifact.name + .artifact.version) | length' "$JSON_FILE" 2>/dev/null || echo "0") - + echo "" echo "============================================" echo "Grype Security Scan Summary" @@ -151,11 +170,11 @@ jobs: 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%, }" From fbb3be2f0edd57a9d25065876e76ff43e7cf901c Mon Sep 17 00:00:00 2001 From: vviveksharma Date: Fri, 13 Mar 2026 15:38:28 +0530 Subject: [PATCH 2/2] fix: detect newly built images in grype scan + point grype ref to branch --- .github/workflows/ci-main-pull-request.yml | 2 +- .github/workflows/grype.yml | 17 ++++++----------- 2 files changed, 7 insertions(+), 12 deletions(-) diff --git a/.github/workflows/ci-main-pull-request.yml b/.github/workflows/ci-main-pull-request.yml index db5a188..3792069 100644 --- a/.github/workflows/ci-main-pull-request.yml +++ b/.github/workflows/ci-main-pull-request.yml @@ -949,7 +949,7 @@ jobs: run-grype-image: name: "Grype Docker image scan" if: ${{ inputs.perform-grype-image-scan }} - uses: chef/common-github-actions/.github/workflows/grype.yml@main + uses: chef/common-github-actions/.github/workflows/grype.yml@add-plpgsql-support needs: checkout secrets: inherit with: diff --git a/.github/workflows/grype.yml b/.github/workflows/grype.yml index 136c4be..183bd8c 100644 --- a/.github/workflows/grype.yml +++ b/.github/workflows/grype.yml @@ -79,22 +79,17 @@ jobs: echo "Found build-docker.sh script - using it to build images" chmod +x build-docker.sh - # Snapshot image IDs before build to detect newly built images - BEFORE_IMAGES=$(docker images --format "{{.ID}}" | sort) + # Snapshot image names before build to detect newly built images + BEFORE_IMAGES=$(docker images --format "{{.Repository}}:{{.Tag}}" | grep -v "^" | sort) GITHUB_TOKEN="${{ secrets.GH_TOKEN }}" ./build-docker.sh # Detect newly built images by comparing before/after snapshots - AFTER_IMAGES=$(docker images --format "{{.ID}}" | sort) - NEW_IDS=$(comm -13 <(echo "$BEFORE_IMAGES") <(echo "$AFTER_IMAGES")) + AFTER_IMAGES=$(docker images --format "{{.Repository}}:{{.Tag}}" | grep -v "^" | sort) + IMAGES=$(comm -13 <(echo "$BEFORE_IMAGES") <(echo "$AFTER_IMAGES")) - if [ -n "$NEW_IDS" ]; then - IMAGES="" - for ID in $NEW_IDS; do - IMG=$(docker images --format "{{.Repository}}:{{.Tag}}" --filter "id=$ID" | grep -v "^" | head -1) - [ -n "$IMG" ] && IMAGES="${IMAGES}${IMG}"$'\n' - done - IMAGES=$(echo "$IMAGES" | sed '/^$/d') + if [ -n "$IMAGES" ]; then + echo "Detected newly built images via before/after diff" fi # Fallback: try matching by repo name prefix