# MCP-Prozessbäume: Unerwartete Kindprozesse finden

Lokale MCP-Server machen es leicht, eine saubere Protokollgrenze mit einer sauberen Ausführungsgrenze zu verwechseln. Ein Agent sendet einen Tool-Aufruf, der Server liefert ein Ergebnis zurück und das Protokoll wirkt abgeschlossen. Währenddessen kann der Server eine Shell, einen Paket-Runner, eine Laufzeitumgebung, einen Compiler, einen SSH-Client oder einen in ein Projektverzeichnis heruntergeladenen Helfer starten. Der Prozessbaum zeigt, wohin die Arbeit tatsächlich gelangt ist.

Bei der Prüfung behandle ich einen MCP-Server nicht als einen einzelnen Prozess. Ich betrachte den Server und jeden von ihm gestarteten Nachkommen als eine gemeinsame Ausführungsfamilie, bis diese Nachkommen beendet sind. Diese Regel findet Fehler, die Tool-Protokolle regelmäßig übersehen: ein Argument, das eine Shell dazu bringt, etwas anderes aufzurufen, einen über `PATH` ausgewählten Helfer, einen Kindprozess mit weitergereichtem API-Token oder ein langlebiges Programm, das einen Socket öffnet, nachdem die ursprüngliche Anfrage scheinbar abgeschlossen war.

## Aus einem Tool-Aufruf können viele lokale Programme werden

Ein MCP-Server startet aus ganz normalen Gründen häufig Kindprozesse. Ein Repository-Tool kann `git` aufrufen, ein Code-Tool einen Formatter, ein Infrastruktur-Tool `ssh` und ein paketbewusster Server zunächst eine Laufzeitumgebung und anschließend ein Paketprogramm. Die Existenz eines Kindprozesses beweist noch keinen Einbruch.

Wichtig ist die Unterscheidung zwischen einem **erwarteten Helfer** und einem **nicht erklärten Ausführungspfad**. Ein erwarteter Helfer hat eine dokumentierte Aufgabe, einen bekannten Speicherort der ausführbaren Datei, zum Aufruf passende Argumente und eine zur Aufgabe passende Lebensdauer. Ein nicht erklärter Pfad verletzt eine dieser Annahmen. Er kann trotzdem harmlos sein, braucht aber eine Erklärung, bevor er Zugangsdaten, Dateisystemzugriff oder Netzwerkzugriff erhält.

Eine übergeordnete PID allein reicht nicht aus. Die Erstellung von Prozessen unter Unix erzeugt eine Abstammungslinie, aber keinen Sicherheitsvertrag. Ein Nachkomme übernimmt häufig die Benutzer-ID, das aktuelle Arbeitsverzeichnis, Umgebungsvariablen, Ressourcenlimits und manchmal geöffnete Dateideskriptoren des Elternprozesses. Was genau übernommen wird, hängt davon ab, wie der Elternprozess den Kindprozess startet. Meist erhält der Kindprozess jedoch genügend Kontext, um an den entscheidenden Stellen wie der Elternprozess zu handeln.

Darum ist «Der MCP-Server ruft nur einen Befehl auf» keine brauchbare Zusicherung. `tool-server` kann `/bin/sh -c` starten, das wiederum `node` startet, das ein Paket-Skript startet, das anschließend `curl` ausführt. Wer nur `tool-server` dokumentiert, hält den uninteressantesten Teil der Kette fest.

Das POSIX-Modell von `exec` erklärt die kritische Stelle. Ein Prozess kann sein Prozessabbild durch ein anderes Programm ersetzen, ohne die PID zu ändern. Eine einfache Prozessliste vor und nach einer Anfrage kann daher einen kurzlebigen Zwischenprozess übersehen. Wenn die Aufgabe sensibel genug ist, brauchst du sowohl eine Baumansicht als auch Ereignisdaten.

## Erstelle eine Baseline, bevor du einen Agenten arbeiten lässt

Eine Baseline ist eine Aufzeichnung der Kindprozesse, die dein Server während einer unauffälligen, freigegebenen Anfrage startet. Sie liefert einen Vergleichspunkt für spätere Beobachtungen. Ohne sie wirkt jeder Interpreter verdächtig und jede Warnung führt zu einer Diskussion darüber, was normal sein könnte.

Starte den Server manuell in einer eigenen Terminal-Sitzung. Notiere sofort seine PID und erfasse anschließend die Prozesstabelle mit vollständigen Befehlszeilen:

