7 Min. Lesezeit

ANSI-Escape-Sequenzen für agentensichere Terminals bereinigen

Die Bereinigung von ANSI-Escape-Sequenzen hält Terminalsteuerungen, OSC-Payloads und Cursor-Tricks aus dem Kontext von KI-Agenten heraus und bewahrt dennoch brauchbare Befehlsergebnisse.

ANSI-Escape-Sequenzen für agentensichere Terminals bereinigen

Terminalausgabe ist Eingabe. Sobald ein Agent ein Befehlsergebnis lesen kann, kann jedes Byte darin versuchen, die Wahrnehmung des Agenten oder das Verhalten eines nahegelegenen Terminals zu verändern. stdout ist nicht vertrauenswürdig, nur weil dein eigener Befehl die Ausgabe erzeugt hat. Der Befehl leitet häufig Daten weiter, die jemand anderes kontrolliert hat.

Die Bereinigung von ANSI-Escape-Sequenzen gehört an die Grenze, an der Tool-Ausgabe zum Agent-Kontext wird. Sie sollte erfolgen, bevor ein Modell den Text erhält, bevor ein Mensch ein Live-Protokoll öffnet und bevor ein Sitzungsprotokoll wieder abgespielt werden kann. Farbcodes zu entfernen, nachdem das Modell sie bereits gelesen hat, beseitigt nur Spuren.

Das bedeutet nicht, dass jedes Befehlsergebnis in einen sterilen Block verwandelt werden muss. Agenten brauchen brauchbare Diagnosen. Die Aufgabe besteht darin, den semantischen Inhalt zu erhalten und zugleich Terminalanweisungen, Cursorbewegungen, Zwischenablageanforderungen, Hyperlinks und anderen Steuerverkehr nicht über eine Vertrauensgrenze hinweg zu transportieren.

Ein Terminalprotokoll ist nicht vertrauenswürdige Eingabe

Ein Shell-Befehl besitzt seine Ausgabe nicht. git log gibt Commit-Nachrichten aus. Ein Compiler zeigt Pfade und Quelltextausschnitte. Ein Test-Runner kann eine Testvorlage wiederholen. Ein SSH-Client zeigt ein Server-Banner, bevor er eine Eingabeaufforderung ausgibt. Ein Paketmanager liest Namen, Versionen, Metadaten und Fehlermeldungen von entfernten Registries. In jedem dieser Fälle kann eine externe Partei Text beeinflussen, der später einen Agenten erreicht.

Der häufigste Fehler beginnt mit einer praktischen Vereinfachung: stdout und stderr erfassen, zusammenfügen und das Ergebnis an die Unterhaltung des Agenten anhängen. Dadurch erhält die Ausgabe zwei Aufgaben. Sie berichtet ein Programmergebnis und dient zugleich als Anweisung im Kontext eines Sprachmodells. Terminalsteuerzeichen machen die erste Aufgabe unzuverlässiger, promptähnlicher Inhalt macht die zweite gefährlich.

Stell dir ein Repository vor, das einen Dateinamen mit Wagenrücklauf und Escape-Sequenz enthält. Ein Befehl, der diese Datei auflistet, kann eine Anzeige erzeugen, die ihren eigenen Präfix überschreibt. Ein Prüfer sieht einen harmlosen Pfad. Der rohe Byte-Strom enthält etwas anderes. Wenn ein Tool diesen Strom anschließend ohne sichtbare Bytegrenzen oder Steuerzeichenrichtlinie in eine Agent-Nachricht umwandelt, erhält der Agent ein mehrdeutiges Artefakt.

Beschränke das Bedrohungsmodell nicht auf einen böswilligen Repository-Mitwirkenden. Build-Artefakte, Stacktraces von Abhängigkeiten, Netzwerkgeräte, Datenbankfehler und entfernte Befehlsausgaben überschreiten dieselbe Grenze. Ein kompromittierter Server braucht keinen Shell-Zugriff auf dem Agent-Host, um ein Banner zurückzugeben, das ein Protokoll gezielt verfälscht.

Es gibt zwei getrennte Risiken:

  • Ein Terminalemulator kann Steueranweisungen ausführen, wenn jemand rohe Ausgabe betrachtet.
  • Ein Agent kann sichtbaren, verborgenen oder umgeordneten Text als Anweisung statt als Daten interpretieren.

Ein sauberes Agent-Protokoll verringert das zweite Risiko. Ein Viewer, der Terminalsteuerzeichen niemals interpretiert, verringert das erste. Wenn Menschen erfasste Ausgabe prüfen, brauchst du beides.

