8282 required : false
8383 type : string
8484 default : ' '
85+ blackduck-fail-on-blocker :
86+ description : ' Fail the pipeline if BlackDuck SCA scan finds BLOCKER vulnerabilities'
87+ required : false
88+ type : boolean
89+ default : false
90+ blackduck-fail-on-critical :
91+ description : ' Fail the pipeline if BlackDuck SCA scan finds CRITICAL vulnerabilities'
92+ required : false
93+ type : boolean
94+ default : false
95+ blackduck-fail-on-major :
96+ description : ' Fail the pipeline if BlackDuck SCA scan finds MAJOR vulnerabilities'
97+ required : false
98+ type : boolean
99+ default : false
85100
86101env :
87102 # Set the default SBOM filename prefix
@@ -245,9 +260,57 @@ jobs:
245260 path : ${{ inputs.ruby-app-directory != '' && format('{0}/Gemfile.lock', inputs.ruby-app-directory) || 'Gemfile.lock' }}
246261 name : ${{ github.event.repository.name }}-Gemfile-lock.txt
247262
263+ - name : Construct BlackDuck detect arguments
264+ id : detect-args
265+ run : |
266+ # Start with base arguments (always exclude PIP detector)
267+ DETECT_ARGS="--detect.excluded.detector.types=PIP"
268+
269+ # Add low accuracy mode if requested
270+ if [[ "${{ inputs.blackduck-force-low-accuracy-mode }}" == "true" ]]; then
271+ DETECT_ARGS="${DETECT_ARGS} --detect.accuracy.required=NONE"
272+ fi
273+
274+ # Add source path if ruby-app-directory is specified
275+ if [[ -n "${{ inputs.ruby-app-directory }}" ]]; then
276+ DETECT_ARGS="${DETECT_ARGS} --detect.source.path=${{ inputs.ruby-app-directory }}"
277+ fi
278+
279+ echo "DETECT_ARGS=${DETECT_ARGS}" >> $GITHUB_ENV
280+ echo "Constructed detect_args: ${DETECT_ARGS}"
281+
282+ - name : Construct BlackDuck failure severities
283+ id : failure-severities
284+ run : |
285+ SEVERITIES=""
286+
287+ if [[ "${{ inputs.blackduck-fail-on-blocker }}" == "true" ]]; then
288+ SEVERITIES="BLOCKER"
289+ fi
290+
291+ if [[ "${{ inputs.blackduck-fail-on-critical }}" == "true" ]]; then
292+ if [[ -n "$SEVERITIES" ]]; then
293+ SEVERITIES="${SEVERITIES},CRITICAL"
294+ else
295+ SEVERITIES="CRITICAL"
296+ fi
297+ fi
298+
299+ if [[ "${{ inputs.blackduck-fail-on-major }}" == "true" ]]; then
300+ if [[ -n "$SEVERITIES" ]]; then
301+ SEVERITIES="${SEVERITIES},MAJOR"
302+ else
303+ SEVERITIES="MAJOR"
304+ fi
305+ fi
306+
307+ echo "FAILURE_SEVERITIES=${SEVERITIES}" >> $GITHUB_ENV
308+ echo "Enforcement policy: ${SEVERITIES}"
309+
248310 - name : BlackDuck SCA scan
311+ id : blackduck-scan
249312 uses : blackduck-inc/black-duck-security-scan@v2.1.1
250- continue-on-error : true # Allow pipeline to continue even with policy violations
313+ continue-on-error : false # Allow pipeline to continue even with policy violations
251314 env :
252315 GOPRIVATE : ${{ inputs.go-private-modules }}
253316 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
@@ -257,10 +320,79 @@ jobs:
257320 blackducksca_url : ${{ secrets.BLACKDUCK_SBOM_URL }} # BLACKDUCK_URL, should be https://progresssoftware.app.blackduck.com/
258321 blackducksca_token : ${{ secrets.BLACKDUCK_SCA_TOKEN }} # was BLACKDUCK_API_KEY
259322 blackducksca_scan_full : true # Force INTELLIGENT scan mode for all branches (uploads results to server)
260- 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') }}
261- # blackducksca_scan_failure_severities: 'BLOCKER,CRITICAL'
323+ detect_args : ${{ env.DETECT_ARGS }}
324+ blackducksca_scan_failure_severities : ${{ env.FAILURE_SEVERITIES }}
262325 # ignore python per https://documentation.blackduck.com/bundle/detect/page/packagemgrs/python.html
263326
327+ - name : Check BlackDuck SCA results and report violations
328+ if : ${{ inputs.blackduck-fail-on-blocker == true || inputs.blackduck-fail-on-critical == true || inputs.blackduck-fail-on-major == true }}
329+ run : |
330+ echo "Checking BlackDuck SCA scan results..."
331+ echo "Enforcement policy: BLOCKER=${{ inputs.blackduck-fail-on-blocker }}, CRITICAL=${{ inputs.blackduck-fail-on-critical }}, MAJOR=${{ inputs.blackduck-fail-on-major }}"
332+
333+ # Parse bridge.log for policy violation counts
334+ BRIDGE_LOG=".bridge/bridge.log"
335+
336+ if [ ! -f "$BRIDGE_LOG" ]; then
337+ echo "⚠️ Bridge log not found at $BRIDGE_LOG"
338+ if [ "${{ steps.blackduck-scan.outcome }}" == "failure" ]; then
339+ echo "❌ BlackDuck SCA scan failed - check logs above for details"
340+ exit 1
341+ fi
342+ exit 0
343+ fi
344+
345+ # Extract policy violation counts by severity from bridge.log
346+ # Example: "1 match has a severity level of BLOCKER", "0 matches have a severity level of CRITICAL"
347+ BLOCKER_COUNT=$(grep -oP '\d+(?=\s+matches?\s+have?\s+a\s+severity\s+level\s+of\s+BLOCKER)' "$BRIDGE_LOG" | tail -1 || echo 0)
348+ CRITICAL_COUNT=$(grep -oP '\d+(?=\s+matches?\s+have?\s+a\s+severity\s+level\s+of\s+CRITICAL)' "$BRIDGE_LOG" | tail -1 || echo 0)
349+ MAJOR_COUNT=$(grep -oP '\d+(?=\s+matches?\s+have?\s+a\s+severity\s+level\s+of\s+MAJOR)' "$BRIDGE_LOG" | tail -1 || echo 0)
350+
351+ echo ""
352+ echo "============================================"
353+ echo "BlackDuck SCA Policy Violation Summary"
354+ echo "============================================"
355+ echo "BLOCKER violations: $BLOCKER_COUNT"
356+ echo "CRITICAL violations: $CRITICAL_COUNT"
357+ echo "MAJOR violations: $MAJOR_COUNT"
358+ echo "============================================"
359+
360+ # Check for policy violations based on enabled flags
361+ SHOULD_FAIL=false
362+ VIOLATION_REASONS=""
363+
364+ if [ "${{ inputs.blackduck-fail-on-blocker }}" == "true" ] && [ "$BLOCKER_COUNT" -gt 0 ]; then
365+ echo "❌ Found $BLOCKER_COUNT BLOCKER severity policy violation(s)"
366+ VIOLATION_REASONS="${VIOLATION_REASONS}- $BLOCKER_COUNT BLOCKER severity violation(s)\n"
367+ SHOULD_FAIL=true
368+ fi
369+
370+ if [ "${{ inputs.blackduck-fail-on-critical }}" == "true" ] && [ "$CRITICAL_COUNT" -gt 0 ]; then
371+ echo "❌ Found $CRITICAL_COUNT CRITICAL severity policy violation(s)"
372+ VIOLATION_REASONS="${VIOLATION_REASONS}- $CRITICAL_COUNT CRITICAL severity violation(s)\n"
373+ SHOULD_FAIL=true
374+ fi
375+
376+ if [ "${{ inputs.blackduck-fail-on-major }}" == "true" ] && [ "$MAJOR_COUNT" -gt 0 ]; then
377+ echo "❌ Found $MAJOR_COUNT MAJOR severity policy violation(s)"
378+ VIOLATION_REASONS="${VIOLATION_REASONS}- $MAJOR_COUNT MAJOR severity violation(s)\n"
379+ SHOULD_FAIL=true
380+ fi
381+
382+ if [ "$SHOULD_FAIL" == "true" ]; then
383+ echo ""
384+ echo "============================================"
385+ echo "❌ BUILD FAILED: Policy violations detected"
386+ echo "============================================"
387+ echo -e "$VIOLATION_REASONS"
388+ echo "Review the BlackDuck scan results above for component details."
389+ echo "============================================"
390+ exit 1
391+ else
392+ echo ""
393+ echo "✅ No policy-violating vulnerabilities found within configured thresholds"
394+ fi
395+
264396# original from https://github.com/progress-platform-services/common-github-actions/blob/main/.github/workflows/examples/ci-all-sbom-main.yml
265397 generate-msft-sbom :
266398 name : Generate MSFT SBOM
0 commit comments