```sh
ps -axo pid,ppid,user,etime,stat,command > mcp-process-baseline.txt
```

Die Spalten sind wichtig. `PID` identifiziert den Prozess, `PPID` seinen direkten Elternprozess, `USER` das Konto, dem er gehört, `ETIME` die bisherige Laufzeit und `STAT` kann einen angehaltenen oder Zombieprozess sichtbar machen. Wie vollständig `COMMAND` ist, hängt davon ab, was das Betriebssystem melden kann. Trotzdem findest du hier zuerst Shell-Wrapper, temporäre Pfade und unerwartete Optionen.

Unter macOS liefert `ps` keine überall verfügbare Baumansicht. Direkte Kindprozesse kannst du mit `pgrep` auflisten:

```sh
pgrep -P 48192 -alf
```

Ersetze `48192` durch die PID des Servers. Eine typische Ausgabe sieht so aus:

```text
48207 /usr/bin/python3 /Users/me/tools/format_request.py
48211 /usr/bin/ssh -o BatchMode=yes build.example
```

Wiederhole den Befehl für jede Kind-PID, bis keine neuen Nachkommen mehr auftauchen. Das klingt mühsam und ist es bei einer einmaligen Prüfung auch. Gleichzeitig zwingt es dich, die tatsächliche Kette zu sehen, statt einem Diagramm in der Dokumentation zu vertrauen. Wenn du `pstree` über deinen normalen Paketprozess installierst, liefert `pstree -p 48192` eine besser lesbare Momentaufnahme. Mache das Anzeigeprogramm nicht zum Bestandteil deiner Sicherheitszusage. Es spart lediglich Tipparbeit.

Führe einige erwartete Anfragen aus: eine schreibgeschützte Repository-Abfrage, die Formatter-Aktion und eine SSH-Aktion, falls der Server eine solche unterstützt. Speichere für jede Anfrage eine Momentaufnahme. Notiere die Pfade der Programme, normale Argumente, übliche Arbeitsverzeichnisse und ob Helfer beendet werden. Ein Compiler, der bei einer Build-Anfrage auftaucht, kann erwartbar sein. Derselbe Compiler bei einer Anfrage zum Zusammenfassen einer Textdatei wäre eine Abweichung.

Erstelle die Baseline nicht anhand eines Entwicklungs-Checkouts voller veränderlicher Wrapper und Paket-Skripte und erkläre das Ergebnis dann als vertrauenswürdig. Diese Einrichtung zeigt nur, was deine aktuelle Maschine zufällig ausführt. Sie sagt nicht, was der Server ausführen dürfen sollte. Notiere die erwarteten Pfade der ausführbaren Dateien separat.

## Shell-Wrapper verbergen den Prozess, den du prüfen wolltest

Shell-Aufrufe sind beliebt, weil sich dynamische Befehle damit einfach zusammensetzen lassen. Sie verwandeln aber eine Argumentgrenze in Text, und Text kann durch Anführungszeichen, Expansion, Globbing, Umleitungen, Befehlsersetzung und Shell-Funktionen eine Bedeutung erhalten. Der Autor des Servers möchte vielleicht einen Formatter ausführen, während der tatsächliche Kindprozess eine Shell startet, die entscheidet, was der Formatter-Befehl bedeutet.

Das ist eine häufige Ursache irreführender Prüfungen. Jemand sieht in der Quelltext-Implementierung den Namen einer erlaubten Binärdatei und nimmt an, dass genau diese Datei ausgeführt wird. Die Laufzeitdaten zeigen jedoch `/bin/sh -c ...`, und die Shell löst den Rest erst später auf. Das sind unterschiedliche Aussagen.

Vergleiche diese beiden Muster in einer Server-Implementierung:

```js
spawn("/usr/bin/git", ["status", "--short"], {
  cwd: repositoryPath,
  shell: false
});
```

```js
exec(`git -C ${repositoryPath} status --short`);
```

Beim ersten Muster bleiben Programm und Argumente getrennt. `repositoryPath` muss weiterhin validiert werden, aber Shell-Parsing wird vermieden. Beim zweiten Muster kann `repositoryPath` die Befehlsgrammatik verändern, wenn das Quoting fehlerhaft ist. Außerdem kommt ein Shell-Prozess in den Baum, der möglicherweise weitere Prozesse startet, bevor der endgültige Befehl ausgeführt wird.

