From f7957ab9f59969a4d8b473b15e103f37bc6dc27f Mon Sep 17 00:00:00 2001 From: DragonSlayer_14 Date: Wed, 9 Sep 2026 20:42:46 +0200 Subject: [PATCH 01/24] 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/24] =?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/24] =?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/24] =?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/24] =?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/24] =?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/24] =?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/24] 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/24] =?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/24] 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/24] 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/24] 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/24] =?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/24] 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/24] 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/24] =?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/24] 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/24] 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" From 9855f91adf9b4723d8408553abc1a30e1c324afa Mon Sep 17 00:00:00 2001 From: DragonSlayer_14 Date: Thu, 10 Sep 2026 20:14:35 +0200 Subject: [PATCH 19/24] =?UTF-8?q?Feat:=20Badges=20f=C3=BCr=20Main-=20und?= =?UTF-8?q?=20Testing-Workflows=20zum=20README=20hinzugef=C3=BCgt?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- README.md | 3 +++ 1 file changed, 3 insertions(+) diff --git a/README.md b/README.md index 6948abd..5601ca6 100644 --- a/README.md +++ b/README.md @@ -1,3 +1,6 @@ +[![Main Release & Publish](https://gitea.creative-dragonslayer.de/Linuxapps/MirrorPackage/actions/workflows/main.yaml/badge.svg?branch=main)](https://gitea.creative-dragonslayer.de/Linuxapps/MirrorPackage/actions?workflow=main.yaml) +[![Testing Build, Publish & Preview Release](https://gitea.creative-dragonslayer.de/Linuxapps/MirrorPackage/actions/workflows/testing.yaml/badge.svg?branch=testing)](https://gitea.creative-dragonslayer.de/Linuxapps/MirrorPackage/actions?workflow=testing.yaml) + # mirror-package Automatisiertes Werkzeug zum Extrahieren, Herunterladen und Spiegeln vorkompilierter Linux-Pakete aus GitHub-Releases in eine selbstgehostete Gitea- / Forgejo-Paket-Registry. From 906aa81c6c4e55e37c0c692c212d8c908cadaf2d Mon Sep 17 00:00:00 2001 From: DragonSlayer_14 Date: Thu, 10 Sep 2026 20:25:50 +0200 Subject: [PATCH 20/24] CI: Packaging-Tools & Rustup-Cross-Targets gepinnt und gecacht - cargo-binstall/cargo-deb/cargo-generate-rpm auf feste Versionen gepinnt (vorher "latest", nicht reproduzierbar) und ~/.cargo/bin versionsbasiert gecacht - rustup target add fuer aarch64/i686 in eigenen Schritt ausgelagert und ~/.rustup/.../lib/rustlib/ gecacht, Cache-Key basiert auf der aufgeloesten rustc-Version (nicht auf dem gleitenden "stable"-Label), damit bei einem Rust-Update kein veralteter Cache mit neuerem Compiler gemischt wird - x86_64-unknown-linux-gnu aus rustup target add entfernt (Host- Target, war bereits Teil der stable-Toolchain, No-Op) - drei neue customManagers-Eintraege in renovate.json fuer cargo-binstall (github-releases) sowie cargo-deb/cargo-generate-rpm (crates.io), damit Renovate die gepinnten Versionen aktuell haelt Co-Authored-By: Claude Sonnet 5 Claude-Session: https://claude.ai/code/session_01Kh9v73QApBwJj96w6A8R55 --- .gitea/workflows/main.yaml | 42 ++++++++++++++++++++++++++++++----- .gitea/workflows/testing.yaml | 42 ++++++++++++++++++++++++++++++----- renovate.json | 28 +++++++++++++++++++++++ 3 files changed, 100 insertions(+), 12 deletions(-) diff --git a/.gitea/workflows/main.yaml b/.gitea/workflows/main.yaml index c63f78b..056e2c8 100644 --- a/.gitea/workflows/main.yaml +++ b/.gitea/workflows/main.yaml @@ -9,6 +9,10 @@ jobs: release-and-publish: name: Build, Publish Packages (Stable) & Create Release runs-on: ubuntu-latest + env: + CARGO_BINSTALL_VERSION: "1.23.0" + CARGO_DEB_VERSION: "3.8.0" + CARGO_GENERATE_RPM_VERSION: "0.21.0" steps: - name: Checkout Repository uses: actions/checkout@v7 @@ -30,18 +34,44 @@ jobs: restore-keys: | cargo-${{ runner.os }}- - - name: Install Cross-Compilation Toolchains & Dependencies + - name: Install Cross-Compilation Toolchains (apt) run: | sudo apt-get update sudo apt-get install -y gcc-aarch64-linux-gnu gcc-i686-linux-gnu - rustup target add x86_64-unknown-linux-gnu aarch64-unknown-linux-gnu i686-unknown-linux-gnu - - name: Install Packaging Tools (Prebuilt Binaries) + - name: Ermittle Rust-Version für Rustup-Target-Cache-Key + run: echo "RUST_VERSION=$(rustc --version | awk '{print $2}')" >> "$GITHUB_ENV" + + - name: Cache Rustup Cross-Compilation-Targets + id: cache-rustup-targets + uses: actions/cache@v6 + with: + path: | + ~/.rustup/toolchains/stable-x86_64-unknown-linux-gnu/lib/rustlib/aarch64-unknown-linux-gnu + ~/.rustup/toolchains/stable-x86_64-unknown-linux-gnu/lib/rustlib/i686-unknown-linux-gnu + key: rustup-targets-${{ runner.os }}-${{ env.RUST_VERSION }} + + - name: Add Rust Cross-Compilation Targets + if: steps.cache-rustup-targets.outputs.cache-hit != 'true' + run: rustup target add aarch64-unknown-linux-gnu i686-unknown-linux-gnu + + - name: PATH um Cargo-bin-Verzeichnis ergänzen run: | mkdir -p ~/.cargo/bin - curl -fsSL https://github.com/cargo-bins/cargo-binstall/releases/latest/download/cargo-binstall-x86_64-unknown-linux-musl.tgz | tar -xz -C ~/.cargo/bin - echo "$HOME/.cargo/bin" >> $GITHUB_PATH - ~/.cargo/bin/cargo-binstall -y --no-symlinks cargo-deb cargo-generate-rpm + echo "$HOME/.cargo/bin" >> "$GITHUB_PATH" + + - name: Cache Packaging-Tools (cargo-binstall, cargo-deb, cargo-generate-rpm) + id: cache-packaging-tools + uses: actions/cache@v6 + with: + path: ~/.cargo/bin + key: packaging-tools-${{ runner.os }}-${{ env.CARGO_BINSTALL_VERSION }}-${{ env.CARGO_DEB_VERSION }}-${{ env.CARGO_GENERATE_RPM_VERSION }} + + - name: Install Packaging Tools (Prebuilt Binaries) + if: steps.cache-packaging-tools.outputs.cache-hit != 'true' + run: | + curl -fsSL "https://github.com/cargo-bins/cargo-binstall/releases/download/v${CARGO_BINSTALL_VERSION}/cargo-binstall-x86_64-unknown-linux-musl.tgz" | tar -xz -C ~/.cargo/bin + ~/.cargo/bin/cargo-binstall -y --no-symlinks "cargo-deb@${CARGO_DEB_VERSION}" "cargo-generate-rpm@${CARGO_GENERATE_RPM_VERSION}" - name: Run Tests run: | diff --git a/.gitea/workflows/testing.yaml b/.gitea/workflows/testing.yaml index a818c08..913b2b9 100644 --- a/.gitea/workflows/testing.yaml +++ b/.gitea/workflows/testing.yaml @@ -9,6 +9,10 @@ jobs: build-and-publish: name: Build, Publish Packages (Testing) & Create Preview Release runs-on: ubuntu-latest + env: + CARGO_BINSTALL_VERSION: "1.23.0" + CARGO_DEB_VERSION: "3.8.0" + CARGO_GENERATE_RPM_VERSION: "0.21.0" steps: - name: Checkout Repository uses: actions/checkout@v7 @@ -30,18 +34,44 @@ jobs: restore-keys: | cargo-${{ runner.os }}- - - name: Install Cross-Compilation Toolchains & Dependencies + - name: Install Cross-Compilation Toolchains (apt) run: | sudo apt-get update sudo apt-get install -y gcc-aarch64-linux-gnu gcc-i686-linux-gnu - rustup target add x86_64-unknown-linux-gnu aarch64-unknown-linux-gnu i686-unknown-linux-gnu - - name: Install Packaging Tools (Prebuilt Binaries) + - name: Ermittle Rust-Version für Rustup-Target-Cache-Key + run: echo "RUST_VERSION=$(rustc --version | awk '{print $2}')" >> "$GITHUB_ENV" + + - name: Cache Rustup Cross-Compilation-Targets + id: cache-rustup-targets + uses: actions/cache@v6 + with: + path: | + ~/.rustup/toolchains/stable-x86_64-unknown-linux-gnu/lib/rustlib/aarch64-unknown-linux-gnu + ~/.rustup/toolchains/stable-x86_64-unknown-linux-gnu/lib/rustlib/i686-unknown-linux-gnu + key: rustup-targets-${{ runner.os }}-${{ env.RUST_VERSION }} + + - name: Add Rust Cross-Compilation Targets + if: steps.cache-rustup-targets.outputs.cache-hit != 'true' + run: rustup target add aarch64-unknown-linux-gnu i686-unknown-linux-gnu + + - name: PATH um Cargo-bin-Verzeichnis ergänzen run: | mkdir -p ~/.cargo/bin - curl -fsSL https://github.com/cargo-bins/cargo-binstall/releases/latest/download/cargo-binstall-x86_64-unknown-linux-musl.tgz | tar -xz -C ~/.cargo/bin - echo "$HOME/.cargo/bin" >> $GITHUB_PATH - ~/.cargo/bin/cargo-binstall -y --no-symlinks cargo-deb cargo-generate-rpm + echo "$HOME/.cargo/bin" >> "$GITHUB_PATH" + + - name: Cache Packaging-Tools (cargo-binstall, cargo-deb, cargo-generate-rpm) + id: cache-packaging-tools + uses: actions/cache@v6 + with: + path: ~/.cargo/bin + key: packaging-tools-${{ runner.os }}-${{ env.CARGO_BINSTALL_VERSION }}-${{ env.CARGO_DEB_VERSION }}-${{ env.CARGO_GENERATE_RPM_VERSION }} + + - name: Install Packaging Tools (Prebuilt Binaries) + if: steps.cache-packaging-tools.outputs.cache-hit != 'true' + run: | + curl -fsSL "https://github.com/cargo-bins/cargo-binstall/releases/download/v${CARGO_BINSTALL_VERSION}/cargo-binstall-x86_64-unknown-linux-musl.tgz" | tar -xz -C ~/.cargo/bin + ~/.cargo/bin/cargo-binstall -y --no-symlinks "cargo-deb@${CARGO_DEB_VERSION}" "cargo-generate-rpm@${CARGO_GENERATE_RPM_VERSION}" - name: Run Tests run: | diff --git a/renovate.json b/renovate.json index 481e4f8..f6417c7 100644 --- a/renovate.json +++ b/renovate.json @@ -54,6 +54,34 @@ "depNameTemplate": "trufflesecurity/trufflehog", "datasourceTemplate": "github-releases", "extractVersionTemplate": "^v(?.*)$" + }, + { + "customType": "regex", + "fileMatch": ["^\\.gitea/workflows/.+\\.ya?ml$"], + "matchStrings": [ + "CARGO_BINSTALL_VERSION:\\s*\"(?[^\"]+)\"" + ], + "depNameTemplate": "cargo-bins/cargo-binstall", + "datasourceTemplate": "github-releases", + "extractVersionTemplate": "^v(?.*)$" + }, + { + "customType": "regex", + "fileMatch": ["^\\.gitea/workflows/.+\\.ya?ml$"], + "matchStrings": [ + "CARGO_DEB_VERSION:\\s*\"(?[^\"]+)\"" + ], + "depNameTemplate": "cargo-deb", + "datasourceTemplate": "crate" + }, + { + "customType": "regex", + "fileMatch": ["^\\.gitea/workflows/.+\\.ya?ml$"], + "matchStrings": [ + "CARGO_GENERATE_RPM_VERSION:\\s*\"(?[^\"]+)\"" + ], + "depNameTemplate": "cargo-generate-rpm", + "datasourceTemplate": "crate" } ] } From e8820fde59a84a0db404c2b3d9577a97fdcf0bbe Mon Sep 17 00:00:00 2001 From: DragonSlayer_14 Date: Thu, 10 Sep 2026 20:41:10 +0200 Subject: [PATCH 21/24] CI: Unit-Tests bei PRs nach testing hinzugefuegt Neuer Workflow, der bei jedem PR mit Ziel-Branch testing cargo test ausfuehrt. Nutzt denselben Cargo-Cache-Key wie main.yaml/testing.yaml, damit sich der Cache zwischen den Workflows teilen laesst. Co-Authored-By: Claude Sonnet 5 Claude-Session: https://claude.ai/code/session_01Kh9v73QApBwJj96w6A8R55 --- .gitea/workflows/unit-tests.yaml | 34 ++++++++++++++++++++++++++++++++ 1 file changed, 34 insertions(+) create mode 100644 .gitea/workflows/unit-tests.yaml diff --git a/.gitea/workflows/unit-tests.yaml b/.gitea/workflows/unit-tests.yaml new file mode 100644 index 0000000..0a1209e --- /dev/null +++ b/.gitea/workflows/unit-tests.yaml @@ -0,0 +1,34 @@ +name: Unit-Tests + +on: + pull_request: + branches: + - testing + +jobs: + test: + name: Unit-Tests + runs-on: ubuntu-latest + steps: + - name: Checkout Repository + uses: actions/checkout@v7 + + - name: Install Rust Toolchain + uses: actions-rust-lang/setup-rust-toolchain@v1 + with: + 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: Run Tests + run: cargo test From b4d2e387da8a8acff04298323b169e1d2a292e83 Mon Sep 17 00:00:00 2001 From: DragonSlayer_14 Date: Thu, 10 Sep 2026 20:50:48 +0200 Subject: [PATCH 22/24] CI: pull_request-Types fuer Unit-Tests explizit gesetzt types: opened/synchronize/reopened explizit eingetragen, damit der Workflow sicher auch bei neuen Commits auf einem offenen PR (synchronize) erneut laeuft, statt sich auf ein nicht dokumentiertes Gitea-Actions-Default zu verlassen. Co-Authored-By: Claude Sonnet 5 Claude-Session: https://claude.ai/code/session_01Kh9v73QApBwJj96w6A8R55 --- .gitea/workflows/unit-tests.yaml | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/.gitea/workflows/unit-tests.yaml b/.gitea/workflows/unit-tests.yaml index 0a1209e..047eb94 100644 --- a/.gitea/workflows/unit-tests.yaml +++ b/.gitea/workflows/unit-tests.yaml @@ -2,6 +2,10 @@ name: Unit-Tests on: pull_request: + types: + - opened + - synchronize + - reopened branches: - testing From 2a16a866ed28157f99bad08aa1ae15b93a4baee9 Mon Sep 17 00:00:00 2001 From: DragonSlayer_14 Date: Thu, 10 Sep 2026 22:18:58 +0200 Subject: [PATCH 23/24] Fix: Build-Nummer-Ermittlung uebersah bereits veroeffentlichte RPM/Deb/Arch-Versionen Die bisherige Abfrage GET /api/v1/packages/{owner}?limit=50 listete alle Pakete des Owners ungefiltert (inkl. Docker-Image-Tags aus main.yaml/testing.yaml), ohne Pagination. Dadurch konnte die tatsaechlich relevante Paket-Version verdraengt werden, das Skript hielt eine bereits genutzte Build-Nummer faelschlich fuer frei, und der anschliessende Registry-Upload schlug mit 409 (Datei existiert bereits) fehl - beobachtet beim RPM-Upload. Fragt jetzt stattdessen gezielt pro Paket-Typ (debian/rpm/arch) die Latest-Version ab (GET .../{type}/{name}/-/latest, ein Request ohne Pagination). Alle drei Ergebnisse fliessen weiterhin in dasselbe Set ein, sodass die naechste Build-Nummer weiterhin als globales Maximum ueber alle Typen hinweg berechnet wird - deb/rpm/arch bleiben also mit einer gemeinsamen Nummer synchron, auch wenn ein frueherer Lauf nur bei einem der Typen erfolgreich hochgeladen hat. Co-Authored-By: Claude Sonnet 5 Claude-Session: https://claude.ai/code/session_01Kh9v73QApBwJj96w6A8R55 --- scripts/get-build-number.py | 34 ++++++++++++++++++++++++---------- 1 file changed, 24 insertions(+), 10 deletions(-) diff --git a/scripts/get-build-number.py b/scripts/get-build-number.py index e726e2f..4896194 100755 --- a/scripts/get-build-number.py +++ b/scripts/get-build-number.py @@ -9,9 +9,14 @@ import os import re import subprocess import sys +import urllib.error import urllib.parse import urllib.request +# Paket-Typen, unter denen dasselbe Release veroeffentlicht wird und die sich +# daher eine gemeinsame Build-Nummer teilen muessen. +PACKAGE_TYPES = ("debian", "rpm", "arch") + def get_cargo_pkg_info(): try: @@ -83,20 +88,29 @@ def query_existing_build_numbers(name, version, gitea_url, repo, owner, token=No except Exception as e: sys.stderr.write(f"[Hinweis] Konnte Gitea Releases nicht abfragen: {e}\n") - # 2. Gitea Packages API (falls Token vorhanden) - if gitea_url and owner and token: - try: - url = f"{gitea_url.rstrip('/')}/api/v1/packages/{owner}?limit=50" - req = urllib.request.Request(url, headers=headers) - with urllib.request.urlopen(req, timeout=10) as resp: - packages = json.loads(resp.read().decode()) - for pkg in packages: + # 2. Gitea Packages API (falls Token vorhanden): pro Paket-Typ gezielt nur die + # neueste Version abfragen (ein Request, keine Pagination noetig). Das haelt + # die Abfrage schnell unabhaengig von der Historie und ist unbeeinflusst von + # Docker-Tags/anderen Paket-Typen, die unter demselben Owner haengen. + if gitea_url and owner and token and name: + for pkg_type in PACKAGE_TYPES: + try: + url = ( + f"{gitea_url.rstrip('/')}/api/v1/packages/{owner}/" + f"{pkg_type}/{urllib.parse.quote(name, safe='')}/-/latest" + ) + req = urllib.request.Request(url, headers=headers) + with urllib.request.urlopen(req, timeout=10) as resp: + pkg = json.loads(resp.read().decode()) pkg_ver = pkg.get("version", "") m = pattern.search(pkg_ver) if m: build_nums.add(int(m.group(1))) - except Exception as e: - sys.stderr.write(f"[Hinweis] Konnte Gitea Packages nicht abfragen: {e}\n") + except urllib.error.HTTPError as e: + if e.code != 404: + sys.stderr.write(f"[Hinweis] Konnte neueste {pkg_type}-Paketversion nicht abfragen: {e}\n") + except Exception as e: + sys.stderr.write(f"[Hinweis] Konnte neueste {pkg_type}-Paketversion nicht abfragen: {e}\n") # 3. Lokales Target-Verzeichnis prüfen target_dirs = ["target/debian", "target/generate-rpm", "target/arch"] From 4d110b7c521aacebbb30b5b313ebe3cfb09edba9 Mon Sep 17 00:00:00 2001 From: DragonSlayer_14 Date: Thu, 10 Sep 2026 22:32:57 +0200 Subject: [PATCH 24/24] Fix: Alte Paketierungs-Artefakte aus dem Cargo-Cache bereinigen Der target-Cache (aus dem vorherigen Caching-Fix) haelt zwischen Laeufen auch target/debian, target/generate-rpm und target/arch am Leben, obwohl diese nie geleert wurden. Bei einem Cache-Treffer wurden dadurch alte .deb/.rpm/.pkg.tar.zst-Dateien aus frueheren Builds wiederhergestellt und beim Registry-Publish erneut mit hochgeladen - Gitea lehnte die bereits existierende Datei mit 409 ab und riss den kompletten Schritt ab, bevor die eigentlich neue Datei drankam (beobachtet beim RPM-Upload trotz korrekt berechneter neuer Build-Nummer). Die drei Paketierungs-Ausgabeordner werden jetzt direkt nach dem Cache-Restore geleert, der eigentliche Kompilierungs-Cache (target//release/...) bleibt davon unberuehrt. Co-Authored-By: Claude Sonnet 5 Claude-Session: https://claude.ai/code/session_01Kh9v73QApBwJj96w6A8R55 --- .gitea/workflows/main.yaml | 3 +++ .gitea/workflows/testing.yaml | 3 +++ 2 files changed, 6 insertions(+) diff --git a/.gitea/workflows/main.yaml b/.gitea/workflows/main.yaml index 056e2c8..d8bc9fe 100644 --- a/.gitea/workflows/main.yaml +++ b/.gitea/workflows/main.yaml @@ -34,6 +34,9 @@ jobs: restore-keys: | cargo-${{ runner.os }}- + - name: Alte Paketierungs-Ausgaben aus dem Cache entfernen + run: rm -rf target/debian target/generate-rpm target/arch + - name: Install Cross-Compilation Toolchains (apt) run: | sudo apt-get update diff --git a/.gitea/workflows/testing.yaml b/.gitea/workflows/testing.yaml index 913b2b9..c4e1371 100644 --- a/.gitea/workflows/testing.yaml +++ b/.gitea/workflows/testing.yaml @@ -34,6 +34,9 @@ jobs: restore-keys: | cargo-${{ runner.os }}- + - name: Alte Paketierungs-Ausgaben aus dem Cache entfernen + run: rm -rf target/debian target/generate-rpm target/arch + - name: Install Cross-Compilation Toolchains (apt) run: | sudo apt-get update