Skip to content
Merged
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
35 changes: 34 additions & 1 deletion .github/workflows/checkov-iac.yml
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down Expand Up @@ -64,4 +67,34 @@ jobs:
if: always()
with:
sarif_file: 'checkov-results.sarif'
category: checkov
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)"
31 changes: 20 additions & 11 deletions .github/workflows/full-stack-with-guard.yml
Original file line number Diff line number Diff line change
Expand Up @@ -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 }}
Expand All @@ -100,51 +100,60 @@ 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 }}
fail-on-issues: ${{ inputs.fail-on-issues }}

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 }}
soft-fail: ${{ !inputs.fail-on-issues }}

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]
Expand Down
21 changes: 21 additions & 0 deletions .github/workflows/semgrep-sast.yml
Original file line number Diff line number Diff line change
Expand Up @@ -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:
Expand Down Expand Up @@ -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:
Expand Down
25 changes: 25 additions & 0 deletions .github/workflows/trivy-container-scan.yml
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down Expand Up @@ -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
Expand Down