Akzeptiere «Wir bereinigen die Eingabe» nicht als Ersatz für einen Argumentvektor. Bereinigungsregeln veralten, sobald Optionen hinzukommen. Entwickler ergänzen eine Funktion, erlauben Leerzeichen, fügen ein bedingtes Flag hinzu und schon beschreibt der alte Filter die Befehlsgrammatik nicht mehr. Ein explizites Programm mit einem Array von Argumenten macht die Grenze im Code und in den Audit-Daten sichtbar.

Wenn ein Server tatsächlich Shell-Syntax benötigt, begrenze sie. Verwende ein festes Skript außerhalb des vom Agenten beschreibbaren Arbeitsbereichs, übergib Daten über Positionsparameter und notiere Pfad und Digest des Skripts in deinen Deployment-Unterlagen. Setze niemals eine Shell-Zeile aus Tool-Eingaben zusammen und erkläre den resultierenden Kindprozess nur deshalb als erwartet, weil er aus deinem Quellbaum stammt.

Der schwierige Fall ist Paketwerkzeug. Befehle wie Paket-Runner führen häufig Lifecycle-Skripte aus, die das Projekt bereitstellt. Ein Server, der einen Paketbefehl in einem vom Agenten bearbeiteten Checkout ausführt, kann daher Befehle starten, die der Agent in der Projektkonfiguration abgelegt hat. Das ist kein Fehler des Paketmanagers. Es ist eine Ausführungsentscheidung, die du getroffen hast, indem du ein veränderliches Repository als Quelle vertrauenswürdiger Anweisungen behandelst.

## `PATH` und Arbeitsverzeichnisse verändern die Bedeutung eines Befehls

`git` ist keine Identität einer ausführbaren Datei. Es ist eine Suchanfrage. Der Prozess löst sie durch die Suche in `PATH` auf. Ein vom Agenten kontrolliertes Repository kann diese Suche beeinflussen, wenn der Server lokale Projektverzeichnisse oder geerbte Shell-Konfiguration einbezieht. Eine Datei namens `git` in einem solchen Verzeichnis kann vor `/usr/bin/git` ausgeführt werden.

Prüfe die Umgebung, die ein Prozess erhält, nicht nur den im Tool-Protokoll angezeigten Befehl. Für einen Prozess, der dir gehört, kannst du unter vielen macOS-Versionen Umgebungsinformationen über `ps` abfragen, wobei die Verfügbarkeit variiert. Praktisch ist es, wenn der Server beim Start bewusst eine kurze, bereinigte Umgebung protokolliert: `PATH`, `HOME`, `TMPDIR`, das aktuelle Verzeichnis und die Pfade festgelegter Programme. Schreibe keine Zugriffstoken, Sitzungscookies oder vollständigen Umgebungsdumps in ein gemeinsames Projekt-Log.

Löse sensible Befehle in absolute Pfade auf. Das ist keine Formsache, wenn ein Agent den Arbeitsbaum verändern kann. Zum Beispiel:

```sh
/usr/bin/git -C /Users/me/work/repo status --short
/usr/bin/ssh -o BatchMode=yes -o IdentitiesOnly=yes host.example
```

Absolute Pfade beseitigen ein Suchproblem. Sie machen das Zielprogramm aber nicht automatisch sicher. `git` kann unter bestimmten Konfigurationen Hooks oder externe Diff-Programme aufrufen. SSH kann Konfigurationen laden und Helfer starten. Das Arbeitsverzeichnis steuert außerdem relative Dateizugriffe, Projektkonfiguration und temporäre Ausgaben. Notiere es bei der Prüfung eines Kindprozesses.

Mit `lsof` kannst du das aktuelle Verzeichnis und die geöffneten Dateien eines Prozesses untersuchen:

```sh
lsof -nP -p 48207 | sed -n '1,35p'
```

Achte in der Spalte für Dateideskriptoren auf `cwd`, unter `txt` auf das ausführbare Programm und auf Dateien im Projekt, in temporären Verzeichnissen oder an Orten für Zugangsdaten. `lsof` liefert eine Momentaufnahme. Ein kurzlebiger Kindprozess kann auftauchen und verschwinden, bevor du den Befehl ausführst. Die Ausgabe erkennt aber zuverlässig einen Server, der einen Helfer unauffällig am Leben hält.

