PHPUnit und Composer Audit: Sicherheitsluecken in Testabhaengigkeiten erkennen
AI generated
@test
assert
PHPUnit · Security · Supply Chain
Composer Audit fuer Testabhaengigkeiten
Warum Dev-Dependencies kein Sicherheits-Blindfleck sein duerfen

Sicherheitsluecken in Produktionsabhaengigkeiten werden meist ernst genommen, waehrend Test- und Dev-Abhaengigkeiten wie PHPUnit-Plugins oder Coverage-Werkzeuge oft unbeachtet bleiben. Dabei laufen genau diese Pakete mit vollen Rechten in der CI/CD-Pipeline und sind ein reales Einfallstor fuer Supply-Chain-Angriffe, das composer audit gezielt aufdecken kann.

14 Min. Lesezeit Composer Audit Supply Chain Security CI/CD

1. Warum Dev-Dependencies oft uebersehen werden

In vielen Sicherheitsprozessen liegt der Fokus fast ausschliesslich auf require-Abhaengigkeiten, also den Paketen, die tatsaechlich mit in die Produktion ausgeliefert werden. Das ergibt auf den ersten Blick Sinn, denn nur dieser Code laeuft schliesslich auf dem produktiven Server und ist direkt von aussen erreichbar. Test- und Dev-Abhaengigkeiten unter require-dev, etwa PHPUnit selbst, Mocking-Bibliotheken, Coverage-Werkzeuge oder statische Analyse-Tools, fallen bei dieser Betrachtung meist komplett durchs Raster.

Dieser blinde Fleck ist gefaehrlich, weil Dev-Abhaengigkeiten keineswegs harmlos sind, nur weil sie nicht in Produktion laufen. Sie werden bei jedem composer install auf Entwicklerrechnern und in jeder CI-Pipeline ausgefuehrt, oft mit denselben Rechten wie der eigentliche Build-Prozess, inklusive Zugriff auf Umgebungsvariablen, SSH-Keys fuer Deployments und manchmal sogar auf Produktionszugangsdaten, die versehentlich in der CI-Umgebung verfuegbar sind.

2. Composer Audit: Funktionsweise und Aufruf

Seit Composer 2.4 steht der eingebaute Befehl composer audit zur Verfuegung, der die composer.lock-Datei gegen eine Sicherheitsdatenbank abgleicht, standardmaessig die FriendsOfPHP Security Advisories Datenbank. Der Befehl prueft dabei alle Pakete in der Lock-Datei, unabhaengig davon, ob sie unter require oder require-dev deklariert sind, sofern nicht explizit gefiltert wird. Das bedeutet, ein einfacher Aufruf ohne zusaetzliche Optionen deckt bereits Test- und Dev-Pakete mit ab.

Der Befehl gibt fuer jede gefundene Schwachstelle das betroffene Paket, die installierte Version, die CVE-Kennung sowie einen kurzen Titel der Advisory aus. Wichtig ist, den Rueckgabewert des Befehls in Skripten auszuwerten: composer audit liefert bei gefundenen Schwachstellen einen nicht-null Exit-Code zurueck, was sich direkt als Abbruchbedingung in einer CI-Pipeline nutzen laesst.


# Vollstaendiger Audit inklusive require-dev (Standardverhalten)
composer audit

# Nur Produktionsabhaengigkeiten pruefen (require-dev ausschliessen)
composer audit --no-dev

# Maschinenlesbare Ausgabe fuer die Weiterverarbeitung im CI-Skript
composer audit --format=json > audit-report.json

3. Warum Sicherheitsluecken in Testwerkzeugen trotzdem gefaehrlich sind

