From 7ead7d7716eedd11ca1ee2735e33125e4490a255 Mon Sep 17 00:00:00 2001 From: Daniel Date: Thu, 30 Apr 2026 15:08:05 +0200 Subject: [PATCH] =?UTF-8?q?feat:=20SARIF-Parser=20f=C3=BCr=20Findings-Coun?= =?UTF-8?q?ts,=20an=20Deployment=20Guard=20durchreichen=20(Phase=202)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .github/workflows/checkov-iac.yml | 35 ++++++++++++++++++++- .github/workflows/full-stack-with-guard.yml | 31 +++++++++++------- .github/workflows/semgrep-sast.yml | 21 +++++++++++++ .github/workflows/trivy-container-scan.yml | 25 +++++++++++++++ 4 files changed, 100 insertions(+), 12 deletions(-) diff --git a/.github/workflows/checkov-iac.yml b/.github/workflows/checkov-iac.yml index 615f39f..da95c24 100644 --- a/.github/workflows/checkov-iac.yml +++ b/.github/workflows/checkov-iac.yml @@ -37,6 +37,9 @@ jobs: checkov: name: Checkov IaC Scan runs-on: ubuntu-latest + outputs: + total_count: ${{ steps.parse.outputs.total_count }} + critical_count: ${{ steps.parse.outputs.critical_count }} permissions: contents: read security-events: write # Required for SARIF upload to GitHub Security tab @@ -64,4 +67,34 @@ jobs: if: always() with: sarif_file: 'checkov-results.sarif' - category: checkov \ No newline at end of file + category: checkov + - name: Parse SARIF for Deployment Guard + id: parse + if: always() + shell: bash + run: | + # checkov-action v12 schreibt manchmal in ein Verzeichnis statt direkt zur Datei + SARIF="" + for candidate in \ + checkov-results.sarif \ + checkov-results.sarif/results_sarif.sarif \ + results_sarif.sarif; do + if [ -f "$candidate" ]; then + SARIF="$candidate" + break + fi + done + if [ -z "$SARIF" ]; then + echo "total_count=0" >> $GITHUB_OUTPUT + echo "critical_count=0" >> $GITHUB_OUTPUT + exit 0 + fi + # Checkov: alle level=error sind failed checks. CRITICAL via properties.severity. + TOTAL=$(jq '[.runs[0].results[]? | select(.level == "error")] | length' "$SARIF") + CRIT=$(jq '[.runs[0].results[]? + | select(.level == "error") + | select((.properties.severity? // "" | ascii_upcase) == "CRITICAL") + ] | length' "$SARIF") + echo "total_count=${TOTAL:-0}" >> $GITHUB_OUTPUT + echo "critical_count=${CRIT:-0}" >> $GITHUB_OUTPUT + echo "Checkov: $TOTAL failed, $CRIT CRITICAL (parsed: $SARIF)" \ No newline at end of file diff --git a/.github/workflows/full-stack-with-guard.yml b/.github/workflows/full-stack-with-guard.yml index 87cd68c..bf7e1a7 100644 --- a/.github/workflows/full-stack-with-guard.yml +++ b/.github/workflows/full-stack-with-guard.yml @@ -91,7 +91,7 @@ on: jobs: trivy: if: inputs.skip-trivy == false - uses: ./.github/workflows/trivy-container-scan.yml + uses: BlueCodeIT/pipeline-security-templates/.github/workflows/trivy-container-scan.yml@main with: image: ${{ inputs.trivy-image }} dockerfile: ${{ inputs.trivy-dockerfile }} @@ -100,7 +100,7 @@ jobs: semgrep: if: inputs.skip-semgrep == false - uses: ./.github/workflows/semgrep-sast.yml + uses: BlueCodeIT/pipeline-security-templates/.github/workflows/semgrep-sast.yml@main with: config: ${{ inputs.semgrep-config }} severity: ${{ inputs.semgrep-severity }} @@ -108,7 +108,7 @@ jobs: checkov: if: inputs.skip-checkov == false - uses: ./.github/workflows/checkov-iac.yml + uses: BlueCodeIT/pipeline-security-templates/.github/workflows/checkov-iac.yml@main with: directory: ${{ inputs.checkov-directory }} framework: ${{ inputs.checkov-framework }} @@ -116,35 +116,44 @@ jobs: deployment-guard: name: Deployment Guard Risk Score - if: inputs.skip-guard == false runs-on: ubuntu-latest needs: [trivy, semgrep, checkov] if: always() && inputs.skip-guard == false + permissions: + contents: read steps: - name: Check API key is set + id: check-key env: GUARD_API_KEY: ${{ secrets.guard-api-key }} run: | if [ -z "$GUARD_API_KEY" ]; then echo "⚠️ GUARD_API_KEY secret not set — skipping Deployment Guard." echo "Get a free API key: https://www.bluecodeit.com/signup" - exit 0 + echo "key_set=false" >> $GITHUB_OUTPUT + else + echo "key_set=true" >> $GITHUB_OUTPUT fi - name: Checkout - if: env.GUARD_API_KEY != '' + if: steps.check-key.outputs.key_set == 'true' uses: actions/checkout@v4 with: - fetch-depth: 2 # Required for diff analysis + fetch-depth: 2 - name: Run Deployment Guard - if: env.GUARD_API_KEY != '' + if: steps.check-key.outputs.key_set == 'true' uses: BlueCodeIT/deployment-guard-action@v1 with: api-key: ${{ secrets.guard-api-key }} - fail-on-warn: ${{ inputs.guard-fail-on-warn }} - - summary: + fail-on-blocked: ${{ inputs.guard-fail-on-warn }} + trivy-critical-cves: ${{ needs.trivy.outputs.critical_count || '0' }} + trivy-high-cves: ${{ needs.trivy.outputs.high_count || '0' }} + semgrep-findings: ${{ needs.semgrep.outputs.total_count || '0' }} + semgrep-high-severity: ${{ needs.semgrep.outputs.high_count || '0' }} + checkov-failed-checks: ${{ needs.checkov.outputs.total_count || '0' }} + checkov-critical-failures: ${{ needs.checkov.outputs.critical_count || '0' }} + summary: name: Security + Risk Summary runs-on: ubuntu-latest needs: [trivy, semgrep, checkov, deployment-guard] diff --git a/.github/workflows/semgrep-sast.yml b/.github/workflows/semgrep-sast.yml index 37fe868..dd0e013 100644 --- a/.github/workflows/semgrep-sast.yml +++ b/.github/workflows/semgrep-sast.yml @@ -33,6 +33,9 @@ jobs: semgrep: name: Semgrep Static Analysis runs-on: ubuntu-latest + outputs: + total_count: ${{ steps.parse.outputs.total_count }} + high_count: ${{ steps.parse.outputs.high_count }} container: image: semgrep/semgrep:1.92.0 permissions: @@ -98,6 +101,24 @@ jobs: sarif_file: 'semgrep-results.sarif' category: semgrep + - name: Parse SARIF for Deployment Guard + id: parse + if: always() + shell: sh + run: | + # jq nachinstallieren (Alpine-Container) + apk add --no-cache jq >/dev/null 2>&1 || true + if [ ! -f semgrep-results.sarif ]; then + echo "total_count=0" >> "$GITHUB_OUTPUT" + echo "high_count=0" >> "$GITHUB_OUTPUT" + exit 0 + fi + # Semgrep SARIF: level "error" = HIGH, sonst niedriger + TOTAL=$(jq '[.runs[0].results[]?] | length' semgrep-results.sarif) + HIGH=$(jq '[.runs[0].results[]? | select(.level == "error")] | length' semgrep-results.sarif) + echo "total_count=${TOTAL:-0}" >> "$GITHUB_OUTPUT" + echo "high_count=${HIGH:-0}" >> "$GITHUB_OUTPUT" + echo "Semgrep: $TOTAL total, $HIGH HIGH" - name: Run Semgrep again (table output for logs) if: always() env: diff --git a/.github/workflows/trivy-container-scan.yml b/.github/workflows/trivy-container-scan.yml index e624d9b..384f6fc 100644 --- a/.github/workflows/trivy-container-scan.yml +++ b/.github/workflows/trivy-container-scan.yml @@ -36,6 +36,9 @@ jobs: trivy: name: Trivy Vulnerability Scan runs-on: ubuntu-latest + outputs: + critical_count: ${{ steps.parse.outputs.critical_count }} + high_count: ${{ steps.parse.outputs.high_count }} permissions: contents: read security-events: write # Required to upload SARIF to GitHub Security tab @@ -65,6 +68,28 @@ jobs: sarif_file: 'trivy-results.sarif' category: trivy-container + - name: Parse SARIF for Deployment Guard + id: parse + if: always() + shell: bash + run: | + if [ ! -f trivy-results.sarif ]; then + echo "critical_count=0" >> $GITHUB_OUTPUT + echo "high_count=0" >> $GITHUB_OUTPUT + exit 0 + fi + # Trivy SARIF: properties.security-severity = CVSS-Score (string) + # CRITICAL: >= 9.0, HIGH: 7.0–8.9 + CRIT=$(jq '[.runs[0].results[]? + | (.properties."security-severity" // "0" | tonumber) + | select(. >= 9.0)] | length' trivy-results.sarif) + HIGH=$(jq '[.runs[0].results[]? + | (.properties."security-severity" // "0" | tonumber) + | select(. >= 7.0 and . < 9.0)] | length' trivy-results.sarif) + echo "critical_count=${CRIT:-0}" >> $GITHUB_OUTPUT + echo "high_count=${HIGH:-0}" >> $GITHUB_OUTPUT + echo "Trivy: $CRIT CRITICAL, $HIGH HIGH" + - name: Run Trivy (table output for logs) if: always() uses: aquasecurity/trivy-action@0.28.0