Ein Kindprozess, der aus `/private/var/folders/...` gestartet wird, verdient mehr Aufmerksamkeit als einer aus einem verwalteten Anwendungsverzeichnis, besonders wenn sein Name wie ein normales Dienstprogramm aussieht. Temporäre Orte sind legitime Speicherplätze für Build-Ergebnisse. Sie sind aber auch geeignete Verstecke zwischen anderen vergänglichen Dateien. Frage, welche Komponente ihn erstellt hat und warum diese Komponente dort eine ausführbare Datei benötigte.

## Netzwerkaktivität muss zur angeforderten Aktion passen

Ein lokaler Kindprozess kann die Grenze der MCP-Anfrage überschreiten, auch wenn er keine verdächtige Datei schreibt. Ein Formatter sollte normalerweise keine ausgehende Verbindung öffnen. Ein SSH-Helfer sollte sich mit dem angeforderten Host verbinden und anschließend beendet werden. Eine Paketinstallation darf Registries kontaktieren, aber diese Aktion muss ausdrücklich geprüft werden, weil sie neuen Code herunterladen und ausführen kann.

Untersuche die Sockets des Servers und jedes Kindprozesses, der lange genug läuft:

```sh
lsof -nP -i -p 48211
```

Die Ausgabe enthält normalerweise Protokoll, lokale Adresse, entfernte Adresse und Verbindungsstatus. Die Optionen `-nP` verhindern Namens- und Dienstauflösungen. So bleibt die Ausgabe wörtlich und während der Untersuchung entsteht kein zusätzlicher Resolver-Verkehr. Ein Eintrag mit `LISTEN` bedeutet, dass der Prozess lokale oder Netzwerkverbindungen annimmt. Ein Eintrag mit `ESTABLISHED` zeigt einen aktiven Kommunikationspartner. Vergleiche jeden Eintrag mit der Aktion, die ihn ausgelöst hat.

Reagiere nicht auf jeden Prozess mit einer Netzwerkbibliothek über. Manche Entwicklerwerkzeuge prüfen Update-Dienste, den Zertifikatsstatus oder Metadaten zu Abhängigkeiten. Ich sehe dieses Verhalten trotzdem als Grund, den Server stärker zu begrenzen. Ein Tool-Aufruf, der angeblich lokalen Code liest, sollte keinen nicht deklarierten ausgehenden Kanal eröffnen, nur weil ein Helfer ihn praktisch findet.

Trenne die Injektion von Zugangsdaten von der Prozessbeobachtung. Die Prozessprüfung kann zeigen, dass `curl` ausgeführt wurde. Sie kann nicht garantieren, dass ein als Umgebungsvariable übergebenes Token nie kopiert, protokolliert oder an einen Enkelprozess vererbt wurde. Häufig lautet die Empfehlung: «Lege den API-Schlüssel einfach für einen Befehl in die Umgebung des Kindprozesses.» Das ist beliebt, weil es einfach ist und in einer Demo funktioniert. Für agentengesteuerte Aktionen ist es falsch, denn ein Kindprozess kann seine Umgebung ausgeben, sie weiterreichen oder nach der sichtbaren Rückkehr des Befehls am Leben bleiben.

Sallyport setzt für seine HTTP- und SSH-Kanäle eine andere Grenze: Der Agent erhält weder die gespeicherten API- noch die SSH-Zugangsdaten, sondern die App führt die Aktion selbst aus. Das macht die Prüfung lokaler MCP-Kindprozesse nicht überflüssig, verhindert aber, dass jeder Helferprozess zu einem möglichen Träger des Geheimnisses wird.

## Ein Fehler beginnt oft mit einem vernünftigen Komfort-Wrapper

Betrachte einen lokalen Repository-MCP-Server mit einem Tool `run_test`. Der Autor möchte einen flexiblen Befehl ermöglichen. Deshalb wechselt der Handler in das Repository und führt über einen Paketbefehl ein vom Projekt definiertes Testskript aus. Der Agent darf den Checkout bearbeiten, weil das Bearbeiten von Code zu seiner Aufgabe gehört.

Der Agent ändert das Projektskript als Teil eines vorgeschlagenen Fixes. Der Server führt das Testtool aus. Der Paketbefehl startet eine Shell, diese startet die Laufzeitumgebung, die das veränderte Skript ausführt. Das Skript startet einen Hintergrundhelfer und leitet dessen Ausgabe in eine temporäre Datei um. Das Tool meldet «Tests bestanden», weil der Vordergrundbefehl erfolgreich beendet wurde.