Der Gedanke 'PHPUnit laeuft doch nur lokal und in der Pipeline, nicht auf dem produktiven Server' unterschaetzt zwei Angriffsvektoren. Erstens laeuft PHPUnit mit allen Rechten des ausfuehrenden Prozesses, und in vielen CI-Setups hat dieser Prozess Zugriff auf Deployment-Secrets, Datenbank-Zugangsdaten fuer Testumgebungen, die produktiven Daten aehneln, oder sogar auf Cloud-Provider-Credentials fuer automatisierte Deployments. Eine kompromittierte Testabhaengigkeit mit Code-Ausfuehrung kann diese Secrets exfiltrieren, ganz ohne dass produktiver Code jemals angefasst wird.

Zweitens sind gerade Test- und Analyse-Tools haeufig Ziel von Supply-Chain-Angriffen, weil sie im Vergleich zu grossen Frameworks weniger Aufmerksamkeit von Sicherheitsforschern erhalten und trotzdem in tausenden Projekten als Dev-Abhaengigkeit installiert sind. Ein bekannter Fall betraf ein kompromittiertes Paket, das bei der Installation ueber ein post-install-Skript Schadcode ausfuehrte, lange bevor ein einziger Test tatsaechlich lief. Genau solche Angriffe treffen jeden Entwickler, der composer install ausfuehrt, unabhaengig davon, ob je ein Test gestartet wird.

4. Composer Audit in die CI-Pipeline integrieren

Ein composer audit-Schritt sollte fruh in der Pipeline stehen, idealerweise noch vor dem eigentlichen PHPUnit-Lauf, damit eine gefundene Schwachstelle den Build stoppt, bevor kompromittierter Code ueberhaupt ausgefuehrt wird. Fuer Magento-Projekte empfiehlt sich, den Audit sowohl im Standard-Composer-Root als auch, falls vorhanden, in separaten composer.json-Dateien einzelner Module durchzufuehren, sofern diese eigene Abhaengigkeiten deklarieren.

Ein pragmatischer Ansatz ist, den Audit-Schritt als eigenstaendigen, schnellen CI-Job zu definieren, der unabhaengig vom laengeren Testlauf existiert und damit auch bei einem fehlschlagenden PHPUnit-Lauf separat sichtbar bleibt. So laesst sich klar unterscheiden, ob ein Build wegen eines Testfehlers oder wegen einer Sicherheitsluecke fehlgeschlagen ist, was fuer die Priorisierung der Behebung entscheidend ist.


# .gitlab-ci.yml (Ausschnitt)
composer-audit:
  stage: security
  script:
    - composer audit --format=json --no-interaction | tee audit-report.json
  artifacts:
    paths:
      - audit-report.json
    when: always
  allow_failure: false

5. Abgestufte Behandlung nach Schweregrad

Nicht jede gemeldete Schwachstelle rechtfertigt einen sofortigen Build-Abbruch. composer audit liefert im JSON-Format auch den Schweregrad der jeweiligen Advisory, sodass sich eine differenzierte Policy umsetzen laesst: Schwachstellen mit hohem oder kritischem Schweregrad blockieren den Merge sofort, waehrend niedrige oder mittlere Schweregrade zunaechst nur eine Warnung erzeugen und in ein Ticket ueberfuehrt werden, das innerhalb einer definierten Frist bearbeitet werden muss.

Diese Abstufung verhindert, dass Teams composer audit nach wiederholten Fehlalarmen bei geringfuegigen Schwachstellen komplett ignorieren oder den Check faul mit allow_failure: true entschaerfen. Eine realistische, nach Risiko gestaffelte Policy erhoeht die Akzeptanz im Team deutlich gegenueber einer starren Alles-oder-Nichts-Regel.


<?php
// tools/audit-policy.php
declare(strict_types=1);

$report = json_decode((string) file_get_contents(__DIR__ . '/../audit-report.json'), true);
$blockingSeverities = ['critical', 'high'];
$blocked = false;

