Skip to content
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
29 changes: 28 additions & 1 deletion .github/workflows/ci-main-pull-request.yml
Original file line number Diff line number Diff line change
Expand Up @@ -161,6 +161,21 @@ on:
required: false
type: boolean
default: false
perform-wiz-scan:
description: 'Perform Wiz CLI scan on Docker image'
required: false
type: boolean
default: false
wiz-fail-build:
description: 'Fail the build on Wiz policy violations'
required: false
type: boolean
default: true
wiz-image-skip-aws:
description: 'Skip AWS ECR login for Wiz image scan'
required: false
type: boolean
default: false
build:
description: 'CI Build (language-specific)'
required: false
Expand Down Expand Up @@ -908,6 +923,16 @@ 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-wiz-image:
name: 'Wiz CLI Docker image scan'
if: ${{ inputs.perform-wiz-scan }}
uses: chef/common-github-actions/.github/workflows/wiz.yml@test-pipeline
needs: checkout
secrets: inherit
with:
fail-build: ${{ inputs.wiz-fail-build }}
wiz-image-skip-aws: ${{ inputs.wiz-image-skip-aws }}

# run-srcclr:
# if: ${{ inputs.perform-srcclr-scan == true }}
Expand Down Expand Up @@ -1460,10 +1485,12 @@ 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@test-pipeline
needs: checkout # TODO: fix set-application-version
secrets: inherit
with:
github-event-name: ${{ inputs.github-event-name }}
github-branch-name: ${{ inputs.github-branch-name }}
version: ${{ inputs.version }}
export-github-sbom: ${{ inputs.export-github-sbom }}
perform-blackduck-sca-scan: ${{ inputs.perform-blackduck-sca-scan }}
Expand Down
131 changes: 101 additions & 30 deletions .github/workflows/sbom.yml
Original file line number Diff line number Diff line change
Expand Up @@ -29,6 +29,16 @@ name: Download SBOM from Insights and Convert to CSV
on:
workflow_call:
inputs:
github-event-name:
description: 'GitHub event name (pass github.event_name from calling workflow for PR comment detection)'
required: false
type: string
default: ''
github-branch-name:
description: 'GitHub branch name (pass github.ref_name from calling workflow for branch-specific logic)'
required: false
type: string
default: ''
version:
description: 'Version of the project'
required: true
Expand Down Expand Up @@ -208,7 +218,7 @@ jobs:
generate-blackduck-sbom:
name: Blackduck SCA Scan (PURPLE)
runs-on: ubuntu-latest
if: ${{ inputs.perform-blackduck-sca-scan == true }}
if: ${{ inputs.perform-blackduck-sca-scan == true }}
steps:
- name: 'Echo inputs'
run: |
Expand All @@ -218,6 +228,7 @@ jobs:
echo "DETECT_PROJECT_GROUP_NAME: ${{ inputs.blackduck-project-group-name }}"
echo "DETECT_PROJECT_NAME: ${{ inputs.blackduck-project-name }}"
echo "DETECT_PROJECT_VERSION_NAME: ${{ inputs.version }}"
echo "GitHub branch: ${{ inputs.github-branch-name }}"

- name: Checkout source
uses: actions/checkout@v6
Expand Down Expand Up @@ -266,6 +277,9 @@ jobs:
# Start with base arguments (always exclude PIP detector)
DETECT_ARGS="--detect.excluded.detector.types=PIP"

# Add timeout configurations to prevent FAILURE_TIMEOUT errors
DETECT_ARGS="${DETECT_ARGS} --detect.timeout=1800"

# Add low accuracy mode if requested
if [[ "${{ inputs.blackduck-force-low-accuracy-mode }}" == "true" ]]; then
DETECT_ARGS="${DETECT_ARGS} --detect.accuracy.required=NONE"
Expand All @@ -275,6 +289,11 @@ jobs:
if [[ -n "${{ inputs.ruby-app-directory }}" ]]; then
DETECT_ARGS="${DETECT_ARGS} --detect.source.path=${{ inputs.ruby-app-directory }}"
fi

if [[ "${{ inputs.github-event-name }}" == "pull_request" ]]; then
# RAPID scan for PRs - automatically compares against baseline from target branch
DETECT_ARGS="${DETECT_ARGS} --detect.blackduck.scan.mode=RAPID"
fi

echo "DETECT_ARGS=${DETECT_ARGS}" >> $GITHUB_ENV
echo "Constructed detect_args: ${DETECT_ARGS}"
Expand Down Expand Up @@ -307,58 +326,110 @@ jobs:
echo "FAILURE_SEVERITIES=${SEVERITIES}" >> $GITHUB_ENV
echo "Enforcement policy: ${SEVERITIES}"

