From f7957ab9f59969a4d8b473b15e103f37bc6dc27f Mon Sep 17 00:00:00 2001 From: DragonSlayer_14 Date: Wed, 9 Sep 2026 20:42:46 +0200 Subject: [PATCH 01/18] Fix: `.junie/plans`-Ordner von IDEA-Indexierung ausgeschlossen --- .gitignore | 1 + .idea/rust-template.iml | 1 + 2 files changed, 2 insertions(+) diff --git a/.gitignore b/.gitignore index 608b40b..c5aa50f 100644 --- a/.gitignore +++ b/.gitignore @@ -109,3 +109,4 @@ fabric.properties # Built Visual Studio Code Extensions *.vsix +.junie/plans diff --git a/.idea/rust-template.iml b/.idea/rust-template.iml index bbe0a70..b958305 100644 --- a/.idea/rust-template.iml +++ b/.idea/rust-template.iml @@ -4,6 +4,7 @@ + From 59a81773cc3f463875c0e383261b17f718065078 Mon Sep 17 00:00:00 2001 From: DragonSlayer_14 Date: Wed, 9 Sep 2026 20:43:03 +0200 Subject: [PATCH 02/18] =?UTF-8?q?Fix:=20Abh=C3=A4ngigkeit=20`serde=5Fjson`?= =?UTF-8?q?=20entfernt=20(bereits=20=C3=BCber=20`serde`=20abgedeckt)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- Cargo.lock | 1 - Cargo.toml | 1 - 2 files changed, 2 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index acc769d..adfe01e 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -874,7 +874,6 @@ dependencies = [ "logger-ctdra", "reqwest", "serde", - "serde_json", "tokio", ] diff --git a/Cargo.toml b/Cargo.toml index c7c07d2..f4f6639 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -15,7 +15,6 @@ clap = { version = "4.6.6", features = ["derive", "env"] } reqwest = { version = "0.13.4", features = ["json", "stream"] } tokio = { version = "1.53.1", features = ["full"] } serde = { version = "1.0.229", features = ["derive"] } -serde_json = "1.0.151" anyhow = "1.0.104" [profile.release] From d9089afdc9a2e4913019a07829778060af4d5a6a Mon Sep 17 00:00:00 2001 From: DragonSlayer_14 Date: Wed, 9 Sep 2026 20:43:19 +0200 Subject: [PATCH 03/18] =?UTF-8?q?Feat:=20IDEA-Run-Konfiguration=20f=C3=BCr?= =?UTF-8?q?=20Cargo=20hinzugef=C3=BCgt=20-=20Standard-Konfiguration=20f?= =?UTF-8?q?=C3=BCr=20`cargo=20run`=20mit=20Dev-Profil=20-=20Terminal-Emula?= =?UTF-8?q?tion=20und=20Projektverzeichnis=20als=20Arbeitsordner?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .idea/runConfigurations/Run.xml | 20 ++++++++++++++++++++ 1 file changed, 20 insertions(+) create mode 100644 .idea/runConfigurations/Run.xml diff --git a/.idea/runConfigurations/Run.xml b/.idea/runConfigurations/Run.xml new file mode 100644 index 0000000..2abd370 --- /dev/null +++ b/.idea/runConfigurations/Run.xml @@ -0,0 +1,20 @@ + + + + \ No newline at end of file From 9a59fff032360b26348ad9f30423eb55f9d148a1 Mon Sep 17 00:00:00 2001 From: DragonSlayer_14 Date: Wed, 9 Sep 2026 21:54:28 +0200 Subject: [PATCH 04/18] =?UTF-8?q?Feat:=20Renovate=20f=C3=BCr=20automatisch?= =?UTF-8?q?e=20Dependency-Updates=20eingerichtet?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - renovate.json mit Zeitplan und Gruppierung für Gitea Actions und Docker-Images - Gitea-Workflow zur wöchentlichen Ausführung von Renovate hinzugefügt - Workflow-Datei in IDEA JSON-Schema-Zuordnungen registriert Co-Authored-By: Claude Sonnet 5 Claude-Session: https://claude.ai/code/session_01FKMjR9pBxshZYZdhKyT5Ky --- .gitea/workflows/renovate.yaml | 25 +++++++++++++++++++++++++ .idea/jsonSchemas.xml | 3 +++ renovate.json | 16 ++++++++++++++++ 3 files changed, 44 insertions(+) create mode 100644 .gitea/workflows/renovate.yaml create mode 100644 renovate.json diff --git a/.gitea/workflows/renovate.yaml b/.gitea/workflows/renovate.yaml new file mode 100644 index 0000000..fee0b8e --- /dev/null +++ b/.gitea/workflows/renovate.yaml @@ -0,0 +1,25 @@ +name: Renovate + +on: + schedule: + - cron: "0 4 * * 1" + workflow_dispatch: + +jobs: + renovate: + name: Dependency-Updates prüfen & Pull Requests erstellen + runs-on: ubuntu-latest + container: ghcr.io/renovatebot/renovate:43.279.0 + steps: + - name: Renovate ausführen + run: renovate + env: + RENOVATE_PLATFORM: gitea + RENOVATE_ENDPOINT: ${{ gitea.server_url || github.server_url }}/api/v1/ + RENOVATE_TOKEN: ${{ secrets.RENOVATE_TOKEN }} + RENOVATE_REPOSITORIES: ${{ gitea.repository || github.repository }} + RENOVATE_AUTODISCOVER: "false" + RENOVATE_GIT_AUTHOR: "Renovate Bot " + RENOVATE_HOST_RULES: >- + [{"hostType":"cargo","matchHost":"gitea.creative-dragonslayer.de","token":"${{ secrets.RENOVATE_TOKEN }}"}] + LOG_LEVEL: info diff --git a/.idea/jsonSchemas.xml b/.idea/jsonSchemas.xml index 0ae7be4..cd2a4f5 100644 --- a/.idea/jsonSchemas.xml +++ b/.idea/jsonSchemas.xml @@ -17,6 +17,9 @@ + + diff --git a/renovate.json b/renovate.json new file mode 100644 index 0000000..30693da --- /dev/null +++ b/renovate.json @@ -0,0 +1,16 @@ +{ + "$schema": "https://docs.renovatebot.com/renovate-schema.json", + "extends": ["config:recommended"], + "timezone": "Europe/Berlin", + "schedule": ["before 6am on monday"], + "packageRules": [ + { + "matchManagers": ["github-actions"], + "groupName": "Gitea Actions" + }, + { + "matchManagers": ["dockerfile", "docker-compose"], + "groupName": "Docker-Images" + } + ] +} From f56a5c71abc818b9c8e3f496d75cb7b80826bcc7 Mon Sep 17 00:00:00 2001 From: DragonSlayer_14 Date: Wed, 9 Sep 2026 22:24:14 +0200 Subject: [PATCH 05/18] =?UTF-8?q?Feat:=20Gitea-Action=20f=C3=BCr=20Securit?= =?UTF-8?q?y-Scans=20mit=20Trivy=20&=20OSV-Scanner=20hinzugef=C3=BCgt?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - Trivy (fs-Scan: vuln, secret, misconfig) und OSV-Scanner (Cargo.lock) laufen als FOSS-Tools ohne Anmeldedaten - Bei Funden wird automatisch ein Gitea-Issue (Label security-scan) erstellt bzw. bei jedem weiteren Lauf per Kommentar aktualisiert, solange es offen ist; nach Funden wird nicht automatisch geschlossen Co-Authored-By: Claude Sonnet 5 Claude-Session: https://claude.ai/code/session_01FKMjR9pBxshZYZdhKyT5Ky --- .gitea/workflows/security-scan.yaml | 52 +++++++ scripts/report-security-issue.py | 206 ++++++++++++++++++++++++++++ 2 files changed, 258 insertions(+) create mode 100644 .gitea/workflows/security-scan.yaml create mode 100644 scripts/report-security-issue.py diff --git a/.gitea/workflows/security-scan.yaml b/.gitea/workflows/security-scan.yaml new file mode 100644 index 0000000..058bd9d --- /dev/null +++ b/.gitea/workflows/security-scan.yaml @@ -0,0 +1,52 @@ +name: Security Scans + +on: + push: + branches: + - main + - testing + - dev + pull_request: + schedule: + - cron: "0 5 * * 1" + workflow_dispatch: + +jobs: + security-scan: + name: Trivy & OSV-Scanner + runs-on: ubuntu-latest + steps: + - name: Checkout Repository + uses: actions/checkout@v7 + + - name: Run Trivy Scanner + uses: aquasecurity/trivy-action@v0.36.0 + with: + scan-type: fs + scan-ref: . + scanners: vuln,secret,misconfig + severity: CRITICAL,HIGH + format: json + output: trivy-results.json + exit-code: "0" + + - name: Install OSV-Scanner + run: | + curl -fsSL -o /usr/local/bin/osv-scanner \ + "https://github.com/google/osv-scanner/releases/download/v2.5.1/osv-scanner_linux_amd64" + chmod +x /usr/local/bin/osv-scanner + + - name: Run OSV-Scanner + run: | + set +e + osv-scanner scan source --recursive --format json --output-file osv-results.json . + echo "OSV_EXIT=$?" >> "$GITHUB_ENV" + + - name: Ergebnisse & Gitea-Issue erstellen/aktualisieren + env: + GITEA_URL: ${{ gitea.server_url || github.server_url }} + REPO: ${{ gitea.repository || github.repository }} + TOKEN: ${{ secrets.SECURITY_TOKEN }} + RUN_URL: ${{ gitea.server_url || github.server_url }}/${{ gitea.repository || github.repository }}/actions/runs/${{ gitea.run_id || github.run_id }} + run: | + python3 scripts/report-security-issue.py trivy-results.json osv-results.json diff --git a/scripts/report-security-issue.py b/scripts/report-security-issue.py new file mode 100644 index 0000000..c7b9ebb --- /dev/null +++ b/scripts/report-security-issue.py @@ -0,0 +1,206 @@ +#!/usr/bin/env python3 +"""Erstellt oder kommentiert ein Gitea-Issue mit den Ergebnissen der Security-Scans. + +Sucht ein offenes Issue mit dem Label "security-scan". Existiert eines, wird +der aktuelle Scan-Stand als neuer Kommentar angehängt (die Historie bleibt +erhalten). Existiert keines (z.B. weil das letzte geschlossen wurde), wird ein +neues Issue erstellt. Gibt es keine Funde mehr, wird ein offenes Issue nur +kommentiert, nicht geschlossen. +""" + +import json +import os +import sys +import urllib.error +import urllib.request + +LABEL_NAME = "security-scan" +LABEL_COLOR = "#b60205" +ISSUE_TITLE = "Security-Scan: Offene Schwachstellen" + +SEVERITY_ORDER = {"CRITICAL": 0, "HIGH": 1, "MEDIUM": 2, "LOW": 3, "UNKNOWN": 4, "-": 5} + + +def api(method, path, token, gitea_url, data=None): + url = f"{gitea_url}/api/v1{path}" + body = json.dumps(data).encode() if data is not None else None + req = urllib.request.Request(url, data=body, method=method) + req.add_header("Authorization", f"token {token}") + req.add_header("Content-Type", "application/json") + try: + with urllib.request.urlopen(req) as resp: + raw = resp.read() + return json.loads(raw) if raw else None + except urllib.error.HTTPError as e: + print(f"Gitea API Fehler ({method} {path}): {e.code} {e.read().decode()}", file=sys.stderr) + raise + + +def load_trivy(path): + findings = [] + if not path or not os.path.isfile(path): + return findings + with open(path) as f: + data = json.load(f) + for result in data.get("Results", []) or []: + target = result.get("Target", "?") + for vuln in result.get("Vulnerabilities", []) or []: + findings.append({ + "source": "Trivy", + "id": vuln.get("VulnerabilityID", "?"), + "severity": vuln.get("Severity", "UNKNOWN"), + "package": vuln.get("PkgName", "?"), + "installed": vuln.get("InstalledVersion", "?"), + "fixed": vuln.get("FixedVersion") or "-", + "target": target, + }) + for misc in result.get("Misconfigurations", []) or []: + findings.append({ + "source": "Trivy (Misconfig)", + "id": misc.get("ID", "?"), + "severity": misc.get("Severity", "UNKNOWN"), + "package": misc.get("Title", "?"), + "installed": "-", + "fixed": "-", + "target": target, + }) + for secret in result.get("Secrets", []) or []: + findings.append({ + "source": "Trivy (Secret)", + "id": secret.get("RuleID", "?"), + "severity": secret.get("Severity", "UNKNOWN"), + "package": secret.get("Title", "?"), + "installed": "-", + "fixed": "-", + "target": target, + }) + return findings + + +def load_osv(path): + findings = [] + if not path or not os.path.isfile(path): + return findings + with open(path) as f: + data = json.load(f) + for result in data.get("results", []) or []: + source = (result.get("source") or {}).get("path", "?") + for pkg in result.get("packages", []) or []: + info = pkg.get("package", {}) + pkg_name = f"{info.get('name', '?')} ({info.get('ecosystem', '?')})" + for vuln in pkg.get("vulnerabilities", []) or []: + findings.append({ + "source": "OSV-Scanner", + "id": vuln.get("id", "?"), + "severity": "-", + "package": pkg_name, + "installed": info.get("version", "?"), + "fixed": "-", + "target": source, + }) + return findings + + +def print_summary(findings): + if not findings: + print("Keine Funde.") + return + findings = sorted(findings, key=lambda f: (SEVERITY_ORDER.get(f["severity"], 9), f["id"])) + widths = { + key: max(len(key), *(len(str(f[key])) for f in findings)) + for key in ("source", "id", "severity", "package", "installed", "fixed", "target") + } + header = ("source", "id", "severity", "package", "installed", "fixed", "target") + row_fmt = " ".join(f"{{:{widths[k]}}}" for k in header) + print(row_fmt.format(*header)) + print(row_fmt.format(*("-" * widths[k] for k in header))) + for f in findings: + print(row_fmt.format(*(str(f[k]) for k in header))) + + +def build_report(findings, run_url): + findings = sorted(findings, key=lambda f: (SEVERITY_ORDER.get(f["severity"], 9), f["id"])) + lines = [ + "Automatisch erstellt vom Security-Scan-Workflow.", + f"Lauf: {run_url}" if run_url else "", + "", + "| Quelle | ID | Schweregrad | Paket | Installiert | Fix | Ziel |", + "|---|---|---|---|---|---|---|", + ] + for f in findings: + lines.append( + f"| {f['source']} | {f['id']} | {f['severity']} | {f['package']} | " + f"{f['installed']} | {f['fixed']} | {f['target']} |" + ) + return "\n".join(lines) + + +def ensure_label(token, gitea_url, repo): + labels = api("GET", f"/repos/{repo}/labels?limit=50", token, gitea_url) or [] + for label in labels: + if label.get("name") == LABEL_NAME: + return label["id"] + created = api("POST", f"/repos/{repo}/labels", token, gitea_url, { + "name": LABEL_NAME, + "color": LABEL_COLOR, + "description": "Automatisch verwaltet vom Security-Scan-Workflow", + }) + return created["id"] + + +def find_open_issue(token, gitea_url, repo): + issues = api( + "GET", + f"/repos/{repo}/issues?state=open&type=issues&labels={LABEL_NAME}", + token, + gitea_url, + ) or [] + for issue in issues: + if issue.get("title") == ISSUE_TITLE: + return issue + return None + + +def main(): + trivy_path = sys.argv[1] if len(sys.argv) > 1 else None + osv_path = sys.argv[2] if len(sys.argv) > 2 else None + + gitea_url = os.environ["GITEA_URL"].rstrip("/") + repo = os.environ["REPO"] + token = os.environ["TOKEN"] + run_url = os.environ.get("RUN_URL", "") + osv_exit = int(os.environ.get("OSV_EXIT", "0")) + + findings = load_trivy(trivy_path) + load_osv(osv_path) + print_summary(findings) + + open_issue = find_open_issue(token, gitea_url, repo) + + if findings: + report = build_report(findings, run_url) + if open_issue: + print(f"Kommentiere bestehendes Issue #{open_issue['number']} mit {len(findings)} Fund(en).") + api("POST", f"/repos/{repo}/issues/{open_issue['number']}/comments", token, gitea_url, {"body": report}) + else: + label_id = ensure_label(token, gitea_url, repo) + print(f"Erstelle neues Issue mit {len(findings)} Fund(en).") + api("POST", f"/repos/{repo}/issues", token, gitea_url, { + "title": ISSUE_TITLE, + "body": report, + "labels": [label_id], + }) + elif open_issue: + print(f"Keine aktuellen Funde mehr. Kommentiere Issue #{open_issue['number']}.") + api("POST", f"/repos/{repo}/issues/{open_issue['number']}/comments", token, gitea_url, { + "body": f"Aktueller Scan hat keine offenen Schwachstellen mehr gefunden.\n\n{run_url}".strip(), + }) + else: + print("Keine Funde und kein offenes Issue vorhanden.") + + has_trivy_findings = any(f["source"].startswith("Trivy") for f in findings) + if has_trivy_findings or osv_exit == 1: + sys.exit(1) + + +if __name__ == "__main__": + main() From 9445a82b53c3e44b9bf715ad094c3f98674c95b8 Mon Sep 17 00:00:00 2001 From: DragonSlayer_14 Date: Wed, 9 Sep 2026 22:43:30 +0200 Subject: [PATCH 06/18] =?UTF-8?q?Feat:=20TruffleHog-Secret-Scan=20als=20ei?= =?UTF-8?q?gene=20Gitea-Action=20hinzugef=C3=BCgt?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - Neuer Workflow trufflehog-scan.yaml scannt die komplette Git-Historie und meldet Funde in ein eigenes Issue (Label security-scan-trufflehog), getrennt vom Trivy/OSV-Issue, aber ebenfalls über SECURITY_TOKEN - report-security-issue.py: Titel/Label per ISSUE_TITLE/ISSUE_LABEL konfigurierbar, TruffleHog-JSONL-Parsing (ohne Secret-Wert im Report), Workflow schlägt bei verifizierten Funden fehl - Rotations-Anleitung wird automatisch an Issues mit TruffleHog-Funden angehängt Co-Authored-By: Claude Sonnet 5 Claude-Session: https://claude.ai/code/session_01FKMjR9pBxshZYZdhKyT5Ky --- .gitea/workflows/trufflehog-scan.yaml | 48 +++++++++++++++++ scripts/report-security-issue.py | 74 +++++++++++++++++++++++---- 2 files changed, 112 insertions(+), 10 deletions(-) create mode 100644 .gitea/workflows/trufflehog-scan.yaml diff --git a/.gitea/workflows/trufflehog-scan.yaml b/.gitea/workflows/trufflehog-scan.yaml new file mode 100644 index 0000000..a53dcbb --- /dev/null +++ b/.gitea/workflows/trufflehog-scan.yaml @@ -0,0 +1,48 @@ +name: TruffleHog Secret Scan + +on: + push: + branches: + - main + - testing + - dev + pull_request: + schedule: + - cron: "0 5 * * 1" + workflow_dispatch: + +jobs: + trufflehog-scan: + name: TruffleHog + runs-on: ubuntu-latest + steps: + - name: Checkout Repository + uses: actions/checkout@v7 + with: + fetch-depth: 0 + + - name: Install TruffleHog + run: | + curl -fsSL -o trufflehog.tar.gz \ + "https://github.com/trufflesecurity/trufflehog/releases/download/v3.97.4/trufflehog_3.97.4_linux_amd64.tar.gz" + tar -xzf trufflehog.tar.gz trufflehog + chmod +x trufflehog + sudo mv trufflehog /usr/local/bin/trufflehog + rm trufflehog.tar.gz + + - name: Run TruffleHog Scanner + run: | + set +e + trufflehog git file://. --results=verified,unknown --fail --json > trufflehog-results.json + echo "TRUFFLEHOG_EXIT=$?" >> "$GITHUB_ENV" + + - name: Ergebnisse & Gitea-Issue erstellen/aktualisieren + env: + GITEA_URL: ${{ gitea.server_url || github.server_url }} + REPO: ${{ gitea.repository || github.repository }} + TOKEN: ${{ secrets.SECURITY_TOKEN }} + RUN_URL: ${{ gitea.server_url || github.server_url }}/${{ gitea.repository || github.repository }}/actions/runs/${{ gitea.run_id || github.run_id }} + ISSUE_TITLE: "Security-Scan: TruffleHog Secrets" + ISSUE_LABEL: "security-scan-trufflehog" + run: | + python3 scripts/report-security-issue.py "" "" trufflehog-results.json diff --git a/scripts/report-security-issue.py b/scripts/report-security-issue.py index c7b9ebb..75f02d1 100644 --- a/scripts/report-security-issue.py +++ b/scripts/report-security-issue.py @@ -1,11 +1,15 @@ #!/usr/bin/env python3 """Erstellt oder kommentiert ein Gitea-Issue mit den Ergebnissen der Security-Scans. -Sucht ein offenes Issue mit dem Label "security-scan". Existiert eines, wird -der aktuelle Scan-Stand als neuer Kommentar angehängt (die Historie bleibt -erhalten). Existiert keines (z.B. weil das letzte geschlossen wurde), wird ein -neues Issue erstellt. Gibt es keine Funde mehr, wird ein offenes Issue nur -kommentiert, nicht geschlossen. +Sucht ein offenes Issue mit dem Label ISSUE_LABEL (Standard: "security-scan"). +Existiert eines, wird der aktuelle Scan-Stand als neuer Kommentar angehängt +(die Historie bleibt erhalten). Existiert keines (z.B. weil das letzte +geschlossen wurde), wird ein neues Issue erstellt. Gibt es keine Funde mehr, +wird ein offenes Issue nur kommentiert, nicht geschlossen. + +Titel und Label lassen sich per Umgebungsvariable ISSUE_TITLE / ISSUE_LABEL +überschreiben, damit z.B. TruffleHog-Funde in ein eigenes Issue laufen statt +in das gemeinsame Trivy/OSV-Issue. """ import json @@ -14,11 +18,20 @@ import sys import urllib.error import urllib.request -LABEL_NAME = "security-scan" +LABEL_NAME = os.environ.get("ISSUE_LABEL", "security-scan") LABEL_COLOR = "#b60205" -ISSUE_TITLE = "Security-Scan: Offene Schwachstellen" +ISSUE_TITLE = os.environ.get("ISSUE_TITLE", "Security-Scan: Offene Schwachstellen") -SEVERITY_ORDER = {"CRITICAL": 0, "HIGH": 1, "MEDIUM": 2, "LOW": 3, "UNKNOWN": 4, "-": 5} +SEVERITY_ORDER = { + "VERIFIED": -1, + "CRITICAL": 0, + "HIGH": 1, + "MEDIUM": 2, + "LOW": 3, + "UNKNOWN": 4, + "-": 5, + "UNVERIFIED": 6, +} def api(method, path, token, gitea_url, data=None): @@ -101,6 +114,32 @@ def load_osv(path): return findings +def load_trufflehog(path): + findings = [] + if not path or not os.path.isfile(path): + return findings + with open(path) as f: + for line in f: + line = line.strip() + if not line: + continue + try: + entry = json.loads(line) + except json.JSONDecodeError: + continue + git_meta = ((entry.get("SourceMetadata") or {}).get("Data") or {}).get("Git") or {} + findings.append({ + "source": "TruffleHog", + "id": entry.get("DetectorName", "?"), + "severity": "VERIFIED" if entry.get("Verified") else "UNVERIFIED", + "package": git_meta.get("file", "?"), + "installed": "-", + "fixed": "-", + "target": git_meta.get("commit", "-"), + }) + return findings + + def print_summary(findings): if not findings: print("Keine Funde.") @@ -118,6 +157,16 @@ def print_summary(findings): print(row_fmt.format(*(str(f[k]) for k in header))) +TRUFFLEHOG_GUIDANCE = """### Vorgehen bei gefundenen Secrets + +1. **Sofort rotieren/widerrufen**: Das betroffene Secret (Token, Passwort, Schlüssel) beim jeweiligen Dienst ungültig machen und durch ein neues ersetzen. Ein einmal committetes Secret gilt als kompromittiert, auch wenn es später aus der Historie entfernt wird. +2. **Ursache beheben**: Neues Secret nur noch über Umgebungsvariablen/Secrets-Store einbinden, nicht erneut hart codieren. +3. **Historie bereinigen (optional, manuell, erst nach Schritt 1)**: Mit `git filter-repo` oder BFG Repo-Cleaner den Commit-Inhalt entfernen, danach `git push --force` in Absprache mit allen Mitwirkenden – bestehende Clones/Forks werden dadurch ungültig. +4. **Issue schließen**, sobald rotiert wurde. TruffleHog findet das alte Secret ggf. weiterhin in der Historie – nach der Rotation ist das unkritisch. + +> Der Wert des Secrets selbst wird hier bewusst nicht ausgegeben, auch nicht gekürzt – nur Detector, Datei und Commit. Fund lässt sich über "Ziel" (Commit-Hash) und "Paket" (Dateipfad) lokalisieren.""" + + def build_report(findings, run_url): findings = sorted(findings, key=lambda f: (SEVERITY_ORDER.get(f["severity"], 9), f["id"])) lines = [ @@ -132,6 +181,9 @@ def build_report(findings, run_url): f"| {f['source']} | {f['id']} | {f['severity']} | {f['package']} | " f"{f['installed']} | {f['fixed']} | {f['target']} |" ) + if any(f["source"] == "TruffleHog" for f in findings): + lines.append("") + lines.append(TRUFFLEHOG_GUIDANCE) return "\n".join(lines) @@ -164,14 +216,16 @@ def find_open_issue(token, gitea_url, repo): def main(): trivy_path = sys.argv[1] if len(sys.argv) > 1 else None osv_path = sys.argv[2] if len(sys.argv) > 2 else None + trufflehog_path = sys.argv[3] if len(sys.argv) > 3 else None gitea_url = os.environ["GITEA_URL"].rstrip("/") repo = os.environ["REPO"] token = os.environ["TOKEN"] run_url = os.environ.get("RUN_URL", "") osv_exit = int(os.environ.get("OSV_EXIT", "0")) + trufflehog_exit = int(os.environ.get("TRUFFLEHOG_EXIT", "0")) - findings = load_trivy(trivy_path) + load_osv(osv_path) + findings = load_trivy(trivy_path) + load_osv(osv_path) + load_trufflehog(trufflehog_path) print_summary(findings) open_issue = find_open_issue(token, gitea_url, repo) @@ -198,7 +252,7 @@ def main(): print("Keine Funde und kein offenes Issue vorhanden.") has_trivy_findings = any(f["source"].startswith("Trivy") for f in findings) - if has_trivy_findings or osv_exit == 1: + if has_trivy_findings or osv_exit == 1 or trufflehog_exit == 183: sys.exit(1) From 134d3048cf8e17ae1424563d67205681eb1d0c86 Mon Sep 17 00:00:00 2001 From: DragonSlayer_14 Date: Wed, 9 Sep 2026 23:00:41 +0200 Subject: [PATCH 07/18] =?UTF-8?q?Fix:=20Security-Scan-Report=20robuster=20?= =?UTF-8?q?gemacht=20&=20TruffleHog=20l=C3=A4uft=20bei=20jedem=20Push?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - OSV-Scanner-Funde erhalten jetzt eine echte Severity (aus CVSS-Score der Gruppen abgeleitet) statt hartcodiert "-", damit kritische Funde nicht unten in der Tabelle verschwinden - Unerwartete Exit-Codes von osv-scanner/trufflehog gelten jetzt als Fehlschlag statt stillschweigend als "keine Funde" - Timeout für Gitea-API-Requests ergänzt - Label-Suche paginiert jetzt vollständig statt nur die ersten 50 - Markdown-Tabellenzellen werden escaped (Pipe/Zeilenumbruch) - Doppeltes Sortieren der Findings entfernt, Dict-Aufbau der Loader über gemeinsamen Helper vereinheitlicht - trufflehog-scan.yaml läuft nun bei jedem Push, nicht nur auf main/testing/dev Co-Authored-By: Claude Sonnet 5 Claude-Session: https://claude.ai/code/session_01FKMjR9pBxshZYZdhKyT5Ky --- .gitea/workflows/trufflehog-scan.yaml | 4 - scripts/report-security-issue.py | 166 +++++++++++++++++--------- 2 files changed, 107 insertions(+), 63 deletions(-) diff --git a/.gitea/workflows/trufflehog-scan.yaml b/.gitea/workflows/trufflehog-scan.yaml index a53dcbb..d374757 100644 --- a/.gitea/workflows/trufflehog-scan.yaml +++ b/.gitea/workflows/trufflehog-scan.yaml @@ -2,10 +2,6 @@ name: TruffleHog Secret Scan on: push: - branches: - - main - - testing - - dev pull_request: schedule: - cron: "0 5 * * 1" diff --git a/scripts/report-security-issue.py b/scripts/report-security-issue.py index 75f02d1..0e6fb52 100644 --- a/scripts/report-security-issue.py +++ b/scripts/report-security-issue.py @@ -29,7 +29,6 @@ SEVERITY_ORDER = { "MEDIUM": 2, "LOW": 3, "UNKNOWN": 4, - "-": 5, "UNVERIFIED": 6, } @@ -41,7 +40,7 @@ def api(method, path, token, gitea_url, data=None): req.add_header("Authorization", f"token {token}") req.add_header("Content-Type", "application/json") try: - with urllib.request.urlopen(req) as resp: + with urllib.request.urlopen(req, timeout=10) as resp: raw = resp.read() return json.loads(raw) if raw else None except urllib.error.HTTPError as e: @@ -49,6 +48,34 @@ def api(method, path, token, gitea_url, data=None): raise +def make_finding(source, id, severity, package, installed="-", fixed="-", target="-"): + return { + "source": source, + "id": id, + "severity": severity, + "package": package, + "installed": installed, + "fixed": fixed, + "target": target, + } + + +def cvss_score_to_severity(score): + try: + score = float(score) + except (TypeError, ValueError): + return "UNKNOWN" + if score >= 9.0: + return "CRITICAL" + if score >= 7.0: + return "HIGH" + if score >= 4.0: + return "MEDIUM" + if score > 0.0: + return "LOW" + return "UNKNOWN" + + def load_trivy(path): findings = [] if not path or not os.path.isfile(path): @@ -58,35 +85,31 @@ def load_trivy(path): for result in data.get("Results", []) or []: target = result.get("Target", "?") for vuln in result.get("Vulnerabilities", []) or []: - findings.append({ - "source": "Trivy", - "id": vuln.get("VulnerabilityID", "?"), - "severity": vuln.get("Severity", "UNKNOWN"), - "package": vuln.get("PkgName", "?"), - "installed": vuln.get("InstalledVersion", "?"), - "fixed": vuln.get("FixedVersion") or "-", - "target": target, - }) + findings.append(make_finding( + "Trivy", + vuln.get("VulnerabilityID", "?"), + vuln.get("Severity", "UNKNOWN"), + vuln.get("PkgName", "?"), + installed=vuln.get("InstalledVersion", "?"), + fixed=vuln.get("FixedVersion") or "-", + target=target, + )) for misc in result.get("Misconfigurations", []) or []: - findings.append({ - "source": "Trivy (Misconfig)", - "id": misc.get("ID", "?"), - "severity": misc.get("Severity", "UNKNOWN"), - "package": misc.get("Title", "?"), - "installed": "-", - "fixed": "-", - "target": target, - }) + findings.append(make_finding( + "Trivy (Misconfig)", + misc.get("ID", "?"), + misc.get("Severity", "UNKNOWN"), + misc.get("Title", "?"), + target=target, + )) for secret in result.get("Secrets", []) or []: - findings.append({ - "source": "Trivy (Secret)", - "id": secret.get("RuleID", "?"), - "severity": secret.get("Severity", "UNKNOWN"), - "package": secret.get("Title", "?"), - "installed": "-", - "fixed": "-", - "target": target, - }) + findings.append(make_finding( + "Trivy (Secret)", + secret.get("RuleID", "?"), + secret.get("Severity", "UNKNOWN"), + secret.get("Title", "?"), + target=target, + )) return findings @@ -101,16 +124,21 @@ def load_osv(path): for pkg in result.get("packages", []) or []: info = pkg.get("package", {}) pkg_name = f"{info.get('name', '?')} ({info.get('ecosystem', '?')})" + severity_by_id = {} + for group in pkg.get("groups", []) or []: + label = cvss_score_to_severity(group.get("max_severity")) + for vuln_id in group.get("ids", []) or []: + severity_by_id[vuln_id] = label for vuln in pkg.get("vulnerabilities", []) or []: - findings.append({ - "source": "OSV-Scanner", - "id": vuln.get("id", "?"), - "severity": "-", - "package": pkg_name, - "installed": info.get("version", "?"), - "fixed": "-", - "target": source, - }) + vuln_id = vuln.get("id", "?") + findings.append(make_finding( + "OSV-Scanner", + vuln_id, + severity_by_id.get(vuln_id, "UNKNOWN"), + pkg_name, + installed=info.get("version", "?"), + target=source, + )) return findings @@ -128,23 +156,24 @@ def load_trufflehog(path): except json.JSONDecodeError: continue git_meta = ((entry.get("SourceMetadata") or {}).get("Data") or {}).get("Git") or {} - findings.append({ - "source": "TruffleHog", - "id": entry.get("DetectorName", "?"), - "severity": "VERIFIED" if entry.get("Verified") else "UNVERIFIED", - "package": git_meta.get("file", "?"), - "installed": "-", - "fixed": "-", - "target": git_meta.get("commit", "-"), - }) + findings.append(make_finding( + "TruffleHog", + entry.get("DetectorName", "?"), + "VERIFIED" if entry.get("Verified") else "UNVERIFIED", + git_meta.get("file", "?"), + target=git_meta.get("commit", "-"), + )) return findings +def sort_findings(findings): + return sorted(findings, key=lambda f: (SEVERITY_ORDER.get(f["severity"], 9), f["id"])) + + def print_summary(findings): if not findings: print("Keine Funde.") return - findings = sorted(findings, key=lambda f: (SEVERITY_ORDER.get(f["severity"], 9), f["id"])) widths = { key: max(len(key), *(len(str(f[key])) for f in findings)) for key in ("source", "id", "severity", "package", "installed", "fixed", "target") @@ -167,8 +196,11 @@ TRUFFLEHOG_GUIDANCE = """### Vorgehen bei gefundenen Secrets > Der Wert des Secrets selbst wird hier bewusst nicht ausgegeben, auch nicht gekürzt – nur Detector, Datei und Commit. Fund lässt sich über "Ziel" (Commit-Hash) und "Paket" (Dateipfad) lokalisieren.""" +def escape_md_cell(value): + return str(value).replace("|", "\\|").replace("\r", " ").replace("\n", " ") + + def build_report(findings, run_url): - findings = sorted(findings, key=lambda f: (SEVERITY_ORDER.get(f["severity"], 9), f["id"])) lines = [ "Automatisch erstellt vom Security-Scan-Workflow.", f"Lauf: {run_url}" if run_url else "", @@ -177,10 +209,8 @@ def build_report(findings, run_url): "|---|---|---|---|---|---|---|", ] for f in findings: - lines.append( - f"| {f['source']} | {f['id']} | {f['severity']} | {f['package']} | " - f"{f['installed']} | {f['fixed']} | {f['target']} |" - ) + cells = (f["source"], f["id"], f["severity"], f["package"], f["installed"], f["fixed"], f["target"]) + lines.append("| " + " | ".join(escape_md_cell(c) for c in cells) + " |") if any(f["source"] == "TruffleHog" for f in findings): lines.append("") lines.append(TRUFFLEHOG_GUIDANCE) @@ -188,10 +218,15 @@ def build_report(findings, run_url): def ensure_label(token, gitea_url, repo): - labels = api("GET", f"/repos/{repo}/labels?limit=50", token, gitea_url) or [] - for label in labels: - if label.get("name") == LABEL_NAME: - return label["id"] + page = 1 + while True: + labels = api("GET", f"/repos/{repo}/labels?limit=50&page={page}", token, gitea_url) or [] + for label in labels: + if label.get("name") == LABEL_NAME: + return label["id"] + if len(labels) < 50: + break + page += 1 created = api("POST", f"/repos/{repo}/labels", token, gitea_url, { "name": LABEL_NAME, "color": LABEL_COLOR, @@ -225,7 +260,7 @@ def main(): osv_exit = int(os.environ.get("OSV_EXIT", "0")) trufflehog_exit = int(os.environ.get("TRUFFLEHOG_EXIT", "0")) - findings = load_trivy(trivy_path) + load_osv(osv_path) + load_trufflehog(trufflehog_path) + findings = sort_findings(load_trivy(trivy_path) + load_osv(osv_path) + load_trufflehog(trufflehog_path)) print_summary(findings) open_issue = find_open_issue(token, gitea_url, repo) @@ -251,8 +286,21 @@ def main(): else: print("Keine Funde und kein offenes Issue vorhanden.") + osv_ok_exits = {0, 1} + trufflehog_ok_exits = {0, 183} + if osv_exit not in osv_ok_exits: + print(f"WARNUNG: osv-scanner beendete sich mit unerwartetem Exit-Code {osv_exit} - Scan evtl. unvollständig.", file=sys.stderr) + if trufflehog_exit not in trufflehog_ok_exits: + print(f"WARNUNG: trufflehog beendete sich mit unerwartetem Exit-Code {trufflehog_exit} - Scan evtl. unvollständig.", file=sys.stderr) + has_trivy_findings = any(f["source"].startswith("Trivy") for f in findings) - if has_trivy_findings or osv_exit == 1 or trufflehog_exit == 183: + if ( + has_trivy_findings + or osv_exit == 1 + or trufflehog_exit == 183 + or osv_exit not in osv_ok_exits + or trufflehog_exit not in trufflehog_ok_exits + ): sys.exit(1) From 90c9a208712f07c972ca55a6166d2fecff38fa1f Mon Sep 17 00:00:00 2001 From: DragonSlayer_14 Date: Thu, 10 Sep 2026 00:12:07 +0200 Subject: [PATCH 08/18] Fix: Trivy-Scan bricht ab, wenn Gitea-Actions-Cache nicht erreichbar ist MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Bei Cache-Restore-Fehlschlag fällt setup-trivy auf einen Git-Checkout von aquasecurity/trivy zurück, um das Install-Skript zu holen. Dabei wird github.token als Auth-Token an github.com übergeben - unter Gitea ist das aber der lokale Gitea-Runner-Token, der von GitHub abgelehnt wird (401, danach Abbruch wegen deaktivierter Prompts). token-setup-trivy leer lassen, damit der Fallback-Checkout anonym gegen das öffentliche GitHub-Repo läuft. Co-Authored-By: Claude Sonnet 5 Claude-Session: https://claude.ai/code/session_01FKMjR9pBxshZYZdhKyT5Ky --- .gitea/workflows/security-scan.yaml | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/.gitea/workflows/security-scan.yaml b/.gitea/workflows/security-scan.yaml index 058bd9d..0cdde52 100644 --- a/.gitea/workflows/security-scan.yaml +++ b/.gitea/workflows/security-scan.yaml @@ -29,6 +29,12 @@ jobs: format: json output: trivy-results.json exit-code: "0" + # github.token ist unter Gitea Actions der Gitea-Runner-Token, nicht gültig für + # github.com. Leer lassen, damit der interne setup-trivy-Fallback (Klon von + # aquasecurity/trivy zwecks Install-Skript, falls der Binary-Cache-Restore + # fehlschlägt) anonym gegen das öffentliche GitHub-Repo läuft statt mit einem + # ungültigen Token einen 401 zu provozieren. + token-setup-trivy: "" - name: Install OSV-Scanner run: | From 9800ddcfc443a08d7ab2874601bc6a75f1623824 Mon Sep 17 00:00:00 2001 From: DragonSlayer_14 Date: Thu, 10 Sep 2026 00:13:01 +0200 Subject: [PATCH 09/18] =?UTF-8?q?Schema:=20Gitea-Workflow-Dateien=20f?= =?UTF-8?q?=C3=BCr=20Security-=20und=20TruffleHog-Scans=20in=20JSON-Schema?= =?UTF-8?q?-Zuordnungen=20erg=C3=A4nzt?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .idea/jsonSchemas.xml | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/.idea/jsonSchemas.xml b/.idea/jsonSchemas.xml index cd2a4f5..2c9f918 100644 --- a/.idea/jsonSchemas.xml +++ b/.idea/jsonSchemas.xml @@ -20,6 +20,12 @@ + + + + From 50a093afcfccde000cae81c85954f9b7499e9286 Mon Sep 17 00:00:00 2001 From: DragonSlayer_14 Date: Thu, 10 Sep 2026 00:15:27 +0200 Subject: [PATCH 10/18] Fix: Trivy manuell installieren statt setup-trivy (Gitea act_runner Bug) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Der vorherige Fix (token-setup-trivy: "") schlug fehl: der act_runner wertet einen leeren String-Input offenbar als "nicht angegeben", statt ihn als expliziten leeren Wert an actions/checkout durchzureichen - dessen eigener required-Input "token" fehlt dadurch komplett und der Schritt bricht ab ("Input required and not supplied: token"). Damit lässt sich der git-basierte Installationsweg von setup-trivy (Checkout von github.com/aquasecurity/trivy) nicht zuverlässig unter Gitea nutzen. Trivy wird jetzt wie schon TruffleHog per curl direkt von den GitHub-Releases installiert, trivy-action überspringt die eigene Installation via skip-setup-trivy und nutzt nur noch die Scan-Funktionalität. Co-Authored-By: Claude Sonnet 5 Claude-Session: https://claude.ai/code/session_01FKMjR9pBxshZYZdhKyT5Ky --- .gitea/workflows/security-scan.yaml | 24 ++++++++++++++++++------ 1 file changed, 18 insertions(+), 6 deletions(-) diff --git a/.gitea/workflows/security-scan.yaml b/.gitea/workflows/security-scan.yaml index 0cdde52..c30b2ec 100644 --- a/.gitea/workflows/security-scan.yaml +++ b/.gitea/workflows/security-scan.yaml @@ -19,9 +19,27 @@ jobs: - name: Checkout Repository uses: actions/checkout@v7 + # setup-trivy (intern von trivy-action genutzt) installiert Trivy bei Cache-Miss über + # einen Git-Checkout von github.com/aquasecurity/trivy mit github.token als Auth. + # Unter Gitea ist github.token der lokale Gitea-Runner-Token, für github.com ungültig + # -> 401. Ein leerer token-setup-trivy-Input sollte eigentlich einen anonymen Checkout + # auslösen, wird vom act_runner aber als "nicht angegeben" gewertet, wodurch + # actions/checkout am eigenen required-Input "token" scheitert. Daher Trivy hier + # manuell installieren (wie im TruffleHog-Workflow) und den setup-trivy-Schritt der + # Action per skip-setup-trivy komplett umgehen. + - name: Install Trivy + run: | + curl -fsSL -o trivy.tar.gz \ + "https://github.com/aquasecurity/trivy/releases/download/v0.70.0/trivy_0.70.0_Linux-64bit.tar.gz" + tar -xzf trivy.tar.gz trivy + chmod +x trivy + sudo mv trivy /usr/local/bin/trivy + rm trivy.tar.gz + - name: Run Trivy Scanner uses: aquasecurity/trivy-action@v0.36.0 with: + skip-setup-trivy: "true" scan-type: fs scan-ref: . scanners: vuln,secret,misconfig @@ -29,12 +47,6 @@ jobs: format: json output: trivy-results.json exit-code: "0" - # github.token ist unter Gitea Actions der Gitea-Runner-Token, nicht gültig für - # github.com. Leer lassen, damit der interne setup-trivy-Fallback (Klon von - # aquasecurity/trivy zwecks Install-Skript, falls der Binary-Cache-Restore - # fehlschlägt) anonym gegen das öffentliche GitHub-Repo läuft statt mit einem - # ungültigen Token einen 401 zu provozieren. - token-setup-trivy: "" - name: Install OSV-Scanner run: | From eac398a73e10ac8b87dbb6780f39cae5807ae1ac Mon Sep 17 00:00:00 2001 From: DragonSlayer_14 Date: Thu, 10 Sep 2026 00:20:38 +0200 Subject: [PATCH 11/18] Revert: Trivy-Workflow wieder auf trivy-action ohne Workarounds MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Manueller Install und token-setup-trivy-Override haben unter dem Gitea act_runner beide nicht wie erwartet funktioniert. Zurück auf den ursprünglichen Aufbau mit trivy-action@v0.36.0 pur. Co-Authored-By: Claude Sonnet 5 Claude-Session: https://claude.ai/code/session_01FKMjR9pBxshZYZdhKyT5Ky --- .gitea/workflows/security-scan.yaml | 18 ------------------ 1 file changed, 18 deletions(-) diff --git a/.gitea/workflows/security-scan.yaml b/.gitea/workflows/security-scan.yaml index c30b2ec..058bd9d 100644 --- a/.gitea/workflows/security-scan.yaml +++ b/.gitea/workflows/security-scan.yaml @@ -19,27 +19,9 @@ jobs: - name: Checkout Repository uses: actions/checkout@v7 - # setup-trivy (intern von trivy-action genutzt) installiert Trivy bei Cache-Miss über - # einen Git-Checkout von github.com/aquasecurity/trivy mit github.token als Auth. - # Unter Gitea ist github.token der lokale Gitea-Runner-Token, für github.com ungültig - # -> 401. Ein leerer token-setup-trivy-Input sollte eigentlich einen anonymen Checkout - # auslösen, wird vom act_runner aber als "nicht angegeben" gewertet, wodurch - # actions/checkout am eigenen required-Input "token" scheitert. Daher Trivy hier - # manuell installieren (wie im TruffleHog-Workflow) und den setup-trivy-Schritt der - # Action per skip-setup-trivy komplett umgehen. - - name: Install Trivy - run: | - curl -fsSL -o trivy.tar.gz \ - "https://github.com/aquasecurity/trivy/releases/download/v0.70.0/trivy_0.70.0_Linux-64bit.tar.gz" - tar -xzf trivy.tar.gz trivy - chmod +x trivy - sudo mv trivy /usr/local/bin/trivy - rm trivy.tar.gz - - name: Run Trivy Scanner uses: aquasecurity/trivy-action@v0.36.0 with: - skip-setup-trivy: "true" scan-type: fs scan-ref: . scanners: vuln,secret,misconfig From 186251e21abf5a153d89819c86e77f89dcc35ead Mon Sep 17 00:00:00 2001 From: DragonSlayer_14 Date: Thu, 10 Sep 2026 19:28:40 +0200 Subject: [PATCH 12/18] Fix: Trivy manuell via curl installieren statt trivy-action/setup-trivy MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit setup-trivy's Install-Step macht immer einen git clone von aquasecurity/trivy gegen github.com, unabhängig vom Cache-Status. Das scheitert im Gitea act_runner mit "could not read Username for 'https://github.com'", da der Git-Smart-HTTP-Handshake blockiert wird. Plain curl-Downloads von GitHub-Release-Assets funktionieren dagegen (siehe OSV-Scanner-Step), daher wird Trivy jetzt genauso installiert. Co-Authored-By: Claude Sonnet 5 Claude-Session: https://claude.ai/code/session_01Kh9v73QApBwJj96w6A8R55 --- .gitea/workflows/security-scan.yaml | 28 +++++++++++++++++++--------- 1 file changed, 19 insertions(+), 9 deletions(-) diff --git a/.gitea/workflows/security-scan.yaml b/.gitea/workflows/security-scan.yaml index 058bd9d..2084812 100644 --- a/.gitea/workflows/security-scan.yaml +++ b/.gitea/workflows/security-scan.yaml @@ -19,16 +19,26 @@ jobs: - name: Checkout Repository uses: actions/checkout@v7 + - name: Install Trivy + env: + TRIVY_VERSION: "0.70.0" + run: | + curl -fsSL -o trivy.tar.gz \ + "https://github.com/aquasecurity/trivy/releases/download/v${TRIVY_VERSION}/trivy_${TRIVY_VERSION}_Linux-64bit.tar.gz" + tar -xzf trivy.tar.gz trivy + chmod +x trivy + sudo mv trivy /usr/local/bin/trivy + rm -f trivy.tar.gz + - name: Run Trivy Scanner - uses: aquasecurity/trivy-action@v0.36.0 - with: - scan-type: fs - scan-ref: . - scanners: vuln,secret,misconfig - severity: CRITICAL,HIGH - format: json - output: trivy-results.json - exit-code: "0" + run: | + trivy fs \ + --scanners vuln,secret,misconfig \ + --severity CRITICAL,HIGH \ + --format json \ + --output trivy-results.json \ + --exit-code 0 \ + . - name: Install OSV-Scanner run: | From f1c261ebddc00b25caffd2622d5f7da51d4c2a40 Mon Sep 17 00:00:00 2001 From: DragonSlayer_14 Date: Thu, 10 Sep 2026 19:38:49 +0200 Subject: [PATCH 13/18] =?UTF-8?q?Fix:=20Versions-Tracking=20f=C3=BCr=20Tri?= =?UTF-8?q?vy/OSV-Scanner/TruffleHog=20&=20Renovate-Update?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - Trivy 0.70.0 -> 0.74.0, Renovate-Container 43.279.0 -> 44.79.2 (OSV-Scanner und TruffleHog waren bereits aktuell) - OSV-Scanner- und TruffleHog-Versionen in Env-Vars ausgelagert (TruffleHog nutzte die Version zuvor zweimal im String, das verhinderte einen sauberen Regex-Match) - Neuer customManagers-Regex-Block in renovate.json, da die drei Scanner-Versionen als curl-Strings in run-Blöcken stecken und vom github-actions-Manager (nur uses:/container:/services:) nicht erfasst werden Co-Authored-By: Claude Sonnet 5 Claude-Session: https://claude.ai/code/session_01Kh9v73QApBwJj96w6A8R55 --- .gitea/workflows/renovate.yaml | 2 +- .gitea/workflows/security-scan.yaml | 6 +++-- .gitea/workflows/trufflehog-scan.yaml | 4 +++- renovate.json | 32 +++++++++++++++++++++++++++ 4 files changed, 40 insertions(+), 4 deletions(-) diff --git a/.gitea/workflows/renovate.yaml b/.gitea/workflows/renovate.yaml index fee0b8e..9fd8195 100644 --- a/.gitea/workflows/renovate.yaml +++ b/.gitea/workflows/renovate.yaml @@ -9,7 +9,7 @@ jobs: renovate: name: Dependency-Updates prüfen & Pull Requests erstellen runs-on: ubuntu-latest - container: ghcr.io/renovatebot/renovate:43.279.0 + container: ghcr.io/renovatebot/renovate:44.79.2 steps: - name: Renovate ausführen run: renovate diff --git a/.gitea/workflows/security-scan.yaml b/.gitea/workflows/security-scan.yaml index 2084812..5a8adf5 100644 --- a/.gitea/workflows/security-scan.yaml +++ b/.gitea/workflows/security-scan.yaml @@ -21,7 +21,7 @@ jobs: - name: Install Trivy env: - TRIVY_VERSION: "0.70.0" + TRIVY_VERSION: "0.74.0" run: | curl -fsSL -o trivy.tar.gz \ "https://github.com/aquasecurity/trivy/releases/download/v${TRIVY_VERSION}/trivy_${TRIVY_VERSION}_Linux-64bit.tar.gz" @@ -41,9 +41,11 @@ jobs: . - name: Install OSV-Scanner + env: + OSV_SCANNER_VERSION: "2.5.1" run: | curl -fsSL -o /usr/local/bin/osv-scanner \ - "https://github.com/google/osv-scanner/releases/download/v2.5.1/osv-scanner_linux_amd64" + "https://github.com/google/osv-scanner/releases/download/v${OSV_SCANNER_VERSION}/osv-scanner_linux_amd64" chmod +x /usr/local/bin/osv-scanner - name: Run OSV-Scanner diff --git a/.gitea/workflows/trufflehog-scan.yaml b/.gitea/workflows/trufflehog-scan.yaml index d374757..d6b917e 100644 --- a/.gitea/workflows/trufflehog-scan.yaml +++ b/.gitea/workflows/trufflehog-scan.yaml @@ -18,9 +18,11 @@ jobs: fetch-depth: 0 - name: Install TruffleHog + env: + TRUFFLEHOG_VERSION: "3.97.4" run: | curl -fsSL -o trufflehog.tar.gz \ - "https://github.com/trufflesecurity/trufflehog/releases/download/v3.97.4/trufflehog_3.97.4_linux_amd64.tar.gz" + "https://github.com/trufflesecurity/trufflehog/releases/download/v${TRUFFLEHOG_VERSION}/trufflehog_${TRUFFLEHOG_VERSION}_linux_amd64.tar.gz" tar -xzf trufflehog.tar.gz trufflehog chmod +x trufflehog sudo mv trufflehog /usr/local/bin/trufflehog diff --git a/renovate.json b/renovate.json index 30693da..efd7ab7 100644 --- a/renovate.json +++ b/renovate.json @@ -12,5 +12,37 @@ "matchManagers": ["dockerfile", "docker-compose"], "groupName": "Docker-Images" } + ], + "customManagers": [ + { + "customType": "regex", + "fileMatch": ["^\\.gitea/workflows/.+\\.ya?ml$"], + "matchStrings": [ + "TRIVY_VERSION:\\s*\"(?[^\"]+)\"" + ], + "depNameTemplate": "aquasecurity/trivy", + "datasourceTemplate": "github-releases", + "extractVersionTemplate": "^v(?.*)$" + }, + { + "customType": "regex", + "fileMatch": ["^\\.gitea/workflows/.+\\.ya?ml$"], + "matchStrings": [ + "OSV_SCANNER_VERSION:\\s*\"(?[^\"]+)\"" + ], + "depNameTemplate": "google/osv-scanner", + "datasourceTemplate": "github-releases", + "extractVersionTemplate": "^v(?.*)$" + }, + { + "customType": "regex", + "fileMatch": ["^\\.gitea/workflows/.+\\.ya?ml$"], + "matchStrings": [ + "TRUFFLEHOG_VERSION:\\s*\"(?[^\"]+)\"" + ], + "depNameTemplate": "trufflesecurity/trufflehog", + "datasourceTemplate": "github-releases", + "extractVersionTemplate": "^v(?.*)$" + } ] } From a91e76b3e15e946258fcac80954a8fc2849e3bee Mon Sep 17 00:00:00 2001 From: DragonSlayer_14 Date: Thu, 10 Sep 2026 19:42:12 +0200 Subject: [PATCH 14/18] Renovate: Gruppierung auf Actions/Cargo/Docker umgestellt MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - Actions-Gruppe jetzt dateibasiert (matchFileNames statt matchManagers), da die Custom-Regex-Manager für Trivy/OSV-Scanner/ TruffleHog als Manager-Typ "regex" laufen und von matchManagers: ["github-actions"] nicht erfasst wurden - Neue eigene Gruppe für alle Cargo-Dependencies - separateMajorMinor/separateMinorPatch in allen drei Gruppen deaktiviert, sonst reisst Renovate Major-Updates trotz groupName standardmaessig in einen eigenen PR Co-Authored-By: Claude Sonnet 5 Claude-Session: https://claude.ai/code/session_01Kh9v73QApBwJj96w6A8R55 --- renovate.json | 16 +++++++++++++--- 1 file changed, 13 insertions(+), 3 deletions(-) diff --git a/renovate.json b/renovate.json index efd7ab7..dca8c37 100644 --- a/renovate.json +++ b/renovate.json @@ -5,12 +5,22 @@ "schedule": ["before 6am on monday"], "packageRules": [ { - "matchManagers": ["github-actions"], - "groupName": "Gitea Actions" + "matchFileNames": [".gitea/workflows/**"], + "groupName": "Gitea Actions", + "separateMajorMinor": false, + "separateMinorPatch": false + }, + { + "matchManagers": ["cargo"], + "groupName": "Cargo Dependencies", + "separateMajorMinor": false, + "separateMinorPatch": false }, { "matchManagers": ["dockerfile", "docker-compose"], - "groupName": "Docker-Images" + "groupName": "Docker-Images", + "separateMajorMinor": false, + "separateMinorPatch": false } ], "customManagers": [ From b3ada8828845aca2edde29a7c7b70d6bd2b709bf Mon Sep 17 00:00:00 2001 From: DragonSlayer_14 Date: Thu, 10 Sep 2026 19:51:48 +0200 Subject: [PATCH 15/18] Renovate: PRs nur noch gegen dev statt Default-Branch baseBranches auf dev eingeschraenkt, passend zum bestehenden dev -> testing -> main Promotion-Flow. Renovate liest Dependency- Dateien jetzt direkt von dev und oeffnet PRs nur dort; main/testing werden nicht separat gescannt. Die renovate.json selbst muss laut Renovate-Doku trotzdem ueber den Gitea-Default-Branch (main) gefunden werden koennen. Co-Authored-By: Claude Sonnet 5 Claude-Session: https://claude.ai/code/session_01Kh9v73QApBwJj96w6A8R55 --- renovate.json | 1 + 1 file changed, 1 insertion(+) diff --git a/renovate.json b/renovate.json index dca8c37..481e4f8 100644 --- a/renovate.json +++ b/renovate.json @@ -3,6 +3,7 @@ "extends": ["config:recommended"], "timezone": "Europe/Berlin", "schedule": ["before 6am on monday"], + "baseBranches": ["dev"], "packageRules": [ { "matchFileNames": [".gitea/workflows/**"], From 072bde05e1462ecdd0f16f3fa194409252aa8be5 Mon Sep 17 00:00:00 2001 From: DragonSlayer_14 Date: Thu, 10 Sep 2026 19:53:41 +0200 Subject: [PATCH 16/18] =?UTF-8?q?Feat:=20Zeitplan=20des=20TruffleHog-Scans?= =?UTF-8?q?=20um=20eine=20Stunde=20nach=20hinten=20verschoben=20(Cron=20"0?= =?UTF-8?q?=205=20*=20*=201"=20=E2=86=92=20"0=206=20*=20*=201").?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .gitea/workflows/trufflehog-scan.yaml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.gitea/workflows/trufflehog-scan.yaml b/.gitea/workflows/trufflehog-scan.yaml index d6b917e..d63bbb9 100644 --- a/.gitea/workflows/trufflehog-scan.yaml +++ b/.gitea/workflows/trufflehog-scan.yaml @@ -4,7 +4,7 @@ on: push: pull_request: schedule: - - cron: "0 5 * * 1" + - cron: "0 6 * * 1" workflow_dispatch: jobs: From ea1530680c97198bf2357dc5f705ca7bba248bbf Mon Sep 17 00:00:00 2001 From: DragonSlayer_14 Date: Thu, 10 Sep 2026 20:03:36 +0200 Subject: [PATCH 17/18] CI: actions/cache@v6 fuer Cargo- und Scanner-Binaries ergaenzt - main.yaml/testing.yaml: Cargo-Registry & target/ ueber Cargo.lock-Hash gecacht (groesster Zeitfresser bei 3 Cross-Compile-Targets) - security-scan.yaml: Trivy- und OSV-Scanner-Binary versionsbasiert gecacht, zusaetzlich Trivy-Vulnerability-DB tagesbasiert gecacht (sonst voller OCI-Download bei jedem Lauf) - trufflehog-scan.yaml: TruffleHog-Binary versionsbasiert gecacht Installation aller drei Scanner-Binaries von sudo-basiertem /usr/local/bin auf $HOME/.local/bin umgestellt, da actions/cache sonst ohne sudo nicht zuverlaessig in root-Verzeichnisse restoren kann. TRIVY_VERSION/OSV_SCANNER_VERSION/TRUFFLEHOG_VERSION dafuer auf Job-Ebene verschoben (Cache-Steps brauchen sie fuer den Cache-Key); die Renovate-customManagers-Regexe matchen weiterhin, da sie auf dem rohen Dateitext arbeiten statt auf der YAML-Struktur. Co-Authored-By: Claude Sonnet 5 Claude-Session: https://claude.ai/code/session_01Kh9v73QApBwJj96w6A8R55 --- .gitea/workflows/main.yaml | 11 +++++++ .gitea/workflows/security-scan.yaml | 45 ++++++++++++++++++++++----- .gitea/workflows/testing.yaml | 11 +++++++ .gitea/workflows/trufflehog-scan.yaml | 19 +++++++++-- 4 files changed, 76 insertions(+), 10 deletions(-) diff --git a/.gitea/workflows/main.yaml b/.gitea/workflows/main.yaml index c6434ec..c63f78b 100644 --- a/.gitea/workflows/main.yaml +++ b/.gitea/workflows/main.yaml @@ -19,6 +19,17 @@ jobs: toolchain: stable cache: false + - name: Cache Cargo-Abhängigkeiten & Build-Artefakte + uses: actions/cache@v6 + with: + path: | + ~/.cargo/registry + ~/.cargo/git + target + key: cargo-${{ runner.os }}-${{ hashFiles('Cargo.lock') }} + restore-keys: | + cargo-${{ runner.os }}- + - name: Install Cross-Compilation Toolchains & Dependencies run: | sudo apt-get update diff --git a/.gitea/workflows/security-scan.yaml b/.gitea/workflows/security-scan.yaml index 5a8adf5..75df4b3 100644 --- a/.gitea/workflows/security-scan.yaml +++ b/.gitea/workflows/security-scan.yaml @@ -15,21 +15,46 @@ jobs: security-scan: name: Trivy & OSV-Scanner runs-on: ubuntu-latest + env: + TRIVY_VERSION: "0.74.0" + OSV_SCANNER_VERSION: "2.5.1" steps: - name: Checkout Repository uses: actions/checkout@v7 + - name: Lokales bin-Verzeichnis zum PATH hinzufügen + run: | + mkdir -p "$HOME/.local/bin" + echo "$HOME/.local/bin" >> "$GITHUB_PATH" + + - name: Cache Trivy-Binary + id: cache-trivy + uses: actions/cache@v6 + with: + path: ~/.local/bin/trivy + key: trivy-bin-${{ runner.os }}-${{ env.TRIVY_VERSION }} + - name: Install Trivy - env: - TRIVY_VERSION: "0.74.0" + if: steps.cache-trivy.outputs.cache-hit != 'true' run: | curl -fsSL -o trivy.tar.gz \ "https://github.com/aquasecurity/trivy/releases/download/v${TRIVY_VERSION}/trivy_${TRIVY_VERSION}_Linux-64bit.tar.gz" tar -xzf trivy.tar.gz trivy chmod +x trivy - sudo mv trivy /usr/local/bin/trivy + mv trivy "$HOME/.local/bin/trivy" rm -f trivy.tar.gz + - name: Ermittle Cache-Datum für Trivy-DB + run: echo "CACHE_DATE=$(date -u +%Y-%m-%d)" >> "$GITHUB_ENV" + + - name: Cache Trivy-Schwachstellen-Datenbank + uses: actions/cache@v6 + with: + path: ~/.cache/trivy + key: trivy-db-${{ runner.os }}-${{ env.CACHE_DATE }} + restore-keys: | + trivy-db-${{ runner.os }}- + - name: Run Trivy Scanner run: | trivy fs \ @@ -40,13 +65,19 @@ jobs: --exit-code 0 \ . + - name: Cache OSV-Scanner-Binary + id: cache-osv-scanner + uses: actions/cache@v6 + with: + path: ~/.local/bin/osv-scanner + key: osv-scanner-bin-${{ runner.os }}-${{ env.OSV_SCANNER_VERSION }} + - name: Install OSV-Scanner - env: - OSV_SCANNER_VERSION: "2.5.1" + if: steps.cache-osv-scanner.outputs.cache-hit != 'true' run: | - curl -fsSL -o /usr/local/bin/osv-scanner \ + curl -fsSL -o "$HOME/.local/bin/osv-scanner" \ "https://github.com/google/osv-scanner/releases/download/v${OSV_SCANNER_VERSION}/osv-scanner_linux_amd64" - chmod +x /usr/local/bin/osv-scanner + chmod +x "$HOME/.local/bin/osv-scanner" - name: Run OSV-Scanner run: | diff --git a/.gitea/workflows/testing.yaml b/.gitea/workflows/testing.yaml index 47047aa..a818c08 100644 --- a/.gitea/workflows/testing.yaml +++ b/.gitea/workflows/testing.yaml @@ -19,6 +19,17 @@ jobs: toolchain: stable cache: false + - name: Cache Cargo-Abhängigkeiten & Build-Artefakte + uses: actions/cache@v6 + with: + path: | + ~/.cargo/registry + ~/.cargo/git + target + key: cargo-${{ runner.os }}-${{ hashFiles('Cargo.lock') }} + restore-keys: | + cargo-${{ runner.os }}- + - name: Install Cross-Compilation Toolchains & Dependencies run: | sudo apt-get update diff --git a/.gitea/workflows/trufflehog-scan.yaml b/.gitea/workflows/trufflehog-scan.yaml index d63bbb9..5e4fcb6 100644 --- a/.gitea/workflows/trufflehog-scan.yaml +++ b/.gitea/workflows/trufflehog-scan.yaml @@ -11,21 +11,34 @@ jobs: trufflehog-scan: name: TruffleHog runs-on: ubuntu-latest + env: + TRUFFLEHOG_VERSION: "3.97.4" steps: - name: Checkout Repository uses: actions/checkout@v7 with: fetch-depth: 0 + - name: Lokales bin-Verzeichnis zum PATH hinzufügen + run: | + mkdir -p "$HOME/.local/bin" + echo "$HOME/.local/bin" >> "$GITHUB_PATH" + + - name: Cache TruffleHog-Binary + id: cache-trufflehog + uses: actions/cache@v6 + with: + path: ~/.local/bin/trufflehog + key: trufflehog-bin-${{ runner.os }}-${{ env.TRUFFLEHOG_VERSION }} + - name: Install TruffleHog - env: - TRUFFLEHOG_VERSION: "3.97.4" + if: steps.cache-trufflehog.outputs.cache-hit != 'true' run: | curl -fsSL -o trufflehog.tar.gz \ "https://github.com/trufflesecurity/trufflehog/releases/download/v${TRUFFLEHOG_VERSION}/trufflehog_${TRUFFLEHOG_VERSION}_linux_amd64.tar.gz" tar -xzf trufflehog.tar.gz trufflehog chmod +x trufflehog - sudo mv trufflehog /usr/local/bin/trufflehog + mv trufflehog "$HOME/.local/bin/trufflehog" rm trufflehog.tar.gz - name: Run TruffleHog Scanner From 1b9a2902600c266149a6855f418d52004629cbae Mon Sep 17 00:00:00 2001 From: DragonSlayer_14 Date: Thu, 10 Sep 2026 20:07:40 +0200 Subject: [PATCH 18/18] Feat: Hebt Version auf 1.0.3 an --- Cargo.lock | 2 +- Cargo.toml | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index adfe01e..8e147db 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -866,7 +866,7 @@ dependencies = [ [[package]] name = "mirror-package" -version = "1.0.2" +version = "1.0.3" dependencies = [ "anyhow", "clap", diff --git a/Cargo.toml b/Cargo.toml index f4f6639..6c751d6 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "mirror-package" -version = "1.0.2" +version = "1.0.3" edition = "2024" authors = ['DragonSlayer_14'] readme = "README.md"