foreach ($report['advisories'] ?? [] as $package => $advisories) {
    foreach ($advisories as $advisory) {
        $severity = $advisory['severity'] ?? 'unknown';
        $isDev = str_contains($package, 'phpunit') || str_contains($package, 'mockery');

        if (in_array($severity, $blockingSeverities, true)) {
            fwrite(STDERR, sprintf(
                "BLOCKIEREND: %s (%s, Dev-Paket: %s)\n",
                $package, $severity, $isDev ? 'ja' : 'nein'
            ));
            $blocked = true;
        } else {
            fwrite(STDOUT, sprintf("WARNUNG: %s (%s)\n", $package, $severity));
        }
    }
}

exit($blocked ? 1 : 0);

6. Das Risiko von post-install-Skripten und Plugins

Neben bekannten CVEs birgt Composer selbst ein strukturelles Risiko: Pakete koennen ueber die scripts-Sektion in ihrer composer.json beliebigen PHP-Code bei der Installation ausfuehren, und Composer-Plugins koennen sich sogar in den Installationsprozess selbst einklinken. Ein boesartiges oder kompromittiertes Test-Utility-Paket kann so schon beim blossen composer install Code ausfuehren, ganz unabhaengig davon, ob es jemals in einem Test tatsaechlich referenziert wird.

Composer bietet seit Version 2.2 die Konfigurationsoption allow-plugins, die explizit festlegt, welche Pakete Composer-Plugin-Code ausfuehren duerfen. Fuer eine bewusste Sicherheitsstrategie lohnt es sich, diese Liste bewusst kurz zu halten und neue Eintraege nur nach kurzer Pruefung des jeweiligen Pakets zu erlauben, statt pauschal alles zu bestaetigen, was composer bei der ersten Installation vorschlaegt.


{
    "config": {
        "allow-plugins": {
            "composer/installers": true,
            "phpstan/extension-installer": true,
            "dealerdirect/phpcodesniffer-composer-installer": false
        }
    }
}

7. Automatisierte Updates fuer Testabhaengigkeiten

Ein einmaliger composer audit-Lauf schuetzt nur gegen bereits bekannte Schwachstellen zum Zeitpunkt der Pruefung, nicht gegen kuenftig entdeckte. Ergaenzend dazu sollte ein Tool wie Renovate oder Dependabot eingerichtet werden, das automatisiert Pull-Requests fuer veraltete Abhaengigkeiten erstellt, inklusive Testabhaengigkeiten unter require-dev. Wichtig ist, diese Tools nicht nur fuer require, sondern explizit auch fuer require-dev zu konfigurieren, da manche Standardkonfigurationen Dev-Abhaengigkeiten mit niedrigerer Prioritaet oder gar nicht beruecksichtigen.

Ein sinnvoller Kompromiss ist, Sicherheitsupdates fuer Dev-Abhaengigkeiten mit hoher Prioritaet automatisch zu mergen, sofern die CI-Pipeline gruen bleibt, waehrend reine Feature-Updates ohne Sicherheitsbezug manuell geprueft werden. So bleibt der Wartungsaufwand ueberschaubar, ohne bekannte Luecken unnoetig lange offen zu lassen.

8. Kontinuierliches Monitoring jenseits einzelner CI-Laeufe

Ein composer audit-Schritt im CI-Pipeline prueft immer nur den Stand zum Zeitpunkt eines konkreten Merge- oder Pull-Requests. Wird danach eine neue Schwachstelle in einem bereits installierten Paket bekannt, faellt das erst beim naechsten Commit auf, der irgendeine CI-Pipeline ausloest, was in ruhigen Projektphasen Wochen dauern kann. Ein zusaetzlicher, zeitgesteuerter Job, der taeglich oder woechentlich unabhaengig von Code-Aenderungen gegen die aktuelle composer.lock laeuft, schliesst genau diese Luecke.

