Von blindem Vertrauen zu geprüftem Code
KI-Assistenten wie Claude beschleunigen die Entwicklung, übernehmen aber auch unsichere Muster aus Trainingsdaten, etwa fehlende Input-Validierung, veraltete Krypto-Funktionen oder permissive Standardrechte. Mit gezieltem Security-Prompting und einem Review-Prozess, der KI-Code wie jeden menschlichen Beitrag behandelt, lassen sich diese Risiken systematisch reduzieren.
Inhaltsverzeichnis
- 1. Warum KI-generierter Code eigene Risiken mitbringt
- 2. Fehlende Input-Validierung und Sanitization
- 3. Veraltete Kryptografie-Vorschläge erkennen
- 4. Unsichere Defaults aus Trainingsdaten
- 5. Security-bewusstes Prompting in der Praxis
- 6. Magento- und Hyvä-spezifische Fallstricke
- 7. KI-Code im Review-Prozess wie jeden anderen Beitrag behandeln
- 8. Statische Analyse und automatisierte Sicherheitsprüfung
- 9. Unsichere und sichere Prompts im direkten Vergleich
- 10. Zusammenfassung
- 11. FAQ
1. Warum KI-generierter Code eigene Risiken mitbringt
Große Sprachmodelle wie Claude generieren Code auf Basis von Mustern, die sie in riesigen Mengen an Trainingsdaten gesehen haben. Ein erheblicher Teil dieser Daten stammt aus öffentlichen Repositories, Tutorials und Stack-Overflow-Antworten, die oft aus didaktischen Gründen vereinfacht wurden oder schlicht veraltet sind. Ein Tutorial-Snippet, das eine Datenbankabfrage ohne Prepared Statement zeigt, weil es um das Konzept einer Schleife geht, wird vom Modell nicht automatisch als schlechtes Vorbild erkannt, wenn der Kontext das nicht explizit einfordert.
Das Ergebnis ist kein böswilliges Verhalten der KI, sondern eine statistische Wahrscheinlichkeit: Wenn im Trainingskorpus viele Beispiele für ein bestimmtes Muster ohne Sicherheitsvorkehrung existieren, ist die Wahrscheinlichkeit hoch, dass das Modell dieses Muster reproduziert, sofern der Prompt keine Gegenanweisung enthält. Entwickler, die KI-generierten Code unreflektiert übernehmen, verlagern damit implizit Sicherheitsentscheidungen an ein System, das keine Verantwortung für die Konsequenzen trägt und kein Bewusstsein für den spezifischen Bedrohungskontext der eigenen Anwendung hat. Die folgenden Abschnitte zeigen konkrete Schwachstellenmuster und wie man ihnen mit Prompting, Review und Tooling begegnet.
2. Fehlende Input-Validierung und Sanitization
Der häufigste Schwachstellentyp in schnell generiertem Code betrifft die Behandlung von Nutzereingaben. Ein KI-Assistent, der nach einer Funktion zum Verarbeiten von Formulardaten gefragt wird, liefert oft eine funktionale Lösung, die Werte direkt aus $_POST oder einem Request-Objekt entgegennimmt und ohne Validierung weiterreicht. Das Modell optimiert primär auf die angefragte Funktionalität, nicht auf implizite Sicherheitsanforderungen, die im Prompt nicht erwähnt wurden. In einer Magento-Umgebung bedeutet das konkret: fehlende Escaping-Aufrufe in phtml-Templates, ungeprüfte Parameter in Controller-Actions oder String-Konkatenation in SQL-Statements, die eigentlich über das Query-Builder-Interface laufen sollten.
Besonders tückisch ist, dass generierter Code oft syntaktisch einwandfrei aussieht und auf den ersten Blick funktioniert, weil Testdaten in der Entwicklung selten bösartig sind. Die Lücke zeigt sich erst, wenn ein Angreifer gezielt Sonderzeichen, überlange Strings oder verschachtelte Payloads einschleust. Deshalb sollte jede Funktion, die externe Eingaben verarbeitet, explizit auf Validierungs- und Escaping-Logik geprüft werden, unabhängig davon, ob der Code von einem Menschen oder einer KI stammt. Wer Claude gezielt nach Escaping-Strategien fragt, etwa escapeHtml(), escapeUrl() oder escapeJs() im Hyvä-Kontext, bekommt in der Regel korrekten Code, weil das Modell dann explizit auf diese Anforderung reagiert.
<?php
declare(strict_types=1);
namespace Mironsoft\Contact\Controller\Index;
use Magento\Framework\App\Action\HttpPostActionInterface;
use Magento\Framework\App\RequestInterface;
use Magento\Framework\Controller\Result\JsonFactory;
use Magento\Framework\Controller\ResultInterface;
use Magento\Framework\Escaper;
/**
* Handles contact form submissions with explicit input validation.
*/
final class Submit implements HttpPostActionInterface
{
/**
* @param RequestInterface $request HTTP request object
* @param JsonFactory $resultJsonFactory Factory for JSON responses
* @param Escaper $escaper Magento escaper for output encoding
*/
public function __construct(
private readonly RequestInterface $request,
private readonly JsonFactory $resultJsonFactory,
private readonly Escaper $escaper
) {
}
/**
* Executes the controller action.
*
* @return ResultInterface
*/
public function execute(): ResultInterface
{
$result = $this->resultJsonFactory->create();
// WRONG pattern an unguided AI assistant frequently produces:
// $email = $this->request->getParam('email');
// $message = $this->request->getParam('message');
// $this->mailSend($email, $message); // no validation at all
// RIGHT: explicit validation before any further processing
$email = trim((string) $this->request->getParam('email', ''));
$message = trim((string) $this->request->getParam('message', ''));
if (!filter_var($email, FILTER_VALIDATE_EMAIL)) {
return $result->setData(['success' => false, 'error' => 'Invalid email address']);
}
if ($message === '' || mb_strlen($message) > 5000) {
return $result->setData(['success' => false, 'error' => 'Invalid message length']);
}
// Output encoding applied explicitly before echoing back
$safeMessage = $this->escaper->escapeHtml($message);
return $result->setData(['success' => true, 'preview' => $safeMessage]);
}
}
3. Veraltete Kryptografie-Vorschläge erkennen
Trainingsdaten von Sprachmodellen enthalten Code aus vielen Jahren und Projekten unterschiedlicher Qualität. Für kryptografische Operationen bedeutet das ein reales Risiko: Ein Modell kann md5() oder sha1() zum Hashen von Passwörtern vorschlagen, weil diese Funktionen in unzähligen älteren Tutorials und Legacy-Projekten so verwendet werden, obwohl beide Algorithmen für Passwort-Hashing seit Jahren als ungeeignet gelten. Ähnlich verhält es sich mit selbstgebauten Verschlüsselungsroutinen, statischen Initialisierungsvektoren oder hartkodierten Schlüsseln in Beispielcode, der eigentlich nur zur Veranschaulichung eines Konzepts gedacht war.
Die Lösung liegt nicht darin, KI-generierten Kryptografie-Code grundsätzlich zu misstrauen, sondern darin, bei sicherheitskritischen Operationen explizit nach aktuellen, geprüften Standardfunktionen zu fragen. PHP bietet mit password_hash() und password_verify() bereits sichere, adaptive Hashing-Funktionen, die Salt-Erzeugung und Cost-Faktor automatisch handhaben. Für symmetrische Verschlüsselung ist sodium_crypto_secretbox() aus der libsodium-Erweiterung der aktuelle Standard, nicht die veraltete mcrypt-Extension, die seit PHP 7.2 entfernt ist, aber in älteren Trainingsbeispielen noch auftaucht. Ein Prompt wie „nutze password_hash mit PASSWORD_ARGON2ID, keine eigene Hash-Implementierung” eliminiert dieses Risiko fast vollständig.
<?php
declare(strict_types=1);
namespace Mironsoft\Customer\Model;
/**
* Demonstrates the difference between an outdated crypto suggestion
* and the currently recommended approach for password hashing.
*/
final class PasswordHasher
{
// WRONG: an AI assistant without explicit security context may suggest this,
// because md5() appears frequently in older training examples
public function hashInsecure(string $password): string
{
return md5($password); // broken: fast, unsalted, reversible via rainbow tables
}
/**
* Hashes a password using PHP's built-in adaptive hashing function.
*
* @param string $password Plaintext password to hash
* @return string Argon2id hash including salt and cost parameters
*/
public function hashSecure(string $password): string
{
// RIGHT: explicit prompt "use password_hash with PASSWORD_ARGON2ID"
// produces this instead of a hand-rolled or outdated algorithm
return password_hash($password, PASSWORD_ARGON2ID, [
'memory_cost' => 65536,
'time_cost' => 4,
'threads' => 2,
]);
}
/**
* Verifies a plaintext password against a stored Argon2id hash.
*
* @param string $password Plaintext password to verify
* @param string $hash Stored hash to verify against
* @return bool True if the password matches the hash
*/
public function verify(string $password, string $hash): bool
{
return password_verify($password, $hash);
}
}
4. Unsichere Defaults aus Trainingsdaten
Ein weiteres wiederkehrendes Muster sind übermäßig permissive Standardeinstellungen. Wird ein KI-Assistent gebeten, eine CORS-Konfiguration, eine Dateiupload-Funktion oder eine API-Route zu erstellen, tendiert die Antwort ohne explizite Einschränkung oft zur maximal offenen Variante, weil diese in Beispielcode am häufigsten vorkommt und am wenigsten Rückfragen erzeugt. Access-Control-Allow-Origin: *, Upload-Verzeichnisse ohne Dateityp-Whitelist oder Admin-Endpunkte ohne explizite Rollenprüfung sind typische Symptome. Das Modell liefert damit oft den Code, der am schnellsten „funktioniert”, nicht den Code, der für den Produktivbetrieb geeignet ist.
Dieses Muster ist besonders relevant für Magento-Erweiterungen, weil ACL-Konfiguration und Berechtigungsprüfung in acl.xml und isAllowed()-Aufrufen leicht vergessen werden, wenn sie nicht explizit im Prompt gefordert werden. Ein Backend-Controller ohne ADMIN_RESOURCE-Konstante oder ohne korrekte Vererbung der ACL-Ressourcen ist für einen Menschen im Review schwerer zu erkennen als ein offensichtlicher SQL-Injection-Vektor, weil der Code auf den ersten Blick vollständig wirkt. Die Gegenmaßnahme ist ein fester Bestandteil des eigenen Prompting-Vokabulars: „mit geringstmöglichen Rechten”, „nur für angemeldete Admin-Benutzer mit Ressource X” oder „Whitelist statt Blacklist” als Standardformulierung bei jeder Anfrage, die Zugriffsrechte betrifft. Ohne diesen Zusatz liefert ein KI-Assistent typischerweise Access-Control-Allow-Origin: * statt einer Origin-Whitelist, ein Upload-Verzeichnis ohne Dateityp-Prüfung statt eines MIME-Whitelist-Checks und einen Admin-Endpunkt ohne ADMIN_RESOURCE-Konstante statt einer erzwungenen isAllowed()-Prüfung, drei Muster, die sich mit einem einzigen Prompt-Zusatz wie „Apply least-privilege defaults” zuverlässig vermeiden lassen.
5. Security-bewusstes Prompting in der Praxis
Der wirksamste Hebel gegen unsichere KI-generierte Vorschläge ist nicht nachträgliches Reparieren, sondern das Prompting selbst. Ein Prompt, der Sicherheitsanforderungen explizit benennt, führt bei Claude nachweislich zu anderem Output als ein rein funktionaler Prompt. Statt „Schreibe eine Funktion, die eine Datei hochlädt” liefert „Schreibe eine Funktion, die eine Datei hochlädt, mit MIME-Type-Whitelist, Größenlimit, zufälligem Dateinamen und Speicherung außerhalb des Webroots” systematisch sichereren Code, weil das Modell die genannten Anforderungen als harte Constraints behandelt, nicht als optionale Ergänzung.
Für wiederkehrende Aufgaben lohnt sich ein projektweiter Security-Kontext, etwa in einer CLAUDE.md-Datei, die grundsätzliche Anforderungen wie Prepared Statements, Escaping-Pflicht in Templates, ACL-Prüfung in Controllern und die Ablehnung veralteter Krypto-Funktionen dauerhaft festlegt. Dieser Kontext wird bei jeder Anfrage automatisch mitgeladen und muss nicht in jedem einzelnen Prompt wiederholt werden. Zusätzlich hilft es, Claude explizit zu bitten, nach der Codegenerierung eine kurze Selbstprüfung der sicherheitsrelevanten Aspekte durchzuführen, etwa mit einem abschließenden Prompt wie „Prüfe den generierten Code auf Injection-Risiken, fehlende Validierung und unsichere Defaults, bevor du antwortest”.
{
"weak_prompt": "Write a PHP function that queries products by category id from the request.",
"strong_prompt": "Write a PHP function that queries products by category id from the request. Requirements: validate the category id as a positive integer before use, use Magento's repository/collection API with parameterized filters (never string-concatenated SQL), throw a NoSuchEntityException on invalid input, and add strict_types=1 with full PHPDoc.",
"why_it_matters": "The weak prompt leaves input handling, SQL construction, and error behavior fully up to the model's training-data priors. The strong prompt turns each risk into an explicit constraint the model must satisfy.",
"project_level_context_file": "CLAUDE.md",
"recommended_standing_instructions": [
"Always use prepared statements or repository APIs, never raw string concatenation for SQL",
"Always escape output in phtml templates via $escaper",
"Always require ACL resource checks in admin controllers",
"Never suggest md5, sha1 or mcrypt for security-relevant hashing or encryption",
"Ask before applying permissive defaults (CORS, file upload types, public routes)"
]
}
6. Magento- und Hyvä-spezifische Fallstricke
Magento bringt eigene Sicherheitsschichten mit, die ein allgemein trainiertes Sprachmodell nicht automatisch kennt oder korrekt anwendet, wenn der Kontext fehlt. Die Query-Builder-API über Magento\Framework\DB\Select oder Repository-Interfaces mit SearchCriteriaBuilder ersetzt rohes SQL, wird aber von einem generisch trainierten Modell nicht automatisch bevorzugt, wenn nach „einer Datenbankabfrage” gefragt wird, ohne Magento explizit zu erwähnen. Ebenso wichtig: CSRF-Schutz über Formkeys in klassischen Formularen und die korrekte Verwendung von isAjax()-Prüfungen bei AJAX-Endpunkten werden oft ausgelassen, wenn der Prompt nur die fachliche Anforderung beschreibt.
Im Hyvä-Kontext kommt eine weitere Ebene hinzu: Alpine.js-Komponenten mit x-html statt x-text öffnen XSS-Lücken, wenn dynamische Nutzereingaben ungeprüft ins DOM geschrieben werden, und der Hyvä-CSP-Mechanismus verlangt explizit registrierte Inline-Skripte über $hyvaCsp->registerInlineScript(). Ein KI-Assistent, der ohne diesen Hintergrund nach einer interaktiven Komponente gefragt wird, generiert oft funktionalen, aber CSP-inkompatiblen oder XSS-anfälligen Code. Wer diese Magento- und Hyvä-spezifischen Anforderungen im Projektkontext hinterlegt, reduziert das Risiko erheblich, ohne bei jeder Anfrage erneut daran erinnern zu müssen.
<?php
declare(strict_types=1);
namespace Mironsoft\Catalog\Model;
use Magento\Catalog\Api\ProductRepositoryInterface;
use Magento\Framework\Api\SearchCriteriaBuilderFactory;
use Magento\Framework\Exception\NoSuchEntityException;
/**
* Loads products by category using the repository API instead of raw SQL.
*/
final class CategoryProductLoader
{
/**
* @param ProductRepositoryInterface $productRepository Repository for product entities
* @param SearchCriteriaBuilderFactory $searchCriteriaBuilderFactory Factory for search criteria
*/
public function __construct(
private readonly ProductRepositoryInterface $productRepository,
private readonly SearchCriteriaBuilderFactory $searchCriteriaBuilderFactory
) {
}
/**
* Returns products filtered by a validated category id.
*
* @param int $categoryId Positive category entity id
* @return \Magento\Catalog\Api\Data\ProductInterface[]
* @throws NoSuchEntityException
*/
public function getByCategoryId(int $categoryId): array
{
if ($categoryId <= 0) {
throw new NoSuchEntityException(__('Invalid category id'));
}
$searchCriteriaBuilder = $this->searchCriteriaBuilderFactory->create();
$searchCriteria = $searchCriteriaBuilder
->addFilter('category_id', $categoryId, 'eq')
->create();
return $this->productRepository->getList($searchCriteria)->getItems();
}
}
7. KI-Code im Review-Prozess wie jeden anderen Beitrag behandeln
Die grundlegende Regel im Umgang mit KI-generiertem Code lautet: Er verdient exakt dieselbe Prüftiefe wie Code eines Junior-Entwicklers, unabhängig davon, wie überzeugend er formatiert und kommentiert ist. Ein häufig beobachteter Fehler in Teams, die KI-Assistenten neu einführen, ist eine Art Autoritätsbias: sauber formatierter, gut dokumentierter Code wirkt vertrauenswürdiger, unabhängig von seiner tatsächlichen Korrektheit. Claude produziert in der Regel gut lesbaren, konsistent formatierten Code, was das Risiko erhöht, dass Reviewer die inhaltliche Prüfung oberflächlicher durchführen als bei sichtlich hingeschludertem Code.
Ein belastbarer Review-Prozess behandelt die Herkunft des Codes als irrelevant für die Prüftiefe, macht sie aber im Pull Request transparent, etwa durch einen Hinweis „mit Claude Code erstellt, manuell geprüft”. Das schafft Nachvollziehbarkeit, ohne KI-Code pauschal zu misstrauen oder pauschal zu privilegieren. Besonders wichtig ist die Prüfung an drei Stellen: jede Stelle, die externe Eingaben entgegennimmt, jede Stelle, die auf Dateisystem, Datenbank oder externe Dienste zugreift, und jede Stelle, die Berechtigungen oder Authentifizierung betrifft. Diese drei Kategorien decken die überwiegende Mehrheit sicherheitsrelevanter Fehler ab, unabhängig davon, ob der Code von einem Menschen oder einer KI stammt.
8. Statische Analyse und automatisierte Sicherheitsprüfung
Menschliches Review allein skaliert nicht zuverlässig, besonders wenn KI-Assistenten die Menge an produziertem Code pro Entwickler deutlich erhöhen. Statische Analysewerkzeuge wie PHPStan mit sicherheitsrelevanten Regeln, PHP_CodeSniffer mit dem Magento-Coding-Standard und spezialisierte Security-Scanner wie psalm-plugin-security-analysis oder progpilot finden viele der beschriebenen Muster automatisiert und konsistent, ohne dass jeder Reviewer jedes Detail manuell prüfen muss. Diese Tools sind kein Ersatz für menschliches Urteilsvermögen, aber ein verlässliches Sicherheitsnetz, das dieselben Regeln unabhängig von Tagesform und Erfahrungslevel des Reviewers anwendet.
In der CI-Pipeline lässt sich dieses Netz so konfigurieren, dass ein Pull Request mit KI-generiertem Code, der eine bekannte unsichere Funktion wie md5() für Passwort-Hashing oder eine ungeparametrisierte SQL-Abfrage enthält, automatisch blockiert wird, bevor ein Mensch überhaupt reviewen muss. Composer-Audit (composer audit) prüft zusätzlich, ob von Claude vorgeschlagene Third-Party-Pakete bekannte Schwachstellen enthalten, ein Aspekt, der bei der Übernahme von KI-Empfehlungen für neue Abhängigkeiten leicht übersehen wird, weil das Modell selten Informationen über aktuelle CVEs eines Pakets im Trainingskorpus hat.
#!/usr/bin/env bash
# ci-security-gate.sh - runs before merging any PR, including AI-generated ones
set -euo pipefail
echo "[1/4] PHPStan static analysis (level 5)"
bin/analyse app/code/Mironsoft --level=5
echo "[2/4] Magento coding standard + security sniffs"
bin/phpcs --standard=Magento2 app/code/Mironsoft
echo "[3/4] Dependency vulnerability audit"
bin/composer audit
echo "[4/4] Grep for known insecure patterns as a fast pre-filter"
if grep -rEn "md5\(|sha1\(|mcrypt_|eval\(" app/code/Mironsoft --include="*.php"; then
echo "[BLOCKED] Insecure pattern found - manual review required" >&2
exit 1
fi
echo "[OK] Security gate passed"
9. Unsichere und sichere Prompts im direkten Vergleich
Die folgende Übersicht zeigt anhand konkreter Aufgabenstellungen, wie ein unspezifischer Prompt zu risikobehaftetem Code führt und wie eine explizite Sicherheitsanforderung im Prompt das Ergebnis systematisch verändert.
| Aufgabe | Risiko bei unspezifischem Prompt | Sicherheitsbewusster Prompt-Zusatz | Effekt |
|---|---|---|---|
| Passwort speichern | md5($password) |
„nutze password_hash mit PASSWORD_ARGON2ID” | Sicheres, adaptives Hashing statt gebrochenem Algorithmus |
| Datenbankabfrage | String-Konkatenation im SQL | „nutze Repository-API mit SearchCriteria” | Kein SQL-Injection-Vektor |
| Datei-Upload | Jede Dateiendung akzeptiert | „MIME-Whitelist, Größenlimit, Speicherung außerhalb Webroot” | Kein Remote-Code-Execution über Upload |
| Admin-Controller | Fehlende ACL-Prüfung | „ADMIN_RESOURCE setzen, isAllowed() erzwingen” | Kein unautorisierter Zugriff auf Backend-Funktionen |
| CORS-Konfiguration | Access-Control-Allow-Origin: * |
„nur explizit erlaubte Origins zulassen” | Kein Cross-Origin-Datenabfluss |
Der Musterunterschied ist konsistent: Sobald ein Sicherheitsaspekt explizit im Prompt benannt wird, behandelt Claude ihn als harte Anforderung und liefert entsprechend abgesicherten Code. Bleibt er unerwähnt, entscheidet das Modell anhand statistischer Wahrscheinlichkeit aus den Trainingsdaten, was in sicherheitskritischen Kontexten zu inakzeptablen Ergebnissen führen kann. Diese Erkenntnis lässt sich direkt in Team-Standards überführen, etwa als verpflichtende Prompt-Checkliste für sicherheitsrelevante Anfragen.
Mironsoft
Secure Coding, Code Review und KI-gestützte Magento-Entwicklung
KI-generierten Code sicher in Produktion bringen?
Wir richten Prompting-Standards, Review-Prozesse und automatisierte Security-Gates für Teams ein, die Claude Code produktiv einsetzen, damit Geschwindigkeit nicht auf Kosten der Sicherheit eures Magento-Shops geht.
Security-Review
Manuelle und automatisierte Prüfung von KI-generiertem und bestehendem Code
Prompting-Standards
Projektweite CLAUDE.md-Konventionen für sicherheitsbewusste Codegenerierung
CI-Security-Gates
PHPStan, Composer Audit und statische Scanner in die Pipeline integrieren
10. Zusammenfassung
Sicherheitslücken durch KI-generierten Code entstehen selten durch offensichtlich falschen Code, sondern durch stillschweigend übernommene Muster aus Trainingsdaten: fehlende Input-Validierung, veraltete Krypto-Funktionen wie md5() für Passwörter und übermäßig permissive Defaults bei CORS, Datei-Uploads oder Zugriffsrechten. Diese Muster lassen sich mit gezieltem, sicherheitsbewusstem Prompting deutlich reduzieren, etwa durch explizite Anforderungen wie „nutze password_hash mit ARGON2ID” oder „Whitelist statt Blacklist”, idealerweise dauerhaft in einer projektweiten CLAUDE.md hinterlegt.
Der entscheidende Grundsatz bleibt unabhängig vom eingesetzten Werkzeug derselbe: KI-generierter Code verdient exakt dieselbe Review-Tiefe wie jeder andere Beitrag zum Projekt, weder mehr Vertrauen wegen sauberer Formatierung noch pauschales Misstrauen wegen der Herkunft. Statische Analyse, Security-Scanner und CI-Gates automatisieren einen Teil dieser Prüfung zuverlässig, ersetzen aber nicht das menschliche Urteilsvermögen bei Eingabevalidierung, Zugriffskontrolle und Datenzugriff, den drei Bereichen mit der größten Fehlerdichte.
Sicherheitslücken durch KI-generierten Code vermeiden - Das Wichtigste auf einen Blick
Input-Validierung
Jede externe Eingabe explizit validieren und escapen, unabhängig davon, ob der Code von Mensch oder KI stammt.
Aktuelle Kryptografie
password_hash() mit ARGON2ID statt md5()/sha1(), sodium_crypto_secretbox() statt veraltetem mcrypt.
Security-Prompting
Sicherheitsanforderungen explizit im Prompt und in einer projektweiten CLAUDE.md festhalten.
Review & Tooling
KI-Code wie jeden anderen Beitrag reviewen, PHPStan, Composer Audit und Security-Scanner in CI integrieren.