From b6cf6c78ca2875551c92778869930a889a29a121 Mon Sep 17 00:00:00 2001 From: JonasPollokZweitag <„jonas.pollok@zweitag.de“> Date: Wed, 17 Dec 2025 07:54:51 +0100 Subject: [PATCH 01/53] chore: add setup-python action --- .github/actions/setup-python/action.yml | 27 +++++++++++++++++++++++++ 1 file changed, 27 insertions(+) create mode 100644 .github/actions/setup-python/action.yml diff --git a/.github/actions/setup-python/action.yml b/.github/actions/setup-python/action.yml new file mode 100644 index 0000000..ec1a995 --- /dev/null +++ b/.github/actions/setup-python/action.yml @@ -0,0 +1,27 @@ +--- +name: Setup Python +description: | + Setup all dependencies for running Python +inputs: + working-directory: + description: | + The working directory to install dependencies in + required: true + extensions-package-registry-password: + description: | + The password for the extensions package registry + required: true +runs: + using: composite + steps: + - name: Install poetry + run: pipx install poetry==1.8.4 + shell: bash + - uses: actions/setup-python@v5 + with: + python-version: "3.11" + cache: "poetry" + cache-dependency-path: "${{ inputs.working-directory }}/poetry.lock" + - run: poetry install + shell: bash + working-directory: ${{ inputs.working-directory }} From 2f7eba8ae4f9dfa2eb0fbf02b30ae6944e1032dd Mon Sep 17 00:00:00 2001 From: JonasPollokZweitag <„jonas.pollok@zweitag.de“> Date: Wed, 17 Dec 2025 07:55:12 +0100 Subject: [PATCH 02/53] chore: add setup-trivy action --- .github/actions/setup-trivy/action.yml | 21 +++++++++++++++++++++ 1 file changed, 21 insertions(+) create mode 100644 .github/actions/setup-trivy/action.yml diff --git a/.github/actions/setup-trivy/action.yml b/.github/actions/setup-trivy/action.yml new file mode 100644 index 0000000..2867d61 --- /dev/null +++ b/.github/actions/setup-trivy/action.yml @@ -0,0 +1,21 @@ +name: Setup Trivy +description: | + Setup Trivy for Docker and configuration scanning +inputs: + working-directory: + description: | + The working directory to use Trivy in + required: false + default: . +runs: + using: composite + steps: + - name: Install Trivy + run: | + sudo apt-get install wget apt-transport-https gnupg + wget -qO - https://aquasecurity.github.io/trivy-repo/deb/public.key | gpg --dearmor | sudo tee /usr/share/keyrings/trivy.gpg > /dev/null + echo "deb [signed-by=/usr/share/keyrings/trivy.gpg] https://aquasecurity.github.io/trivy-repo/deb generic main" | sudo tee -a /etc/apt/sources.list.d/trivy.list + sudo apt-get update + sudo apt-get install trivy + shell: bash + working-directory: ${{ inputs.working-directory }} From 691003ce793f20deb2dd69a57bc274dbd9d57a5f Mon Sep 17 00:00:00 2001 From: JonasPollokZweitag <„jonas.pollok@zweitag.de“> Date: Wed, 17 Dec 2025 07:56:02 +0100 Subject: [PATCH 03/53] chore: add test reporter templates --- security-scanning/config_scan_template.hbs | 67 ++++++++++++++++++++++ security-scanning/image_scan_template.hbs | 66 +++++++++++++++++++++ 2 files changed, 133 insertions(+) create mode 100644 security-scanning/config_scan_template.hbs create mode 100644 security-scanning/image_scan_template.hbs diff --git a/security-scanning/config_scan_template.hbs b/security-scanning/config_scan_template.hbs new file mode 100644 index 0000000..6b5fe16 --- /dev/null +++ b/security-scanning/config_scan_template.hbs @@ -0,0 +1,67 @@ +## Summary of {{ctrf.tool.name}} Security Scan Results + +| **Tests 📝** | **Passed ✅** | **Failed ❌** | +| --- | --- | --- | +| {{ctrf.summary.tests}} | {{ctrf.summary.passed}} | {{ctrf.summary.failed}} | + +{{#if (eq ctrf.summary.passed ctrf.summary.tests)}} +### All Tests Passed! 🎉 +{{else}} +## Failed Tests: +{{#each ctrf.tests}} +{{#if (eq status "failed")}} +
+ ❌ {{name}} + + + + + + {{#if lines}} + + + + + {{/if}} + {{#if severity}} + + + + + {{/if}} + {{#if guideline}} + + + + + {{/if}} + {{#if description}} + + + + + {{/if}} + {{#if resolution}} + + + + + {{/if}} + {{#if references}} + + + + + {{/if}} +
file{{file}}
lines{{lines}}
severity{{severity}}
guideline + {{guideline}} +
description{{description}}
proposed solution{{resolution}}
references + {{#each references}} + {{this}}{{#unless @last}}, {{/unless}} + {{/each}} +
+
+ +{{/if}} +{{/each}} +{{/if}} diff --git a/security-scanning/image_scan_template.hbs b/security-scanning/image_scan_template.hbs new file mode 100644 index 0000000..a57e78e --- /dev/null +++ b/security-scanning/image_scan_template.hbs @@ -0,0 +1,66 @@ +## Summary of {{ctrf.tool.name}} Security Scan Results + +{{#if (eq ctrf.summary.passed ctrf.summary.tests)}} +### No vulnerabilities with set severity found! 🎉 +{{else}} +## Found Vulnerabilities: +{{#each ctrf.tests}} +
+ ❌ {{id}} {{pkgName}} + + + + + + {{#if installedVersion}} + + + + + {{/if}} + {{#if fixedVersion}} + + + + + {{/if}} + {{#if status}} + + + + + {{/if}} + {{#if severity}} + + + + + {{/if}} + {{#if description}} + + + + + {{/if}} + {{#if source}} + + + + + {{/if}} + {{#if references}} + + + + + {{/if}} +
image{{image}}
installed version{{installedVersion}}
fixed version{{fixedVersion}}
status{{status}}
severity{{severity}}
description{{description}}
source + {{source.URL}} +
references + {{#each references}} + {{this}}{{#unless @last}}, {{/unless}} + {{/each}} +
+
+{{/each}} +{{/if}} From 1d7267e9734d38139f5ab30f8bd18bf06811cf39 Mon Sep 17 00:00:00 2001 From: JonasPollokZweitag <„jonas.pollok@zweitag.de“> Date: Wed, 17 Dec 2025 07:56:52 +0100 Subject: [PATCH 04/53] chore: add scan report -> ctrf converter --- security-scanning/checkov2ctrf.py | 68 ++++++++++++++++++++ security-scanning/trivyconfig2crtf.py | 90 +++++++++++++++++++++++++++ security-scanning/trivyimage2ctrf.py | 80 ++++++++++++++++++++++++ 3 files changed, 238 insertions(+) create mode 100644 security-scanning/checkov2ctrf.py create mode 100644 security-scanning/trivyconfig2crtf.py create mode 100644 security-scanning/trivyimage2ctrf.py diff --git a/security-scanning/checkov2ctrf.py b/security-scanning/checkov2ctrf.py new file mode 100644 index 0000000..b741019 --- /dev/null +++ b/security-scanning/checkov2ctrf.py @@ -0,0 +1,68 @@ +import json +import sys + + +def extract_checks(target, status): + # extracts checks of a check_type (e.g. terraform) with a status ('fail' oder 'pass'). + checks = [] + key = "failed_checks" if status == "fail" else "passed_checks" + for check in target.get("results", {}).get(key, []): + checks.append({ + "name": check.get("check_name"), + "status": "passed" if status == "pass" else "failed", + "duration": 1, + "file": check.get("file_path"), + "lines": check.get("file_line_range"), + "guideline": check.get("guideline") if check.get("guideline") != 'null' else "", + }) + return checks + + +def checkov_to_ctrf(checkov_json): + tests = [] + for target in checkov_json: + tests.extend(extract_checks(target, "fail")) + tests.extend(extract_checks(target, "pass")) + + total = len(tests) + passed = sum(1 for t in tests if t["status"] == "passed") + failed = sum(1 for t in tests if t["status"] == "failed") + pending = 0 + skipped = 0 + other = 0 + start = 0 + stop = 1 + return { + "results": { + "tool": { + "name": "Checkov " + }, + "summary": { + "tests": total, + "passed": passed, + "failed": failed, + "pending": pending, + "skipped": skipped, + "other": other, + "start": start, + "stop": stop + }, + "tests": tests, + "environment": { + "appName": "kamium-deployment", + "buildName": "kamium-deployment", + "buildNumber": "1" + } + } + } + + +if __name__ == "__main__": + if len(sys.argv) != 3: + print("Usage: python checkov2ctrf.py ") + sys.exit(1) + with open(sys.argv[1]) as old_f: + checkov_json = json.load(old_f) + ctrf_json = checkov_to_ctrf(checkov_json) + with open(sys.argv[2], "w") as new_f: + json.dump(ctrf_json, new_f, indent=2) diff --git a/security-scanning/trivyconfig2crtf.py b/security-scanning/trivyconfig2crtf.py new file mode 100644 index 0000000..81998f9 --- /dev/null +++ b/security-scanning/trivyconfig2crtf.py @@ -0,0 +1,90 @@ +import json +import sys + + +def extract_checks_from_trivy_result(result): + checks = [] + misconfigs = result.get("Misconfigurations", []) + for misconf in misconfigs: + lines = None + cause = misconf.get("CauseMetadata", {}) + if "StartLine" in cause and "EndLine" in cause: + lines = [cause["StartLine"], cause["EndLine"]] + elif "Code" in cause and "Lines" in cause["Code"] and cause["Code"]["Lines"]: + # Fallback: nehme die ersten und letzten Zeilennummern aus Code.Lines + code_lines = cause["Code"]["Lines"] + if isinstance(code_lines, list) and code_lines: + lines = [code_lines[0].get( + "Number"), code_lines[-1].get("Number")] + + checks.append({ + "name": misconf.get("Title", misconf.get("ID", "")), + "status": "failed" if misconf.get("Status") == "FAIL" else "passed", + "duration": 1, + "file": result.get("Target", ""), + "lines": lines, + "guideline": misconf.get("PrimaryURL", ""), + "severity": misconf.get("Severity", ""), + "description": misconf.get("Description", ""), + "message": misconf.get("Message", ""), + "resolution": misconf.get("Resolution", ""), + "references": misconf.get("References", []), + "type": misconf.get("Type", ""), + "id": misconf.get("ID", ""), + }) + return checks + + +def trivy_to_ctrf(trivy_json): + tests = [] + successes_sum = 0 + results = trivy_json.get("Results", []) + for result in results: + tests.extend(extract_checks_from_trivy_result(result)) + + # Successful scans have no misconfigurations + misconf_summary = result.get("MisconfSummary", {}) + successes_sum += misconf_summary.get("Successes", 0) + + total = len(tests) + successes_sum + passed = successes_sum + failed = sum(1 for t in tests if t["status"] == "failed") + pending = 0 + skipped = 0 + other = 0 + start = 0 + stop = 1 + return { + "results": { + "tool": { + "name": "Trivy Configuration" + }, + "summary": { + "tests": total, + "passed": passed, + "failed": failed, + "pending": pending, + "skipped": skipped, + "other": other, + "start": start, + "stop": stop + }, + "tests": tests, + "environment": { + "appName": "kamium-elastic", + "buildName": "kamium-elastic", + "buildNumber": "1" + } + } + } + + +if __name__ == "__main__": + if len(sys.argv) != 3: + print("Usage: python trivy2ctrf.py ") + sys.exit(1) + with open(sys.argv[1]) as old_f: + trivy_json = json.load(old_f) + ctrf_json = trivy_to_ctrf(trivy_json) + with open(sys.argv[2], "w") as new_f: + json.dump(ctrf_json, new_f, indent=2) diff --git a/security-scanning/trivyimage2ctrf.py b/security-scanning/trivyimage2ctrf.py new file mode 100644 index 0000000..38f7699 --- /dev/null +++ b/security-scanning/trivyimage2ctrf.py @@ -0,0 +1,80 @@ +import json +import sys + + +def extract_checks_from_trivy_result(target): + checks = [] + + vulnerabilities = target.get("Vulnerabilities", []) + + for vuln in vulnerabilities: + checks.append({ + "name": vuln.get("PkgID", ""), + "status": vuln.get("Status", "unknown"), + "duration": 1, + "severity": vuln.get("Severity", ""), + "id": vuln.get("VulnerabilityID", ""), + "pkgName": vuln.get("PkgName", ""), + "installedVersion": vuln.get("InstalledVersion", ""), + "fixedVersion": vuln.get("FixedVersion", "no fix available"), + "image": target.get("Target", ""), + "source": vuln.get("DataSource", []), + "description": vuln.get("Description", ""), + "references": vuln.get("References", []) + }) + return checks + + +def trivy_to_ctrf(trivy_json): + tests = [] + successes_sum = 0 + results = trivy_json.get("Results", []) + for result in results: + tests.extend(extract_checks_from_trivy_result(result)) + + # Successful scans have no misconfigurations + misconf_summary = result.get("MisconfSummary", {}) + successes_sum += misconf_summary.get("Successes", 0) + + total = len(tests) + passed = 0 + failed = len(tests) + pending = 0 + skipped = 0 + other = 0 + start = 0 + stop = 1 + return { + "results": { + "tool": { + "name": "Trivy Image" + }, + "summary": { + "tests": total, + "passed": passed, + "failed": failed, + "pending": pending, + "skipped": skipped, + "other": other, + "start": start, + "stop": stop + }, + "tests": tests, + "environment": { + "appName": "kamium-elastic", + "buildName": "kamium-elastic", + "buildNumber": "1" + } + } + } + + +if __name__ == "__main__": + if len(sys.argv) != 3: + print("Usage: python trivy2ctrf.py ") + sys.exit(1) + with open(sys.argv[1]) as old_f: + trivy_json = json.load(old_f) + ctrf_json = trivy_to_ctrf(trivy_json) + with open(sys.argv[2], "w") as new_f: + json.dump(ctrf_json, new_f, indent=2) From e41d1411b5e9a441c38e7b5a850de82d4157e1a0 Mon Sep 17 00:00:00 2001 From: JonasPollokZweitag <„jonas.pollok@zweitag.de“> Date: Thu, 18 Dec 2025 13:23:39 +0100 Subject: [PATCH 05/53] chore: add first version of configurable security scan action --- .github/workflows/security-scan.yaml | 309 +++++++++++++++++++++++++++ 1 file changed, 309 insertions(+) create mode 100644 .github/workflows/security-scan.yaml diff --git a/.github/workflows/security-scan.yaml b/.github/workflows/security-scan.yaml new file mode 100644 index 0000000..0709163 --- /dev/null +++ b/.github/workflows/security-scan.yaml @@ -0,0 +1,309 @@ +# .github/workflows/build-and-test.yml +name: Build and Test + +on: + workflow_call: + inputs: + scan-tool: + description: 'Tool das für den Scan verwendet werden soll, aktuell verfügbar: "checkov", "trivy"' + required: true + default: 'trivy' + type: string + scan-type: + description: 'Art des Scans, aktuell nur für Trivy verfügbar: "image", "filesystem" (default), "config"' + required: false + default: 'filesystem' + type: string + trivyignorefile: + description: 'Pfad zur Trivy Ignore-Datei' + default: '' + required: false + type: string + checkovbaseline: + description: 'Pfad zur Checkov Baseline-Datei' + default: '' + required: false + type: string + path: + description: 'Pfad in dem der Scan ausgeführt werden soll (bei image-scans muss der Ordner die Dockerfile enthalten)' + required: false + default: '.' + type: string + use-test-reporter: + description: 'Ob die Testergebnisse als Report angehängt werden sollen' + required: false + default: 'true' + type: boolean + issue-on-findings: + description: 'An welchen Github User bei gefailelten Scans ein Github Issue erstellt werden soll (Komma getrennte Liste: ''@user1'', ''@user2''). Wenn leer, wird kein Issue erstellt.' + required: false + default: 'false' + type: string + secrets: + GITHUB_TOKEN: + description: 'GitHub Token zum Download der Trivy-DB' + required: false + DOCKER_IMAGE_SECRETS: + description: 'Docker Image Build Secrets' + required: false + +jobs: + + trivy_configuration_scan: + if: ${{ inputs.scan-tool == 'trivy' && inputs.scan-type == 'config' }} + outputs: + NOTIFICATION: ${{ steps.scan.outcome == 'failure' && 'true' || 'false' }} + runs-on: ubuntu-latest + + steps: + - name: checkout repository + uses: actions/checkout@v4 + + - name: setup trivy + uses: ./.github/actions/setup-trivy + + - name: scan configuration for full report + if: always() + run: | + trivy config ${{ inputs.path }} --exit-code 0 + + - name: scan configuration for new medium, high, critical and unknown severities + if: always() + env: + REPORT: ${{ inputs.use-test-reporter == true && '-f json >> security-scanning/trivy.json' || '' }} + IGNOREFILE : ${{ inputs.trivyignorefile != '' && '--ignorefile ' + inputs.trivyignorefile || '' }} + id: scan + run: | + trivy config ${{ inputs.path }} \ + --exit-code 1 \ + --skip-db-update \ + --severity HIGH,CRITICAL,UNKNOWN \ + --scanners vuln \ + --timeout 10m \ + $IGNOREFILE \ + $REPORT + + - name: convert Trivy report to CTRF format + if: always() && ${{ inputs.use-test-reporter }} + run: | + python3 security-scanning/trivyconfig2ctrf.py ./security-scanning/trivy.json ./security-scanning/trivy.ctrf.json + + - name: Publish Test Report + if: always() && ${{ inputs.use-test-reporter }} + uses: ctrf-io/github-test-reporter@v1 + with: + report-path: './security-scanning/trivy.ctrf.json' + template-path: './security-scanning/config_scan_template.hbs' + custom-report: true + + checkov_scan: + if: ${{ inputs.scan-tool == 'checkov' }} + outputs: + NOTIFICATION: ${{ steps.scan.outcome == 'failure' && 'true' || 'false' }} + runs-on: ubuntu-latest + + steps: + - name: checkout repository + uses: actions/checkout@v4 + + - uses: actions/setup-python@v5 + with: + python-version: "3.13" + + - name: setup Checkov + run: | + pip install checkov + + - name: run Checkov + env: + BASELINE: ${{ inputs.checkovbaseline != '' && '--baseline ' + inputs.checkovbaseline || '' }} + run: | + checkov \ + --directory ${{ inputs.path }} \ + --output json \ + $BASELINE \ + --soft-fail-on LOW > ./security-scanning/checkov.json + + - name: convert Checkov report to CTRF format + if: always() && ${{ inputs.use-test-reporter }} + run: | + echo "erstelle datei" > ./security-scanning/checkov.ctrf.json + python3 security-scanning/checkov2ctrf.py ./security-scanning/checkov.json ./security-scanning/checkov.ctrf.json + + - name: Publish Test Report + if: always() && ${{ inputs.use-test-reporter }} + uses: ctrf-io/github-test-reporter@v1 + with: + report-path: './security-scanning/checkov.ctrf.json' + template-path: './security-scanning/config_scan_template.hbs' + custom-report: true + + filesystem_scan: + if: ${{ inputs.scan-tool == 'trivy' && (inputs.scan-type == 'filesystem' || inputs.scan-type == '') }} + outputs: + NOTIFICATION: ${{ steps.scan.outcome == 'failure' && 'true' || 'false' }} + runs-on: ubuntu-latest + + steps: + - name: checkout repository + uses: actions/checkout@v4 + + - name: setup trivy + uses: ./.github/actions/setup-trivy + + - name: download vulnerabilities database from aws + uses: nick-fields/retry@v3 + with: + timeout_minutes : 5 + max_attempts: 3 + retry_wait_seconds: 60 + command: GITHUB_TOKEN=${{ secrets.GITHUB_TOKEN }} trivy fs --download-db-only --db-repository "${{ env.TRIVY_DB_REPOSITORY }}" + + - name: download vulnerabilities database if aws failed + if: failure() + uses: nick-fields/retry@v3 + with: + timeout_minutes : 5 + max_attempts: 3 + retry_wait_seconds: 60 + command: GITHUB_TOKEN=${{ secrets.GITHUB_TOKEN }} trivy fs --download-db-only + + - name: scan filesystem for full report + if: always() + run: | + trivy fs ${{ inputs.path }} --exit-code 0 --skip-db-update + + # ob mit report geht weiß ich noch nicht + - name: scan filesystem for new medium, high, critical and unknown severities with report and ignorefile + if: always() + id: scan + env: + REPORT: ${{ inputs.use-test-reporter == true && '-f json > ./security-scanning/trivy.json' || '' }} + IGNOREFILE : ${{ inputs.trivyignorefile != '' && '--ignorefile ' + inputs.trivyignorefile || '' }} + run: | + trivy fs ${{ inputs.path }} \ + --exit-code 1 \ + --skip-db-update \ + --severity MEDIUM,HIGH,CRITICAL,UNKNOWN \ + $IGNOREFILE \ + $REPORT + + - name: Publish Test Report + if: always() && ${{ inputs.use-test-reporter }} + uses: ctrf-io/github-test-reporter@v1 + with: + report-path: './security-scanning/trivy.ctrf.json' + template-path: './security-scanning/config_scan_template.hbs' + custom-report: true + + image_scan: + if: ${{ inputs.scan-tool == 'trivy' && inputs.scan-type == 'image' }} + outputs: + NOTIFICATION: ${{ steps.scan.outcome == 'failure' && 'true' || 'false' }} + runs-on: ubuntu-latest + + steps: + - name: checkout repository + uses: actions/checkout@v4 + + - name: setup trivy + uses: ./.github/actions/setup-trivy + + - name: download vulnerabilities database from aws + uses: nick-fields/retry@v3 + with: + timeout_minutes : 5 + max_attempts: 3 + retry_wait_seconds: 60 + command: GITHUB_TOKEN=${{ secrets.GITHUB_TOKEN }} trivy image --download-db-only + + - name: download vulnerabilities database if aws failed + if: failure() + uses: nick-fields/retry@v3 + with: + timeout_minutes : 5 + max_attempts: 3 + retry_wait_seconds: 60 + command: GITHUB_TOKEN=${{ secrets.GITHUB_TOKEN }} trivy image --download-db-only + + - name: build docker image + uses: docker/build-push-action@v4 + with: + context: ${{ inputs.path }} + push: false + tags: security-scan-image + secrets: ${{ secrets.DOCKER_IMAGE_SECRETS }} + + - name: scan image for full report + run : | + trivy image security-scan-image --exit-code 0 --skip-db-update --scanners vuln --timeout 10m --list-all-pkgs + + - name: scan docker image for high, critical and unknown severities + if: always() + env: + REPORT: ${{ inputs.use-test-reporter == true && '-f json >> security-scanning/trivy.json' || '' }} + IGNOREFILE : ${{ inputs.trivyignorefile != '' && '--ignorefile ' + inputs.trivyignorefile || '' }} + id: scan + run: | + trivy image security-scan-image \ + --exit-code 1 \ + --skip-db-update \ + --severity HIGH,CRITICAL,UNKNOWN \ + --scanners vuln \ + --timeout 10m \ + $IGNOREFILE \ + $REPORT + + - name: convert Trivy report to CTRF format + if: always() && ${{ inputs.use-test-reporter }} + run: | + python3 security-scanning/trivyimage2ctrf.py ./security-scanning/trivy.json ./security-scanning/trivy.ctrf.json + + - name: Publish Test Report + uses: ctrf-io/github-test-reporter@v1 + if: always() && ${{ inputs.use-test-reporter }} + with: + report-path: './security-scanning/trivy.ctrf.json' + template-path: './security-scanning/image_scan_template.hbs' + custom-report: true + + create_issues: + needs: [trivy_configuration_scan, checkov_scan, filesystem_scan, image_scan] + runs-on: ubuntu-latest + if: ${{ inputs.Issue-on-findings != 'false' }} + + steps: + steps: + - name: Create issue/Comment on issue + if: ${{ always() && (needs.image_scan.outputs.NOTIFICATION == 'true' || needs.trivy_configuration_scan.outputs.NOTIFICATION == 'true' || needs.filesystem_scan.outputs.NOTIFICATION == 'true') }} + uses: actions/github-script@v7 + with: + script: | + const repo = context.repo.repo; + const owner = context.repo.owner; + const issue_title = 'Security scan failed'; + const issue_body = 'One or more security scans failed. Please check the workflow run for more information: https://github.com/${{ github.repository }}/actions/runs/${{ github.run_id }}\nPlease check if the vulnerabilities are fixable. If there is a fix: Create a ticket for the fix or resolve it.\n' + const assignees = [${{ inputs.issue-on-findings }}]; + const existing_issue = await github.rest.issues.listForRepo({ + owner, + repo, + state: 'open', + labels: 'security-scan-failure' + }); + if (existing_issue.data.length === 0) { + await github.rest.issues.create({ + owner, + repo, + title: issue_title, + body: issue_body, + labels: ['security-scan-failure'] + }); + } else { + const issue_number = existing_issue.data[0].number; + await github.rest.issues.createComment({ + owner, + repo, + issue_number, + body: issue_body + }); + } From 2a6187f2768bb40f9b4c5edead77752e76ba4300 Mon Sep 17 00:00:00 2001 From: JonasPollokZweitag <„jonas.pollok@zweitag.de“> Date: Thu, 18 Dec 2025 13:30:42 +0100 Subject: [PATCH 06/53] fix: : with space --- .github/workflows/security-scan.yaml | 16 ++++++++-------- 1 file changed, 8 insertions(+), 8 deletions(-) diff --git a/.github/workflows/security-scan.yaml b/.github/workflows/security-scan.yaml index 0709163..ce5a209 100644 --- a/.github/workflows/security-scan.yaml +++ b/.github/workflows/security-scan.yaml @@ -71,7 +71,7 @@ jobs: if: always() env: REPORT: ${{ inputs.use-test-reporter == true && '-f json >> security-scanning/trivy.json' || '' }} - IGNOREFILE : ${{ inputs.trivyignorefile != '' && '--ignorefile ' + inputs.trivyignorefile || '' }} + IGNOREFILE: ${{ inputs.trivyignorefile != '' && '--ignorefile ' + inputs.trivyignorefile || '' }} id: scan run: | trivy config ${{ inputs.path }} \ @@ -154,7 +154,7 @@ jobs: - name: download vulnerabilities database from aws uses: nick-fields/retry@v3 with: - timeout_minutes : 5 + timeout_minutes: 5 max_attempts: 3 retry_wait_seconds: 60 command: GITHUB_TOKEN=${{ secrets.GITHUB_TOKEN }} trivy fs --download-db-only --db-repository "${{ env.TRIVY_DB_REPOSITORY }}" @@ -163,7 +163,7 @@ jobs: if: failure() uses: nick-fields/retry@v3 with: - timeout_minutes : 5 + timeout_minutes: 5 max_attempts: 3 retry_wait_seconds: 60 command: GITHUB_TOKEN=${{ secrets.GITHUB_TOKEN }} trivy fs --download-db-only @@ -179,7 +179,7 @@ jobs: id: scan env: REPORT: ${{ inputs.use-test-reporter == true && '-f json > ./security-scanning/trivy.json' || '' }} - IGNOREFILE : ${{ inputs.trivyignorefile != '' && '--ignorefile ' + inputs.trivyignorefile || '' }} + IGNOREFILE: ${{ inputs.trivyignorefile != '' && '--ignorefile ' + inputs.trivyignorefile || '' }} run: | trivy fs ${{ inputs.path }} \ --exit-code 1 \ @@ -212,7 +212,7 @@ jobs: - name: download vulnerabilities database from aws uses: nick-fields/retry@v3 with: - timeout_minutes : 5 + timeout_minutes: 5 max_attempts: 3 retry_wait_seconds: 60 command: GITHUB_TOKEN=${{ secrets.GITHUB_TOKEN }} trivy image --download-db-only @@ -221,7 +221,7 @@ jobs: if: failure() uses: nick-fields/retry@v3 with: - timeout_minutes : 5 + timeout_minutes: 5 max_attempts: 3 retry_wait_seconds: 60 command: GITHUB_TOKEN=${{ secrets.GITHUB_TOKEN }} trivy image --download-db-only @@ -235,14 +235,14 @@ jobs: secrets: ${{ secrets.DOCKER_IMAGE_SECRETS }} - name: scan image for full report - run : | + run: | trivy image security-scan-image --exit-code 0 --skip-db-update --scanners vuln --timeout 10m --list-all-pkgs - name: scan docker image for high, critical and unknown severities if: always() env: REPORT: ${{ inputs.use-test-reporter == true && '-f json >> security-scanning/trivy.json' || '' }} - IGNOREFILE : ${{ inputs.trivyignorefile != '' && '--ignorefile ' + inputs.trivyignorefile || '' }} + IGNOREFILE: ${{ inputs.trivyignorefile != '' && '--ignorefile ' + inputs.trivyignorefile || '' }} id: scan run: | trivy image security-scan-image \ From ee9857b0bede6d4ea1c03c23d9c1f557db2d6926 Mon Sep 17 00:00:00 2001 From: JonasPollokZweitag <„jonas.pollok@zweitag.de“> Date: Thu, 18 Dec 2025 14:27:41 +0100 Subject: [PATCH 07/53] fix: removed double steps call --- .github/workflows/security-scan.yaml | 1 - 1 file changed, 1 deletion(-) diff --git a/.github/workflows/security-scan.yaml b/.github/workflows/security-scan.yaml index ce5a209..b5142e0 100644 --- a/.github/workflows/security-scan.yaml +++ b/.github/workflows/security-scan.yaml @@ -272,7 +272,6 @@ jobs: runs-on: ubuntu-latest if: ${{ inputs.Issue-on-findings != 'false' }} - steps: steps: - name: Create issue/Comment on issue if: ${{ always() && (needs.image_scan.outputs.NOTIFICATION == 'true' || needs.trivy_configuration_scan.outputs.NOTIFICATION == 'true' || needs.filesystem_scan.outputs.NOTIFICATION == 'true') }} From 8252341735bfee49e6286033e72254753430ca37 Mon Sep 17 00:00:00 2001 From: JonasPollokZweitag <„jonas.pollok@zweitag.de“> Date: Thu, 18 Dec 2025 14:29:01 +0100 Subject: [PATCH 08/53] chore: added real name --- .github/workflows/security-scan.yaml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/.github/workflows/security-scan.yaml b/.github/workflows/security-scan.yaml index b5142e0..6f7ef95 100644 --- a/.github/workflows/security-scan.yaml +++ b/.github/workflows/security-scan.yaml @@ -1,5 +1,5 @@ -# .github/workflows/build-and-test.yml -name: Build and Test +# .github/workflows/security-scan.yaml +name: Security Scan on: workflow_call: From d3b557b34860874378013772e973bb6a25119d44 Mon Sep 17 00:00:00 2001 From: JonasPollokZweitag <„jonas.pollok@zweitag.de“> Date: Wed, 14 Jan 2026 08:18:31 +0100 Subject: [PATCH 09/53] chore: split in two different workflows --- .github/workflows/checkov-scan.yaml | 109 ++++++++++++++++++ .../{security-scan.yaml => trivy-scan.yaml} | 80 +++---------- 2 files changed, 123 insertions(+), 66 deletions(-) create mode 100644 .github/workflows/checkov-scan.yaml rename .github/workflows/{security-scan.yaml => trivy-scan.yaml} (73%) diff --git a/.github/workflows/checkov-scan.yaml b/.github/workflows/checkov-scan.yaml new file mode 100644 index 0000000..abb9166 --- /dev/null +++ b/.github/workflows/checkov-scan.yaml @@ -0,0 +1,109 @@ +# .github/workflows/security-scan.yaml +name: Security Scan + +on: + workflow_call: + inputs: + checkovbaseline: + description: 'Path to the Checkov baseline file (default: none)' + default: '' + required: false + type: string + path: + description: 'Directory path where the scan should be performed (default: .)' + required: false + default: '.' + type: string + use-test-reporter: + description: 'Attach the test results as a report (default: true)' + required: false + default: 'true' + type: boolean + issue-on-findings: + description: 'GitHub users to assign when creating an issue for failed scans (comma-separated list: ''@user1'', ''@user2''). If left empty, no issue will be created.' + required: false + default: 'false' + type: string + +jobs: + + checkov_scan: + outputs: + NOTIFICATION: ${{ steps.scan.outcome == 'failure' && 'true' || 'false' }} + runs-on: ubuntu-latest + + steps: + - name: checkout repository + uses: actions/checkout@v4 + + - uses: actions/setup-python@v5 + with: + python-version: "3.13" + + - name: setup Checkov + run: | + pip install checkov + + - name: run Checkov + env: + BASELINE: ${{ inputs.checkovbaseline != '' && '--baseline ' + inputs.checkovbaseline || '' }} + run: | + checkov \ + --directory ${{ inputs.path }} \ + --output json \ + $BASELINE \ + --soft-fail-on LOW > ./security-scanning/checkov.json + + - name: convert Checkov report to CTRF format + if: always() && ${{ inputs.use-test-reporter }} + run: | + echo "create file > ./security-scanning/checkov.ctrf.json + python3 security-scanning/checkov2ctrf.py ./security-scanning/checkov.json ./security-scanning/checkov.ctrf.json + + - name: Publish Test Report + if: always() && ${{ inputs.use-test-reporter }} + uses: ctrf-io/github-test-reporter@v1 + with: + report-path: './security-scanning/checkov.ctrf.json' + template-path: './security-scanning/config_scan_template.hbs' + custom-report: true + + create_issue: + needs: [configuration_scan, checkov_scan, filesystem_scan, image_scan] + runs-on: ubuntu-latest + if: ${{ inputs.issue-on-findings != 'false' }} + + steps: + - name: Create issue/Comment on issue + if: ${{ always() && (needs.image_scan.outputs.NOTIFICATION == 'true' || needs. configuration_scan.outputs.NOTIFICATION == 'true' || needs.filesystem_scan.outputs.NOTIFICATION == 'true') }} + uses: actions/github-script@v7 + with: + script: | + const repo = context.repo.repo; + const owner = context.repo.owner; + const issue_title = 'Security scan failed'; + const issue_body = 'One or more security scans failed. Please check the workflow run for more information: https://github.com/${{ github.repository }}/actions/runs/${{ github.run_id }}\nPlease check if the vulnerabilities are fixable. If there is a fix: Create a ticket for the fix or resolve it.\n' + const assignees = [${{ inputs.issue-on-findings }}]; + const existing_issue = await github.rest.issues.listForRepo({ + owner, + repo, + state: 'open', + labels: 'security-scan-failure' + }); + if (existing_issue.data.length === 0) { + await github.rest.issues.create({ + owner, + repo, + title: issue_title, + body: issue_body, + labels: ['security-scan-failure'] + }); + } else { + const issue_number = existing_issue.data[0].number; + await github.rest.issues.createComment({ + owner, + repo, + issue_number, + body: issue_body + }); + } diff --git a/.github/workflows/security-scan.yaml b/.github/workflows/trivy-scan.yaml similarity index 73% rename from .github/workflows/security-scan.yaml rename to .github/workflows/trivy-scan.yaml index 6f7ef95..3449e76 100644 --- a/.github/workflows/security-scan.yaml +++ b/.github/workflows/trivy-scan.yaml @@ -4,53 +4,43 @@ name: Security Scan on: workflow_call: inputs: - scan-tool: - description: 'Tool das für den Scan verwendet werden soll, aktuell verfügbar: "checkov", "trivy"' - required: true - default: 'trivy' - type: string scan-type: - description: 'Art des Scans, aktuell nur für Trivy verfügbar: "image", "filesystem" (default), "config"' + description: 'Type of scan to perform: "image", "filesystem" (default), or "config"' required: false default: 'filesystem' type: string trivyignorefile: - description: 'Pfad zur Trivy Ignore-Datei' - default: '' - required: false - type: string - checkovbaseline: - description: 'Pfad zur Checkov Baseline-Datei' + description: 'Path to the Trivy ignore file (default: none)' default: '' required: false type: string path: - description: 'Pfad in dem der Scan ausgeführt werden soll (bei image-scans muss der Ordner die Dockerfile enthalten)' + description: 'Directory path where the scan should be performed (default: .) (for image scans, the directory must contain the Dockerfile)' required: false default: '.' type: string use-test-reporter: - description: 'Ob die Testergebnisse als Report angehängt werden sollen' + description: 'Whether to attach the test results as a report (default: true)' required: false default: 'true' type: boolean issue-on-findings: - description: 'An welchen Github User bei gefailelten Scans ein Github Issue erstellt werden soll (Komma getrennte Liste: ''@user1'', ''@user2''). Wenn leer, wird kein Issue erstellt.' + description: 'GitHub users to assign when creating an issue for failed scans (comma-separated list: ''@user1'', ''@user2''). If left empty, no issue will be created.' required: false default: 'false' type: string secrets: GITHUB_TOKEN: - description: 'GitHub Token zum Download der Trivy-DB' - required: false + description: 'GitHub Token for downloading the Trivy DB' + required: DOCKER_IMAGE_SECRETS: description: 'Docker Image Build Secrets' required: false jobs: - trivy_configuration_scan: - if: ${{ inputs.scan-tool == 'trivy' && inputs.scan-type == 'config' }} + configuration_scan: + if: ${{ inputs.scan-type == 'config' }} outputs: NOTIFICATION: ${{ steps.scan.outcome == 'failure' && 'true' || 'false' }} runs-on: ubuntu-latest @@ -96,50 +86,8 @@ jobs: template-path: './security-scanning/config_scan_template.hbs' custom-report: true - checkov_scan: - if: ${{ inputs.scan-tool == 'checkov' }} - outputs: - NOTIFICATION: ${{ steps.scan.outcome == 'failure' && 'true' || 'false' }} - runs-on: ubuntu-latest - - steps: - - name: checkout repository - uses: actions/checkout@v4 - - - uses: actions/setup-python@v5 - with: - python-version: "3.13" - - - name: setup Checkov - run: | - pip install checkov - - - name: run Checkov - env: - BASELINE: ${{ inputs.checkovbaseline != '' && '--baseline ' + inputs.checkovbaseline || '' }} - run: | - checkov \ - --directory ${{ inputs.path }} \ - --output json \ - $BASELINE \ - --soft-fail-on LOW > ./security-scanning/checkov.json - - - name: convert Checkov report to CTRF format - if: always() && ${{ inputs.use-test-reporter }} - run: | - echo "erstelle datei" > ./security-scanning/checkov.ctrf.json - python3 security-scanning/checkov2ctrf.py ./security-scanning/checkov.json ./security-scanning/checkov.ctrf.json - - - name: Publish Test Report - if: always() && ${{ inputs.use-test-reporter }} - uses: ctrf-io/github-test-reporter@v1 - with: - report-path: './security-scanning/checkov.ctrf.json' - template-path: './security-scanning/config_scan_template.hbs' - custom-report: true - filesystem_scan: - if: ${{ inputs.scan-tool == 'trivy' && (inputs.scan-type == 'filesystem' || inputs.scan-type == '') }} + if: ${{ inputs.scan-type == 'filesystem' || inputs.scan-type == '' }} outputs: NOTIFICATION: ${{ steps.scan.outcome == 'failure' && 'true' || 'false' }} runs-on: ubuntu-latest @@ -197,7 +145,7 @@ jobs: custom-report: true image_scan: - if: ${{ inputs.scan-tool == 'trivy' && inputs.scan-type == 'image' }} + if: ${{ inputs.scan-type == 'image' }} outputs: NOTIFICATION: ${{ steps.scan.outcome == 'failure' && 'true' || 'false' }} runs-on: ubuntu-latest @@ -268,13 +216,13 @@ jobs: custom-report: true create_issues: - needs: [trivy_configuration_scan, checkov_scan, filesystem_scan, image_scan] + needs: [configuration_scan, filesystem_scan, image_scan] runs-on: ubuntu-latest - if: ${{ inputs.Issue-on-findings != 'false' }} + if: ${{ inputs.issue-on-findings != 'false' }} steps: - name: Create issue/Comment on issue - if: ${{ always() && (needs.image_scan.outputs.NOTIFICATION == 'true' || needs.trivy_configuration_scan.outputs.NOTIFICATION == 'true' || needs.filesystem_scan.outputs.NOTIFICATION == 'true') }} + if: ${{ always() && (needs.image_scan.outputs.NOTIFICATION == 'true' || needs.configuration_scan.outputs.NOTIFICATION == 'true' || needs.filesystem_scan.outputs.NOTIFICATION == 'true') }} uses: actions/github-script@v7 with: script: | From 4f5b774e8e89afbeb88af1d6a7c946a32658389a Mon Sep 17 00:00:00 2001 From: JonasPollokZweitag <„jonas.pollok@zweitag.de“> Date: Wed, 14 Jan 2026 08:23:42 +0100 Subject: [PATCH 10/53] chore: removed python setup action --- .github/actions/setup-python/action.yml | 27 ------------------------- .github/workflows/checkov-scan.yaml | 2 +- 2 files changed, 1 insertion(+), 28 deletions(-) delete mode 100644 .github/actions/setup-python/action.yml diff --git a/.github/actions/setup-python/action.yml b/.github/actions/setup-python/action.yml deleted file mode 100644 index ec1a995..0000000 --- a/.github/actions/setup-python/action.yml +++ /dev/null @@ -1,27 +0,0 @@ ---- -name: Setup Python -description: | - Setup all dependencies for running Python -inputs: - working-directory: - description: | - The working directory to install dependencies in - required: true - extensions-package-registry-password: - description: | - The password for the extensions package registry - required: true -runs: - using: composite - steps: - - name: Install poetry - run: pipx install poetry==1.8.4 - shell: bash - - uses: actions/setup-python@v5 - with: - python-version: "3.11" - cache: "poetry" - cache-dependency-path: "${{ inputs.working-directory }}/poetry.lock" - - run: poetry install - shell: bash - working-directory: ${{ inputs.working-directory }} diff --git a/.github/workflows/checkov-scan.yaml b/.github/workflows/checkov-scan.yaml index abb9166..d08de34 100644 --- a/.github/workflows/checkov-scan.yaml +++ b/.github/workflows/checkov-scan.yaml @@ -57,7 +57,7 @@ jobs: - name: convert Checkov report to CTRF format if: always() && ${{ inputs.use-test-reporter }} run: | - echo "create file > ./security-scanning/checkov.ctrf.json + touch ./security-scanning/checkov.ctrf.json python3 security-scanning/checkov2ctrf.py ./security-scanning/checkov.json ./security-scanning/checkov.ctrf.json - name: Publish Test Report From 88e4e26e3abf41dc6671e2561f6583afe77d54cc Mon Sep 17 00:00:00 2001 From: JonasPollokZweitag <„jonas.pollok@zweitag.de“> Date: Wed, 14 Jan 2026 08:28:41 +0100 Subject: [PATCH 11/53] chore: use format instead of + --- .github/workflows/checkov-scan.yaml | 2 +- .github/workflows/trivy-scan.yaml | 6 +++--- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/.github/workflows/checkov-scan.yaml b/.github/workflows/checkov-scan.yaml index d08de34..f54db11 100644 --- a/.github/workflows/checkov-scan.yaml +++ b/.github/workflows/checkov-scan.yaml @@ -46,7 +46,7 @@ jobs: - name: run Checkov env: - BASELINE: ${{ inputs.checkovbaseline != '' && '--baseline ' + inputs.checkovbaseline || '' }} + BASELINE: ${{ inputs.checkovbaseline != '' && format('--baseline {0}', inputs.checkovbaseline) || '' }} run: | checkov \ --directory ${{ inputs.path }} \ diff --git a/.github/workflows/trivy-scan.yaml b/.github/workflows/trivy-scan.yaml index 3449e76..05e4b3e 100644 --- a/.github/workflows/trivy-scan.yaml +++ b/.github/workflows/trivy-scan.yaml @@ -61,7 +61,7 @@ jobs: if: always() env: REPORT: ${{ inputs.use-test-reporter == true && '-f json >> security-scanning/trivy.json' || '' }} - IGNOREFILE: ${{ inputs.trivyignorefile != '' && '--ignorefile ' + inputs.trivyignorefile || '' }} + IGNOREFILE: ${{ inputs.trivyignorefile != '' && format('--ignorefile {0}', inputs.trivyignorefile) || '' }} id: scan run: | trivy config ${{ inputs.path }} \ @@ -127,7 +127,7 @@ jobs: id: scan env: REPORT: ${{ inputs.use-test-reporter == true && '-f json > ./security-scanning/trivy.json' || '' }} - IGNOREFILE: ${{ inputs.trivyignorefile != '' && '--ignorefile ' + inputs.trivyignorefile || '' }} + IGNOREFILE: ${{ inputs.trivyignorefile != '' && format('--ignorefile {0}', inputs.trivyignorefile) || '' }} run: | trivy fs ${{ inputs.path }} \ --exit-code 1 \ @@ -190,7 +190,7 @@ jobs: if: always() env: REPORT: ${{ inputs.use-test-reporter == true && '-f json >> security-scanning/trivy.json' || '' }} - IGNOREFILE: ${{ inputs.trivyignorefile != '' && '--ignorefile ' + inputs.trivyignorefile || '' }} + IGNOREFILE: ${{ inputs.trivyignorefile != '' && format('--ignorefile {0}', inputs.trivyignorefile) || '' }} id: scan run: | trivy image security-scan-image \ From 73bf9612da078835d01159ffc33bd4438ef4052a Mon Sep 17 00:00:00 2001 From: JonasPollokZweitag <„jonas.pollok@zweitag.de“> Date: Wed, 14 Jan 2026 08:30:02 +0100 Subject: [PATCH 12/53] chore: removed dependency to removed step --- .github/workflows/checkov-scan.yaml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/.github/workflows/checkov-scan.yaml b/.github/workflows/checkov-scan.yaml index f54db11..66ba9c8 100644 --- a/.github/workflows/checkov-scan.yaml +++ b/.github/workflows/checkov-scan.yaml @@ -69,13 +69,13 @@ jobs: custom-report: true create_issue: - needs: [configuration_scan, checkov_scan, filesystem_scan, image_scan] + needs: [checkov_scan] runs-on: ubuntu-latest if: ${{ inputs.issue-on-findings != 'false' }} steps: - name: Create issue/Comment on issue - if: ${{ always() && (needs.image_scan.outputs.NOTIFICATION == 'true' || needs. configuration_scan.outputs.NOTIFICATION == 'true' || needs.filesystem_scan.outputs.NOTIFICATION == 'true') }} + if: ${{ always() && needs.checkov_scan.outputs.NOTIFICATION == 'true' }} uses: actions/github-script@v7 with: script: | From 73e6ab9be30bdfb151e207d185b8c9785870eef1 Mon Sep 17 00:00:00 2001 From: JonasPollokZweitag <„jonas.pollok@zweitag.de“> Date: Wed, 14 Jan 2026 08:31:14 +0100 Subject: [PATCH 13/53] fix: set missing required parameter --- .github/workflows/trivy-scan.yaml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/trivy-scan.yaml b/.github/workflows/trivy-scan.yaml index 05e4b3e..c82901b 100644 --- a/.github/workflows/trivy-scan.yaml +++ b/.github/workflows/trivy-scan.yaml @@ -32,7 +32,7 @@ on: secrets: GITHUB_TOKEN: description: 'GitHub Token for downloading the Trivy DB' - required: + required: false DOCKER_IMAGE_SECRETS: description: 'Docker Image Build Secrets' required: false From af16ef4b7d9a64cdaaea5f56dc71671a3408e38b Mon Sep 17 00:00:00 2001 From: JonasPollokZweitag <„jonas.pollok@zweitag.de“> Date: Wed, 14 Jan 2026 08:39:01 +0100 Subject: [PATCH 14/53] chore: renamed GITHUB_TOKEN secret --- .github/workflows/trivy-scan.yaml | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/.github/workflows/trivy-scan.yaml b/.github/workflows/trivy-scan.yaml index c82901b..bb65302 100644 --- a/.github/workflows/trivy-scan.yaml +++ b/.github/workflows/trivy-scan.yaml @@ -30,7 +30,7 @@ on: default: 'false' type: string secrets: - GITHUB_TOKEN: + GH_TOKEN: description: 'GitHub Token for downloading the Trivy DB' required: false DOCKER_IMAGE_SECRETS: @@ -105,7 +105,7 @@ jobs: timeout_minutes: 5 max_attempts: 3 retry_wait_seconds: 60 - command: GITHUB_TOKEN=${{ secrets.GITHUB_TOKEN }} trivy fs --download-db-only --db-repository "${{ env.TRIVY_DB_REPOSITORY }}" + command: GITHUB_TOKEN=${{ secrets.GH_TOKEN }} trivy fs --download-db-only --db-repository "${{ env.TRIVY_DB_REPOSITORY }}" - name: download vulnerabilities database if aws failed if: failure() @@ -114,7 +114,7 @@ jobs: timeout_minutes: 5 max_attempts: 3 retry_wait_seconds: 60 - command: GITHUB_TOKEN=${{ secrets.GITHUB_TOKEN }} trivy fs --download-db-only + command: GITHUB_TOKEN=${{ secrets.GH_TOKEN }} trivy fs --download-db-only - name: scan filesystem for full report if: always() @@ -163,7 +163,7 @@ jobs: timeout_minutes: 5 max_attempts: 3 retry_wait_seconds: 60 - command: GITHUB_TOKEN=${{ secrets.GITHUB_TOKEN }} trivy image --download-db-only + command: GITHUB_TOKEN=${{ secrets.GH_TOKEN }} trivy image --download-db-only - name: download vulnerabilities database if aws failed if: failure() @@ -172,7 +172,7 @@ jobs: timeout_minutes: 5 max_attempts: 3 retry_wait_seconds: 60 - command: GITHUB_TOKEN=${{ secrets.GITHUB_TOKEN }} trivy image --download-db-only + command: GITHUB_TOKEN=${{ secrets.GH_TOKEN }} trivy image --download-db-only - name: build docker image uses: docker/build-push-action@v4 From a53cd54b7785210e604336c6915a24c7e5aa66ef Mon Sep 17 00:00:00 2001 From: JonasPollokZweitag <„jonas.pollok@zweitag.de“> Date: Wed, 14 Jan 2026 08:53:33 +0100 Subject: [PATCH 15/53] chore: use native bash for retry --- .github/workflows/trivy-scan.yaml | 118 +++++++++++++++++++++++------- 1 file changed, 91 insertions(+), 27 deletions(-) diff --git a/.github/workflows/trivy-scan.yaml b/.github/workflows/trivy-scan.yaml index bb65302..53ab9bf 100644 --- a/.github/workflows/trivy-scan.yaml +++ b/.github/workflows/trivy-scan.yaml @@ -37,6 +37,9 @@ on: description: 'Docker Image Build Secrets' required: false +env: + TRIVY_DB_REPOSITORY: public.ecr.aws/aquasecurity/trivy-db,aquasec/trivy-db,ghcr.io/aquasecurity/trivy-db + jobs: configuration_scan: @@ -50,7 +53,7 @@ jobs: uses: actions/checkout@v4 - name: setup trivy - uses: ./.github/actions/setup-trivy + uses: aquasecurity/setup-trivy@e07451d2e059ed86c2870430ea286b3a9e0bf241 - name: scan configuration for full report if: always() @@ -97,24 +100,52 @@ jobs: uses: actions/checkout@v4 - name: setup trivy - uses: ./.github/actions/setup-trivy + uses: aquasecurity/setup-trivy@e07451d2e059ed86c2870430ea286b3a9e0bf241 - name: download vulnerabilities database from aws - uses: nick-fields/retry@v3 - with: - timeout_minutes: 5 - max_attempts: 3 - retry_wait_seconds: 60 - command: GITHUB_TOKEN=${{ secrets.GH_TOKEN }} trivy fs --download-db-only --db-repository "${{ env.TRIVY_DB_REPOSITORY }}" + run: | + MAX_RETRIES=3 + RETRY_DELAY=60 + ATTEMPT=1 + + until [ $ATTEMPT -gt $MAX_RETRIES ] + do + echo "Attempt $ATTEMPT of $MAX_RETRIES..." + GITHUB_TOKEN=${{ secrets.GH_TOKEN }} trivy fs --download-db-only --db-repository "${{ env.TRIVY_DB_REPOSITORY }}" + + if [ $? -eq 0 ]; then + echo "Trivy DB download succeeded." + exit 0 + else + echo "Trivy DB download failed. Retrying in $RETRY_DELAY seconds..." + ATTEMPT=$((ATTEMPT + 1)) + sleep $RETRY_DELAY + fi + done + exit 1 - name: download vulnerabilities database if aws failed if: failure() - uses: nick-fields/retry@v3 - with: - timeout_minutes: 5 - max_attempts: 3 - retry_wait_seconds: 60 - command: GITHUB_TOKEN=${{ secrets.GH_TOKEN }} trivy fs --download-db-only + run: | + MAX_RETRIES=3 + RETRY_DELAY=60 + ATTEMPT=1 + + until [ $ATTEMPT -gt $MAX_RETRIES ] + do + echo "Attempt $ATTEMPT of $MAX_RETRIES..." + GITHUB_TOKEN=${{ secrets.GH_TOKEN }} trivy fs --download-db-only + + if [ $? -eq 0 ]; then + echo "Trivy DB download succeeded." + exit 0 + else + echo "Trivy DB download failed. Retrying in $RETRY_DELAY seconds..." + ATTEMPT=$((ATTEMPT + 1)) + sleep $RETRY_DELAY + fi + done + exit 1 - name: scan filesystem for full report if: always() @@ -155,24 +186,57 @@ jobs: uses: actions/checkout@v4 - name: setup trivy - uses: ./.github/actions/setup-trivy + uses: aquasecurity/setup-trivy@e07451d2e059ed86c2870430ea286b3a9e0bf241 - name: download vulnerabilities database from aws - uses: nick-fields/retry@v3 - with: - timeout_minutes: 5 - max_attempts: 3 - retry_wait_seconds: 60 - command: GITHUB_TOKEN=${{ secrets.GH_TOKEN }} trivy image --download-db-only + run: | + MAX_RETRIES=3 + RETRY_DELAY=60 + ATTEMPT=1 + + until [ $ATTEMPT -gt $MAX_RETRIES ] + do + echo "Attempt $ATTEMPT of $MAX_RETRIES..." + GITHUB_TOKEN=${{ secrets.GH_TOKEN }} trivy fs --download-db-only --db-repository "${{ env.TRIVY_DB_REPOSITORY }}" + + if [ $? -eq 0 ]; then + echo "Trivy DB download succeeded." + exit 0 + else + echo "Trivy DB download failed. Retrying in $RETRY_DELAY seconds..." + ATTEMPT=$((ATTEMPT + 1)) + sleep $RETRY_DELAY + fi + done + exit 1 - name: download vulnerabilities database if aws failed if: failure() - uses: nick-fields/retry@v3 - with: - timeout_minutes: 5 - max_attempts: 3 - retry_wait_seconds: 60 - command: GITHUB_TOKEN=${{ secrets.GH_TOKEN }} trivy image --download-db-only + run: | + MAX_RETRIES=3 + RETRY_DELAY=60 + ATTEMPT=1 + + until [ $ATTEMPT -gt $MAX_RETRIES ] + do + echo "Attempt $ATTEMPT of $MAX_RETRIES..." + GITHUB_TOKEN=${{ secrets.GH_TOKEN }} trivy fs --download-db-only + + if [ $? -eq 0 ]; then + echo "Trivy DB download succeeded." + exit 0 + else + echo "Trivy DB download failed. Retrying in $RETRY_DELAY seconds..." + ATTEMPT=$((ATTEMPT + 1)) + sleep $RETRY_DELAY + fi + done + exit 1 + + - name: scan filesystem for full report + if: always() + run: | + trivy fs ${{ inputs.path }} --exit-code 0 --skip-db-update - name: build docker image uses: docker/build-push-action@v4 From 9ed9eaba0d326678db955904ed6ed7cb74a0387b Mon Sep 17 00:00:00 2001 From: JonasPollokZweitag <„jonas.pollok@zweitag.de“> Date: Wed, 14 Jan 2026 09:33:47 +0100 Subject: [PATCH 16/53] chore: checkout scripts in checkov workflow --- .github/workflows/checkov-scan.yaml | 39 ++++++++++++++++++----------- 1 file changed, 25 insertions(+), 14 deletions(-) diff --git a/.github/workflows/checkov-scan.yaml b/.github/workflows/checkov-scan.yaml index 66ba9c8..07ca053 100644 --- a/.github/workflows/checkov-scan.yaml +++ b/.github/workflows/checkov-scan.yaml @@ -28,23 +28,33 @@ on: jobs: checkov_scan: - outputs: - NOTIFICATION: ${{ steps.scan.outcome == 'failure' && 'true' || 'false' }} - runs-on: ubuntu-latest + outputs: + NOTIFICATION: ${{ steps.scan.outcome == 'failure' && 'true' || 'false' }} + runs-on: ubuntu-latest - steps: + steps: - name: checkout repository uses: actions/checkout@v4 + - name: checkout security-scanning scripts + uses: actions/checkout@v4 + with: + repository: zweitag/github-actions + path: _security-tools + sparse-checkout: security-scanning + + - name: create output folder + run: mkdir -p ./scan-results + - uses: actions/setup-python@v5 with: python-version: "3.13" - name: setup Checkov - run: | - pip install checkov + run: pip install checkov - name: run Checkov + id: scan env: BASELINE: ${{ inputs.checkovbaseline != '' && format('--baseline {0}', inputs.checkovbaseline) || '' }} run: | @@ -52,20 +62,21 @@ jobs: --directory ${{ inputs.path }} \ --output json \ $BASELINE \ - --soft-fail-on LOW > ./security-scanning/checkov.json + --soft-fail-on LOW > ./scan-results/checkov.json - name: convert Checkov report to CTRF format - if: always() && ${{ inputs.use-test-reporter }} + if: always() && inputs.use-test-reporter run: | - touch ./security-scanning/checkov.ctrf.json - python3 security-scanning/checkov2ctrf.py ./security-scanning/checkov.json ./security-scanning/checkov.ctrf.json - + python3 _security-tools/security-scanning/checkov2ctrf.py \ + ./scan-results/checkov.json \ + ./scan-results/checkov.ctrf.json + - name: Publish Test Report - if: always() && ${{ inputs.use-test-reporter }} + if: always() && inputs.use-test-reporter uses: ctrf-io/github-test-reporter@v1 with: - report-path: './security-scanning/checkov.ctrf.json' - template-path: './security-scanning/config_scan_template.hbs' + report-path: './scan-results/checkov.ctrf.json' + template-path: '_security-tools/security-scanning/config_scan_template.hbs' custom-report: true create_issue: From 60a208d5b755a85a36de4748577c67a8e442d0ee Mon Sep 17 00:00:00 2001 From: JonasPollokZweitag <„jonas.pollok@zweitag.de“> Date: Wed, 14 Jan 2026 09:41:37 +0100 Subject: [PATCH 17/53] fix: wrong indentation --- .github/workflows/checkov-scan.yaml | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/.github/workflows/checkov-scan.yaml b/.github/workflows/checkov-scan.yaml index 07ca053..ce3fb68 100644 --- a/.github/workflows/checkov-scan.yaml +++ b/.github/workflows/checkov-scan.yaml @@ -28,9 +28,9 @@ on: jobs: checkov_scan: - outputs: - NOTIFICATION: ${{ steps.scan.outcome == 'failure' && 'true' || 'false' }} - runs-on: ubuntu-latest + outputs: + NOTIFICATION: ${{ steps.scan.outcome == 'failure' && 'true' || 'false' }} + runs-on: ubuntu-latest steps: - name: checkout repository From 6cede3f73725917e72c8e0bf7219561b5bc7cb62 Mon Sep 17 00:00:00 2001 From: JonasPollokZweitag <„jonas.pollok@zweitag.de“> Date: Wed, 14 Jan 2026 09:47:29 +0100 Subject: [PATCH 18/53] fix: wrong indentation --- .github/workflows/checkov-scan.yaml | 92 ++++++++++++++--------------- 1 file changed, 46 insertions(+), 46 deletions(-) diff --git a/.github/workflows/checkov-scan.yaml b/.github/workflows/checkov-scan.yaml index ce3fb68..0cfe25b 100644 --- a/.github/workflows/checkov-scan.yaml +++ b/.github/workflows/checkov-scan.yaml @@ -32,52 +32,52 @@ jobs: NOTIFICATION: ${{ steps.scan.outcome == 'failure' && 'true' || 'false' }} runs-on: ubuntu-latest - steps: - - name: checkout repository - uses: actions/checkout@v4 - - - name: checkout security-scanning scripts - uses: actions/checkout@v4 - with: - repository: zweitag/github-actions - path: _security-tools - sparse-checkout: security-scanning - - - name: create output folder - run: mkdir -p ./scan-results - - - uses: actions/setup-python@v5 - with: - python-version: "3.13" - - - name: setup Checkov - run: pip install checkov - - - name: run Checkov - id: scan - env: - BASELINE: ${{ inputs.checkovbaseline != '' && format('--baseline {0}', inputs.checkovbaseline) || '' }} - run: | - checkov \ - --directory ${{ inputs.path }} \ - --output json \ - $BASELINE \ - --soft-fail-on LOW > ./scan-results/checkov.json - - - name: convert Checkov report to CTRF format - if: always() && inputs.use-test-reporter - run: | - python3 _security-tools/security-scanning/checkov2ctrf.py \ - ./scan-results/checkov.json \ - ./scan-results/checkov.ctrf.json - - - name: Publish Test Report - if: always() && inputs.use-test-reporter - uses: ctrf-io/github-test-reporter@v1 - with: - report-path: './scan-results/checkov.ctrf.json' - template-path: '_security-tools/security-scanning/config_scan_template.hbs' - custom-report: true + steps: + - name: checkout repository + uses: actions/checkout@v4 + + - name: checkout security-scanning scripts + uses: actions/checkout@v4 + with: + repository: zweitag/github-actions + path: _security-tools + sparse-checkout: security-scanning + + - name: create output folder + run: mkdir -p ./scan-results + + - uses: actions/setup-python@v5 + with: + python-version: "3.13" + + - name: setup Checkov + run: pip install checkov + + - name: run Checkov + id: scan + env: + BASELINE: ${{ inputs.checkovbaseline != '' && format('--baseline {0}', inputs.checkovbaseline) || '' }} + run: | + checkov \ + --directory ${{ inputs.path }} \ + --output json \ + $BASELINE \ + --soft-fail-on LOW > ./scan-results/checkov.json + + - name: convert Checkov report to CTRF format + if: always() && inputs.use-test-reporter + run: | + python3 _security-tools/security-scanning/checkov2ctrf.py \ + ./scan-results/checkov.json \ + ./scan-results/checkov.ctrf.json + + - name: Publish Test Report + if: always() && inputs.use-test-reporter + uses: ctrf-io/github-test-reporter@v1 + with: + report-path: './scan-results/checkov.ctrf.json' + template-path: '_security-tools/security-scanning/config_scan_template.hbs' + custom-report: true create_issue: needs: [checkov_scan] From c97c47c7ca7ea554bb3cf2b0e2c3a9bce173ae9f Mon Sep 17 00:00:00 2001 From: JonasPollokZweitag <„jonas.pollok@zweitag.de“> Date: Wed, 14 Jan 2026 09:55:53 +0100 Subject: [PATCH 19/53] fix: remove quotes from boolean --- .github/workflows/checkov-scan.yaml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/checkov-scan.yaml b/.github/workflows/checkov-scan.yaml index 0cfe25b..b89ce5e 100644 --- a/.github/workflows/checkov-scan.yaml +++ b/.github/workflows/checkov-scan.yaml @@ -17,7 +17,7 @@ on: use-test-reporter: description: 'Attach the test results as a report (default: true)' required: false - default: 'true' + default: true type: boolean issue-on-findings: description: 'GitHub users to assign when creating an issue for failed scans (comma-separated list: ''@user1'', ''@user2''). If left empty, no issue will be created.' From 8ff1cacbf59843c91264ed49cab87fcabadd2ad0 Mon Sep 17 00:00:00 2001 From: JonasPollokZweitag <„jonas.pollok@zweitag.de“> Date: Wed, 14 Jan 2026 10:03:15 +0100 Subject: [PATCH 20/53] test: added branch reference to scanning checkout --- .github/workflows/checkov-scan.yaml | 1 + 1 file changed, 1 insertion(+) diff --git a/.github/workflows/checkov-scan.yaml b/.github/workflows/checkov-scan.yaml index b89ce5e..9e9fa3d 100644 --- a/.github/workflows/checkov-scan.yaml +++ b/.github/workflows/checkov-scan.yaml @@ -40,6 +40,7 @@ jobs: uses: actions/checkout@v4 with: repository: zweitag/github-actions + ref: chore--create-configurable-Security-Scan path: _security-tools sparse-checkout: security-scanning From a23df454a5ae7dc829d634763d977bffc0d44837 Mon Sep 17 00:00:00 2001 From: JonasPollokZweitag <„jonas.pollok@zweitag.de“> Date: Wed, 14 Jan 2026 10:28:46 +0100 Subject: [PATCH 21/53] chore: checkout security scripts from repo --- .github/workflows/trivy-scan.yaml | 61 ++++++++++++++++++++++++------- 1 file changed, 47 insertions(+), 14 deletions(-) diff --git a/.github/workflows/trivy-scan.yaml b/.github/workflows/trivy-scan.yaml index 53ab9bf..96e406c 100644 --- a/.github/workflows/trivy-scan.yaml +++ b/.github/workflows/trivy-scan.yaml @@ -52,6 +52,17 @@ jobs: - name: checkout repository uses: actions/checkout@v4 + - name: checkout security-scanning scripts + uses: actions/checkout@v4 + with: + repository: zweitag/github-actions + ref: chore--create-configurable-Security-Scan + path: _security-tools + sparse-checkout: security-scanning + + - name: create output folder + run: mkdir -p ./scan-results + - name: setup trivy uses: aquasecurity/setup-trivy@e07451d2e059ed86c2870430ea286b3a9e0bf241 @@ -60,10 +71,10 @@ jobs: run: | trivy config ${{ inputs.path }} --exit-code 0 - - name: scan configuration for new medium, high, critical and unknown severities + - name: scan configuration with configured severities if: always() env: - REPORT: ${{ inputs.use-test-reporter == true && '-f json >> security-scanning/trivy.json' || '' }} + REPORT: ${{ inputs.use-test-reporter == true && '-f json >> ./scan-results/trivy.json' || '' }} IGNOREFILE: ${{ inputs.trivyignorefile != '' && format('--ignorefile {0}', inputs.trivyignorefile) || '' }} id: scan run: | @@ -79,14 +90,14 @@ jobs: - name: convert Trivy report to CTRF format if: always() && ${{ inputs.use-test-reporter }} run: | - python3 security-scanning/trivyconfig2ctrf.py ./security-scanning/trivy.json ./security-scanning/trivy.ctrf.json + python3 _security-tools/security-scanning/trivyconfig2ctrf.py ./scan-results/trivy.json ./scan-results/trivy.ctrf.json - name: Publish Test Report if: always() && ${{ inputs.use-test-reporter }} uses: ctrf-io/github-test-reporter@v1 with: - report-path: './security-scanning/trivy.ctrf.json' - template-path: './security-scanning/config_scan_template.hbs' + report-path: './scan-results/trivy.ctrf.json' + template-path: '_security-tools/security-scanning/config_scan_template.hbs' custom-report: true filesystem_scan: @@ -99,6 +110,17 @@ jobs: - name: checkout repository uses: actions/checkout@v4 + - name: checkout security-scanning scripts + uses: actions/checkout@v4 + with: + repository: zweitag/github-actions + ref: chore--create-configurable-Security-Scan + path: _security-tools + sparse-checkout: security-scanning + + - name: create output folder + run: mkdir -p ./scan-results + - name: setup trivy uses: aquasecurity/setup-trivy@e07451d2e059ed86c2870430ea286b3a9e0bf241 @@ -153,11 +175,11 @@ jobs: trivy fs ${{ inputs.path }} --exit-code 0 --skip-db-update # ob mit report geht weiß ich noch nicht - - name: scan filesystem for new medium, high, critical and unknown severities with report and ignorefile + - name: scan filesystem for configured severities if: always() id: scan env: - REPORT: ${{ inputs.use-test-reporter == true && '-f json > ./security-scanning/trivy.json' || '' }} + REPORT: ${{ inputs.use-test-reporter == true && '-f json > ./scan-results/trivy.json' || '' }} IGNOREFILE: ${{ inputs.trivyignorefile != '' && format('--ignorefile {0}', inputs.trivyignorefile) || '' }} run: | trivy fs ${{ inputs.path }} \ @@ -171,8 +193,8 @@ jobs: if: always() && ${{ inputs.use-test-reporter }} uses: ctrf-io/github-test-reporter@v1 with: - report-path: './security-scanning/trivy.ctrf.json' - template-path: './security-scanning/config_scan_template.hbs' + report-path: './scan-results/trivy.ctrf.json' + template-path: '_security-tools/security-scanning/config_scan_template.hbs' custom-report: true image_scan: @@ -185,6 +207,17 @@ jobs: - name: checkout repository uses: actions/checkout@v4 + - name: checkout security-scanning scripts + uses: actions/checkout@v4 + with: + repository: zweitag/github-actions + ref: chore--create-configurable-Security-Scan + path: _security-tools + sparse-checkout: security-scanning + + - name: create output folder + run: mkdir -p ./scan-results + - name: setup trivy uses: aquasecurity/setup-trivy@e07451d2e059ed86c2870430ea286b3a9e0bf241 @@ -250,10 +283,10 @@ jobs: run: | trivy image security-scan-image --exit-code 0 --skip-db-update --scanners vuln --timeout 10m --list-all-pkgs - - name: scan docker image for high, critical and unknown severities + - name: scan docker image for configured severities if: always() env: - REPORT: ${{ inputs.use-test-reporter == true && '-f json >> security-scanning/trivy.json' || '' }} + REPORT: ${{ inputs.use-test-reporter == true && '-f json >> ./scan-results/trivy.json' || '' }} IGNOREFILE: ${{ inputs.trivyignorefile != '' && format('--ignorefile {0}', inputs.trivyignorefile) || '' }} id: scan run: | @@ -269,14 +302,14 @@ jobs: - name: convert Trivy report to CTRF format if: always() && ${{ inputs.use-test-reporter }} run: | - python3 security-scanning/trivyimage2ctrf.py ./security-scanning/trivy.json ./security-scanning/trivy.ctrf.json + python3 _security-tools/security-scanning/trivyimage2ctrf.py ./scan-results/trivy.json ./scan-results/trivy.ctrf.json - name: Publish Test Report uses: ctrf-io/github-test-reporter@v1 if: always() && ${{ inputs.use-test-reporter }} with: - report-path: './security-scanning/trivy.ctrf.json' - template-path: './security-scanning/image_scan_template.hbs' + report-path: './scan-results/trivy.ctrf.json' + template-path: '_security-tools/security-scanning/image_scan_template.hbs' custom-report: true create_issues: From 57c4eaf9c5ab2702e43270fdae37ac4d689dc78c Mon Sep 17 00:00:00 2001 From: JonasPollokZweitag <„jonas.pollok@zweitag.de“> Date: Wed, 14 Jan 2026 11:10:53 +0100 Subject: [PATCH 22/53] fix: removed quotes from boolean --- .github/workflows/trivy-scan.yaml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/trivy-scan.yaml b/.github/workflows/trivy-scan.yaml index 96e406c..b32d047 100644 --- a/.github/workflows/trivy-scan.yaml +++ b/.github/workflows/trivy-scan.yaml @@ -22,7 +22,7 @@ on: use-test-reporter: description: 'Whether to attach the test results as a report (default: true)' required: false - default: 'true' + default: true type: boolean issue-on-findings: description: 'GitHub users to assign when creating an issue for failed scans (comma-separated list: ''@user1'', ''@user2''). If left empty, no issue will be created.' From 0c7a2e197d6e65c6e02131d2599a4d66e9123d06 Mon Sep 17 00:00:00 2001 From: JonasPollokZweitag <„jonas.pollok@zweitag.de“> Date: Wed, 14 Jan 2026 11:18:11 +0100 Subject: [PATCH 23/53] fix: -o instead of > --- .github/workflows/trivy-scan.yaml | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/.github/workflows/trivy-scan.yaml b/.github/workflows/trivy-scan.yaml index b32d047..d06d9f7 100644 --- a/.github/workflows/trivy-scan.yaml +++ b/.github/workflows/trivy-scan.yaml @@ -74,7 +74,7 @@ jobs: - name: scan configuration with configured severities if: always() env: - REPORT: ${{ inputs.use-test-reporter == true && '-f json >> ./scan-results/trivy.json' || '' }} + REPORT: ${{ inputs.use-test-reporter == true && '-f json -o ./scan-results/trivy.json' || '' }} IGNOREFILE: ${{ inputs.trivyignorefile != '' && format('--ignorefile {0}', inputs.trivyignorefile) || '' }} id: scan run: | @@ -179,7 +179,7 @@ jobs: if: always() id: scan env: - REPORT: ${{ inputs.use-test-reporter == true && '-f json > ./scan-results/trivy.json' || '' }} + REPORT: ${{ inputs.use-test-reporter == true && '-f json -o ./scan-results/trivy.json' || '' }} IGNOREFILE: ${{ inputs.trivyignorefile != '' && format('--ignorefile {0}', inputs.trivyignorefile) || '' }} run: | trivy fs ${{ inputs.path }} \ @@ -286,7 +286,7 @@ jobs: - name: scan docker image for configured severities if: always() env: - REPORT: ${{ inputs.use-test-reporter == true && '-f json >> ./scan-results/trivy.json' || '' }} + REPORT: ${{ inputs.use-test-reporter == true && '-f json -o ./scan-results/trivy.json' || '' }} IGNOREFILE: ${{ inputs.trivyignorefile != '' && format('--ignorefile {0}', inputs.trivyignorefile) || '' }} id: scan run: | From 0977cd2e501098f5870b63df6fe99911556f502f Mon Sep 17 00:00:00 2001 From: JonasPollokZweitag <„jonas.pollok@zweitag.de“> Date: Thu, 15 Jan 2026 12:25:40 +0100 Subject: [PATCH 24/53] fix: enable continue on error --- .github/workflows/trivy-scan.yaml | 24 ++++++++++-------------- 1 file changed, 10 insertions(+), 14 deletions(-) diff --git a/.github/workflows/trivy-scan.yaml b/.github/workflows/trivy-scan.yaml index d06d9f7..bab93e7 100644 --- a/.github/workflows/trivy-scan.yaml +++ b/.github/workflows/trivy-scan.yaml @@ -67,12 +67,11 @@ jobs: uses: aquasecurity/setup-trivy@e07451d2e059ed86c2870430ea286b3a9e0bf241 - name: scan configuration for full report - if: always() run: | trivy config ${{ inputs.path }} --exit-code 0 - name: scan configuration with configured severities - if: always() + continue-on-error: true env: REPORT: ${{ inputs.use-test-reporter == true && '-f json -o ./scan-results/trivy.json' || '' }} IGNOREFILE: ${{ inputs.trivyignorefile != '' && format('--ignorefile {0}', inputs.trivyignorefile) || '' }} @@ -80,20 +79,18 @@ jobs: run: | trivy config ${{ inputs.path }} \ --exit-code 1 \ - --skip-db-update \ --severity HIGH,CRITICAL,UNKNOWN \ - --scanners vuln \ --timeout 10m \ $IGNOREFILE \ $REPORT - name: convert Trivy report to CTRF format - if: always() && ${{ inputs.use-test-reporter }} + if: ${{ always() && inputs.use-test-reporter }} run: | python3 _security-tools/security-scanning/trivyconfig2ctrf.py ./scan-results/trivy.json ./scan-results/trivy.ctrf.json - name: Publish Test Report - if: always() && ${{ inputs.use-test-reporter }} + if: ${{ always() && inputs.use-test-reporter }} uses: ctrf-io/github-test-reporter@v1 with: report-path: './scan-results/trivy.ctrf.json' @@ -125,6 +122,7 @@ jobs: uses: aquasecurity/setup-trivy@e07451d2e059ed86c2870430ea286b3a9e0bf241 - name: download vulnerabilities database from aws + continue-on-error: true run: | MAX_RETRIES=3 RETRY_DELAY=60 @@ -177,6 +175,7 @@ jobs: # ob mit report geht weiß ich noch nicht - name: scan filesystem for configured severities if: always() + continue-on-error: true id: scan env: REPORT: ${{ inputs.use-test-reporter == true && '-f json -o ./scan-results/trivy.json' || '' }} @@ -190,7 +189,7 @@ jobs: $REPORT - name: Publish Test Report - if: always() && ${{ inputs.use-test-reporter }} + if: ${{ always() && inputs.use-test-reporter }} uses: ctrf-io/github-test-reporter@v1 with: report-path: './scan-results/trivy.ctrf.json' @@ -222,6 +221,7 @@ jobs: uses: aquasecurity/setup-trivy@e07451d2e059ed86c2870430ea286b3a9e0bf241 - name: download vulnerabilities database from aws + continue-on-error: true run: | MAX_RETRIES=3 RETRY_DELAY=60 @@ -266,11 +266,6 @@ jobs: done exit 1 - - name: scan filesystem for full report - if: always() - run: | - trivy fs ${{ inputs.path }} --exit-code 0 --skip-db-update - - name: build docker image uses: docker/build-push-action@v4 with: @@ -285,6 +280,7 @@ jobs: - name: scan docker image for configured severities if: always() + continue-on-error: true env: REPORT: ${{ inputs.use-test-reporter == true && '-f json -o ./scan-results/trivy.json' || '' }} IGNOREFILE: ${{ inputs.trivyignorefile != '' && format('--ignorefile {0}', inputs.trivyignorefile) || '' }} @@ -300,13 +296,13 @@ jobs: $REPORT - name: convert Trivy report to CTRF format - if: always() && ${{ inputs.use-test-reporter }} + if: ${{ always() && inputs.use-test-reporter }} run: | python3 _security-tools/security-scanning/trivyimage2ctrf.py ./scan-results/trivy.json ./scan-results/trivy.ctrf.json - name: Publish Test Report uses: ctrf-io/github-test-reporter@v1 - if: always() && ${{ inputs.use-test-reporter }} + if: ${{ always() && inputs.use-test-reporter }} with: report-path: './scan-results/trivy.ctrf.json' template-path: '_security-tools/security-scanning/image_scan_template.hbs' From f7b8cd4f88df719f7d037d9b00587048a3107216 Mon Sep 17 00:00:00 2001 From: JonasPollokZweitag <„jonas.pollok@zweitag.de“> Date: Thu, 15 Jan 2026 12:58:12 +0100 Subject: [PATCH 25/53] chore: renamed trivyconfig2ctrf convert script --- security-scanning/{trivyconfig2crtf.py => trivyconfig2ctrf.py} | 0 1 file changed, 0 insertions(+), 0 deletions(-) rename security-scanning/{trivyconfig2crtf.py => trivyconfig2ctrf.py} (100%) diff --git a/security-scanning/trivyconfig2crtf.py b/security-scanning/trivyconfig2ctrf.py similarity index 100% rename from security-scanning/trivyconfig2crtf.py rename to security-scanning/trivyconfig2ctrf.py From 7b87f9bad10315ca313569efa587aa530ae39c45 Mon Sep 17 00:00:00 2001 From: JonasPollokZweitag <„jonas.pollok@zweitag.de“> Date: Thu, 15 Jan 2026 14:28:21 +0100 Subject: [PATCH 26/53] chore: reduced scans to one job --- .github/workflows/trivy-scan.yaml | 277 +++++++++--------------------- 1 file changed, 81 insertions(+), 196 deletions(-) diff --git a/.github/workflows/trivy-scan.yaml b/.github/workflows/trivy-scan.yaml index bab93e7..9d917d7 100644 --- a/.github/workflows/trivy-scan.yaml +++ b/.github/workflows/trivy-scan.yaml @@ -1,4 +1,4 @@ -# .github/workflows/security-scan.yaml +# .github/workflows/trivy-scan.yaml name: Security Scan on: @@ -27,7 +27,7 @@ on: issue-on-findings: description: 'GitHub users to assign when creating an issue for failed scans (comma-separated list: ''@user1'', ''@user2''). If left empty, no issue will be created.' required: false - default: 'false' + default: '' type: string secrets: GH_TOKEN: @@ -41,9 +41,7 @@ env: TRIVY_DB_REPOSITORY: public.ecr.aws/aquasecurity/trivy-db,aquasec/trivy-db,ghcr.io/aquasecurity/trivy-db jobs: - - configuration_scan: - if: ${{ inputs.scan-type == 'config' }} + security_scan: outputs: NOTIFICATION: ${{ steps.scan.outcome == 'failure' && 'true' || 'false' }} runs-on: ubuntu-latest @@ -65,64 +63,9 @@ jobs: - name: setup trivy uses: aquasecurity/setup-trivy@e07451d2e059ed86c2870430ea286b3a9e0bf241 - - - name: scan configuration for full report - run: | - trivy config ${{ inputs.path }} --exit-code 0 - - - name: scan configuration with configured severities - continue-on-error: true - env: - REPORT: ${{ inputs.use-test-reporter == true && '-f json -o ./scan-results/trivy.json' || '' }} - IGNOREFILE: ${{ inputs.trivyignorefile != '' && format('--ignorefile {0}', inputs.trivyignorefile) || '' }} - id: scan - run: | - trivy config ${{ inputs.path }} \ - --exit-code 1 \ - --severity HIGH,CRITICAL,UNKNOWN \ - --timeout 10m \ - $IGNOREFILE \ - $REPORT - - - name: convert Trivy report to CTRF format - if: ${{ always() && inputs.use-test-reporter }} - run: | - python3 _security-tools/security-scanning/trivyconfig2ctrf.py ./scan-results/trivy.json ./scan-results/trivy.ctrf.json - - - name: Publish Test Report - if: ${{ always() && inputs.use-test-reporter }} - uses: ctrf-io/github-test-reporter@v1 - with: - report-path: './scan-results/trivy.ctrf.json' - template-path: '_security-tools/security-scanning/config_scan_template.hbs' - custom-report: true - - filesystem_scan: - if: ${{ inputs.scan-type == 'filesystem' || inputs.scan-type == '' }} - outputs: - NOTIFICATION: ${{ steps.scan.outcome == 'failure' && 'true' || 'false' }} - runs-on: ubuntu-latest - - steps: - - name: checkout repository - uses: actions/checkout@v4 - - - name: checkout security-scanning scripts - uses: actions/checkout@v4 - with: - repository: zweitag/github-actions - ref: chore--create-configurable-Security-Scan - path: _security-tools - sparse-checkout: security-scanning - - - name: create output folder - run: mkdir -p ./scan-results - - - name: setup trivy - uses: aquasecurity/setup-trivy@e07451d2e059ed86c2870430ea286b3a9e0bf241 - - - name: download vulnerabilities database from aws - continue-on-error: true + + - name: download vulnerabilities database + if: inputs.scan-type != 'config' run: | MAX_RETRIES=3 RETRY_DELAY=60 @@ -142,43 +85,56 @@ jobs: sleep $RETRY_DELAY fi done - exit 1 - - - name: download vulnerabilities database if aws failed - if: failure() - run: | - MAX_RETRIES=3 - RETRY_DELAY=60 - ATTEMPT=1 - until [ $ATTEMPT -gt $MAX_RETRIES ] - do - echo "Attempt $ATTEMPT of $MAX_RETRIES..." - GITHUB_TOKEN=${{ secrets.GH_TOKEN }} trivy fs --download-db-only + echo "Trying fallback without custom repository..." + GITHUB_TOKEN=${{ secrets.GH_TOKEN }} trivy fs --download-db-only - if [ $? -eq 0 ]; then - echo "Trivy DB download succeeded." - exit 0 - else - echo "Trivy DB download failed. Retrying in $RETRY_DELAY seconds..." - ATTEMPT=$((ATTEMPT + 1)) - sleep $RETRY_DELAY - fi - done - exit 1 + - name: scan for full report (config) + if: inputs.scan-type == 'config' + run: trivy config ${{ inputs.path }} --exit-code 0 - - name: scan filesystem for full report - if: always() + - name: scan for full report (filesystem) + if: inputs.scan-type == 'filesystem' + run: trivy fs ${{ inputs.path }} --exit-code 0 --skip-db-update + + - name: scan for full report (image - filesystem) + if: inputs.scan-type == 'image' + run: trivy fs ${{ inputs.path }} --exit-code 0 --skip-db-update + + - name: build docker image + if: inputs.scan-type == 'image' + uses: docker/build-push-action@v4 + with: + context: ${{ inputs.path }} + push: false + tags: security-scan-image + secrets: ${{ secrets.DOCKER_IMAGE_SECRETS }} + + - name: scan for full report (image) + if: inputs.scan-type == 'image' + run: trivy image security-scan-image --exit-code 0 --skip-db-update --scanners vuln --timeout 10m --list-all-pkgs + + - name: scan with configured severities (config) + if: inputs.scan-type == 'config' + id: scan-config + continue-on-error: true + env: + REPORT: ${{ inputs.use-test-reporter && '-f json -o ./scan-results/trivy.json' || '' }} + IGNOREFILE: ${{ inputs.trivyignorefile != '' && format('--ignorefile {0}', inputs.trivyignorefile) || '' }} run: | - trivy fs ${{ inputs.path }} --exit-code 0 --skip-db-update + trivy config ${{ inputs.path }} \ + --exit-code 1 \ + --severity HIGH,CRITICAL,UNKNOWN \ + --timeout 10m \ + $IGNOREFILE \ + $REPORT - # ob mit report geht weiß ich noch nicht - - name: scan filesystem for configured severities - if: always() + - name: scan with configured severities (filesystem) + if: inputs.scan-type == 'filesystem' + id: scan-filesystem continue-on-error: true - id: scan env: - REPORT: ${{ inputs.use-test-reporter == true && '-f json -o ./scan-results/trivy.json' || '' }} + REPORT: ${{ inputs.use-test-reporter && '-f json -o ./scan-results/trivy.json' || '' }} IGNOREFILE: ${{ inputs.trivyignorefile != '' && format('--ignorefile {0}', inputs.trivyignorefile) || '' }} run: | trivy fs ${{ inputs.path }} \ @@ -188,103 +144,13 @@ jobs: $IGNOREFILE \ $REPORT - - name: Publish Test Report - if: ${{ always() && inputs.use-test-reporter }} - uses: ctrf-io/github-test-reporter@v1 - with: - report-path: './scan-results/trivy.ctrf.json' - template-path: '_security-tools/security-scanning/config_scan_template.hbs' - custom-report: true - - image_scan: - if: ${{ inputs.scan-type == 'image' }} - outputs: - NOTIFICATION: ${{ steps.scan.outcome == 'failure' && 'true' || 'false' }} - runs-on: ubuntu-latest - - steps: - - name: checkout repository - uses: actions/checkout@v4 - - - name: checkout security-scanning scripts - uses: actions/checkout@v4 - with: - repository: zweitag/github-actions - ref: chore--create-configurable-Security-Scan - path: _security-tools - sparse-checkout: security-scanning - - - name: create output folder - run: mkdir -p ./scan-results - - - name: setup trivy - uses: aquasecurity/setup-trivy@e07451d2e059ed86c2870430ea286b3a9e0bf241 - - - name: download vulnerabilities database from aws - continue-on-error: true - run: | - MAX_RETRIES=3 - RETRY_DELAY=60 - ATTEMPT=1 - - until [ $ATTEMPT -gt $MAX_RETRIES ] - do - echo "Attempt $ATTEMPT of $MAX_RETRIES..." - GITHUB_TOKEN=${{ secrets.GH_TOKEN }} trivy fs --download-db-only --db-repository "${{ env.TRIVY_DB_REPOSITORY }}" - - if [ $? -eq 0 ]; then - echo "Trivy DB download succeeded." - exit 0 - else - echo "Trivy DB download failed. Retrying in $RETRY_DELAY seconds..." - ATTEMPT=$((ATTEMPT + 1)) - sleep $RETRY_DELAY - fi - done - exit 1 - - - name: download vulnerabilities database if aws failed - if: failure() - run: | - MAX_RETRIES=3 - RETRY_DELAY=60 - ATTEMPT=1 - - until [ $ATTEMPT -gt $MAX_RETRIES ] - do - echo "Attempt $ATTEMPT of $MAX_RETRIES..." - GITHUB_TOKEN=${{ secrets.GH_TOKEN }} trivy fs --download-db-only - - if [ $? -eq 0 ]; then - echo "Trivy DB download succeeded." - exit 0 - else - echo "Trivy DB download failed. Retrying in $RETRY_DELAY seconds..." - ATTEMPT=$((ATTEMPT + 1)) - sleep $RETRY_DELAY - fi - done - exit 1 - - - name: build docker image - uses: docker/build-push-action@v4 - with: - context: ${{ inputs.path }} - push: false - tags: security-scan-image - secrets: ${{ secrets.DOCKER_IMAGE_SECRETS }} - - - name: scan image for full report - run: | - trivy image security-scan-image --exit-code 0 --skip-db-update --scanners vuln --timeout 10m --list-all-pkgs - - - name: scan docker image for configured severities - if: always() + - name: scan with configured severities (image) + if: inputs.scan-type == 'image' + id: scan-image continue-on-error: true env: - REPORT: ${{ inputs.use-test-reporter == true && '-f json -o ./scan-results/trivy.json' || '' }} + REPORT: ${{ inputs.use-test-reporter && '-f json -o ./scan-results/trivy.json' || '' }} IGNOREFILE: ${{ inputs.trivyignorefile != '' && format('--ignorefile {0}', inputs.trivyignorefile) || '' }} - id: scan run: | trivy image security-scan-image \ --exit-code 1 \ @@ -295,27 +161,46 @@ jobs: $IGNOREFILE \ $REPORT - - name: convert Trivy report to CTRF format - if: ${{ always() && inputs.use-test-reporter }} + - name: set scan outcome + id: scan + if: always() run: | - python3 _security-tools/security-scanning/trivyimage2ctrf.py ./scan-results/trivy.json ./scan-results/trivy.ctrf.json + if [[ "${{ steps.scan-config.outcome }}" == "failure" ]] || \ + [[ "${{ steps.scan-filesystem.outcome }}" == "failure" ]] || \ + [[ "${{ steps.scan-image.outcome }}" == "failure" ]]; then + echo "outcome=failure" >> $GITHUB_OUTPUT + exit 1 + fi + + - name: convert Trivy report to CTRF format (config) + if: always() && inputs.use-test-reporter && inputs.scan-type == 'config' + run: | + python3 _security-tools/security-scanning/trivyconfig2ctrf.py \ + ./scan-results/trivy.json \ + ./scan-results/trivy.ctrf.json + + - name: convert Trivy report to CTRF format (image) + if: ${{ always() && inputs.use-test-reporter && inputs.scan-type == 'image' }} + run: | + python3 _security-tools/security-scanning/trivyimage2ctrf.py \ + ./scan-results/trivy.json \ + ./scan-results/trivy.ctrf.json - name: Publish Test Report - uses: ctrf-io/github-test-reporter@v1 if: ${{ always() && inputs.use-test-reporter }} + uses: ctrf-io/github-test-reporter@v1 with: report-path: './scan-results/trivy.ctrf.json' - template-path: '_security-tools/security-scanning/image_scan_template.hbs' + template-path: '_security-tools/security-scanning/${{ inputs.scan-type == "image" && "image_scan_template.hbs" || "config_scan_template.hbs" }}' custom-report: true - create_issues: - needs: [configuration_scan, filesystem_scan, image_scan] + create_issue: + needs: [security_scan] runs-on: ubuntu-latest - if: ${{ inputs.issue-on-findings != 'false' }} + if: ${{ always() && inputs.issue-on-findings != '' && needs.security_scan.outputs.NOTIFICATION == 'true' }} steps: - name: Create issue/Comment on issue - if: ${{ always() && (needs.image_scan.outputs.NOTIFICATION == 'true' || needs.configuration_scan.outputs.NOTIFICATION == 'true' || needs.filesystem_scan.outputs.NOTIFICATION == 'true') }} uses: actions/github-script@v7 with: script: | From c0643f65bedd2622a7296369f272d8d67409f890 Mon Sep 17 00:00:00 2001 From: JonasPollokZweitag <„jonas.pollok@zweitag.de“> Date: Thu, 15 Jan 2026 14:35:00 +0100 Subject: [PATCH 27/53] fix: single quotes instead of double --- .github/workflows/trivy-scan.yaml | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/.github/workflows/trivy-scan.yaml b/.github/workflows/trivy-scan.yaml index 9d917d7..f26e436 100644 --- a/.github/workflows/trivy-scan.yaml +++ b/.github/workflows/trivy-scan.yaml @@ -187,12 +187,12 @@ jobs: ./scan-results/trivy.ctrf.json - name: Publish Test Report - if: ${{ always() && inputs.use-test-reporter }} - uses: ctrf-io/github-test-reporter@v1 - with: - report-path: './scan-results/trivy.ctrf.json' - template-path: '_security-tools/security-scanning/${{ inputs.scan-type == "image" && "image_scan_template.hbs" || "config_scan_template.hbs" }}' - custom-report: true + if: always() && inputs.use-test-reporter + uses: ctrf-io/github-test-reporter@v1 + with: + report-path: './scan-results/trivy.ctrf.json' + template-path: ${{ format('_security-tools/security-scanning/{0}', inputs.scan-type == 'image' && 'image_scan_template.hbs' || 'config_scan_template.hbs') }} + custom-report: true create_issue: needs: [security_scan] From c0fc2697d9df36e4b1b8cb2aeb5579be5abe619a Mon Sep 17 00:00:00 2001 From: JonasPollokZweitag <„jonas.pollok@zweitag.de“> Date: Thu, 15 Jan 2026 14:47:07 +0100 Subject: [PATCH 28/53] fix: indetation --- .github/workflows/trivy-scan.yaml | 70 +++++++++++++++---------------- 1 file changed, 35 insertions(+), 35 deletions(-) diff --git a/.github/workflows/trivy-scan.yaml b/.github/workflows/trivy-scan.yaml index f26e436..9e0d51f 100644 --- a/.github/workflows/trivy-scan.yaml +++ b/.github/workflows/trivy-scan.yaml @@ -187,12 +187,12 @@ jobs: ./scan-results/trivy.ctrf.json - name: Publish Test Report - if: always() && inputs.use-test-reporter - uses: ctrf-io/github-test-reporter@v1 - with: - report-path: './scan-results/trivy.ctrf.json' - template-path: ${{ format('_security-tools/security-scanning/{0}', inputs.scan-type == 'image' && 'image_scan_template.hbs' || 'config_scan_template.hbs') }} - custom-report: true + if: always() && inputs.use-test-reporter + uses: ctrf-io/github-test-reporter@v1 + with: + report-path: './scan-results/trivy.ctrf.json' + template-path: ${{ format('_security-tools/security-scanning/{0}', inputs.scan-type == 'image' && 'image_scan_template.hbs' || 'config_scan_template.hbs') }} + custom-report: true create_issue: needs: [security_scan] @@ -200,35 +200,35 @@ jobs: if: ${{ always() && inputs.issue-on-findings != '' && needs.security_scan.outputs.NOTIFICATION == 'true' }} steps: - - name: Create issue/Comment on issue - uses: actions/github-script@v7 - with: - script: | - const repo = context.repo.repo; - const owner = context.repo.owner; - const issue_title = 'Security scan failed'; - const issue_body = 'One or more security scans failed. Please check the workflow run for more information: https://github.com/${{ github.repository }}/actions/runs/${{ github.run_id }}\nPlease check if the vulnerabilities are fixable. If there is a fix: Create a ticket for the fix or resolve it.\n' - const assignees = [${{ inputs.issue-on-findings }}]; - const existing_issue = await github.rest.issues.listForRepo({ - owner, - repo, - state: 'open', - labels: 'security-scan-failure' + - name: Create issue/Comment on issue + uses: actions/github-script@v7 + with: + script: | + const repo = context.repo.repo; + const owner = context.repo.owner; + const issue_title = 'Security scan failed'; + const issue_body = 'One or more security scans failed. Please check the workflow run for more information: https://github.com/${{ github.repository }}/actions/runs/${{ github.run_id }}\nPlease check if the vulnerabilities are fixable. If there is a fix: Create a ticket for the fix or resolve it.\n' + const assignees = [${{ inputs.issue-on-findings }}]; + const existing_issue = await github.rest.issues.listForRepo({ + owner, + repo, + state: 'open', + labels: 'security-scan-failure' + }); + if (existing_issue.data.length === 0) { + await github.rest.issues.create({ + owner, + repo, + title: issue_title, + body: issue_body, + labels: ['security-scan-failure'] }); - if (existing_issue.data.length === 0) { - await github.rest.issues.create({ + } else { + const issue_number = existing_issue.data[0].number; + await github.rest.issues.createComment({ owner, repo, - title: issue_title, - body: issue_body, - labels: ['security-scan-failure'] - }); - } else { - const issue_number = existing_issue.data[0].number; - await github.rest.issues.createComment({ - owner, - repo, - issue_number, - body: issue_body - }); - } + issue_number, + body: issue_body + }); + } From c5eea6973fb5df69476bdd92e28d0f75dbec748e Mon Sep 17 00:00:00 2001 From: JonasPollokZweitag <„jonas.pollok@zweitag.de“> Date: Tue, 20 Jan 2026 07:27:03 +0100 Subject: [PATCH 29/53] chore: only allow one ticket assignee --- .github/workflows/trivy-scan.yaml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/.github/workflows/trivy-scan.yaml b/.github/workflows/trivy-scan.yaml index 9e0d51f..0e086c3 100644 --- a/.github/workflows/trivy-scan.yaml +++ b/.github/workflows/trivy-scan.yaml @@ -25,7 +25,7 @@ on: default: true type: boolean issue-on-findings: - description: 'GitHub users to assign when creating an issue for failed scans (comma-separated list: ''@user1'', ''@user2''). If left empty, no issue will be created.' + description: 'One GitHub user to mention when creating an issue for failed scans (e.g., @username). If left empty, no issue will be created.' required: false default: '' type: string @@ -207,7 +207,7 @@ jobs: const repo = context.repo.repo; const owner = context.repo.owner; const issue_title = 'Security scan failed'; - const issue_body = 'One or more security scans failed. Please check the workflow run for more information: https://github.com/${{ github.repository }}/actions/runs/${{ github.run_id }}\nPlease check if the vulnerabilities are fixable. If there is a fix: Create a ticket for the fix or resolve it.\n' + const issue_body = '${{ inputs.issue-on-findings }} One or more security scans failed. Please check the workflow run for more information: https://github.com/${{ github.repository }}/actions/runs/${{ github.run_id }}\nPlease check if the vulnerabilities are fixable. If there is a fix: Create a ticket for the fix or resolve it.\n' const assignees = [${{ inputs.issue-on-findings }}]; const existing_issue = await github.rest.issues.listForRepo({ owner, From 46796ab5d350ffc703262550b6aa9996e042491f Mon Sep 17 00:00:00 2001 From: JonasPollokZweitag <„jonas.pollok@zweitag.de“> Date: Tue, 20 Jan 2026 08:15:23 +0100 Subject: [PATCH 30/53] chore: add converter and template for filesystem --- .github/workflows/trivy-scan.yaml | 14 ++- .../filesystem_scan_template.hbs | 71 ++++++++++++ security-scanning/trivyfs2ctrf.py | 102 ++++++++++++++++++ 3 files changed, 182 insertions(+), 5 deletions(-) create mode 100644 security-scanning/filesystem_scan_template.hbs create mode 100644 security-scanning/trivyfs2ctrf.py diff --git a/.github/workflows/trivy-scan.yaml b/.github/workflows/trivy-scan.yaml index 0e086c3..48afa55 100644 --- a/.github/workflows/trivy-scan.yaml +++ b/.github/workflows/trivy-scan.yaml @@ -97,10 +97,6 @@ jobs: if: inputs.scan-type == 'filesystem' run: trivy fs ${{ inputs.path }} --exit-code 0 --skip-db-update - - name: scan for full report (image - filesystem) - if: inputs.scan-type == 'image' - run: trivy fs ${{ inputs.path }} --exit-code 0 --skip-db-update - - name: build docker image if: inputs.scan-type == 'image' uses: docker/build-push-action@v4 @@ -139,6 +135,7 @@ jobs: run: | trivy fs ${{ inputs.path }} \ --exit-code 1 \ + --scanners vuln \ --skip-db-update \ --severity MEDIUM,HIGH,CRITICAL,UNKNOWN \ $IGNOREFILE \ @@ -186,12 +183,19 @@ jobs: ./scan-results/trivy.json \ ./scan-results/trivy.ctrf.json + - name: convert Trivy report to CTRF format (filessystem) + if: ${{ always() && inputs.use-test-reporter && inputs.scan-type == 'filesystem' }} + run: | + python3 _security-tools/security-scanning/trivyfs2ctrf.py \ + ./scan-results/trivy.json \ + ./scan-results/trivy.ctrf.json + - name: Publish Test Report if: always() && inputs.use-test-reporter uses: ctrf-io/github-test-reporter@v1 with: report-path: './scan-results/trivy.ctrf.json' - template-path: ${{ format('_security-tools/security-scanning/{0}', inputs.scan-type == 'image' && 'image_scan_template.hbs' || 'config_scan_template.hbs') }} + template-path: ${{ format('_security-tools/security-scanning/{0}_scan_template.hbs', inputs.scan-type) }} custom-report: true create_issue: diff --git a/security-scanning/filesystem_scan_template.hbs b/security-scanning/filesystem_scan_template.hbs new file mode 100644 index 0000000..de837a1 --- /dev/null +++ b/security-scanning/filesystem_scan_template.hbs @@ -0,0 +1,71 @@ +## Summary of {{ctrf.tool.name}} Security Scan Results + +| **Failed ❌** | +| --- | +| {{ctrf.summary.failed}} | + +{{#if (eq ctrf.summary.failed 0)}} +### No Vulnerabilities or Secrets Found! 🎉 +{{else}} +## Findings: +{{#each ctrf.tests}} +
+ {{#if (eq type "secret")}}🔑{{else}}🔴{{/if}} {{name}} ({{severity}}) + + + + + + {{#if lines}} + + + + + {{/if}} + {{#if package}} + + + + + {{/if}} + {{#if fixedVersion}} + + + + + {{/if}} + {{#if guideline}} + + + + + {{/if}} + {{#if description}} + + + + + {{/if}} + {{#if resolution}} + + + + + {{/if}} + {{#if references}} + + + + + {{/if}} +
file{{file}}
lines{{lines}}
package{{package}}@{{installedVersion}}
fixed in{{fixedVersion}}
details + {{guideline}} +
description{{description}}
proposed solution{{resolution}}
references + {{#each references}} + {{this}}{{#unless @last}}, {{/unless}} + {{/each}} +
+
+ +{{/each}} +{{/if}} diff --git a/security-scanning/trivyfs2ctrf.py b/security-scanning/trivyfs2ctrf.py new file mode 100644 index 0000000..e37bba7 --- /dev/null +++ b/security-scanning/trivyfs2ctrf.py @@ -0,0 +1,102 @@ +import json +import sys + + +def extract_vulnerabilities_from_result(result): + """Extract vulnerabilities from a Trivy result.""" + tests = [] + vulnerabilities = result.get("Vulnerabilities", []) + + for vuln in vulnerabilities: + tests.append({ + "name": f"{vuln.get('PkgName', '')}@{vuln.get('InstalledVersion', '')} - {vuln.get('VulnerabilityID', '')}", + "status": "failed", + "duration": 1, + "file": result.get("Target", ""), + "lines": None, + "guideline": vuln.get("PrimaryURL", ""), + "severity": vuln.get("Severity", ""), + "description": vuln.get("Description", ""), + "message": vuln.get("Title", ""), + "resolution": f"Update to version {vuln.get('FixedVersion', 'N/A')}" if vuln.get("FixedVersion") else "No fix available", + "references": vuln.get("References", []), + "type": result.get("Type", ""), + "id": vuln.get("VulnerabilityID", ""), + "package": vuln.get("PkgName", ""), + "installedVersion": vuln.get("InstalledVersion", ""), + "fixedVersion": vuln.get("FixedVersion", ""), + }) + + return tests + + +def extract_secrets_from_result(result): + """Extract secrets from a Trivy result.""" + tests = [] + secrets = result.get("Secrets", []) + + for secret in secrets: + lines = None + if secret.get("StartLine") and secret.get("EndLine"): + lines = [secret["StartLine"], secret["EndLine"]] + + tests.append({ + "name": f"{secret.get('RuleID', '')} - {secret.get('Title', '')}", + "status": "failed", + "duration": 1, + "file": result.get("Target", ""), + "lines": lines, + "guideline": "", + "severity": secret.get("Severity", ""), + "description": f"Category: {secret.get('Category', '')}", + "message": secret.get("Match", ""), + "resolution": "Remove or rotate the exposed secret", + "references": [], + "type": "secret", + "id": secret.get("RuleID", ""), + }) + + return tests + + +def trivy_fs_to_ctrf(trivy_json): + """Convert Trivy filesystem scan JSON to CTRF format.""" + tests = [] + results = trivy_json.get("Results", []) + + for result in results: + tests.extend(extract_vulnerabilities_from_result(result)) + tests.extend(extract_secrets_from_result(result)) + + failed = len(tests) + + return { + "results": { + "tool": { + "name": "Trivy Filesystem" + }, + "summary": { + "failed": failed, + }, + "tests": tests, + "environment": { + "appName": trivy_json.get("ArtifactName", ""), + "buildName": trivy_json.get("Metadata", {}).get("Branch", ""), + "buildNumber": trivy_json.get("Metadata", {}).get("Commit", "")[:8] if trivy_json.get("Metadata", {}).get("Commit") else "" + } + } + } + + +if __name__ == "__main__": + if len(sys.argv) != 3: + print("Usage: python trivyfs2ctrf.py ") + sys.exit(1) + + with open(sys.argv[1]) as f: + trivy_json = json.load(f) + + ctrf_json = trivy_fs_to_ctrf(trivy_json) + + with open(sys.argv[2], "w") as f: + json.dump(ctrf_json, f, indent=2) From aa922d6e8cdb209522cf4ee478d999613e0205c0 Mon Sep 17 00:00:00 2001 From: JonasPollokZweitag <„jonas.pollok@zweitag.de“> Date: Tue, 20 Jan 2026 08:19:13 +0100 Subject: [PATCH 31/53] chore: removed @ in assignees array --- .github/workflows/trivy-scan.yaml | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/.github/workflows/trivy-scan.yaml b/.github/workflows/trivy-scan.yaml index 48afa55..d10b380 100644 --- a/.github/workflows/trivy-scan.yaml +++ b/.github/workflows/trivy-scan.yaml @@ -25,7 +25,7 @@ on: default: true type: boolean issue-on-findings: - description: 'One GitHub user to mention when creating an issue for failed scans (e.g., @username). If left empty, no issue will be created.' + description: 'One GitHub user to mention when creating an issue for failed scans (e.g., username). If left empty, no issue will be created.' required: false default: '' type: string @@ -211,8 +211,8 @@ jobs: const repo = context.repo.repo; const owner = context.repo.owner; const issue_title = 'Security scan failed'; - const issue_body = '${{ inputs.issue-on-findings }} One or more security scans failed. Please check the workflow run for more information: https://github.com/${{ github.repository }}/actions/runs/${{ github.run_id }}\nPlease check if the vulnerabilities are fixable. If there is a fix: Create a ticket for the fix or resolve it.\n' - const assignees = [${{ inputs.issue-on-findings }}]; + const issue_body = '@${{ inputs.issue-on-findings }} One or more security scans failed. Please check the workflow run for more information: https://github.com/${{ github.repository }}/actions/runs/${{ github.run_id }}\nPlease check if the vulnerabilities are fixable. If there is a fix: Create a ticket for the fix or resolve it.\n' + const assignees = ['${{ inputs.issue-on-findings }}']; const existing_issue = await github.rest.issues.listForRepo({ owner, repo, From d855220c4c400cc9a86634071fd78cbdd2e1de7a Mon Sep 17 00:00:00 2001 From: JonasPollokZweitag <„jonas.pollok@zweitag.de“> Date: Tue, 20 Jan 2026 08:36:32 +0100 Subject: [PATCH 32/53] chore: use configureable severity level --- .github/workflows/checkov-scan.yaml | 64 ++++++++++++++--------------- .github/workflows/trivy-scan.yaml | 27 +++++++++--- 2 files changed, 53 insertions(+), 38 deletions(-) diff --git a/.github/workflows/checkov-scan.yaml b/.github/workflows/checkov-scan.yaml index 9e9fa3d..18f95c3 100644 --- a/.github/workflows/checkov-scan.yaml +++ b/.github/workflows/checkov-scan.yaml @@ -20,9 +20,9 @@ on: default: true type: boolean issue-on-findings: - description: 'GitHub users to assign when creating an issue for failed scans (comma-separated list: ''@user1'', ''@user2''). If left empty, no issue will be created.' + description: 'One GitHub user to assign when creating an issue for failed scans (e.g., username). If left empty, no issue will be created.' required: false - default: 'false' + default: '' type: string jobs: @@ -86,36 +86,36 @@ jobs: if: ${{ inputs.issue-on-findings != 'false' }} steps: - - name: Create issue/Comment on issue - if: ${{ always() && needs.checkov_scan.outputs.NOTIFICATION == 'true' }} - uses: actions/github-script@v7 - with: - script: | - const repo = context.repo.repo; - const owner = context.repo.owner; - const issue_title = 'Security scan failed'; - const issue_body = 'One or more security scans failed. Please check the workflow run for more information: https://github.com/${{ github.repository }}/actions/runs/${{ github.run_id }}\nPlease check if the vulnerabilities are fixable. If there is a fix: Create a ticket for the fix or resolve it.\n' - const assignees = [${{ inputs.issue-on-findings }}]; - const existing_issue = await github.rest.issues.listForRepo({ - owner, - repo, - state: 'open', - labels: 'security-scan-failure' + - name: Create issue/Comment on issue + uses: actions/github-script@v7 + with: + script: | + const repo = context.repo.repo; + const owner = context.repo.owner; + const issue_title = 'Security scan failed'; + const issue_body = '@${{ inputs.issue-on-findings }} One or more security scans failed. Please check the workflow run for more information: https://github.com/${{ github.repository }}/actions/runs/${{ github.run_id }}\nPlease check if the vulnerabilities are fixable. If there is a fix: Create a ticket for the fix or resolve it.\n' + const assignees = ['${{ inputs.issue-on-findings }}']; + const existing_issue = await github.rest.issues.listForRepo({ + owner, + repo, + state: 'open', + labels: 'security-scan-failure', + assignees: assignees + }); + if (existing_issue.data.length === 0) { + await github.rest.issues.create({ + owner, + repo, + title: issue_title, + body: issue_body, + labels: ['security-scan-failure'] }); - if (existing_issue.data.length === 0) { - await github.rest.issues.create({ + } else { + const issue_number = existing_issue.data[0].number; + await github.rest.issues.createComment({ owner, repo, - title: issue_title, - body: issue_body, - labels: ['security-scan-failure'] - }); - } else { - const issue_number = existing_issue.data[0].number; - await github.rest.issues.createComment({ - owner, - repo, - issue_number, - body: issue_body - }); - } + issue_number, + body: issue_body + }); + } diff --git a/.github/workflows/trivy-scan.yaml b/.github/workflows/trivy-scan.yaml index d10b380..ed5b0b2 100644 --- a/.github/workflows/trivy-scan.yaml +++ b/.github/workflows/trivy-scan.yaml @@ -9,6 +9,16 @@ on: required: false default: 'filesystem' type: string + check-secrets: + description: 'Whether to check for secrets in filesystem scans (default: false)' + required: false + default: 'false' + type: string + severity-level: + description: 'Minimum severity level to report (default: HIGH)' + required: false + default: 'HIGH' + type: string trivyignorefile: description: 'Path to the Trivy ignore file (default: none)' default: '' @@ -25,7 +35,7 @@ on: default: true type: boolean issue-on-findings: - description: 'One GitHub user to mention when creating an issue for failed scans (e.g., username). If left empty, no issue will be created.' + description: 'One GitHub user to assign when creating an issue for failed scans (e.g., username). If left empty, no issue will be created.' required: false default: '' type: string @@ -117,10 +127,11 @@ jobs: env: REPORT: ${{ inputs.use-test-reporter && '-f json -o ./scan-results/trivy.json' || '' }} IGNOREFILE: ${{ inputs.trivyignorefile != '' && format('--ignorefile {0}', inputs.trivyignorefile) || '' }} + SEVERITY: ${{ inputs.severity-level }} run: | trivy config ${{ inputs.path }} \ --exit-code 1 \ - --severity HIGH,CRITICAL,UNKNOWN \ + --severity $SEVERITY \ --timeout 10m \ $IGNOREFILE \ $REPORT @@ -132,12 +143,14 @@ jobs: env: REPORT: ${{ inputs.use-test-reporter && '-f json -o ./scan-results/trivy.json' || '' }} IGNOREFILE: ${{ inputs.trivyignorefile != '' && format('--ignorefile {0}', inputs.trivyignorefile) || '' }} + SCANNERS: ${{ inputs.check-secrets != 'true' && '--scanners vuln' || '' }} + SEVERITY: ${{ inputs.severity-level }} run: | trivy fs ${{ inputs.path }} \ --exit-code 1 \ - --scanners vuln \ --skip-db-update \ - --severity MEDIUM,HIGH,CRITICAL,UNKNOWN \ + --severity $SEVERITY \ + $SCANNERS \ $IGNOREFILE \ $REPORT @@ -148,11 +161,12 @@ jobs: env: REPORT: ${{ inputs.use-test-reporter && '-f json -o ./scan-results/trivy.json' || '' }} IGNOREFILE: ${{ inputs.trivyignorefile != '' && format('--ignorefile {0}', inputs.trivyignorefile) || '' }} + SEVERITY: ${{ inputs.severity-level }} run: | trivy image security-scan-image \ --exit-code 1 \ --skip-db-update \ - --severity HIGH,CRITICAL,UNKNOWN \ + --severity $SEVERITY \ --scanners vuln \ --timeout 10m \ $IGNOREFILE \ @@ -217,7 +231,8 @@ jobs: owner, repo, state: 'open', - labels: 'security-scan-failure' + labels: 'security-scan-failure', + assignees: assignees }); if (existing_issue.data.length === 0) { await github.rest.issues.create({ From b8749413b96e9a755945d5a08b5cbc1f45e97340 Mon Sep 17 00:00:00 2001 From: JonasPollokZweitag <„jonas.pollok@zweitag.de“> Date: Tue, 20 Jan 2026 08:39:18 +0100 Subject: [PATCH 33/53] chore: changed secret check to bool --- .github/workflows/trivy-scan.yaml | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/.github/workflows/trivy-scan.yaml b/.github/workflows/trivy-scan.yaml index ed5b0b2..7e3c6e6 100644 --- a/.github/workflows/trivy-scan.yaml +++ b/.github/workflows/trivy-scan.yaml @@ -12,8 +12,8 @@ on: check-secrets: description: 'Whether to check for secrets in filesystem scans (default: false)' required: false - default: 'false' - type: string + default: false + type: boolean severity-level: description: 'Minimum severity level to report (default: HIGH)' required: false @@ -143,7 +143,7 @@ jobs: env: REPORT: ${{ inputs.use-test-reporter && '-f json -o ./scan-results/trivy.json' || '' }} IGNOREFILE: ${{ inputs.trivyignorefile != '' && format('--ignorefile {0}', inputs.trivyignorefile) || '' }} - SCANNERS: ${{ inputs.check-secrets != 'true' && '--scanners vuln' || '' }} + SCANNERS: ${{ !inputs.check-secrets && '--scanners vuln' || '' }} SEVERITY: ${{ inputs.severity-level }} run: | trivy fs ${{ inputs.path }} \ From 495f1b0c2cd703dd2d766dabe6f531a16e98d5eb Mon Sep 17 00:00:00 2001 From: JonasPollokZweitag <„jonas.pollok@zweitag.de“> Date: Tue, 20 Jan 2026 08:48:37 +0100 Subject: [PATCH 34/53] chore: severities need all levels that are to displayed --- .github/workflows/trivy-scan.yaml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/.github/workflows/trivy-scan.yaml b/.github/workflows/trivy-scan.yaml index 7e3c6e6..5508f9c 100644 --- a/.github/workflows/trivy-scan.yaml +++ b/.github/workflows/trivy-scan.yaml @@ -15,9 +15,9 @@ on: default: false type: boolean severity-level: - description: 'Minimum severity level to report (default: HIGH)' + description: 'Severity levels to report (default: HIGH,CRITICAL,UNKNOWN)' required: false - default: 'HIGH' + default: 'HIGH,CRITICAL,UNKNOWN' type: string trivyignorefile: description: 'Path to the Trivy ignore file (default: none)' From 98bfa36eab3e3e8f7c4c3ab25fce6a9e7c427d73 Mon Sep 17 00:00:00 2001 From: JonasPollokZweitag <„jonas.pollok@zweitag.de“> Date: Tue, 20 Jan 2026 08:51:18 +0100 Subject: [PATCH 35/53] fix: set assignee right --- .github/workflows/trivy-scan.yaml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/.github/workflows/trivy-scan.yaml b/.github/workflows/trivy-scan.yaml index 5508f9c..71b6e6f 100644 --- a/.github/workflows/trivy-scan.yaml +++ b/.github/workflows/trivy-scan.yaml @@ -231,8 +231,7 @@ jobs: owner, repo, state: 'open', - labels: 'security-scan-failure', - assignees: assignees + labels: 'security-scan-failure' }); if (existing_issue.data.length === 0) { await github.rest.issues.create({ @@ -241,6 +240,7 @@ jobs: title: issue_title, body: issue_body, labels: ['security-scan-failure'] + assignees: assignees }); } else { const issue_number = existing_issue.data[0].number; From 218eae710c0d9650af6a51480c7b92fa6783b12c Mon Sep 17 00:00:00 2001 From: JonasPollokZweitag <„jonas.pollok@zweitag.de“> Date: Tue, 20 Jan 2026 08:59:57 +0100 Subject: [PATCH 36/53] chore: removed ticket assignment --- .github/workflows/checkov-scan.yaml | 6 ++---- .github/workflows/trivy-scan.yaml | 4 +--- 2 files changed, 3 insertions(+), 7 deletions(-) diff --git a/.github/workflows/checkov-scan.yaml b/.github/workflows/checkov-scan.yaml index 18f95c3..ec3d0df 100644 --- a/.github/workflows/checkov-scan.yaml +++ b/.github/workflows/checkov-scan.yaml @@ -20,7 +20,7 @@ on: default: true type: boolean issue-on-findings: - description: 'One GitHub user to assign when creating an issue for failed scans (e.g., username). If left empty, no issue will be created.' + description: 'One GitHub user to mention when creating an issue for failed scans (e.g., username). If left empty, no issue will be created.' required: false default: '' type: string @@ -94,13 +94,11 @@ jobs: const owner = context.repo.owner; const issue_title = 'Security scan failed'; const issue_body = '@${{ inputs.issue-on-findings }} One or more security scans failed. Please check the workflow run for more information: https://github.com/${{ github.repository }}/actions/runs/${{ github.run_id }}\nPlease check if the vulnerabilities are fixable. If there is a fix: Create a ticket for the fix or resolve it.\n' - const assignees = ['${{ inputs.issue-on-findings }}']; const existing_issue = await github.rest.issues.listForRepo({ owner, repo, state: 'open', - labels: 'security-scan-failure', - assignees: assignees + labels: 'security-scan-failure' }); if (existing_issue.data.length === 0) { await github.rest.issues.create({ diff --git a/.github/workflows/trivy-scan.yaml b/.github/workflows/trivy-scan.yaml index 71b6e6f..5056e59 100644 --- a/.github/workflows/trivy-scan.yaml +++ b/.github/workflows/trivy-scan.yaml @@ -35,7 +35,7 @@ on: default: true type: boolean issue-on-findings: - description: 'One GitHub user to assign when creating an issue for failed scans (e.g., username). If left empty, no issue will be created.' + description: 'One GitHub user to mention when creating an issue for failed scans (e.g., username). If left empty, no issue will be created.' required: false default: '' type: string @@ -226,7 +226,6 @@ jobs: const owner = context.repo.owner; const issue_title = 'Security scan failed'; const issue_body = '@${{ inputs.issue-on-findings }} One or more security scans failed. Please check the workflow run for more information: https://github.com/${{ github.repository }}/actions/runs/${{ github.run_id }}\nPlease check if the vulnerabilities are fixable. If there is a fix: Create a ticket for the fix or resolve it.\n' - const assignees = ['${{ inputs.issue-on-findings }}']; const existing_issue = await github.rest.issues.listForRepo({ owner, repo, @@ -240,7 +239,6 @@ jobs: title: issue_title, body: issue_body, labels: ['security-scan-failure'] - assignees: assignees }); } else { const issue_number = existing_issue.data[0].number; From 38013c984d19cd769b25e406ff97a79a46b22e17 Mon Sep 17 00:00:00 2001 From: JonasPollokZweitag <„jonas.pollok@zweitag.de“> Date: Tue, 20 Jan 2026 09:31:29 +0100 Subject: [PATCH 37/53] test: count severities seperate in image scan --- security-scanning/image_scan_template.hbs | 6 +++++ security-scanning/trivyimage2ctrf.py | 31 +++++++++++++++++++---- 2 files changed, 32 insertions(+), 5 deletions(-) diff --git a/security-scanning/image_scan_template.hbs b/security-scanning/image_scan_template.hbs index a57e78e..f4cd48f 100644 --- a/security-scanning/image_scan_template.hbs +++ b/security-scanning/image_scan_template.hbs @@ -1,5 +1,11 @@ ## Summary of {{ctrf.tool.name}} Security Scan Results +| **LOW** | **MEDIUM** | **HIGH** | **CRITICAL** | **UNKNOWN** | +| --- | --- | --- | --- | --- | +| {{ctrf.results.extensions.severityCounts.LOW}} | {{ctrf.results.extensions.severityCounts.MEDIUM}} | +{{ctrf.results.extensions.severityCounts.HIGH}} | {{ctrf.results.extensions.severityCounts.CRITICAL}} | +{{ctrf.results.extensions.severityCounts.UNKNOWN}} | + {{#if (eq ctrf.summary.passed ctrf.summary.tests)}} ### No vulnerabilities with set severity found! 🎉 {{else}} diff --git a/security-scanning/trivyimage2ctrf.py b/security-scanning/trivyimage2ctrf.py index 38f7699..ea888e5 100644 --- a/security-scanning/trivyimage2ctrf.py +++ b/security-scanning/trivyimage2ctrf.py @@ -4,9 +4,8 @@ def extract_checks_from_trivy_result(target): checks = [] - vulnerabilities = target.get("Vulnerabilities", []) - + for vuln in vulnerabilities: checks.append({ "name": vuln.get("PkgID", ""), @@ -28,22 +27,41 @@ def extract_checks_from_trivy_result(target): def trivy_to_ctrf(trivy_json): tests = [] successes_sum = 0 + severity_counts = { + "UNKNOWN": 0, + "LOW": 0, + "MEDIUM": 0, + "HIGH": 0, + "CRITICAL": 0 + } + results = trivy_json.get("Results", []) for result in results: - tests.extend(extract_checks_from_trivy_result(result)) + # Sammle Checks/Testeinträge + result_checks = extract_checks_from_trivy_result(result) + tests.extend(result_checks) - # Successful scans have no misconfigurations + # Summiere Misconfig-Erfolgswerte (nicht genutzt, bleibt aber erhalten) misconf_summary = result.get("MisconfSummary", {}) successes_sum += misconf_summary.get("Successes", 0) + # Zähle Severity über alle Tests + for t in tests: + sev = str(t.get("severity", "")).upper().strip() + if sev in severity_counts: + severity_counts[sev] += 1 + else: + severity_counts["UNKNOWN"] += 1 + total = len(tests) - passed = 0 + passed = 0 failed = len(tests) pending = 0 skipped = 0 other = 0 start = 0 stop = 1 + return { "results": { "tool": { @@ -64,6 +82,9 @@ def trivy_to_ctrf(trivy_json): "appName": "kamium-elastic", "buildName": "kamium-elastic", "buildNumber": "1" + }, + "extensions": { + "severityCounts": severity_counts } } } From 3686b2bbca22f8b0d16807d655964e70ed731bad Mon Sep 17 00:00:00 2001 From: JonasPollokZweitag <„jonas.pollok@zweitag.de“> Date: Tue, 20 Jan 2026 09:38:02 +0100 Subject: [PATCH 38/53] chore: little format changes --- security-scanning/config_scan_template.hbs | 2 +- security-scanning/filesystem_scan_template.hbs | 8 +++++++- security-scanning/image_scan_template.hbs | 10 ++++++++-- 3 files changed, 16 insertions(+), 4 deletions(-) diff --git a/security-scanning/config_scan_template.hbs b/security-scanning/config_scan_template.hbs index 6b5fe16..98df2a5 100644 --- a/security-scanning/config_scan_template.hbs +++ b/security-scanning/config_scan_template.hbs @@ -11,7 +11,7 @@ {{#each ctrf.tests}} {{#if (eq status "failed")}}
- ❌ {{name}} + 🔴 {{name}} diff --git a/security-scanning/filesystem_scan_template.hbs b/security-scanning/filesystem_scan_template.hbs index de837a1..718ea51 100644 --- a/security-scanning/filesystem_scan_template.hbs +++ b/security-scanning/filesystem_scan_template.hbs @@ -25,7 +25,13 @@ {{#if package}} - + + + {{/if}} + {{#if installedVersion}} + + + {{/if}} {{#if fixedVersion}} diff --git a/security-scanning/image_scan_template.hbs b/security-scanning/image_scan_template.hbs index f4cd48f..866789c 100644 --- a/security-scanning/image_scan_template.hbs +++ b/security-scanning/image_scan_template.hbs @@ -12,12 +12,18 @@ ## Found Vulnerabilities: {{#each ctrf.tests}}
- ❌ {{id}} {{pkgName}} + 🔴 {{pkgName}}@{{installedVersion}} - {{id}} ({{severity}})
file
package{{package}}@{{installedVersion}}{{package}}
installed version{{installedVersion}}
+ {{#if package}} + + + + + {{/if}} {{#if installedVersion}} @@ -26,7 +32,7 @@ {{/if}} {{#if fixedVersion}} - + {{/if}} From ab313b2165c0c4e327dfe165c2fb3764561f02cc Mon Sep 17 00:00:00 2001 From: JonasPollokZweitag <„jonas.pollok@zweitag.de“> Date: Tue, 20 Jan 2026 09:44:19 +0100 Subject: [PATCH 39/53] chore: extende title in config scan --- security-scanning/config_scan_template.hbs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/security-scanning/config_scan_template.hbs b/security-scanning/config_scan_template.hbs index 98df2a5..af7bee2 100644 --- a/security-scanning/config_scan_template.hbs +++ b/security-scanning/config_scan_template.hbs @@ -11,7 +11,7 @@ {{#each ctrf.tests}} {{#if (eq status "failed")}}
- 🔴 {{name}} + 🔴 {{file}} - {{name}} ({{severity}})
image {{image}}
package{{package}}
installed version
fixed versionfixed in {{fixedVersion}}
From 57a88a68bb33ca94f5851daf632654f8bcc42974 Mon Sep 17 00:00:00 2001 From: JonasPollokZweitag <„jonas.pollok@zweitag.de“> Date: Tue, 20 Jan 2026 09:46:32 +0100 Subject: [PATCH 40/53] chore: removed references from image scan summary --- security-scanning/image_scan_template.hbs | 10 ---------- 1 file changed, 10 deletions(-) diff --git a/security-scanning/image_scan_template.hbs b/security-scanning/image_scan_template.hbs index 866789c..da8fbe4 100644 --- a/security-scanning/image_scan_template.hbs +++ b/security-scanning/image_scan_template.hbs @@ -62,16 +62,6 @@ {{/if}} - {{#if references}} - - - - - {{/if}}
file
references - {{#each references}} - {{this}}{{#unless @last}}, {{/unless}} - {{/each}} -
{{/each}} From ffdfc7b38b0382ee457b1ba029b9e12eb55bf061 Mon Sep 17 00:00:00 2001 From: JonasPollokZweitag <„jonas.pollok@zweitag.de“> Date: Tue, 20 Jan 2026 09:54:00 +0100 Subject: [PATCH 41/53] chore: removed trivy setup action --- .github/actions/setup-trivy/action.yml | 21 ------------------- security-scanning/image_scan_template.hbs | 25 ++++++++++++++++++----- 2 files changed, 20 insertions(+), 26 deletions(-) delete mode 100644 .github/actions/setup-trivy/action.yml diff --git a/.github/actions/setup-trivy/action.yml b/.github/actions/setup-trivy/action.yml deleted file mode 100644 index 2867d61..0000000 --- a/.github/actions/setup-trivy/action.yml +++ /dev/null @@ -1,21 +0,0 @@ -name: Setup Trivy -description: | - Setup Trivy for Docker and configuration scanning -inputs: - working-directory: - description: | - The working directory to use Trivy in - required: false - default: . -runs: - using: composite - steps: - - name: Install Trivy - run: | - sudo apt-get install wget apt-transport-https gnupg - wget -qO - https://aquasecurity.github.io/trivy-repo/deb/public.key | gpg --dearmor | sudo tee /usr/share/keyrings/trivy.gpg > /dev/null - echo "deb [signed-by=/usr/share/keyrings/trivy.gpg] https://aquasecurity.github.io/trivy-repo/deb generic main" | sudo tee -a /etc/apt/sources.list.d/trivy.list - sudo apt-get update - sudo apt-get install trivy - shell: bash - working-directory: ${{ inputs.working-directory }} diff --git a/security-scanning/image_scan_template.hbs b/security-scanning/image_scan_template.hbs index da8fbe4..297e0fe 100644 --- a/security-scanning/image_scan_template.hbs +++ b/security-scanning/image_scan_template.hbs @@ -1,10 +1,25 @@ ## Summary of {{ctrf.tool.name}} Security Scan Results -| **LOW** | **MEDIUM** | **HIGH** | **CRITICAL** | **UNKNOWN** | -| --- | --- | --- | --- | --- | -| {{ctrf.results.extensions.severityCounts.LOW}} | {{ctrf.results.extensions.severityCounts.MEDIUM}} | -{{ctrf.results.extensions.severityCounts.HIGH}} | {{ctrf.results.extensions.severityCounts.CRITICAL}} | -{{ctrf.results.extensions.severityCounts.UNKNOWN}} | + + + + + + + + + + + + + + + + + + + +
LOWMEDIUMHIGHCRITICALUNKNOWN
{{ctrf.extensions.severityCounts.LOW}}{{ctrf.extensions.severityCounts.MEDIUM}}{{ctrf.extensions.severityCounts.HIGH}}{{ctrf.extensions.severityCounts.CRITICAL}}{{ctrf.extensions.severityCounts.UNKNOWN}}
{{#if (eq ctrf.summary.passed ctrf.summary.tests)}} ### No vulnerabilities with set severity found! 🎉 From cb2c6566febe375cccb24e5264b414e3b280ee39 Mon Sep 17 00:00:00 2001 From: JonasPollokZweitag <„jonas.pollok@zweitag.de“> Date: Tue, 20 Jan 2026 10:19:36 +0100 Subject: [PATCH 42/53] chore: redesigned summary tables --- security-scanning/config_scan_template.hbs | 52 +++++++++++++++++-- .../filesystem_scan_template.hbs | 51 ++++++++++++++++-- security-scanning/image_scan_template.hbs | 30 +++++++++-- security-scanning/trivyconfig2ctrf.py | 27 ++++++++-- security-scanning/trivyfs2ctrf.py | 52 ++++++++++++++----- 5 files changed, 185 insertions(+), 27 deletions(-) diff --git a/security-scanning/config_scan_template.hbs b/security-scanning/config_scan_template.hbs index af7bee2..d6fb984 100644 --- a/security-scanning/config_scan_template.hbs +++ b/security-scanning/config_scan_template.hbs @@ -1,8 +1,54 @@ ## Summary of {{ctrf.tool.name}} Security Scan Results -| **Tests 📝** | **Passed ✅** | **Failed ❌** | -| --- | --- | --- | -| {{ctrf.summary.tests}} | {{ctrf.summary.passed}} | {{ctrf.summary.failed}} | + + + + + + + {{#if ctrf.extensions.severityCounts.LOW != 0}} + + {{/if}} + {{#if ctrf.extensions.severityCounts.MEDIUM != 0}} + + {{/if}} + {{#if ctrf.extensions.severityCounts.HIGH != 0}} + + {{/if}} + {{#if ctrf.extensions.severityCounts.CRITICAL != 0}} + + {{/if}} + {{#if ctrf.extensions.severityCounts.UNKNOWN != 0}} + + {{/if}} + + + + + + + + {{#if ctrf.extensions.severityCounts.LOW != 0}} + + {{/if}} + {{#if ctrf.extensions.severityCounts.MEDIUM != 0}} + + {{/if}} + {{#if ctrf.extensions.severityCounts.HIGH != 0}} + + {{/if}} + {{#if ctrf.extensions.severityCounts.CRITICAL != 0}} + + {{/if}} + {{#if ctrf.extensions.severityCounts.UNKNOWN != 0}} + + {{/if}} + {{#if ctrf.extensions.secretsCount != 0}} + + {{/if}} + + +
Tests 📝Passed ✅Failed ❌LOW 🟢MEDIUM 🟡HIGH 🔴CRITICAL 🚨UNKNOWN ❓
{{ctrf.summary.tests}}{{ctrf.summary.passed}}{{ctrf.summary.failed}}{{ctrf.extensions.severityCounts.LOW}}{{ctrf.extensions.severityCounts.MEDIUM}}{{ctrf.extensions.severityCounts.HIGH}}{{ctrf.extensions.severityCounts.CRITICAL}}{{ctrf.extensions.severityCounts.UNKNOWN}}{{ctrf.extensions.secretsCount}}
{{#if (eq ctrf.summary.passed ctrf.summary.tests)}} ### All Tests Passed! 🎉 diff --git a/security-scanning/filesystem_scan_template.hbs b/security-scanning/filesystem_scan_template.hbs index 718ea51..9fdb606 100644 --- a/security-scanning/filesystem_scan_template.hbs +++ b/security-scanning/filesystem_scan_template.hbs @@ -1,8 +1,53 @@ ## Summary of {{ctrf.tool.name}} Security Scan Results -| **Failed ❌** | -| --- | -| {{ctrf.summary.failed}} | + + + + + {{#if ctrf.extensions.severityCounts.LOW != 0}} + + {{/if}} + {{#if ctrf.extensions.severityCounts.MEDIUM != 0}} + + {{/if}} + {{#if ctrf.extensions.severityCounts.HIGH != 0}} + + {{/if}} + {{#if ctrf.extensions.severityCounts.CRITICAL != 0}} + + {{/if}} + {{#if ctrf.extensions.severityCounts.UNKNOWN != 0}} + + {{/if}} + {{#if ctrf.extensions.secretsCount != 0}} + + {{/if}} + + + + + + {{#if ctrf.extensions.severityCounts.LOW != 0}} + + {{/if}} + {{#if ctrf.extensions.severityCounts.MEDIUM != 0}} + + {{/if}} + {{#if ctrf.extensions.severityCounts.HIGH != 0}} + + {{/if}} + {{#if ctrf.extensions.severityCounts.CRITICAL != 0}} + + {{/if}} + {{#if ctrf.extensions.severityCounts.UNKNOWN != 0}} + + {{/if}} + {{#if ctrf.extensions.secretsCount != 0}} + + {{/if}} + + +
Found Vulnerabilities ❌LOW 🟢MEDIUM 🟡HIGH 🔴CRITICAL 🚨UNKNOWN ❓SECRETS 🔑
{{ctrf.summary.failed}}{{ctrf.extensions.severityCounts.LOW}}{{ctrf.extensions.severityCounts.MEDIUM}}{{ctrf.extensions.severityCounts.HIGH}}{{ctrf.extensions.severityCounts.CRITICAL}}{{ctrf.extensions.severityCounts.UNKNOWN}}{{ctrf.extensions.secretsCount}}
{{#if (eq ctrf.summary.failed 0)}} ### No Vulnerabilities or Secrets Found! 🎉 diff --git a/security-scanning/image_scan_template.hbs b/security-scanning/image_scan_template.hbs index 297e0fe..93dd31b 100644 --- a/security-scanning/image_scan_template.hbs +++ b/security-scanning/image_scan_template.hbs @@ -3,20 +3,40 @@ - - - - - + {{#if ctrf.extensions.severityCounts.LOW != 0}} + + {{/if}} + {{#if ctrf.extensions.severityCounts.MEDIUM != 0}} + + {{/if}} + {{#if ctrf.extensions.severityCounts.HIGH != 0}} + + {{/if}} + {{#if ctrf.extensions.severityCounts.CRITICAL != 0}} + + {{/if}} + {{#if ctrf.extensions.severityCounts.UNKNOWN != 0}} + + {{/if}} + {{#if ctrf.extensions.severityCounts.LOW != 0}} + {{/if}} + {{#if ctrf.extensions.severityCounts.MEDIUM != 0}} + {{/if}} + {{#if ctrf.extensions.severityCounts.HIGH != 0}} + {{/if}} + {{#if ctrf.extensions.severityCounts.CRITICAL != 0}} + {{/if}} + {{#if ctrf.extensions.severityCounts.UNKNOWN != 0}} + {{/if}}
LOWMEDIUMHIGHCRITICALUNKNOWNLOW 🟢MEDIUM 🟡HIGH 🔴CRITICAL 🚨UNKNOWN ❓
{{ctrf.extensions.severityCounts.LOW}}{{ctrf.extensions.severityCounts.MEDIUM}}{{ctrf.extensions.severityCounts.HIGH}}{{ctrf.extensions.severityCounts.CRITICAL}}{{ctrf.extensions.severityCounts.UNKNOWN}}
diff --git a/security-scanning/trivyconfig2ctrf.py b/security-scanning/trivyconfig2ctrf.py index 81998f9..3aa5859 100644 --- a/security-scanning/trivyconfig2ctrf.py +++ b/security-scanning/trivyconfig2ctrf.py @@ -10,12 +10,10 @@ def extract_checks_from_trivy_result(result): cause = misconf.get("CauseMetadata", {}) if "StartLine" in cause and "EndLine" in cause: lines = [cause["StartLine"], cause["EndLine"]] - elif "Code" in cause and "Lines" in cause["Code"] and cause["Code"]["Lines"]: - # Fallback: nehme die ersten und letzten Zeilennummern aus Code.Lines + elif "Code" in cause and "Lines" in cause.get("Code", {}) and cause["Code"]["Lines"]: code_lines = cause["Code"]["Lines"] if isinstance(code_lines, list) and code_lines: - lines = [code_lines[0].get( - "Number"), code_lines[-1].get("Number")] + lines = [code_lines[0].get("Number"), code_lines[-1].get("Number")] checks.append({ "name": misconf.get("Title", misconf.get("ID", "")), @@ -38,6 +36,15 @@ def extract_checks_from_trivy_result(result): def trivy_to_ctrf(trivy_json): tests = [] successes_sum = 0 + + severity_counts = { + "UNKNOWN": 0, + "LOW": 0, + "MEDIUM": 0, + "HIGH": 0, + "CRITICAL": 0 + } + results = trivy_json.get("Results", []) for result in results: tests.extend(extract_checks_from_trivy_result(result)) @@ -46,6 +53,14 @@ def trivy_to_ctrf(trivy_json): misconf_summary = result.get("MisconfSummary", {}) successes_sum += misconf_summary.get("Successes", 0) + for misconf in result.get("Misconfigurations", []) or []: + if misconf.get("Status") == "FAIL": + sev = str(misconf.get("Severity", "")).upper().strip() + if sev in severity_counts: + severity_counts[sev] += 1 + else: + severity_counts["UNKNOWN"] += 1 + total = len(tests) + successes_sum passed = successes_sum failed = sum(1 for t in tests if t["status"] == "failed") @@ -54,6 +69,7 @@ def trivy_to_ctrf(trivy_json): other = 0 start = 0 stop = 1 + return { "results": { "tool": { @@ -74,6 +90,9 @@ def trivy_to_ctrf(trivy_json): "appName": "kamium-elastic", "buildName": "kamium-elastic", "buildNumber": "1" + }, + "extensions": { + "severityCounts": severity_counts } } } diff --git a/security-scanning/trivyfs2ctrf.py b/security-scanning/trivyfs2ctrf.py index e37bba7..e05b4d3 100644 --- a/security-scanning/trivyfs2ctrf.py +++ b/security-scanning/trivyfs2ctrf.py @@ -6,7 +6,7 @@ def extract_vulnerabilities_from_result(result): """Extract vulnerabilities from a Trivy result.""" tests = [] vulnerabilities = result.get("Vulnerabilities", []) - + for vuln in vulnerabilities: tests.append({ "name": f"{vuln.get('PkgName', '')}@{vuln.get('InstalledVersion', '')} - {vuln.get('VulnerabilityID', '')}", @@ -26,7 +26,7 @@ def extract_vulnerabilities_from_result(result): "installedVersion": vuln.get("InstalledVersion", ""), "fixedVersion": vuln.get("FixedVersion", ""), }) - + return tests @@ -34,12 +34,12 @@ def extract_secrets_from_result(result): """Extract secrets from a Trivy result.""" tests = [] secrets = result.get("Secrets", []) - + for secret in secrets: lines = None if secret.get("StartLine") and secret.get("EndLine"): lines = [secret["StartLine"], secret["EndLine"]] - + tests.append({ "name": f"{secret.get('RuleID', '')} - {secret.get('Title', '')}", "status": "failed", @@ -55,7 +55,7 @@ def extract_secrets_from_result(result): "type": "secret", "id": secret.get("RuleID", ""), }) - + return tests @@ -63,26 +63,54 @@ def trivy_fs_to_ctrf(trivy_json): """Convert Trivy filesystem scan JSON to CTRF format.""" tests = [] results = trivy_json.get("Results", []) - + + # Severity-Zählung nur für Vulnerabilities + severity_counts = { + "UNKNOWN": 0, + "LOW": 0, + "MEDIUM": 0, + "HIGH": 0, + "CRITICAL": 0 + } + + # Secrets-Zählung + secrets_count = 0 + for result in results: + # Tests sammeln tests.extend(extract_vulnerabilities_from_result(result)) tests.extend(extract_secrets_from_result(result)) - + + # Vulnerability-Severities zählen + for vuln in result.get("Vulnerabilities", []): + sev = str(vuln.get("Severity", "")).upper().strip() + if sev in severity_counts: + severity_counts[sev] += 1 + else: + severity_counts["UNKNOWN"] += 1 + + # Secrets zählen + secrets_count += len(result.get("Secrets", []) or []) + failed = len(tests) - + return { "results": { "tool": { "name": "Trivy Filesystem" }, "summary": { - "failed": failed, + "failed": failed }, "tests": tests, "environment": { "appName": trivy_json.get("ArtifactName", ""), "buildName": trivy_json.get("Metadata", {}).get("Branch", ""), "buildNumber": trivy_json.get("Metadata", {}).get("Commit", "")[:8] if trivy_json.get("Metadata", {}).get("Commit") else "" + }, + "extensions": { + "severityCounts": severity_counts, + "secretsCount": secrets_count } } } @@ -92,11 +120,11 @@ def trivy_fs_to_ctrf(trivy_json): if len(sys.argv) != 3: print("Usage: python trivyfs2ctrf.py ") sys.exit(1) - + with open(sys.argv[1]) as f: trivy_json = json.load(f) - + ctrf_json = trivy_fs_to_ctrf(trivy_json) - + with open(sys.argv[2], "w") as f: json.dump(ctrf_json, f, indent=2) From 143a0b483bcff39f2d3e8fce610089d72ca4ebd3 Mon Sep 17 00:00:00 2001 From: JonasPollokZweitag <„jonas.pollok@zweitag.de“> Date: Tue, 20 Jan 2026 10:39:15 +0100 Subject: [PATCH 43/53] fix: use valid ifs --- security-scanning/config_scan_template.hbs | 22 ++++++------ .../filesystem_scan_template.hbs | 24 ++++++------- security-scanning/image_scan_template.hbs | 20 +++++------ security-scanning/trivyconfig2ctrf.py | 31 +++++++++------- security-scanning/trivyfs2ctrf.py | 35 +++++++++++-------- security-scanning/trivyimage2ctrf.py | 26 ++++++++------ 6 files changed, 87 insertions(+), 71 deletions(-) diff --git a/security-scanning/config_scan_template.hbs b/security-scanning/config_scan_template.hbs index d6fb984..bb2328e 100644 --- a/security-scanning/config_scan_template.hbs +++ b/security-scanning/config_scan_template.hbs @@ -6,19 +6,19 @@ Tests 📝 Passed ✅ Failed ❌ - {{#if ctrf.extensions.severityCounts.LOW != 0}} + {{#if ctrf.extensions.severityCounts.LOW}} LOW 🟢 {{/if}} - {{#if ctrf.extensions.severityCounts.MEDIUM != 0}} + {{#if ctrf.extensions.severityCounts.MEDIUM}} MEDIUM 🟡 {{/if}} - {{#if ctrf.extensions.severityCounts.HIGH != 0}} + {{#if ctrf.extensions.severityCounts.HIGH}} HIGH 🔴 {{/if}} - {{#if ctrf.extensions.severityCounts.CRITICAL != 0}} + {{#if ctrf.extensions.severityCounts.CRITICAL}} CRITICAL 🚨 {{/if}} - {{#if ctrf.extensions.severityCounts.UNKNOWN != 0}} + {{#if ctrf.extensions.severityCounts.UNKNOWN}} UNKNOWN ❓ {{/if}} @@ -28,22 +28,22 @@ {{ctrf.summary.tests}} {{ctrf.summary.passed}} {{ctrf.summary.failed}} - {{#if ctrf.extensions.severityCounts.LOW != 0}} + {{#if ctrf.extensions.severityCounts.LOW}} {{ctrf.extensions.severityCounts.LOW}} {{/if}} - {{#if ctrf.extensions.severityCounts.MEDIUM != 0}} + {{#if ctrf.extensions.severityCounts.MEDIUM}} {{ctrf.extensions.severityCounts.MEDIUM}} {{/if}} - {{#if ctrf.extensions.severityCounts.HIGH != 0}} + {{#if ctrf.extensions.severityCounts.HIGH}} {{ctrf.extensions.severityCounts.HIGH}} {{/if}} - {{#if ctrf.extensions.severityCounts.CRITICAL != 0}} + {{#if ctrf.extensions.severityCounts.CRITICAL}} {{ctrf.extensions.severityCounts.CRITICAL}} {{/if}} - {{#if ctrf.extensions.severityCounts.UNKNOWN != 0}} + {{#if ctrf.extensions.severityCounts.UNKNOWN}} {{ctrf.extensions.severityCounts.UNKNOWN}} {{/if}} - {{#if ctrf.extensions.secretsCount != 0}} + {{#if ctrf.extensions.secretsCount}} {{ctrf.extensions.secretsCount}} {{/if}} diff --git a/security-scanning/filesystem_scan_template.hbs b/security-scanning/filesystem_scan_template.hbs index 9fdb606..cef4c80 100644 --- a/security-scanning/filesystem_scan_template.hbs +++ b/security-scanning/filesystem_scan_template.hbs @@ -4,22 +4,22 @@ Found Vulnerabilities ❌ - {{#if ctrf.extensions.severityCounts.LOW != 0}} + {{#if ctrf.extensions.severityCounts.LOW}} LOW 🟢 {{/if}} - {{#if ctrf.extensions.severityCounts.MEDIUM != 0}} + {{#if ctrf.extensions.severityCounts.MEDIUM}} MEDIUM 🟡 {{/if}} - {{#if ctrf.extensions.severityCounts.HIGH != 0}} + {{#if ctrf.extensions.severityCounts.HIGH}} HIGH 🔴 {{/if}} - {{#if ctrf.extensions.severityCounts.CRITICAL != 0}} + {{#if ctrf.extensions.severityCounts.CRITICAL}} CRITICAL 🚨 {{/if}} - {{#if ctrf.extensions.severityCounts.UNKNOWN != 0}} + {{#if ctrf.extensions.severityCounts.UNKNOWN}} UNKNOWN ❓ {{/if}} - {{#if ctrf.extensions.secretsCount != 0}} + {{#if ctrf.extensions.secretsCount}} SECRETS 🔑 {{/if}} @@ -27,22 +27,22 @@ {{ctrf.summary.failed}} - {{#if ctrf.extensions.severityCounts.LOW != 0}} + {{#if ctrf.extensions.severityCounts.LOW}} {{ctrf.extensions.severityCounts.LOW}} {{/if}} - {{#if ctrf.extensions.severityCounts.MEDIUM != 0}} + {{#if ctrf.extensions.severityCounts.MEDIUM}} {{ctrf.extensions.severityCounts.MEDIUM}} {{/if}} - {{#if ctrf.extensions.severityCounts.HIGH != 0}} + {{#if ctrf.extensions.severityCounts.HIGH}} {{ctrf.extensions.severityCounts.HIGH}} {{/if}} - {{#if ctrf.extensions.severityCounts.CRITICAL != 0}} + {{#if ctrf.extensions.severityCounts.CRITICAL}} {{ctrf.extensions.severityCounts.CRITICAL}} {{/if}} - {{#if ctrf.extensions.severityCounts.UNKNOWN != 0}} + {{#if ctrf.extensions.severityCounts.UNKNOWN}} {{ctrf.extensions.severityCounts.UNKNOWN}} {{/if}} - {{#if ctrf.extensions.secretsCount != 0}} + {{#if ctrf.extensions.secretsCount}} {{ctrf.extensions.secretsCount}} {{/if}} diff --git a/security-scanning/image_scan_template.hbs b/security-scanning/image_scan_template.hbs index 93dd31b..c790ebb 100644 --- a/security-scanning/image_scan_template.hbs +++ b/security-scanning/image_scan_template.hbs @@ -3,38 +3,38 @@ - {{#if ctrf.extensions.severityCounts.LOW != 0}} + {{#if ctrf.extensions.severityCounts.LOW}} {{/if}} - {{#if ctrf.extensions.severityCounts.MEDIUM != 0}} + {{#if ctrf.extensions.severityCounts.MEDIUM}} {{/if}} - {{#if ctrf.extensions.severityCounts.HIGH != 0}} + {{#if ctrf.extensions.severityCounts.HIGH}} {{/if}} - {{#if ctrf.extensions.severityCounts.CRITICAL != 0}} + {{#if ctrf.extensions.severityCounts.CRITICAL}} {{/if}} - {{#if ctrf.extensions.severityCounts.UNKNOWN != 0}} + {{#if ctrf.extensions.severityCounts.UNKNOWN}} {{/if}} - {{#if ctrf.extensions.severityCounts.LOW != 0}} + {{#if ctrf.extensions.severityCounts.LOW}} {{/if}} - {{#if ctrf.extensions.severityCounts.MEDIUM != 0}} + {{#if ctrf.extensions.severityCounts.MEDIUM}} {{/if}} - {{#if ctrf.extensions.severityCounts.HIGH != 0}} + {{#if ctrf.extensions.severityCounts.HIGH}} {{/if}} - {{#if ctrf.extensions.severityCounts.CRITICAL != 0}} + {{#if ctrf.extensions.severityCounts.CRITICAL}} {{/if}} - {{#if ctrf.extensions.severityCounts.UNKNOWN != 0}} + {{#if ctrf.extensions.severityCounts.UNKNOWN}} {{/if}} diff --git a/security-scanning/trivyconfig2ctrf.py b/security-scanning/trivyconfig2ctrf.py index 3aa5859..1603c4b 100644 --- a/security-scanning/trivyconfig2ctrf.py +++ b/security-scanning/trivyconfig2ctrf.py @@ -4,13 +4,13 @@ def extract_checks_from_trivy_result(result): checks = [] - misconfigs = result.get("Misconfigurations", []) + misconfigs = result.get("Misconfigurations", []) or [] for misconf in misconfigs: lines = None - cause = misconf.get("CauseMetadata", {}) + cause = misconf.get("CauseMetadata", {}) or {} if "StartLine" in cause and "EndLine" in cause: lines = [cause["StartLine"], cause["EndLine"]] - elif "Code" in cause and "Lines" in cause.get("Code", {}) and cause["Code"]["Lines"]: + elif "Code" in cause and "Lines" in (cause.get("Code", {}) or {}) and cause["Code"]["Lines"]: code_lines = cause["Code"]["Lines"] if isinstance(code_lines, list) and code_lines: lines = [code_lines[0].get("Number"), code_lines[-1].get("Number")] @@ -45,13 +45,12 @@ def trivy_to_ctrf(trivy_json): "CRITICAL": 0 } - results = trivy_json.get("Results", []) + results = trivy_json.get("Results", []) or [] for result in results: tests.extend(extract_checks_from_trivy_result(result)) - # Successful scans have no misconfigurations - misconf_summary = result.get("MisconfSummary", {}) - successes_sum += misconf_summary.get("Successes", 0) + misconf_summary = result.get("MisconfSummary", {}) or {} + successes_sum += misconf_summary.get("Successes", 0) or 0 for misconf in result.get("Misconfigurations", []) or []: if misconf.get("Status") == "FAIL": @@ -63,14 +62,20 @@ def trivy_to_ctrf(trivy_json): total = len(tests) + successes_sum passed = successes_sum - failed = sum(1 for t in tests if t["status"] == "failed") + failed = sum(1 for t in tests if t.get("status") == "failed") pending = 0 skipped = 0 other = 0 start = 0 stop = 1 - return { + severity_counts_nonzero = {k: v for k, v in severity_counts.items() if v} + extensions = {} + + if severity_counts_nonzero: + extensions["severityCounts"] = severity_counts_nonzero + + result_obj = { "results": { "tool": { "name": "Trivy Configuration" @@ -90,13 +95,15 @@ def trivy_to_ctrf(trivy_json): "appName": "kamium-elastic", "buildName": "kamium-elastic", "buildNumber": "1" - }, - "extensions": { - "severityCounts": severity_counts } } } + if extensions: + result_obj["results"]["extensions"] = extensions + + return result_obj + if __name__ == "__main__": if len(sys.argv) != 3: diff --git a/security-scanning/trivyfs2ctrf.py b/security-scanning/trivyfs2ctrf.py index e05b4d3..5c1e85b 100644 --- a/security-scanning/trivyfs2ctrf.py +++ b/security-scanning/trivyfs2ctrf.py @@ -5,7 +5,7 @@ def extract_vulnerabilities_from_result(result): """Extract vulnerabilities from a Trivy result.""" tests = [] - vulnerabilities = result.get("Vulnerabilities", []) + vulnerabilities = result.get("Vulnerabilities", []) or [] for vuln in vulnerabilities: tests.append({ @@ -33,7 +33,7 @@ def extract_vulnerabilities_from_result(result): def extract_secrets_from_result(result): """Extract secrets from a Trivy result.""" tests = [] - secrets = result.get("Secrets", []) + secrets = result.get("Secrets", []) or [] for secret in secrets: lines = None @@ -62,9 +62,8 @@ def extract_secrets_from_result(result): def trivy_fs_to_ctrf(trivy_json): """Convert Trivy filesystem scan JSON to CTRF format.""" tests = [] - results = trivy_json.get("Results", []) + results = trivy_json.get("Results", []) or [] - # Severity-Zählung nur für Vulnerabilities severity_counts = { "UNKNOWN": 0, "LOW": 0, @@ -73,28 +72,33 @@ def trivy_fs_to_ctrf(trivy_json): "CRITICAL": 0 } - # Secrets-Zählung secrets_count = 0 for result in results: - # Tests sammeln tests.extend(extract_vulnerabilities_from_result(result)) tests.extend(extract_secrets_from_result(result)) - # Vulnerability-Severities zählen - for vuln in result.get("Vulnerabilities", []): + for vuln in result.get("Vulnerabilities", []) or []: sev = str(vuln.get("Severity", "")).upper().strip() if sev in severity_counts: severity_counts[sev] += 1 else: severity_counts["UNKNOWN"] += 1 - # Secrets zählen secrets_count += len(result.get("Secrets", []) or []) failed = len(tests) - return { + severity_counts_nonzero = {k: v for k, v in severity_counts.items() if v} + extensions = {} + + if severity_counts_nonzero: + extensions["severityCounts"] = severity_counts_nonzero + + if secrets_count: + extensions["secretsCount"] = secrets_count + + result_obj = { "results": { "tool": { "name": "Trivy Filesystem" @@ -106,15 +110,16 @@ def trivy_fs_to_ctrf(trivy_json): "environment": { "appName": trivy_json.get("ArtifactName", ""), "buildName": trivy_json.get("Metadata", {}).get("Branch", ""), - "buildNumber": trivy_json.get("Metadata", {}).get("Commit", "")[:8] if trivy_json.get("Metadata", {}).get("Commit") else "" - }, - "extensions": { - "severityCounts": severity_counts, - "secretsCount": secrets_count + "buildNumber": (trivy_json.get("Metadata", {}).get("Commit", "") or "")[:8] } } } + if extensions: + result_obj["results"]["extensions"] = extensions + + return result_obj + if __name__ == "__main__": if len(sys.argv) != 3: diff --git a/security-scanning/trivyimage2ctrf.py b/security-scanning/trivyimage2ctrf.py index ea888e5..669928a 100644 --- a/security-scanning/trivyimage2ctrf.py +++ b/security-scanning/trivyimage2ctrf.py @@ -4,7 +4,7 @@ def extract_checks_from_trivy_result(target): checks = [] - vulnerabilities = target.get("Vulnerabilities", []) + vulnerabilities = target.get("Vulnerabilities", []) or [] for vuln in vulnerabilities: checks.append({ @@ -35,17 +35,14 @@ def trivy_to_ctrf(trivy_json): "CRITICAL": 0 } - results = trivy_json.get("Results", []) + results = trivy_json.get("Results", []) or [] for result in results: - # Sammle Checks/Testeinträge result_checks = extract_checks_from_trivy_result(result) tests.extend(result_checks) - # Summiere Misconfig-Erfolgswerte (nicht genutzt, bleibt aber erhalten) - misconf_summary = result.get("MisconfSummary", {}) - successes_sum += misconf_summary.get("Successes", 0) + misconf_summary = result.get("MisconfSummary", {}) or {} + successes_sum += misconf_summary.get("Successes", 0) or 0 - # Zähle Severity über alle Tests for t in tests: sev = str(t.get("severity", "")).upper().strip() if sev in severity_counts: @@ -62,7 +59,12 @@ def trivy_to_ctrf(trivy_json): start = 0 stop = 1 - return { + severity_counts_nonzero = {k: v for k, v in severity_counts.items() if v} + extensions = {} + if severity_counts_nonzero: + extensions["severityCounts"] = severity_counts_nonzero + + result_obj = { "results": { "tool": { "name": "Trivy Image" @@ -82,13 +84,15 @@ def trivy_to_ctrf(trivy_json): "appName": "kamium-elastic", "buildName": "kamium-elastic", "buildNumber": "1" - }, - "extensions": { - "severityCounts": severity_counts } } } + if extensions: + result_obj["results"]["extensions"] = extensions + + return result_obj + if __name__ == "__main__": if len(sys.argv) != 3: From 169a6c69dbcbbaae5b5b043c458510c8f57eb8d0 Mon Sep 17 00:00:00 2001 From: JonasPollokZweitag <„jonas.pollok@zweitag.de“> Date: Tue, 20 Jan 2026 10:48:47 +0100 Subject: [PATCH 44/53] chore: renamed trivyignorefile -> ignorefile --- .github/workflows/trivy-scan.yaml | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/.github/workflows/trivy-scan.yaml b/.github/workflows/trivy-scan.yaml index 5056e59..2895cf7 100644 --- a/.github/workflows/trivy-scan.yaml +++ b/.github/workflows/trivy-scan.yaml @@ -19,7 +19,7 @@ on: required: false default: 'HIGH,CRITICAL,UNKNOWN' type: string - trivyignorefile: + ignorefile: description: 'Path to the Trivy ignore file (default: none)' default: '' required: false @@ -126,7 +126,7 @@ jobs: continue-on-error: true env: REPORT: ${{ inputs.use-test-reporter && '-f json -o ./scan-results/trivy.json' || '' }} - IGNOREFILE: ${{ inputs.trivyignorefile != '' && format('--ignorefile {0}', inputs.trivyignorefile) || '' }} + IGNOREFILE: ${{ inputs.ignorefile != '' && format('--ignorefile {0}', inputs.ignorefile) || '' }} SEVERITY: ${{ inputs.severity-level }} run: | trivy config ${{ inputs.path }} \ @@ -142,7 +142,7 @@ jobs: continue-on-error: true env: REPORT: ${{ inputs.use-test-reporter && '-f json -o ./scan-results/trivy.json' || '' }} - IGNOREFILE: ${{ inputs.trivyignorefile != '' && format('--ignorefile {0}', inputs.trivyignorefile) || '' }} + IGNOREFILE: ${{ inputs.ignorefile != '' && format('--ignorefile {0}', inputs.ignorefile) || '' }} SCANNERS: ${{ !inputs.check-secrets && '--scanners vuln' || '' }} SEVERITY: ${{ inputs.severity-level }} run: | @@ -160,7 +160,7 @@ jobs: continue-on-error: true env: REPORT: ${{ inputs.use-test-reporter && '-f json -o ./scan-results/trivy.json' || '' }} - IGNOREFILE: ${{ inputs.trivyignorefile != '' && format('--ignorefile {0}', inputs.trivyignorefile) || '' }} + IGNOREFILE: ${{ inputs.ignorefile != '' && format('--ignorefile {0}', inputs.ignorefile) || '' }} SEVERITY: ${{ inputs.severity-level }} run: | trivy image security-scan-image \ From d99f648c5ac6955b48500fee79dc7392e7a8885a Mon Sep 17 00:00:00 2001 From: JonasPollokZweitag <„jonas.pollok@zweitag.de“> Date: Tue, 20 Jan 2026 13:05:03 +0100 Subject: [PATCH 45/53] chore: add Readme for security scans --- Readme.md | 153 ++++++++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 153 insertions(+) diff --git a/Readme.md b/Readme.md index 2f45dfe..1721bf1 100644 --- a/Readme.md +++ b/Readme.md @@ -41,6 +41,159 @@ ANOTHER_KEY=VALUE2 file: 'config/.env.development' ``` +## trivy-scan + +

+ GitHub Actions status +

+ +This Action is part of the Security-Scanning Actions. This Action is for Security-Scanning with [Trivy](https://github.com/aquasecurity/trivy) and provides a cost-effective, reusable security scanning pipeline that works in any repository without relying on paid GitHub Code Scanning features. + +It runs Trivy scans (filesystem, image, or configuration), converts the results into a standardized CTRF report, renders a human-friendly summary via Handlebars templates in the job output, and optionally creates or comments on a GitHub Issue when findings cause the scan to fail. + +### Inputs +- scan-type (string, default: "filesystem"): Type of scan to run. One of "image", "filesystem", or "config". +- check-secrets (boolean, default: false): Whether to include secret scanning (relevant for filesystem scans only). +- severity-level (string, default: "HIGH,CRITICAL,UNKNOWN"): Comma-separated severities to report. Supported: "LOW,MEDIUM,HIGH,CRITICAL,UNKNOWN". +- ignorefile (string, default: ""): Optional path to a Trivy ignore file. +- path (string, default: "."): Directory to scan. For image scans, this directory must contain a Dockerfile. +- use-test-reporter (boolean, default: true): Whether to render CTRF + template. If false, results are available in the scan step logs. +- issue-on-findings (string, default: ""): GitHub username to mention. If set and the scan fails, an Issue will be created/commented and the user mentioned. + +### Example usage + +filesystem scan: +```yaml +jobs: + scan_filesystem: + uses: zweitag/github-actions/.github/workflows/trivy-scan.yaml@main + with: + scan-type: "filesystem" + path: "." + check-secrets: true + secrets: + GH_TOKEN: ${{ secrets.GITHUB_TOKEN }} # Optional: improves reliability of Trivy DB downloads +``` + +image scan: +```yaml +jobs: + scan_docker_image: + uses: zweitag/github-actions/.github/workflows/trivy-scan.yaml@main + with: + scan-type: "image" + path: "." + severity-level: 'HIGH,CRITICAL' + ignorefile: "./.trivyignore.yaml" + use-test-reporter: true + issue-on-findings: "JohnDoe" + secrets: + GH_TOKEN: ${{ secrets.GITHUB_TOKEN }} # Optional: improves reliability of Trivy DB downloads + DOCKER_IMAGE_SECRETS: ${{ secrets.DOCKER_IMAGE_SECRETS }} # Optional: if your Docker build needs secrets +``` + +config-scan: +```yaml +jobs: + scan_configuration: + uses: zweitag/github-actions/.github/workflows/trivy-scan.yaml@main + with: + scan-type: "config" + path: "." + severity-level: "LOW,MEDIUM,HIGH,CRITICAL,UNKNOWN" + secrets: + GH_TOKEN: ${{ secrets.GITHUB_TOKEN }} # Optional: improves reliability of Trivy DB downloads +``` + +### Test-reporter +- Embeds a human‑readable report in the workflow run (Job Summary), rendered from the CTRF JSON via Handlebars. +- If findings are present, it shows: + - A compact summary table (Tests/Passed/Failed, plus severities and Secrets) + - A collapsible findings list with context (file/image, package/version, lines) and references/guidelines +- If no findings match the configured severities, it displays a “no vulnerabilities found” message for quick confirmation. + +### Failure policy and severities +- `severity-level` controls which findings cause the scan step to exit with code 1. +- All findings/vulnerabilities of the set severity are marked as failed. +- Consider adjusting `severity-level` to match your risk policy (e.g., `"HIGH,CRITICAL"`). + +### Why not SARIF + GitHub Code Scanning? +- Many security tools (including Trivy) can export SARIF, which is a natural fit for GitHub Code Scanning. +- Limitation: Comprehensive Code Scanning features for private repositories require GitHub Advanced Security (paid). Public repos are free, but most teams operate in private repos and cannot use SARIF-based Code Scanning without extra licensing. +- So this provides a solution that works in any repo without paid GitHub features, still offering reports, visuals, and CI integration. + +## checkov-scan + +

+ GitHub Actions status +

+ +This Action is part of the Security-Scanning Actions. This Action is for Security-Scanning with [Checkov](https://github.com/bridgecrewio/checkov) and provides a cost-effective, reusable security scanning pipeline that works in any repository without relying on paid GitHub Code Scanning features. + +It runs Checkov scans , converts the results into a standardized CTRF report, renders a human-friendly summary via Handlebars templates in the job output, and optionally creates or comments on a GitHub Issue when findings cause the scan to fail. + +### Checkov +Checkov is a static code analysis tool for infrastructure as code and a software composition analysis tool for images and open source packages. + +It scans cloud infrastructure provisioned using Terraform, Terraform plan, Cloudformation, AWS SAM, Kubernetes, Helm charts, Kustomize, Dockerfile, Serverless, Bicep, OpenAPI, ARM Templates, or OpenTofu and detects security and compliance misconfigurations using graph-based scanning. + +### Inputs +- baseline (string, default: ""): Optional path to a Checkov baseline file to suppress previously acknowledged findings. +- path (string, default: "."): Directory to scan. +- soft-fail-on (string, default: "LOW"): Configures Checkov’s soft-fail behavior. Supported values: + - "": no soft-fail (the job fails on any finding) + - "LOW" | "MEDIUM" | "HIGH" | "CRITICAL": soft-fail threshold (this severity and lower do not fail the job; higher severities fail) + - "any": soft-fail for all findings (the job never fails) +- use-test-reporter (boolean, default: true): Whether to render CTRF + template. If false, results are available in the scan step logs. +- issue-on-findings (string, default: ""): GitHub username to mention. If set and the scan fails, an Issue will be created/commented and the user mentioned. + +### Example usage + +simple scan: +```yaml +jobs: + checkov_scan: + uses: zweitag/github-actions/.github/workflows/security-scan.yaml@main + with: + path: "." + use-test-reporter: true + secrets: + GH_TOKEN: ${{ secrets.GITHUB_TOKEN }} # Optional: improves reliability of dependency downloads +``` + +scan with baseline and issue mention: +```yaml +jobs: + checkov_scan: + uses: zweitag/github-actions/.github/workflows/security-scan.yaml@main + with: + path: "./infra" + baseline: "./.checkov.baseline" + use-test-reporter: true + issue-on-findings: "JohnDoe" + secrets: + GH_TOKEN: ${{ secrets.GITHUB_TOKEN }} # Optional: improves reliability of dependency downloads +``` + +### Test-reporter +- Embeds a human‑readable report in the workflow run (Job Summary), rendered from the CTRF JSON via Handlebars (config_scan_template.hbs). +- If findings are present, it shows: + - A compact summary table (Tests/Passed/Failed) + - A collapsible findings list with context (file path, optional line range) and guidelines +- If no findings match the configured policy, it displays a “no misconfigurations found” message for quick confirmation. + +### Failure policy +- The workflow uses soft-fail-on as failure controller: + - "": no soft-fail (the job fails on any finding) + - "LOW" (default) | "MEDIUM" | "HIGH" | "CRITICAL": soft-fail threshold + - "any": soft-fail for all findings (the job never fails) +- Findings of higher severity cause the step to fail (and may trigger issue creation). +- You can use a baseline file to suppress known findings and reduce noise. + +### Why not SARIF + GitHub Code Scanning? +While Checkov can integrate with Code Scanning, comprehensive features for private repositories require GitHub Advanced Security (paid). +This workflow provides standardized reports (CTRF), visual summaries, and optional issue creation without relying on paid features, making it suitable for any repository. + # License Copyright 2019 Zweitag GmbH From ef785353ffe77f256bbd16e7672bec6adbf82e63 Mon Sep 17 00:00:00 2001 From: JonasPollokZweitag <„jonas.pollok@zweitag.de“> Date: Tue, 20 Jan 2026 13:05:30 +0100 Subject: [PATCH 46/53] chore: small changes in templates --- .github/workflows/checkov-scan.yaml | 12 +++++++++--- security-scanning/config_scan_template.hbs | 2 +- security-scanning/image_scan_template.hbs | 2 +- 3 files changed, 11 insertions(+), 5 deletions(-) diff --git a/.github/workflows/checkov-scan.yaml b/.github/workflows/checkov-scan.yaml index ec3d0df..d3b44b5 100644 --- a/.github/workflows/checkov-scan.yaml +++ b/.github/workflows/checkov-scan.yaml @@ -4,7 +4,7 @@ name: Security Scan on: workflow_call: inputs: - checkovbaseline: + baseline: description: 'Path to the Checkov baseline file (default: none)' default: '' required: false @@ -14,6 +14,11 @@ on: required: false default: '.' type: string + soft-fail-on: + description: 'Lowest severity level to cause a failed scan (default: LOW)' + required: false + default: 'LOW' + type: string use-test-reporter: description: 'Attach the test results as a report (default: true)' required: false @@ -57,13 +62,14 @@ jobs: - name: run Checkov id: scan env: - BASELINE: ${{ inputs.checkovbaseline != '' && format('--baseline {0}', inputs.checkovbaseline) || '' }} + BASELINE: ${{ inputs.baseline != '' && format('--baseline {0}', inputs.baseline) || '' }} + SOFTFAIL: ${{ inputs.soft-fail-on != '' && format('--soft-fail-on {0}', inputs.soft-fail-on) || '' }} run: | checkov \ --directory ${{ inputs.path }} \ --output json \ $BASELINE \ - --soft-fail-on LOW > ./scan-results/checkov.json + $SOFTFAIL > ./scan-results/checkov.json - name: convert Checkov report to CTRF format if: always() && inputs.use-test-reporter diff --git a/security-scanning/config_scan_template.hbs b/security-scanning/config_scan_template.hbs index bb2328e..10c2352 100644 --- a/security-scanning/config_scan_template.hbs +++ b/security-scanning/config_scan_template.hbs @@ -53,7 +53,7 @@ {{#if (eq ctrf.summary.passed ctrf.summary.tests)}} ### All Tests Passed! 🎉 {{else}} -## Failed Tests: +## Findings: {{#each ctrf.tests}} {{#if (eq status "failed")}}
diff --git a/security-scanning/image_scan_template.hbs b/security-scanning/image_scan_template.hbs index c790ebb..4aceb0c 100644 --- a/security-scanning/image_scan_template.hbs +++ b/security-scanning/image_scan_template.hbs @@ -44,7 +44,7 @@ {{#if (eq ctrf.summary.passed ctrf.summary.tests)}} ### No vulnerabilities with set severity found! 🎉 {{else}} -## Found Vulnerabilities: +## Findings: {{#each ctrf.tests}}
🔴 {{pkgName}}@{{installedVersion}} - {{id}} ({{severity}}) From 72c1b0742de853fb78938546d81e6dc7ac7d4f44 Mon Sep 17 00:00:00 2001 From: JonasPollokZweitag <„jonas.pollok@zweitag.de“> Date: Tue, 20 Jan 2026 14:18:03 +0100 Subject: [PATCH 47/53] chore: add test workflow for security scan --- .github/workflows/test-security-scan.yaml | 66 +++++++++++++++++++++++ security-scanning/tests/Dockerfile | 8 +++ 2 files changed, 74 insertions(+) create mode 100644 .github/workflows/test-security-scan.yaml create mode 100644 security-scanning/tests/Dockerfile diff --git a/.github/workflows/test-security-scan.yaml b/.github/workflows/test-security-scan.yaml new file mode 100644 index 0000000..b7f9395 --- /dev/null +++ b/.github/workflows/test-security-scan.yaml @@ -0,0 +1,66 @@ +# .github/workflows/test-security-scan.yaml +name: Test Security Scan Workflows + +on: + pull_request: + paths: + - '.github/workflows/trivy-scan.yaml' + - '.github/workflows/checkov-scan.yaml' + - '.github/workflows/test-security-scan.yaml' + - 'security-scanning/**' + push: + branches: + - main + paths: + - '.github/workflows/trivy-scan.yaml' + - '.github/workflows/checkov-scan.yaml' + - '.github/workflows/test-security-scan.yaml' + - 'security-scanning/**' + +jobs: + # Trivy Scan Tests + + test-trivy-filesystem: + name: Test Trivy Filesystem Scan + uses: ./.github/workflows/trivy-scan.yaml + with: + scan-type: filesystem + severity-level: CRITICAL + use-test-reporter: true + check-secrets: true + issue-on-findings: '' + secrets: + GH_TOKEN: ${{ secrets.GITHUB_TOKEN }} + + test-trivy-config: + name: Test Trivy Config Scan + uses: ./.github/workflows/trivy-scan.yaml + with: + scan-type: config + severity-level: CRITICAL + use-test-reporter: true + issue-on-findings: '' + + test-trivy-image: + name: Test Trivy Image Scan + uses: ./.github/workflows/trivy-scan.yaml + with: + scan-type: image + path: './security-scanning/tests' + severity-level: CRITICAL + use-test-reporter: true + issue-on-findings: '' + secrets: + GH_TOKEN: ${{ secrets.GITHUB_TOKEN }} + + # ========================================== + # Checkov Scan Tests + # ========================================== + + test-checkov: + name: Test Checkov Scan + uses: ./.github/workflows/checkov-scan.yaml + with: + soft-fail-on: any + use-test-reporter: true + issue-on-findings: '' diff --git a/security-scanning/tests/Dockerfile b/security-scanning/tests/Dockerfile new file mode 100644 index 0000000..c49957a --- /dev/null +++ b/security-scanning/tests/Dockerfile @@ -0,0 +1,8 @@ +# security-scanning/tests/Dockerfile +# Minimal test image for security scan workflow testing + +FROM alpine:3.23 + +USER testuser + +CMD ["echo", "This is a test image for security scan workflow testing."] From d11fb9be55e2dc358bfdf745d091b47406cc66f9 Mon Sep 17 00:00:00 2001 From: JonasPollokZweitag <„jonas.pollok@zweitag.de“> Date: Tue, 20 Jan 2026 14:26:05 +0100 Subject: [PATCH 48/53] chore: add Healthcheck to test image --- security-scanning/tests/Dockerfile | 2 ++ 1 file changed, 2 insertions(+) diff --git a/security-scanning/tests/Dockerfile b/security-scanning/tests/Dockerfile index c49957a..7f69534 100644 --- a/security-scanning/tests/Dockerfile +++ b/security-scanning/tests/Dockerfile @@ -6,3 +6,5 @@ FROM alpine:3.23 USER testuser CMD ["echo", "This is a test image for security scan workflow testing."] + +HEALTHCHECK NONE From 3e82945c1953433572f6dbf76b4ad0b7d789e0a4 Mon Sep 17 00:00:00 2001 From: JonasPollokZweitag <„jonas.pollok@zweitag.de“> Date: Tue, 20 Jan 2026 14:34:09 +0100 Subject: [PATCH 49/53] chore: CRITICAL instead of any --- .github/workflows/test-security-scan.yaml | 2 +- Readme.md | 1 - 2 files changed, 1 insertion(+), 2 deletions(-) diff --git a/.github/workflows/test-security-scan.yaml b/.github/workflows/test-security-scan.yaml index b7f9395..628d9da 100644 --- a/.github/workflows/test-security-scan.yaml +++ b/.github/workflows/test-security-scan.yaml @@ -61,6 +61,6 @@ jobs: name: Test Checkov Scan uses: ./.github/workflows/checkov-scan.yaml with: - soft-fail-on: any + soft-fail-on: CRITICAL use-test-reporter: true issue-on-findings: '' diff --git a/Readme.md b/Readme.md index 1721bf1..7d7b69c 100644 --- a/Readme.md +++ b/Readme.md @@ -186,7 +186,6 @@ jobs: - The workflow uses soft-fail-on as failure controller: - "": no soft-fail (the job fails on any finding) - "LOW" (default) | "MEDIUM" | "HIGH" | "CRITICAL": soft-fail threshold - - "any": soft-fail for all findings (the job never fails) - Findings of higher severity cause the step to fail (and may trigger issue creation). - You can use a baseline file to suppress known findings and reduce noise. From a5e6e2a205bb85a246dfd89263e576699ec4891c Mon Sep 17 00:00:00 2001 From: JonasPollokZweitag <„jonas.pollok@zweitag.de“> Date: Tue, 20 Jan 2026 14:49:41 +0100 Subject: [PATCH 50/53] chore: use Test Image that ist empty --- security-scanning/tests/Dockerfile | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/security-scanning/tests/Dockerfile b/security-scanning/tests/Dockerfile index 7f69534..583b299 100644 --- a/security-scanning/tests/Dockerfile +++ b/security-scanning/tests/Dockerfile @@ -1,7 +1,7 @@ # security-scanning/tests/Dockerfile # Minimal test image for security scan workflow testing -FROM alpine:3.23 +FROM scratch USER testuser From 312c69f94b51da72a0cb73dd13d0628237e1eeae Mon Sep 17 00:00:00 2001 From: JonasPollokZweitag <„jonas.pollok@zweitag.de“> Date: Tue, 20 Jan 2026 15:00:04 +0100 Subject: [PATCH 51/53] chore: removed sum of fs scan report --- security-scanning/filesystem_scan_template.hbs | 2 -- 1 file changed, 2 deletions(-) diff --git a/security-scanning/filesystem_scan_template.hbs b/security-scanning/filesystem_scan_template.hbs index cef4c80..f0158a0 100644 --- a/security-scanning/filesystem_scan_template.hbs +++ b/security-scanning/filesystem_scan_template.hbs @@ -3,7 +3,6 @@
LOW 🟢MEDIUM 🟡HIGH 🔴CRITICAL 🚨UNKNOWN ❓
{{ctrf.extensions.severityCounts.LOW}}{{ctrf.extensions.severityCounts.MEDIUM}}{{ctrf.extensions.severityCounts.HIGH}}{{ctrf.extensions.severityCounts.CRITICAL}}{{ctrf.extensions.severityCounts.UNKNOWN}}
- {{#if ctrf.extensions.severityCounts.LOW}} {{/if}} @@ -26,7 +25,6 @@ - {{#if ctrf.extensions.severityCounts.LOW}} {{/if}} From 05810a7fd878deb0d1a0de7d7353932523d48ff9 Mon Sep 17 00:00:00 2001 From: JonasPollokZweitag <„jonas.pollok@zweitag.de“> Date: Tue, 20 Jan 2026 15:11:15 +0100 Subject: [PATCH 52/53] chore: create checkov baseline for tests --- .checkov.baseline | 59 +++++++++++++++++++++++ .github/workflows/test-security-scan.yaml | 1 + security-scanning/tests/Dockerfile | 2 +- 3 files changed, 61 insertions(+), 1 deletion(-) create mode 100644 .checkov.baseline diff --git a/.checkov.baseline b/.checkov.baseline new file mode 100644 index 0000000..a535f4d --- /dev/null +++ b/.checkov.baseline @@ -0,0 +1,59 @@ +{ + "failed_checks": [ + { + "file": "/.github/workflows/checkov-scan.yaml", + "findings": [ + { + "resource": "on(Security Scan)", + "check_ids": [ + "CKV2_GHA_1" + ] + } + ] + }, + { + "file": "/.github/workflows/deployment-status.yaml", + "findings": [ + { + "resource": "on(deployment-status)", + "check_ids": [ + "CKV2_GHA_1" + ] + } + ] + }, + { + "file": "/.github/workflows/global-variables.yaml", + "findings": [ + { + "resource": "on(global-variables)", + "check_ids": [ + "CKV2_GHA_1" + ] + } + ] + }, + { + "file": "/.github/workflows/test-security-scan.yaml", + "findings": [ + { + "resource": "on(Test Security Scan Workflows)", + "check_ids": [ + "CKV2_GHA_1" + ] + } + ] + }, + { + "file": "/.github/workflows/trivy-scan.yaml", + "findings": [ + { + "resource": "on(Security Scan)", + "check_ids": [ + "CKV2_GHA_1" + ] + } + ] + } + ] +} \ No newline at end of file diff --git a/.github/workflows/test-security-scan.yaml b/.github/workflows/test-security-scan.yaml index 628d9da..d55c1c4 100644 --- a/.github/workflows/test-security-scan.yaml +++ b/.github/workflows/test-security-scan.yaml @@ -63,4 +63,5 @@ jobs: with: soft-fail-on: CRITICAL use-test-reporter: true + baseline: '.checkov.baseline' issue-on-findings: '' diff --git a/security-scanning/tests/Dockerfile b/security-scanning/tests/Dockerfile index 583b299..7f69534 100644 --- a/security-scanning/tests/Dockerfile +++ b/security-scanning/tests/Dockerfile @@ -1,7 +1,7 @@ # security-scanning/tests/Dockerfile # Minimal test image for security scan workflow testing -FROM scratch +FROM alpine:3.23 USER testuser From 86c7158fbc55717db1a2f8deb4fab0276312fb26 Mon Sep 17 00:00:00 2001 From: JonasPollokZweitag <„jonas.pollok@zweitag.de“> Date: Tue, 20 Jan 2026 15:24:40 +0100 Subject: [PATCH 53/53] chore: add check in checkov converter to avoid error --- security-scanning/checkov2ctrf.py | 16 ++++++++++------ 1 file changed, 10 insertions(+), 6 deletions(-) diff --git a/security-scanning/checkov2ctrf.py b/security-scanning/checkov2ctrf.py index b741019..74c46e5 100644 --- a/security-scanning/checkov2ctrf.py +++ b/security-scanning/checkov2ctrf.py @@ -1,7 +1,6 @@ import json import sys - def extract_checks(target, status): # extracts checks of a check_type (e.g. terraform) with a status ('fail' oder 'pass'). checks = [] @@ -17,12 +16,18 @@ def extract_checks(target, status): }) return checks - def checkov_to_ctrf(checkov_json): tests = [] - for target in checkov_json: - tests.extend(extract_checks(target, "fail")) - tests.extend(extract_checks(target, "pass")) + + if isinstance(checkov_json, list): + for target in checkov_json: + tests.extend(extract_checks(target, "fail")) + tests.extend(extract_checks(target, "pass")) + elif isinstance(checkov_json, dict): + tests.extend(extract_checks(checkov_json, "fail")) + tests.extend(extract_checks(checkov_json, "pass")) + else: + raise ValueError("Unerwartetes JSON-Format!") total = len(tests) passed = sum(1 for t in tests if t["status"] == "passed") @@ -56,7 +61,6 @@ def checkov_to_ctrf(checkov_json): } } - if __name__ == "__main__": if len(sys.argv) != 3: print("Usage: python checkov2ctrf.py ")
Found Vulnerabilities ❌LOW 🟢
{{ctrf.summary.failed}}{{ctrf.extensions.severityCounts.LOW}}