Terminalsteuerzeichen tun mehr, als Farbe hinzuzufügen

Terminal-Steuersequenzen können den Cursor bewegen, vorherigen Text löschen, einen Fenstertitel setzen, einen anklickbaren Link erzeugen, Inhalte in die Zwischenablage legen, ein Terminal abfragen oder Daten an eine Terminalfunktion übertragen. SGR-Farbe ist nur die bekannteste Teilmenge. Ein Filter, der ESC[ gefolgt von Ziffern und m entfernt, behandelt Farben, lässt aber wesentlich leistungsfähigere Sequenzen bestehen.

ECMA-48 definiert Steuerfunktionen und die allgemeine Form von CSI-Sequenzen. CSI beginnt normalerweise mit ESC und [, gefolgt von Parameterbytes, Zwischenbytes und einem Abschlussbyte. Der Standard erlaubt außerdem einbyteige C1-Formen im Bereich 0x80 bis 0x9f. Terminalemulatoren ergänzen dieses Verhalten durch private Funktionen. Die Dokumentation zu xterm control-sequences beschreibt OSC-, DCS-, APC-, PM- und SOS-Strings einschließlich Abschlusszeichen, die sich von einer CSI-Sequenz unterscheiden.

Diese Grammatik ist wichtig, weil Steuerzeichen nicht immer als saubere Textzeile eintreffen. Ein Programm kann ESC in einem Block und [ im nächsten schreiben. Ein Pseudo-Terminal kann die Ausgabe an jedem Byte teilen. Ein Tool kann einen OSC-String senden, der BEL als Abschluss verwendet, oder die zweibyte ST-Form ESC gefolgt von Backslash. Eine korrekte Grenzkomponente muss den Zustand über mehrere Lesevorgänge hinweg behalten.

Einige Beispiele zeigen, warum ein Farbfilter nicht genügt:

  • ESC[2J fordert das Terminal auf, die Anzeige zu löschen.
  • ESC[H bewegt den Cursor an die Startposition.
  • ESC]8;;URI ESC\\ beginnt in unterstützenden Terminals einen OSC-8-Hyperlink.
  • ESC]52;... BEL ist eine Zwischenablageoperation, die viele Terminalemulatoren erkennen.
  • Ein Wagenrücklauf bewegt den Cursor an Spalte null und kann eine frühere Statuszeile überschreiben.

Eine Sequenz muss nicht von jedem Terminal unterstützt werden, damit sie relevant ist. Ein Ausgabesammler kann nicht vorhersagen, welches Terminal ein Entwickler in sechs Monaten verwendet, welcher Viewer ein Protokoll wiedergibt oder welcher Parser ein Steuerbyte in ein sichtbares Token verwandelt. Beseitige die Mehrdeutigkeit bereits beim Erfassen.

Auch Steuerzeichen außerhalb von Escape-Sequenzen verdienen Aufmerksamkeit. Rückschritt, Wagenrücklauf, Seitenvorschub, Signalton und viele C1-Bytes können die Darstellung ändern oder die zeilenweise Verarbeitung verwirren. Bewahre nur die Steuerzeichen auf, die dein Ausgabeformat benötigt, normalerweise Zeilenvorschub und vielleicht Tabulator. Bewahre Wagenrücklauf nur auf, wenn ein Parser ihm eine ausdrücklich getestete Bedeutung gibt.

Steuerzeichen entfernen, bevor der Text das Modell erreicht

Der sicherste Standard ist einfach: Bytes erfassen, ihre Größe begrenzen, Terminalsteuerzeichen als Bytes parsen, Steueranweisungen verwerfen, die übrigen druckbaren Inhalte mit einer festgelegten Fehlerstrategie dekodieren und erst dann den bereinigten Text an den Agenten senden. Dekodiere nicht zuerst und hoffe, dass eine Unicode-Bereinigung gefährliche Inhalte findet. ESC ist ein ASCII-Byte, C1-Steuerzeichen können direkt auftreten und fehlerhafte Bytefolgen dürfen den Sammler nicht dazu bringen, die Filterung zu überspringen.

Ein praktischer Ausgabeweg hat vier Datensätze, auch wenn du dem Agenten nur einen davon zugänglich machst. Bewahre den rohen Byte-Strom zur Untersuchung in einem geschützten Speicher auf. Erzeuge normalen Text für den Modellkontext. Erzeuge einen Entfernungsbericht für Betreiber und Protokolle. Speichere Metadaten wie Exit-Status, Dauer, Abschneidestatus, Byteanzahl und einen kryptografischen Digest des Rohinhalts.

Der Entfernungsbericht ist wichtig. Stilles Löschen kann ein Problem vor der Person verbergen, die es untersuchen muss. Ein sinnvoller Bericht könnte besagen, dass der Sammler zwei CSI-Sequenzen, einen OSC-String, drei Wagenrückläufe und eine ungültige Bytefolge entfernt hat. Der Payload muss nicht wiedergegeben werden. Einen OSC-Payload im Bericht zu wiederholen, kann dieselbe Gefahr erneut einführen.

Wende die Grenze getrennt auf stdout und stderr an, bevor du beide Streams zusammenführst. Programme verschachteln die Streams auf eine Weise, die ihre ursprüngliche Reihenfolge nach dem Zusammenführen durch einen Wrapper nicht zuverlässig erhält. Wenn ein Agent eine gemeinsame Darstellung braucht, kennzeichne die beiden bereinigten Streams und füge eine vom Sammler vergebene Sequenznummer hinzu. Das bewahrt mehr Wahrheit als ein künstliches Protokoll, das eine nie beobachtete genaue Reihenfolge behauptet.

Größenlimits gehören in dieselbe Komponente. Ein Angreifer kann einen nie abgeschlossenen OSC-String oder sich wiederholende normale Ausgabe verwenden, um Speicher und Kontext zu verbrauchen. Begrenze die insgesamt erfassten Bytes hart und setze ein kleineres Limit für jeden laufenden Steuer-String. Wenn das Limit greift, schließe oder leere die Quelle gemäß deiner Prozessrichtlinie, markiere das Ergebnis als gekürzt und halte das teilweise rohe Artefakt aus der Agent-Nachricht heraus.

Bitte ein Modell nicht darum, zu entscheiden, ob eine Escape-Sequenz harmlos ist. Modelle verarbeiten Text, keine Byteprotokolle, und ihre Entscheidung kann vom umgebenden Kontext abhängen. Ein deterministischer Parser sollte diese Entscheidung treffen, bevor das Modell die Ausgabe sieht.

Isolation bewahrt Beweise, nicht Verhalten

Bereinigung und Isolation lösen unterschiedliche Probleme. Bereinigung erzeugt nutzbaren Text, indem sie Darstellungsanweisungen entfernt. Isolation hält die ursprünglichen Bytes für Menschen mit einem legitimen Untersuchungsgrund verfügbar. Eine rohe Aufzeichnung als «bereinigt» zu bezeichnen, nur weil du sie base64-kodiert hast, verwechselt Transport mit Berechtigung.

Ein Agent braucht normalerweise die ersten paar hundert Zeilen eines fehlgeschlagenen Builds, nicht die bytegenaue Wiedergabe einer Terminalsitzung. Gib ihm normalisierten Text mit eindeutigen Markierungen für Kürzungen und entfernte Inhalte. Wenn er weitere Einzelheiten braucht, kann er einen begrenzten, bereinigten Ausschnitt nach Zeilenbereich oder Suchbegriff anfordern. Reagiere nicht, indem du die Rohaufzeichnung in denselben Kontext kopierst.

Wenn ein Ermittler das Original benötigt, öffne es in einem byteorientierten Viewer, der Steuerbytes sichtbar darstellt und sie niemals an ein Terminal sendet. Hex-Dumps funktionieren gut, weil sie Bytegrenzen sichtbar machen. Eine Darstellung, die ESC durch ^[ ersetzt, kann helfen, muss aber auch C1-Zeichen und String-Payloads korrekt behandeln. Ein Viewer, der die Beweisdaten mit cat ausführt, ist kein forensisches Werkzeug.

Dieser Shell-Befehl erstellt eine Beispieldatei, ohne ihre Steuerzeichen an dein aktuelles Terminal auszugeben. Die Datei enthält roten SGR-Text, eine CSI-Sequenz zum Aufwärtsbewegen des Cursors, einen OSC-8-Hyperlink und einen Wagenrücklauf:

printf 'build: \033[31mFAIL\033[0m\nnotice\033[1A\033]8;;https://example.invalid\033\\open\033]8;;\033\\\rPASS\n' > terminal-sample.bin
od -An -tx1c terminal-sample.bin

Die Ausgabe von od sollte 1b für ESC und 0d für den Wagenrücklauf enthalten. Sie sollte dein Terminal niemals dazu bringen, dem Link zu folgen oder den Cursor zu bewegen, weil od eine Darstellung der Bytes ausgibt, statt sie wiederzugeben.

Isolation braucht ebenfalls Zugriffskontrolle. Ein Rohartefakt kann Geheimnisse enthalten, die ein Befehl versehentlich ausgegeben hat. Das Bereinigen von Terminalsteuerzeichen entfernt keine Tokens, Passwörter oder personenbezogenen Daten. Führe Geheimniserkennung und Schwärzung als separate Stufe mit eigener Richtlinie für Fehlalarme aus. Vermische die Aufgaben nicht: Ein Schwärzer, der ein Zugangsdatenproblem übersieht, darf nicht zugleich entscheiden, ob eine OSC-52-Anforderung bestehen bleibt.

Regex ist kein Terminalparser

Die Aktionsspur offline verifizieren
Das verschlüsselte Audit-Protokoll ist hashverkettet und kann mit sp audit verify offline geprüft werden.

Ein regulärer Ausdruck ist beliebt, weil er farbige Build-Ausgabe in einer Zeile entfernt. Als Sicherheitsgrenze versagt er, weil die Terminalgrammatik zustandsbehaftet und als Datenstrom organisiert ist. Viele Muster haben außerdem Leistungsprobleme bei langen fehlerhaften Eingaben, genau bei Eingaben, die ein Angreifer liefern kann.

Verwende eine Byte-Zustandsmaschine mit ausdrücklich definiertem Verhalten für ESC, CSI und String-Steuerzeichen. Die folgende Python-Funktion ist absichtlich eng gefasst. Sie erhält Tabulator und Zeilenvorschub, bildet Wagenrücklauf nach einer sichtbaren Zeilenumbruchregel ab, verwirft andere C0- und C1-Steuerzeichen und entfernt ESC-Sequenzen einschließlich OSC-, DCS-, APC-, PM- und SOS-Strings. Sie akzeptiert Blöcke erst, nachdem der Aufrufer sie zusammengeführt hat. Eine Produktionsversion muss daher die Zustandsfelder zwischen Lesevorgängen behalten.

def clean_terminal_bytes(data: bytes) -> tuple[str, dict[str, int]]:
    out = bytearray()
    counts = {"esc": 0, "csi": 0, "string": 0, "control": 0}
    i = 0

    while i < len(data):
        b = data[i]

        if b == 0x1b:  # ESC
            counts["esc"] += 1
            i += 1
            if i >= len(data):
                break
            nxt = data[i]

            if nxt == ord('['):  # CSI
                counts["csi"] += 1
                i += 1
                while i < len(data):
                    c = data[i]
                    i += 1
                    if 0x40 <= c <= 0x7e:
                        break
                continue

            if nxt in b']P_^X':  # OSC, DCS, APC, PM, SOS
                counts["string"] += 1
                i += 1
                while i < len(data):
                    c = data[i]
                    if c == 0x07:  # BEL
                        i += 1
                        break
                    if c == 0x1b and i + 1 < len(data) and data[i + 1] == ord('\\'):
                        i += 2
                        break
                    i += 1
                continue

            i += 1  # Two-byte ESC function or unknown ESC form
            continue

        if b == 0x9b:  # Single-byte C1 CSI
            counts["csi"] += 1
            i += 1
            while i < len(data):
                c = data[i]
                i += 1
                if 0x40 <= c <= 0x7e:
                    break
            continue

        if 0x80 <= b <= 0x9f or b < 0x20 and b not in (0x09, 0x0a):
            counts["control"] += 1
            i += 1
            continue

        out.append(b)
        i += 1

    return out.decode("utf-8", errors="replace"), counts

Dieses Beispiel hat Grenzen. Es bildet nicht jede ECMA-48-Steuerfunktion ab und behandelt unbekannte ESC-Formen als entfernbar. Das ist angemessen, wenn Ausgabe in einen Agent-Kontext gelangt. Ein Terminalemulator braucht breite Kompatibilität. Eine Agent-Grenze braucht eine kleine zugelassene Oberfläche.

Kopiere diese Funktion nicht einfach und erkläre die Aufgabe für erledigt. Füge maximale Längen für CSI-Parameter und String-Steuerzeichen hinzu. Bewahre den Parserzustand über Lesegrenzen hinweg. Zähle fehlerhafte und nicht abgeschlossene Sequenzen. Schreibe vor allem Tests, die sicherstellen, dass der rohe Payload nie im bereinigten Ergebnis auftaucht. Ein Sicherheitsfilter braucht negative Tests, nicht nur ansprechende Vorher-Nachher-Bilder.

OSC-Sequenzen verdienen besondere Aufmerksamkeit

Bei OSC-Strings erkennen viele Teams, dass Terminalausgabe mehr als Formatierung transportiert. Xterm dokumentiert OSC-Befehle etwa für Fenstertitel und Hyperlinks. Moderne Terminalemulatoren unterstützen unterschiedliche Teilmengen, daher sind Allowlists, die sich am heutigen lokalen Terminal orientieren, eine schlechte Wahl.

OSC-8-Links können harmlos aussehenden Text in ein anklickbares Ziel verwandeln. Das sichtbare Label kann «build report» lauten, während das Ziel an einen anderen Ort führt. Ein Mensch, der ein bereinigtes Agent-Protokoll liest, braucht keinen aktiven Link. Bewahre das Label als normalen Text auf, wenn du es sicher parsen kannst, oder entferne die gesamte OSC-Hülle und behalte nur die folgenden druckbaren Bytes. Bewahre URI-Ziele nicht auf, sofern dein Produkt keine eigene Richtlinie zur URL-Prüfung und -Darstellung besitzt.

OSC 52 kann ein Terminal auffordern, den Inhalt der Zwischenablage zu setzen. Manche Terminals deaktivieren diese Funktion oder verlangen eine Einstellung, aber der Sammler darf sich darauf nicht verlassen. Wenn ein Rohprotokoll in einem permissiven Terminal wiedergegeben wird, kann der Befehl vom Angreifer gewählten Inhalt in die Zwischenablage eines Betreibers legen. Beim nächsten Einfügen kann dieser in einer Shell, einem Ticket, einem Chat oder einem Feld für Zugangsdaten landen.

Sequenzen zum Setzen des Titels verursachen ein unauffälligeres Problem. Sie können den Fenstertitel in einem Terminal-Multiplexer, einem Aufgabenwechsler des Betriebssystems oder einer Aufzeichnung ändern. Ein Titel, der wie eine Genehmigungsanforderung oder ein erfolgreicher Deploy aussieht, kann einen Betreiber täuschen, der viele Fenster überfliegt. Das Entfernen des gesamten OSC-Verkehrs beseitigt die ganze Klasse, ohne eine Liste pflegen zu müssen, die schnell veraltet.

Versuche nicht, OSC-Strings für das Verständnis des Modells zu erhalten. Ein Agent muss keinen Terminaltitel aktualisieren, keinen Terminal-Hyperlink anklicken und keine Zwischenablageoperation empfangen. Wenn ein Befehlsergebnis nützlichen URL-Text enthält, sollte der Befehl die URL als normalen Text ausgeben oder sie aus einer strukturierten Antwort extrahieren, bevor sie den Terminalpfad erreicht.

Dasselbe gilt für Device-Control-Strings und Application-Program-Commands. Manche Emulatoren ignorieren sie, andere ergänzen im Laufe der Zeit neue Funktionen. Eine Ausgabegrenze sollte standardmäßig ablehnen: Erkennt sie den Beginn eines Steuer-Strings, soll sie bis zu einem gültigen Abschluss oder bis zur konfigurierten Maximallänge einlesen, die Auffälligkeit protokollieren und den Payload aus dem normalen Kontext heraushalten.

Für jedes Tool einen Ausgabevertrag definieren

Entscheidungen neben Aktivitäten aufbewahren
Sessions zeichnet Agent-Läufe getrennt von den einzelnen Aufrufen auf, die sie ausführen.

Ein Bereiniger kann keine Ausgabeschnittstelle reparieren, die von einem Terminalprotokoll zugleich eine Datenbank, einen Bericht und eine Benutzeroberfläche verlangt. Tools sollten angeben, was sie an einen Agenten zurückgeben: normalen UTF-8-Text, strukturiertes JSON über einen Nicht-Terminal-Kanal oder eine Referenz auf ein isoliertes Artefakt. Terminalähnliche Ausgabe sollte die Ausnahme und nicht das Standardformat für den Austausch sein.

Deaktiviere bei eigenen Befehlen die Darstellung, wenn ein Agent sie aufruft. Viele Programme bieten ein No-Color-Flag, einen maschinenlesbaren Modus oder eine Umgebungsvariable. Bevorzuge strukturierten Output nur, wenn du sein Schema kontrollierst und seine Größe begrenzt. Auch JSON kann Prompt-Injection in Stringfeldern enthalten. Kennzeichne daher jedes Feld als Daten und halte nicht vertrauenswürdige Beschreibungen von Anweisungen getrennt.

Führe Befehle, die du nicht kontrollierst, über Pipes statt über ein Pseudo-Terminal aus, sofern sie kein Terminalverhalten benötigen. Ein Pseudo-Terminal begünstigt Fortschrittsanzeigen, Cursor-Überschreibungen, Titelfunktionen und andere Codepfade. Pipes machen die Ausgabe nicht sicher, reduzieren aber die Menge des Terminalprotokolls, die du behandeln musst.

Protokolliere die Herkunft zusammen mit dem Ergebnis. Agent und Prüfer sollten das aufgerufene Programm, das Arbeitsverzeichnis, den Exit-Code, den Quellstream, das Erfassungslimit und die Information sehen können, ob der Bereiniger Inhalte entfernt hat. Lass Tool-Ausgabe diese Felder nicht imitieren. Lege vom Sammler erzeugte Metadaten in einen eigenen Umschlag außerhalb des nicht vertrauenswürdigen Textes.

Ein solcher Umschlag kann so aussehen:

{
  "command": "test-runner --report plain",
  "exit_code": 1,
  "stdout": "142 tests passed\n",
  "stderr": "fixture failed at tests/login.txt:18\n",
  "sanitizer": {"removed_controls": 4, "truncated": false},
  "raw_artifact": "restricted:sha256:..."
}

Das Feld raw_artifact sollte eine Referenz sein, die der gewöhnliche Agent nicht auflösen kann. Wenn du den Inhalt auf Anfrage ohne neue Autorisierungsentscheidung zurückgibst, war die Referenz nur Kosmetik.

Feindselige Ausgabe außerhalb deiner täglichen Shell testen

Ein Bereiniger, der einen Unit-Test mit grünen und roten Farbcodes besteht, steht erst am Anfang. Teste Bytes, die Parserzustände überschreiten, unerwartet enden und mit Darstellungssteuerzeichen interagieren. Führe den Korpus über denselben Erfassungspfad aus, den deine Agenten verwenden, einschließlich Prozessstart, Pufferung, Protokollspeicherung und Darstellung im Transkript.

Beginne mit einem kleinen Korpus, in dem ein ESC-Byte am Ende eines Blocks und [ am Anfang des nächsten steht. Ergänze CSI-Sequenzen mit ungewöhnlichen Parameterlängen, OSC-Strings mit BEL und ST als Abschluss, einen OSC-String ohne Abschluss, C1-CSI-Bytes, Rückschritte, wiederholte Wagenrückläufe und ungültiges UTF-8 neben einer Escape-Sequenz. Stelle sicher, dass der agentenseitige Text kein ESC, keine Bytes aus dem C1-Bereich und keinen Payload entfernter Strings enthält.

Teste anschließend die Darstellungssemantik. Leite eine Statuszeile wie working 10%\rworking 20%\rfailure durch deine Wagenrücklaufregel. Wenn du sie als Zeilenumbruch erhältst, sieht der Agent den Verlauf. Wenn du das Überschreiben nachbildest, sieht der Agent nur failure. Beide Regeln können funktionieren. Dokumentiere und teste deine Wahl. Stille Verhaltensänderungen bei einer Parseraktualisierung erschweren die Untersuchung eines Vorfalls.

Fuzzing lohnt sich hier, weil Escape-Grammatiken wenige Zustände und einen riesigen Raum fehlerhafter Eingaben haben. Erzeuge zufällige Bytefolgen mit Schwerpunkt auf ESC, BEL, Backslash, C1-Bytes und langen nicht abgeschlossenen Strings. Die Eigenschaften sind klar: Der Filter beendet sich innerhalb eines Zeit- und Speicherbudgets, wirft keine Ausnahme und erzeugt keine unzulässigen Steuerbytes.

Teste auch den menschlichen Pfad. Eine Protokollseite, eine Desktop-Benachrichtigung, ein Terminalbereich und ein kopiertes Transkript können Inhalte jeweils anders interpretieren. Stelle den Rohkorpus nur in einem sicheren Byte-Viewer dar. Zeige bereinigten Text in jeder normalen Oberfläche an. Wenn ein Entwickler bei einer Routineuntersuchung einen rohen Protokolleintrag in ein interaktives Terminal kopieren kann, hat deine Isolationsgrenze eine Lücke.

Menschliche Genehmigung bereinigt kein Transkript

SSH-Schlüssel aus der Ausgabe heraushalten
Sallyport führt SSH-Befehle selbst aus, sodass der Agent den SSH-Schlüssel nie erhält.

Ein Genehmigungsdialog entscheidet, ob ein Agent eine Aktion ausführen darf. Er entscheidet nicht, ob die zurückgegebenen Bytes sicher angezeigt oder in einen Modell-Prompt aufgenommen werden können. Halte diese Kontrollen getrennt, sonst nimmt jemand an, dass die Genehmigung von ssh host command auch jedes Banner, jeden Dateinamen und jeden entfernten Fehler des Hosts bestätigt.

Diese Unterscheidung ist für Aktions-Gateways wichtig. Sallyport kann Zugangsdaten vom Agenten fernhalten und eine menschliche Autorisierung verlangen. Jede Integration, die Ergebnisse solcher Aktionen an einen Agenten sendet, braucht trotzdem eine Grenze zur Ausgabenormalisierung. Zugangsdatenverwaltung und Transkriptsicherheit beantworten unterschiedliche Fragen.

Versuche nicht, eine schwache Ausgabeverarbeitung durch wiederholte Genehmigungen auszugleichen. Eine Person, die jede Leseaktion genehmigen soll, wird nicht jedes Zeichen einer langen Antwort prüfen. Außerdem kann die Antwort erst nach der Genehmigung eintreffen. Eine Prüfung pro Aufruf ist bei sensiblen Aktionen sinnvoll. Sie macht nicht vertrauenswürdige Ausgabe nicht zu vertrauenswürdigem Kontext.

Bewahre den Entscheidungsnachweis neben dem bereinigten Ergebnis auf. Halte fest, dass der Betreiber einen Prozess oder Aufruf genehmigt hat, und speichere dann das tatsächliche Befehlsergebnis als nicht vertrauenswürdige Tool-Daten mit Bereinigungsbericht. Diese Trennung hilft bei einer Untersuchung, zwei Fragen auseinanderzuhalten: Wer hat den Vorgang autorisiert, und welche Daten hat der Vorgang zurückgegeben?

Rohdaten müssen außerhalb des normalen Agentenzugriffs bleiben

Eine häufige Ausweichlösung zerstört ein ansonsten solides Design: Wenn das bereinigte Ergebnis unvollständig wirkt, erhält der Agent ein Tool namens read_raw_output. Dieses Tool verwandelt eine Sicherheitsgrenze in eine bloße Verzögerung. Ein Angreifer muss die bereinigte Zusammenfassung nur verwirrend genug machen, damit der Agent nach dem Original fragt.

Verwende stattdessen eine eingeschränkte Abfrage. Eine Person kann das Artefakt in einem sicheren Viewer öffnen. Ein dedizierter Extraktor kann einen begrenzten Hex-Bereich, einen Digest-Vergleich oder druckbaren Text zurückgeben, nachdem er denselben Parser angewendet hat. Wenn ein Ablauf wirklich eine Untersuchung des Rohprotokolls erfordert, verlange eine ausdrückliche menschliche Entscheidung und verwende einen Viewer, der Terminalsteuerzeichen nicht ausführt.

Halte Aufbewahrungsregeln für Rohdaten und bereinigte Daten getrennt. Rohe Ausgabe braucht möglicherweise eine kürzere Aufbewahrung, weil sie Geheimnisse und schädliche Payloads enthalten kann. Bereinigte Transkripte können für die Prüfung von Agent-Sitzungen nützlich bleiben, enthalten aber weiterhin Geschäftsdaten. Hashes verbinden beide Datensätze, ohne dass normale Benutzer die Rohbytes abrufen müssen.

Die erste Implementierungsaufgabe ist keine Prompt-Regel, die Agenten auffordert, Terminalanweisungen zu ignorieren. Setze einen Byteparser zwischen Prozessausgabe und jedes agentenseitige Transkript, lass unbekannte SteuerSyntax verschwinden und bewahre das Original nur dort auf, wo gewöhnliche Agenten es nicht abrufen können. Damit beseitigst du eine ganze Klasse von Mehrdeutigkeiten, bevor ein Modell, ein Terminal oder ein müder Betreiber darüber nachdenken muss.

FAQ

Sind ANSI-Escape-Codes die einzigen Terminal-Steuersequenzen, die ich entfernen muss?

Nein. ANSI ist eine ungenaue Kurzbezeichnung, die meist Steuersequenzen nach ECMA-48 meint. Terminalemulatoren unterstützen jedoch auch private Erweiterungen. OSC-Befehle, Device-Control-Strings, C1-Steuerzeichen und emulatorspezifische Sequenzen gehören ebenfalls in die Prüfung.

Kann ich Terminalfarben beibehalten und trotzdem einen KI-Agenten schützen?

Farben können erhalten bleiben, wenn ein vertrauenswürdiger Renderer sie erst nach der Verarbeitung des Klartexts durch den Agenten anwendet. Leite die ursprünglichen SGR-Sequenzen nicht bloß zur Darstellung weiter. Die sichere Reihenfolge lautet: Steuerzeichen parsen und entfernen, danach optional eine eingeschränkte Darstellungsebene für Menschen erzeugen.

Ist Base64-kodierte Befehlsausgabe sicher, wenn ein Agent sie untersuchen soll?

Behandle sie als unsichere Daten, bis ein Parser sie normalisiert hat. Base64 verhindert, dass ein Terminal die Bytes während der Übertragung interpretiert. Ein Agent kann sie jedoch dekodieren und den ursprünglichen Steuerzeichenstrom wiedergeben. Halte Rohdaten aus dem Agent-Kontext heraus, sofern eine konkrete Untersuchung sie nicht erfordert.

Warum ist eine Escape-Sequenz wichtig, wenn das Modell keinen Terminalemulator besitzt?

Ein Terminalemulator wendet die Steuerzeichen an, während ein Agent sie als wörtlichen Text, dekodierte Tokens oder bereinigtes Protokoll lesen kann. Diese Empfänger haben unterschiedliche Fehlermöglichkeiten. Die Bereinigung schützt den Agent-Kontext, ein sicherer Viewer das Terminal des Menschen.

Wann sollte ich Ausgabe entfernen, statt sie unter Quarantäne zu stellen?

Standardmäßig solltest du die Ausgabe bereinigen, wenn der Agent Statusinformationen, Diagnosen und gewöhnliche Befehlsergebnisse benötigt. Isoliere sie, wenn die ursprünglichen Bytes für Debugging oder die Untersuchung eines Vorfalls wichtig sind oder sich die Ausgabe nicht sicher reduzieren lässt. Verwende niemals ein rohes Terminalfenster als Isolationsmechanismus.

Warum reicht ein regulärer Ausdruck zum Entfernen von Terminal-Escapes nicht aus?

Die meisten regulären Ausdrücke scheitern, wenn eine Sequenz sich über mehrere Datenblöcke erstreckt, C1-Formen verwendet, einen OSC-Payload enthält oder mit ST statt BEL endet. Eine kleine Zustandsmaschine ist leichter zu prüfen, weil sie Zustände und Abschlussregeln ausdrücklich benennt. Übergib ihr Bytes, keinen bereits dekodierten Text.

Kann sich Terminal-Injection über Agent-Protokolle oder Logs hinweg fortsetzen?

Ja. In einem gemeinsamen Kontext kann sie den nächsten Agent-Prozess ebenso leicht täuschen wie den aktuellen. Sitzungsprotokolle brauchen dieselben Normalisierungsregeln wie laufende Tool-Ausgaben. Bewahre einen separaten geschützten Rohdatensatz auf, wenn Untersuchungen Bytegenauigkeit erfordern.

Verhindern menschliche Genehmigungsdialoge Prompt-Injection über Terminalausgaben?

Eine Genehmigung bestätigt, dass eine Person eine Aktion erlaubt hat. Sie macht die zurückgegebenen Bytes nicht vertrauenswürdig. Ein genehmigter Befehl kann weiterhin schädliche Repository-Inhalte, entfernte Banner, Paketmetadaten oder Logzeilen lesen. Verarbeite die Ausgabe nach jeder genehmigten Aktion.

Wie sollte ich rohe Befehlsausgabe für forensische Zwecke aufbewahren?

Speichere die Rohbytes in einem zugriffsgeschützten Artefaktspeicher oder einem verschlüsselten Audit-Eintrag. Gib dem Agenten nur eine Referenz und eine bereinigte Zusammenfassung. Speichere Byteanzahl, Digest, Parser-Version und Anzahl der entfernten Elemente beim Artefakt. So kann ein Ermittler den Vorgang nachvollziehen, ohne schädliche Bytes an normale Agent-Läufe zu übergeben.

Welche Testfälle sollte ein Terminal-Bereiniger für Agenten enthalten?

Teste fragmentierte Sequenzen, OSC-8-Links, OSC-52-Zwischenablageanforderungen, Wagenrücklauf-Überschreibungen, Rückschritte, C1-Steuerbytes, ungültiges UTF-8 und nicht abgeschlossene Strings. Führe denselben Korpus über Pipes und Pseudo-Terminals aus, weil sich Programme anders verhalten, sobald sie ein TTY erkennen. Ein Bereiniger, der nur eine farbige Compilerfehlermeldung verarbeitet, ist noch nicht vertrauenswürdig.

Sallyport

Sallyport führt API-Aufrufe und SSH-Befehle für Ihren KI-Agenten aus. Die Schlüssel bleiben in einem lokalen Tresor auf Ihrem Mac; Sie geben jeden Lauf frei, und jede Aktion landet in einem versiegelten Journal.

© 2026 Sallyport · Open Source unter Apache-2.0 · Oleg Sotnikov