Ein solcher geplanter Job sollte bei einem Fund nicht nur die Pipeline rot faerben, sondern aktiv eine Benachrichtigung ausloesen, etwa an einen Slack-Kanal oder per E-Mail an das verantwortliche Team, da niemand taeglich manuell in eine ungenutzte, nur informative Pipeline schaut. Erst diese aktive Alarmierung stellt sicher, dass eine neu entdeckte Schwachstelle in einer bereits installierten Testabhaengigkeit zeitnah bemerkt wird, auch wenn zufaellig gerade niemand am betroffenen Modul arbeitet.

9. Besonderheiten im Magento-Projektkontext

Magento-Projekte bringen durch die grosse Zahl an Drittanbieter-Modulen und deren jeweils eigenen composer.json-Abhaengigkeiten eine besonders breite Angriffsflaeche mit. Viele Magento-Extensions deklarieren eigene Testabhaengigkeiten oder Entwicklerwerkzeuge, die selten von jemandem ausserhalb des urspruenglichen Entwicklerteams ueberprueft werden. Ein projektweiter composer audit-Lauf deckt diese transitive Abhaengigkeitskette vollstaendig ab, waehrend eine isolierte Pruefung nur der eigenen app/code-Module blinde Flecken hinterlaesst.

Bei Mark-Shust-Docker-Setups oder aehnlichen Entwicklungsumgebungen empfiehlt es sich, composer audit als Teil des bin/composer-Wrapper-Workflows zu etablieren, etwa als eigenstaendigen Befehl, der vor jedem composer update auf dem gesamten Lock-File laeuft, damit neue Schwachstellen sofort auffallen und nicht erst beim naechsten geplanten Sicherheits-Review Wochen spaeter entdeckt werden.

Massnahme Werkzeug Zeitpunkt
Vollstaendiger Audit inkl. Dev-Deps composer audit (Standardaufruf) Bei jedem CI-Lauf, vor dem Testlauf
Abgestufte Severity-Policy Eigenes Skript auf audit --format=json Als blockierender Pipeline-Schritt
Plugin-Ausfuehrung einschraenken config.allow-plugins in composer.json Einmalig, bei jedem neuen Paket pruefen
Automatisierte Update-PRs Renovate / Dependabot, inkl. require-dev Kontinuierlich im Hintergrund
Transitive Modul-Dependencies pruefen composer audit auf Projekt-Root Vor Extension-Updates und Releases

Mironsoft

Testautomatisierung, Magento-Qualitätssicherung und CI-Integration

Tests, die echte Fehler finden statt nur grün zu leuchten?

Wir prüfen bestehende PHPUnit-Suiten auf Implementierungsdetail-Tests, flaky Tests und fehlende Coverage an kritischen Stellen und bauen daraus eine Teststrategie, die bei jedem Magento-Update wirklich Sicherheit gibt.

Test-Audit

Bestehende Suiten auf Mocking-Antipatterns und blinde Flecken prüfen.

Teststrategie

Unit-, Integrations- und MFTF-Tests sinnvoll für Magento-Projekte kombinieren.

CI-Integration

Schnelle, zuverlässige Testläufe in GitLab CI oder GitHub Actions einrichten.

10. Zusammenfassung

Composer Audit fuer Dev-Deps: Das Wichtigste auf einen Blick

Blindfleck

Dev-Abhaengigkeiten wie PHPUnit-Plugins werden bei Security-Reviews oft ignoriert.

Risiko

Testtools laufen mit vollen CI-Rechten und Zugriff auf Deployment-Secrets.

Werkzeug

composer audit prueft standardmaessig auch require-dev gegen bekannte CVEs.

Durchsetzung

Frueher, blockierender CI-Schritt mit nach Schweregrad gestaffelter Policy.

11. FAQ: Composer Audit fuer Dev-Deps: Das Wichtigste auf einen Blick