Für diese Abfolge ist kein exotischer Exploit nötig. Das Problem ist, dass der Server veränderliche Projektmetadaten als freigegebene ausführbare Konfiguration behandelt hat. Ein Prüfer sieht möglicherweise nur die ursprüngliche MCP-Anfrage und das Erfolgsergebnis. Der Prozessbaum zeigt die entscheidende Geschichte:

```text
mcp-repo-server(48192)
  package-runner(48230)
    sh(48233)
      runtime(48234)
        test-script(48240)
          helper(48247)
```

Der Hintergrundhelfer ist der wichtigste Untersuchungspunkt. Prüfe seine vollständige Befehlszeile, den Pfad der ausführbaren Datei, die Elternkette, das Arbeitsverzeichnis, geöffnete Dateien, Sockets und die Startzeit. Prüfe, ob er nach dem Ende der Serversitzung weiterläuft. Untersuche anschließend die Projektänderung, aus der das Skript stammt. Stelle dies nicht allein als Fehler des Agenten dar. Der Server hat die Ausführung veränderlicher Anweisungen angeboten und das Ergebnis als Test bezeichnet.

Die Behebung hängt vom gewünschten Produktverhalten ab. Ein vorsichtiger Server kann eine festgelegte Testdatei mit festen Argumenten ausführen. Wenn projektspezifische Skripte erforderlich sind, behandle Skriptdatei und Paketmetadaten als ausführbare Eingaben: Zeige sie zur Freigabe an, führe sie in einer eingeschränkten Umgebung aus und verbiete Hintergrundprozesse, soweit möglich. Mindestens muss der Server jeden von ihm gestarteten Nachkommen melden, auch wenn dieser länger lebt als der Tool-Aufruf.

Der entscheidende Unterschied liegt zwischen einem Agenten, der den Server bittet, einen bekannten Testbefehl auszuführen, und einem Agenten, der vor der Ausführung verändert, was dieser Testbefehl bedeutet. Im MCP-Protokoll können beide als `run_test` erscheinen. Ihr Risiko ist sehr unterschiedlich.

## Beobachte Prozessstarts, nicht nur Momentaufnahmen

Momentaufnahmen beantworten die Frage «Was ist jetzt am Leben?». Sie beantworten nicht «Was lief 200 Millisekunden und wurde dann beendet?». Beobachte bei verdächtigen oder sensiblen Tool-Aufrufen die Prozessstarts während der Anfrage.

macOS bietet Endpoint Security für Sicherheitsprodukte mit der erforderlichen Berechtigung. Gewöhnliche lokale Werkzeuge können diese Voraussetzung aber nicht annehmen. Entwirf keine Lösung, die von privilegierter Ereigniserfassung abhängt, wenn du diese Fähigkeit nicht tatsächlich auslieferst und betreibst. Für eine Untersuchung durch Entwickler kannst du einen kontrollierten Wrapper um das Programm verwenden und Start- sowie Enddaten sammeln oder den Server unter einem in deiner Umgebung verfügbaren Prozessmonitor ausführen.

Ein einfacher Wrapper macht die Ausführung sichtbar, wenn du den Befehlsweg des Servers kontrollierst:

```sh
#!/bin/sh
printf '%s pid=%s ppid=%s cwd=%s argv=%s\n' \
  "$(date -u +%Y-%m-%dT%H:%M:%SZ)" "$$" "$PPID" "$(pwd)" "$*" \
  >> "$HOME/.local/state/mcp-exec.log"
exec /usr/bin/git "$@"
```

Dieses Artefakt zeichnet die Identität des Wrappers auf und ersetzt den Wrapper anschließend mit `git`, indem es `exec` verwendet. Der Wrapper bleibt dadurch nicht als irrelevanter Elternprozess bestehen. Das erfasst nicht jeden Nachkommen, den `git` erzeugen könnte, und der Wrapper darf keine Geheimnisse in Argumenten erhalten. Verwende ihn, um eine kontrollierte Annahme zu prüfen, nicht als vollständiges Auditsystem.