- name: BlackDuck SCA scan
id: blackduck-scan
# Full / INTELLIGENT scan — push to main, develop, release branches.
# Persists a baseline to the BlackDuck server so PR scans can diff against it.
- name: BlackDuck SCA Full Scan
id: blackduck-full-scan
if: ${{ inputs.github-event-name != 'pull_request' }}
uses: blackduck-inc/black-duck-security-scan@v2.1.1
continue-on-error: false # Allow pipeline to continue even with policy violations
env:
continue-on-error: false
env:
GOPRIVATE: ${{ inputs.go-private-modules }}
DETECT_PROJECT_GROUP_NAME: ${{ inputs.blackduck-project-group-name}} #'Chef-Agents' # <the_parent_group_of_your_target_project>, Chef, Chef-Agents, Chef-Automate, Chef-Chef360, Chef-Habitat, Chef-Infrastructure-Server, Chef-Shared-Services
DETECT_PROJECT_GROUP_NAME: ${{ inputs.blackduck-project-group-name }}
DETECT_PROJECT_NAME: ${{ inputs.blackduck-project-name }}
DETECT_PROJECT_VERSION_NAME: ${{ inputs.version }} # <the_existing_version_in_your_project>
DETECT_PROJECT_VERSION_NAME: ${{ inputs.version }}
with:
blackducksca_url: ${{ secrets.BLACKDUCK_SBOM_URL }} # BLACKDUCK_URL, should be https://progresssoftware.app.blackduck.com/
blackducksca_url: ${{ secrets.BLACKDUCK_SBOM_URL }}
blackducksca_token: ${{ secrets.BLACKDUCK_SCA_TOKEN }}
blackducksca_scan_failure_severities: ${{ env.FAILURE_SEVERITIES }}
blackducksca_scan_full: true
detect_args: ${{ env.DETECT_ARGS }}

# RAPID scan — pull request events only.
# Diffs against the persisted baseline so only violations *new to this PR* are reported.
- name: BlackDuck SCA PR Scan
id: blackduck-pr-scan
if: ${{ inputs.github-event-name == 'pull_request' }}
uses: blackduck-inc/black-duck-security-scan@v2.1.1
continue-on-error: false
env:
GOPRIVATE: ${{ inputs.go-private-modules }}
DETECT_PROJECT_GROUP_NAME: ${{ inputs.blackduck-project-group-name }}
DETECT_PROJECT_NAME: ${{ inputs.blackduck-project-name }}
DETECT_PROJECT_VERSION_NAME: ${{ inputs.version }}
DETECT_BLACKDUCK_RAPID_COMPARE_MODE: BOM_COMPARE
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)
blackducksca_scan_full: false
detect_args: ${{ env.DETECT_ARGS }}
# ignore python per https://documentation.blackduck.com/bundle/detect/page/packagemgrs/python.html
github_token: ${{ secrets.GH_TOKEN || secrets.GITHUB_TOKEN }}
blackducksca_prComment_enabled: true

- name: Debug - dump bridge.log
if: always()
run: |
BRIDGE_LOG=".bridge/bridge.log"
if [ -f "$BRIDGE_LOG" ]; then
echo "========== bridge.log START =========="
cat "$BRIDGE_LOG"
echo "========== bridge.log END =========="
else
echo "bridge.log not found"
echo "Contents of .bridge/:"
ls -la .bridge/ || true
fi

- 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"

BLOCKER_COUNT=0
CRITICAL_COUNT=0
MAJOR_COUNT=0

if [ ! -f "$BRIDGE_LOG" ]; then
echo "⚠️ Bridge log not found"
echo "⚠️ Bridge log not found - skipping violation count check"
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"

# Works for both RAPID and Full scans:
# - RAPID log has: "Critical and blocking policy violations" → "* Components: N", "* Security: N", etc.
# - Full log has: "Policy Severity counts:" → "N matches have a severity level of BLOCKER", etc.
extract() { grep "$1" "$BRIDGE_LOG" | awk '{print $NF}' | head -1; }