1Prueft composer audit standardmaessig auch Dev-Abhaengigkeiten?
Ja, ein einfacher Aufruf ohne zusaetzliche Optionen prueft alle Pakete in der composer.lock-Datei, unabhaengig davon, ob sie unter require oder require-dev deklariert sind. Nur die Option --no-dev schliesst Dev-Abhaengigkeiten explizit aus.
2Warum sind Sicherheitsluecken in PHPUnit-Erweiterungen ueberhaupt relevant, wenn sie nie in Produktion laufen?
Weil Dev-Abhaengigkeiten bei jedem composer install und in jeder CI-Pipeline ausgefuehrt werden, oft mit Zugriff auf Deployment-Secrets, Datenbank-Zugangsdaten oder Cloud-Credentials. Eine kompromittierte Testabhaengigkeit kann diese Daten exfiltrieren, ohne dass produktiver Code je angefasst wird.
3Wie integriere ich composer audit sinnvoll in eine CI-Pipeline?
Als eigenstaendiger, fruher Pipeline-Schritt vor dem eigentlichen Testlauf, sodass eine gefundene Schwachstelle den Build stoppt, bevor moeglicherweise kompromittierter Code ausgefuehrt wird. Die JSON-Ausgabe laesst sich zusaetzlich fuer eine nach Schweregrad gestaffelte Policy auswerten.
4Sollte jede gemeldete Schwachstelle den Build blockieren?
Nicht zwingend. Eine differenzierte Policy blockiert bei kritischem oder hohem Schweregrad sofort, waehrend niedrigere Schweregrade zunaechst nur eine Warnung erzeugen und als Ticket mit definierter Frist behandelt werden, um Alarmmuedigkeit im Team zu vermeiden.
5Was ist das Risiko von post-install-Skripten in Composer-Paketen?
Pakete koennen ueber die scripts-Sektion ihrer composer.json beliebigen PHP-Code bei der Installation ausfuehren, und Composer-Plugins koennen sich in den Installationsprozess selbst einklinken. Ein kompromittiertes Paket kann so schon bei composer install Schadcode ausfuehren, unabhaengig davon, ob es je referenziert wird.
6Wie schuetzt die allow-plugins-Option in composer.json?
Sie legt explizit fest, welche Pakete Composer-Plugin-Code ausfuehren duerfen. Eine bewusst kurz gehaltene Liste, bei der neue Eintraege erst nach kurzer Pruefung erlaubt werden, reduziert das Risiko, unbemerkt boesartigen Plugin-Code auszufuehren.
7Reicht ein einmaliger composer audit-Lauf aus?
Nein, ein einmaliger Lauf deckt nur zum Pruefzeitpunkt bekannte Schwachstellen ab. Ergaenzend sollte ein Tool wie Renovate oder Dependabot automatisierte Update-Pull-Requests erstellen, explizit auch fuer require-dev-Abhaengigkeiten.
8Warum sind Test- und Analyse-Tools ein beliebtes Ziel fuer Supply-Chain-Angriffe?
Weil sie im Vergleich zu grossen Frameworks weniger Aufmerksamkeit von Sicherheitsforschern erhalten, gleichzeitig aber in tausenden Projekten als Dev-Abhaengigkeit installiert sind und damit eine breite, wenig beachtete Angriffsflaeche bieten.
9Was ist in Magento-Projekten bei composer audit besonders zu beachten?
Die grosse Zahl an Drittanbieter-Modulen mit eigenen composer.json-Abhaengigkeiten vergroessert die Angriffsflaeche erheblich. Ein projektweiter Audit-Lauf, der auch transitive Abhaengigkeiten einzelner Extensions einschliesst, ist daher wichtiger als eine isolierte Pruefung nur der eigenen app/code-Module.
10Wo im Docker-Workflow sollte composer audit ausgefuehrt werden?
Idealerweise ueber den bin/composer-Wrapper als eigenstaendiger Befehl vor jedem composer update, sowie als fester, fruher Schritt in der CI-Pipeline, damit neue Schwachstellen sofort auffallen statt erst bei einem spaeteren, manuell angestossenen Sicherheits-Review.