Bei einem bestehenden Prozess kann `dtruss` unter macOS Systemaufrufe sichtbar machen. Dafür sind jedoch häufig erhöhte Berechtigungen nötig, und die Ausgabe ist umfangreich. Es ist ein Untersuchungswerkzeug, keine Routineüberwachung. Beginne mit dem Prozessbaum, `lsof` und den Anwendungslogs. Verwende Systemaufruf-Überwachung erst, wenn du eine konkrete Frage beantworten musst, etwa ob ein Kindprozess einen weiteren Pfad ausgeführt oder eine Verbindung zu einem Socket hergestellt hat.

Verwende UTC-Zeitstempel und erfasse bei jedem Ereignis die Eltern-PID. Eine Befehlszeile ohne Elternprozess und Zeitangabe ist schwache Evidenz. PID-Werte werden nach dem Ende von Prozessen wiederverwendet. Eine spätere Momentaufnahme kann daher versehentlich einen neuen, unabhängigen Prozess einem alten Vorfall zuordnen. Die Startzeit aus `ps` oder deinem eigenen Ereignisprotokoll hilft, diesen Fehler zu vermeiden.

## Eine Freigabe muss die ausführbare Grenze benennen

Eine Bestätigung wie «run_test ausführen?» liefert einem Menschen zu wenig Information. Die freigebende Person muss wissen, welcher signierte Prozess die Aktion initiiert hat, welches Programm ausgeführt wird, welches Arbeitsverzeichnis gilt, ob die Aktion das Netzwerk erreichen kann und ob sie weitere Programme starten darf. Eine vage Freigabekarte bringt Menschen dazu, vage Aktionen zu genehmigen.

Versuche nicht, das Problem zu lösen, indem du für jeden `fork`-Aufruf eine Freigabe verlangst. Das führt zu Freigabeermüdung. Nutzer bestätigen dann mechanisch oder schalten die Abfragen ab. Prüfe die wichtigen Übergänge: einen neuen Pfad zu einer ausführbaren Datei, den Wechsel von lokaler Arbeit zu Netzwerkzugriff, einen aus einer veränderlichen Projektdatei stammenden Befehl oder einen Kindprozess, der über die Anfrage hinaus bestehen bleibt.

Informationen zur Codesignatur liefern unter macOS ein nützliches Identitätssignal, aber übertreibe ihre Aussage nicht. Untersuche eine ausführbare Datei mit:

```sh
codesign -dv --verbose=4 /path/to/executable 2>&1 | sed -n '1,20p'
```

Der Befehl meldet die Signaturinstanz, falls eine vorhanden ist, und einen Fehler bei unsigniertem Code. Das zeigt, wer die von macOS untersuchte Datei signiert hat. Es sagt nicht, ob die Argumente sicher sind, ob die Konfiguration vertrauenswürdig ist oder ob das Programm anschließend ein unsigniertes Skript ausführt. Prüfe den Pfad des Kindprozesses und seinen Ausführungskontext separat.

Sallyports Sitzungsautorisierung stellt die Codesignatur des verbindenden Prozesses in den Mittelpunkt. Das hilft bei der Entscheidung, ob ein neuer Agentenprozess überhaupt handeln darf. Die Einstellung für Schlüssel pro Aufruf passt zu dem engeren Fall, in dem jede Verwendung einer bestimmten Zugangsdatenart eine menschliche Entscheidung erfordert. Keine dieser Kontrollen sollte dazu dienen, so zu tun, als erkläre die Freigabe eines Elternprozesses automatisch jeden Nachkommen, den er starten kann.

Formuliere Freigaben anhand ihrer Folgen. «Diese Anfrage führt `/usr/bin/ssh` unter deinem Konto aus und verbindet sich mit `host.example`» lässt sich prüfen. «Dieses Tool benötigt Zugriff» nicht. Wenn das Tool Projektskripte aufrufen kann, sage es ausdrücklich. Menschen können fundierte Entscheidungen treffen, wenn die Anfrage eine konkrete Form hat.

## Begrenze den Server, bevor du den Kindprozess untersuchst

Wenn du einen unerwarteten Kindprozess findest, sichere zunächst genügend Beweise, um ihn zu erklären, und beende dann die gesamte Ausführungsfamilie. Beginne nicht damit, temporäre Dateien zu löschen oder den Rechner neu zu starten. Dadurch können der einzige Pfad, die Befehlszeile und die zeitlichen Beweise verschwinden.

Verwende bei einem noch laufenden Kindprozess diese Reihenfolge:

1. Erfasse die `ps`-Ausgabe für den Server, seinen Elternprozess und bekannte Nachkommen. Notiere PIDs, PPIDs, Laufzeit und vollständige Befehle.
2. Führe für den verdächtigen Prozess `lsof -nP -p <pid>` und `lsof -nP -i -p <pid>` aus. Speichere die Ausgabe außerhalb des Agentenarbeitsbereichs.
3. Beende den verdächtigen Nachkommen mit `kill <pid>`, wenn eine normale Beendigung sicher ist. Verwende `kill -KILL <pid>` nur, wenn er nicht beendet wird und die weitere Ausführung ein unvertretbares Risiko darstellt.
4. Stoppe den MCP-Server und widerrufe oder beende die Agentensitzung, die die Aktion ausgelöst hat. Prüfe auf Kindprozesse, die nach dem Ende des Servers einem anderen Elternprozess zugeordnet wurden.
5. Untersuche die ausführbare Datei, die Quelle ihres Starts sowie Änderungen am Repository oder an der Konfiguration, die den Befehl erzeugt haben.

`kill` beendet nicht automatisch eine Prozessgruppe oder alle Kindprozesse. Ein in den Hintergrund verschobener Nachkomme kann weiterlaufen, nachdem sein direkter Elternprozess beendet wurde. Deshalb brauchst du den Baum vor der Bereinigung. Platziere Helfer bei einem kontrollierten Server in einer eigenen Prozessgruppe oder einem überwachten Bereich, damit der Server die Gruppe beim Ende der Sitzung beenden kann. Teste dieses Verhalten mit einem absichtlich in den Hintergrund verschobenen Helfer, bevor du dich darauf verlässt.

Prüfe außerdem die Persistenz. Mit `launchctl print` kannst du unter macOS geladene Launch-Dienste untersuchen, wenn du ein bestimmtes Label oder eine bestimmte Domain hast. Entlade nicht blind fremde Jobs, nur weil ihre Namen unbekannt wirken. Ordne einen verdächtigen Kindprozess zuerst anhand von Pfad der ausführbaren Datei, Startkonfiguration und Zeitstempeln zu. Ein Prozess, der nach einem sauberen Neustart oder dem frischen Start des Servers wiederkehrt, erfordert eine andere Untersuchung als einer, der nur in einer Testsitzung existierte.

## Mache jede Aktion nachträglich erklärbar

Ein nützliches Audit-Log verbindet einen vom Nutzer freigegebenen Agentenprozess mit einer Anfrage, einer ausgeführten Aktion und ihrem beobachteten Ergebnis. Die Prozessdaten sollten den Pfad der ausführbaren Datei, die Elternkette, das Arbeitsverzeichnis, Start- und Endzeit sowie das Ziel von Netzwerkaktivität enthalten. Fehlt der Prozessbaum, kann das Protokoll nicht beantworten, ob ein Helfer zur Anfrage gehörte oder ein unabhängiger Prozess auf dem Rechner war.

Lege keine Geheimnisse in Audit-Daten ab, nur um sie vollständig zu machen. Speichere Namen oder undurchsichtige Kennungen von Zugangsdaten, die für Prüfer erforderlichen Metadaten und das bereinigte Ergebnis der Aktion. Ein Log, das die Herkunft durch das Kopieren von Bearer-Token klärt, hat einen zweiten Zugangsdaten-Speicher mit schwächeren Kontrollen geschaffen.

Manipulationsschutz ist nach einem Vorfall wichtig, weil lokale Logs von demselben Konto bearbeitet werden können, das den Befehl ausgeführt hat. Sallyport führt sowohl seine Sitzungs- als auch seine Aktivitätsjournale aus einem verschlüsselten, hashverketteten Audit-Log. `sp audit verify` prüft diese Kette offline, ohne einen Tresorschlüssel zu benötigen. Das liefert nützliche Belege für eine Agentenaktion. Die Prozessdaten brauchen aber weiterhin genügend Kontext, um zu erklären, was der lokale Server gestartet hat.

Nimm eine Prüfungsfrage in die Betriebsanleitung deines Servers auf: «Welches Programm hat diese Anfrage ausgeführt, und warum durfte dieses Programm in diesem Arbeitsverzeichnis vorhanden sein?» Wenn niemand die Frage anhand der Anfrageaufzeichnung und einer Prozessaufzeichnung beantworten kann, verkleinere den Funktionsumfang des Tools. Ein lokaler MCP-Server, der seine Nachkommen nicht erklären kann, hat mehr Befugnisse, als seine Betreiber sicher prüfen können.