if grep -q "Critical and blocking policy violations" "$BRIDGE_LOG"; then
# RAPID scan — map categories to severity levels
BLOCKER_COUNT=$(extract "\* Components:" || echo 0)
CRITICAL_COUNT=$(extract "\* Security:" || echo 0)
MAJOR_COUNT=$(extract "\* License:" || echo 0)
OTHER_COUNT=$(extract "\* Other:" || echo 0)
[ "${OTHER_COUNT:-0}" -gt 0 ] && CRITICAL_COUNT=$((CRITICAL_COUNT + OTHER_COUNT))
elif grep -q "Policy Severity counts:" "$BRIDGE_LOG"; then
# Full scan
BLOCKER_COUNT=$(grep "severity level of BLOCKER" "$BRIDGE_LOG" | grep -oE '^[0-9]+' || echo 0)
CRITICAL_COUNT=$(grep "severity level of CRITICAL" "$BRIDGE_LOG" | grep -oE '^[0-9]+' || echo 0)
MAJOR_COUNT=$(grep "severity level of MAJOR" "$BRIDGE_LOG" | grep -oE '^[0-9]+' || echo 0)
else
echo "⚠️ No policy violation summary 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 "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, "
[ "${{ 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%, }"
Expand Down
6 changes: 6 additions & 0 deletions .github/workflows/trufflehog.yml
Original file line number Diff line number Diff line change
Expand Up @@ -38,6 +38,12 @@ jobs:
git fetch origin main
git branch main origin/main

- name: Setup git refs for PR scan
if: ${{ inputs.github-event-name == 'pull_request' }}
run: |
git fetch origin main
git branch main origin/main

- name: TruffleHog Full secret scan
id: trufflehog-full-scan
if: ${{ inputs.github-event-name != 'pull_request' }}
Expand Down
116 changes: 116 additions & 0 deletions .github/workflows/wiz.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,116 @@
# wiz.yml
# Wiz CLI security scan for Docker image vulnerabilities and policy violations
# Uses the prgs-community/githubactions-reusableworkflows/actions/wizcli composite action
# which handles Wiz CLI install, AKeyless auth, scanning, and job summary automatically.
# https://docs.wiz.io/wiz-docs/docs/wiz-cli-overview

name: Wiz CLI security scan

on:
workflow_call:
inputs:
fail-build:
description: 'Fail the build on Wiz policy violations'
required: false
type: boolean
default: true
wiz-image-skip-aws:
description: 'Skip AWS ECR login (assumes images are scanned elsewhere)'
required: false
type: boolean
default: false

jobs:
wiz-scan:
name: Wiz CLI container image scan
runs-on: ubuntu-latest
permissions:
id-token: write
contents: read
steps:
- name: Checkout code
uses: actions/checkout@v6
with:
fetch-depth: 0

- name: Configure git for private repos
run: git config --global url."https://${{ secrets.GH_TOKEN }}@github.com/".insteadOf "https://github.com/"

# - name: Configure AWS credentials
# uses: aws-actions/configure-aws-credentials@v4
# if: ${{ !inputs.wiz-image-skip-aws }}
# 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: Login to Amazon ECR
# id: login-ecr
# if: ${{ !inputs.wiz-image-skip-aws }}
# uses: aws-actions/amazon-ecr-login@v2

- name: Build Docker image
id: build-image
env:
GITHUB_TOKEN: ${{ secrets.GH_TOKEN }}
run: |
REPO_NAME=$(basename $(pwd))

if [ ! -f "Dockerfile" ]; then
echo "❌ No Dockerfile found - this workflow requires a Dockerfile to scan Docker image"
exit 1
fi

echo "Building Docker image..."

# 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

IMAGE=$(docker images --format "{{.Repository}}:{{.Tag}}" | grep -E "^${REPO_NAME}" | grep -v "^<none>" | head -1)

if [ -z "$IMAGE" ]; then
echo "⚠️ No image found with prefix ${REPO_NAME} after build-docker.sh"
echo "Checking for any recently built images..."
IMAGE=$(docker images --format "{{.CreatedAt}}\t{{.Repository}}:{{.Tag}}" | sort -r | head -1 | cut -f2)
fi
# Strategy 2: Check for Makefile with compose-build target
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 image..."
docker compose images

IMAGE=$(docker images --format "{{.Repository}}:{{.Tag}}" | grep "^${REPO_NAME}" | grep -v "^<none>" | head -1)

if [ -z "$IMAGE" ]; then
echo "No image found with prefix ${REPO_NAME}, using most recent image"
IMAGE=$(docker images --format "{{.Repository}}:{{.Tag}}" | grep -v "^<none>" | head -1)
fi
# Strategy 3: Fallback to standard docker build
else
echo "Using standard docker build with GITHUB_TOKEN build arg"
docker build --build-arg GITHUB_TOKEN="${{ secrets.GH_TOKEN }}" -t "${REPO_NAME}:latest" .
IMAGE="${REPO_NAME}:latest"
fi

if [ -z "$IMAGE" ]; then
echo "❌ No Docker image found after build"
exit 1
fi

echo "Image to scan: $IMAGE"
echo "IMAGE=$IMAGE" >> "$GITHUB_OUTPUT"

- name: Wiz CLI container image scan
id: wiz-scan
uses: prgs-community/githubactions-reusableworkflows/actions/wizcli@latest
with:
scan-type: 'container-image'
scan-target: ${{ steps.build-image.outputs.IMAGE }}
fail-build: ${{ inputs.fail-build }}