# Fehler bei der URL-Kodierung: Agent-API-Eingaben vor Aufrufen testen

Eine URL ist keine harmlose Zeichenkette. Sie ist eine strukturierte Anweisung, deren Trennzeichen Host, Route, Parameternamen und Werte bestimmen. Wenn ein Agent vom Benutzer stammenden Text falsch mit einer URL verbindet, kann er einen anderen Endpunkt aufrufen als den, den ein Entwickler geprüft hat.

Ich habe erlebt, dass Teams eine Anfrage genehmigten, die im Tool-Protokoll wie `GET /records/alice` aussah. Später verbrachten sie einen ganzen Nachmittag damit herauszufinden, dass der Server eine Route mit einem zusätzlichen Slash, einem doppelten Query-Parameter oder einer decodierten Traversal-Sequenz erhalten hatte. Der Agent brauchte keinen exotischen Exploit. Gewöhnlicher Text wie `a+b`, `%2F`, `&admin=true` oder ein Name in einer nicht lateinischen Schrift reichte aus.

Fehler bei der URL-Kodierung werden leicht unterschätzt, weil ein HTTP-Client oft angeblich „die Kodierung übernimmt“. Er übernimmt einen Teil der Serialisierung, abhängig von seiner API und seinen Standardeinstellungen. Er kann aber nicht entscheiden, ob ein Benutzerwert in ein Pfadsegment, einen Query-Wert, einen Formular-Body oder überhaupt nicht in die Anfrage gehört. Diese Entscheidung muss in der Aktionsdefinition und in den Tests stehen.

## Das Anfrageziel, nicht die sichtbare Zeichenkette, bestimmt den Aufruf

Eine Anfrage kann an jeder Stelle ihre Bedeutung ändern, an der Software die URL parst oder neu zusammensetzt. Die Zeichenfolge, die ein Agent erzeugt, ist nur der Anfang. Deine Client-Bibliothek kann sie normalisieren, ein Reverse Proxy kann sie umschreiben und das Anwendungs-Framework kann sie vor dem Routing oder der Parameterbindung decodieren.

RFC 3986 teilt eine URI in Schema, Authority, Pfad, Query und Fragment. Innerhalb eines Pfads ist `/` ein Trennzeichen. In einem Query erhalten `&` und `=` nach gängigen Konventionen eine Bedeutung, obwohl RFC 3986 keine allgemeine Query-Grammatik definiert. Dieser Unterschied erklärt die meisten Fehler, die pauschal als „Kodierungsprobleme“ bezeichnet werden.

Betrachte eine Aktion, die ein einzelnes Projekt laden soll:

```text
GET https://api.example.test/projects/{project_id}
```

Wenn `project_id` den Wert `north/ops` hat, sind diese Anfrageziele nicht gleichwertig:

```text
/projects/north/ops
/projects/north%2Fops
```

Das erste enthält nach `projects` zwei Pfadsegmente. Das zweite versucht, einen Literal-Slash in einem Segment zu übertragen. Ob die zweite Form erhalten bleibt, hängt vom gesamten Weg zwischen Client und Anwendung ab. Manche Stacks decodieren `%2F` vor dem Routing und machen daraus die erste Form. Andere lehnen sie ab. Ein Client-Test, der nur feststellt, dass „die URL kodiert wurde“, beweist daher wenig.

Dasselbe Problem tritt bei Queries auf. Eine Aktion, die nach einer wörtlichen Phrase suchen soll, darf nicht einfach diese Vorlage verwenden:

```text
/search?q=USER_TEXT
```

und `USER_TEXT` durch Rohtext ersetzen. Bei `red&limit=500` kann das Ziel so aussehen:

```text
/search?q=red&limit=500
```

Die Anwendung sieht nun zwei Query-Parameter. Erstellt der Code dagegen ein Query-Objekt und übergibt `red&limit=500` als Wert von `q`, sollte er Folgendes erzeugen:

```text
/search?q=red%26limit%3D500
```

Genau diese Grenze muss getestet werden: semantisches Feld hinein, exaktes Anfrageziel heraus, geparstes semantisches Feld am Ziel.

Teste nicht nur mit Buchstaben und Zahlen. Solche Werte verbergen genau die Fehler, die Agenten sichtbar machen. Ein Agent erhält Support-Tickets, Titel von Issues, Branch-Namen, Dateipfade, kopierte URLs und freie Texte. Echte Eingaben enthalten Trennzeichen.

## Ein Pfadparameter ist ein Segment, keine unfertige URL

Behandle einen Pfadparameter als einzelnes Segment, sofern der API-Vertrag nicht ausdrücklich einen Pfad vorsieht. Diese eine Regel beseitigt erstaunlich viel Mehrdeutigkeit.

Entwickler hängen Pfade oft zusammen, weil das gut lesbar wirkt:

```javascript
const target = base + "/projects/" + projectId + "/builds";
```

Dieser Code weist `projectId` keine komponentenspezifische Bedeutung zu. Enthält der Wert `/`, `?`, `#` oder `%`, hängt das Ergebnis davon ab, was später mit `target` geschieht. Außerdem lädt er zu einem zweiten Fehler ein: Jemand sieht einen kodierten Wert im Log, ruft „vorsichtshalber“ noch einmal `encodeURIComponent` auf und erzeugt so eine andere Kennung.

Baue Segmente als Daten, kodiere jedes Segment genau einmal und verbinde nur die Trennzeichen, die zur Route gehören. In JavaScript macht diese kleine Hilfsfunktion den Vertrag sichtbar:

```javascript
function pathSegment(value) {
  if (typeof value !== "string" || value.length === 0) {
    throw new Error("project id must be a nonempty string");
  }
  return encodeURIComponent(value);
}

const path = "/projects/" + pathSegment(projectId) + "/builds";
```

`encodeURIComponent` passt hier, weil es `/`, `?`, `#`, `&` und `=` kodiert, die sonst den Pfad verändern oder einen Query-String beziehungsweise ein Fragment beginnen würden. Eine kleine RFC-3986-Menge, darunter Apostrophe und Klammern, bleibt jedoch unescaped. Das ändert normalerweise nicht die Pfadstruktur, aber ein strenger API-Vertrag kann eine engere Kodierung verlangen. Entscheide das anhand der API-Spezifikation, nicht aus Gewohnheit.

Verwende `encodeURI` nicht für ein einzelnes Segment. Die Funktion erhält URI-Trennzeichen, weil sie eine vollständige URI erwartet. Übergibst du ihr `north/ops`, bleibt der Slash erhalten und die Route ändert sich. Die Empfehlung ist beliebt, weil der Funktionsname passend klingt. Ihr Anwendungsbereich ist hier falsch.

Ein Pfad kann durchaus mehrere Segmente enthalten, etwa bei `/{owner}/{repository}`. Modellier das als zwei Felder, nicht als eine freie Zeichenkette `resourcePath`. Wenn ein Endpunkt wirklich eine undurchsichtige Kennung mit Slashes braucht, kann ein Query-Parameter oder JSON-Request-Body die bessere Lösung sein. APIs, die undurchsichtigen Text durch Routing-Schichten zwingen, überlassen allen Beteiligten das Raten über kodierte Slashes.

Es gibt außerdem eine Routing-Entscheidung, die der Client nicht reparieren kann. Viele Proxies und Frameworks normalisieren Punktsegmente wie `.` und `..`, fassen doppelte Slashes zusammen oder lehnen kodierte Trennzeichen ab. Frage den Betreiber des Endpunkts direkt: Findet das Routing vor oder nach dem Decodieren von Prozentsequenzen statt? Teste anschließend die bereitgestellte Route einschließlich Proxy. Die Dokumentation eines Frameworks, das allein auf dem Entwicklerrechner läuft, beantwortet diese Frage nicht.

## Query-Strings brauchen eine festgelegte Grammatik

Ein Query-String ist kein einziger escapeter Block. Er besteht aus Feldern, deren Grammatik zur API gehört. Vor einem sicheren Aufruf durch einen Agenten musst du wiederholte Namen, leere Werte, Arrays, Boolesche Werte, Leerzeichen und den Umgang mit Duplikaten festlegen.

Der WHATWG-URL-Standard und browserorientierte APIs verwenden bei `URLSearchParams` eine formularartige Query-Serialisierung. In dieser Konvention wird ein Leerzeichen oft zu `+`, während ein Literal-Plus zu `%2B` wird. Viele Server-Parser arbeiten genauso. RFC 3986 selbst sagt für eine allgemeine URI nicht, dass `+` ein Leerzeichen bedeutet. Wenn eine Komponente einen allgemeinen Parser und eine andere einen Formularparser verwendet, sind beide Tatsachen wichtig.

Verwende einen Query-Builder statt Stringvorlagen:

```javascript
const query = new URLSearchParams();
query.set("q", userText);
query.set("include_archived", "false");
for (const label of labels) query.append("label", label);

const url = "https://api.example.test/search?" + query.toString();
```

Für `userText = "C++ & systems"` kann die genaue Schreibweise `q=C%2B%2B+%26+systems` lauten. Ein Server mit Formular-Decoding sollte daraus `C++ & systems` wiederherstellen. Dein Regressionstest sollte den semantischen Wert der API erwarten und nicht auf `%20` bestehen. `%20` und `+` können in den anzutreffenden Query-Konventionen beide ein Leerzeichen darstellen. Ein Literal-Plus muss nach dem Parsen jedoch ein Literal-Plus bleiben.

Für wiederholte Felder braucht es eine ausdrückliche Entscheidung:

```text
?label=bug&label=security
?label=bug,security
?label=["bug","security"]
```

Die erste Form enthält einen wiederholten Namen. Die zweite ist ein einzelner Wert mit Komma, sofern die API nichts anderes sagt. Die dritte sieht wie JSON aus, ist aber kein JSON, solange der Server es nicht ausdrücklich parst. Sag einem Agenten nicht einfach „übergib die Labels in der URL“ und lass das Format offen. Gib der Aktion ein Array-Argument und lass sie dieses in genau die vom Endpunkt akzeptierte Form serialisieren.

Doppelte skalare Parameter sind ein weiterer leiser Fehler. Eine Anfrage mit `?role=user&role=admin` kann je nach Framework den ersten Wert, den letzten Wert, ein Array oder einen Fehler liefern. Liest eine Sicherheitsprüfung den ersten Wert, während ein nachgelagerter Dienst den letzten verwendet, entstehen widersprüchliche Entscheidungen. Lehne Duplikate für Felder, die nur einmal vorkommen dürfen, so früh wie möglich ab.

Auch Fragmente verdienen Beachtung. `#section` verlässt den Client normalerweise nicht als Teil einer HTTP-Anfrage. Fügt eine rohe Benutzereingabe ein `#` hinzu, kann ein URL-Objekt vor dem Versand alles danach entfernen. Kodiere das Zeichen innerhalb eines Pfad- oder Query-Werts, wenn es Daten ist. Verlasse dich nicht auf ein Log der ursprünglichen Zeichenkette, um zu erkennen, was den Server erreicht hat.

## Prozentzeichen und die Reihenfolge des Decodierens erzeugen verschiedene Kennungen

Decodiere Prozentsequenzen einmal an einer festgelegten Grenze. Wenn zwei Komponenten dieselbe Eingabe decodieren, kann ein zunächst harmloser Wert nach der ersten Prüfung zu einem Trennzeichen werden.

Der Text `%252F` wird nach einem Decodiervorgang zu `%2F`, nach einem zweiten zu `/`. Das ist wichtig, wenn die Validierung zwischen diesen beiden Vorgängen stattfindet. Ein Gateway könnte einen rohen `/` in einer Kennung ablehnen und `%252F` zulassen. Eine vorgelagerte Anwendung könnte anschließend erneut decodieren und die Route aufteilen. Dasselbe Muster gilt für `%252e`, das zu `%2e` und dann zu `.` wird.

Unterscheide in Entwurf und Tests drei Werte:

1. Die rohe Schreibweise auf der Leitung, etwa `%252F`.
2. Den Wert nach einem Percent-Decoding, etwa `%2F`.
3. Den Anwendungswert nach allen Parsern und Umschreibungen, etwa `/`.

Teams nennen alle drei oft „die URL“. Diese unklare Sprache führt zu schlechten Reviews, weil verschiedene Stufen verglichen werden, ohne dass es auffällt.

RFC 3986 rät URI-Erzeugern, dieselbe Zeichenkette nicht mehr als einmal zu kodieren oder zu decodieren. Das ist ein guter Grundsatz, aber noch kein Implementierungsplan. Definiere, dass deine Aktionsserialisierung dekodierte Anwendungsstrings annimmt, und lege fest, wo der Server rohe Anfragebytes akzeptiert. Alles dazwischen muss die Escapes erhalten oder Formen ablehnen, die es nicht unverändert bewahren kann.

Akzeptiere bereits kodierte Eingaben nicht aus Bequemlichkeit. Eine Agent-Anweisung wie „gib eine URL-kodierte Projekt-ID an“ zwingt das Modell zu raten, ob `%2F` Daten oder eine Anweisung ist. Die nächste Schicht weiß nicht, ob sie das Prozentzeichen erhalten oder zu `%25` kodieren soll. Akzeptiere Klartextfelder, kodiere sie einmal in der Aktionsschicht und lehne fehlerhafte Prozentsequenzen nur dort ab, wo rohe URLs absichtlich akzeptiert werden.

Unicode bringt eine weitere Grenze hinzu. Ein URL-Client wandelt Text normalerweise in UTF-8-Bytes um und kodiert Bytes, die in der gewählten Komponente nicht direkt vorkommen dürfen. Server-Frameworks können Unicode vor der Suche nach einem Benutzer oder einer Ressource unterschiedlich normalisieren. Halte Kennungen auf Anwendungsebene in einer kanonischen Form, wenn deine Domäne das verlangt. Versuche nicht, Identität durch URL-Escaping zu lösen. Escaping überträgt Bytes, entscheidet aber nicht, ob zwei optisch ähnliche Zeichenketten dasselbe Konto bezeichnen.

## Teste den gesamten Anfrageweg mit gewöhnlichen, aber schwierigen Eingaben

Ein nützlicher Kodierungstest prüft zwei Dinge: das vom Client ausgegebene Anfrageziel und die vom Empfänger geparsten Werte. Testest du nur eine Seite, kann ein Proxy oder Framework die Bedeutung dazwischen noch ändern.

Beginne mit einem kontrollierten Echo-Handler in deiner Testumgebung. Er sollte das rohe Anfrageziel aufzeichnen, sofern die Server-Laufzeit es bereitstellt, und anschließend geparsten Pfad sowie Query-Felder zurückgeben. Halte Anmeldedaten von diesem Endpunkt fern. Seine Aufgabe ist die Serialisierung, nicht die Authentifizierung.

Dieser kleine Node-Handler zeigt die Antwortstruktur:

```javascript
import http from "node:http";

http.createServer((req, res) => {
  const url = new URL(req.url, "http://local.test");
  const pairs = [...url.searchParams.entries()];
  res.setHeader("content-type", "application/json");
  res.end(JSON.stringify({
    requestTarget: req.url,
    pathname: url.pathname,
    queryPairs: pairs
  }, null, 2));
}).listen(8787);
```

Sende bekannte Fälle dorthin und bewahre die erwartete Ausgabe auf. Ein Query-Builder, der `C++ & systems` erhält, sollte genau ein `q`-Paar zurückgeben, dessen geparster Wert exakt `C++ & systems` lautet. Ein manuell gebauter Query-String verrät sich oft durch zwei Paare oder dadurch, dass Pluszeichen zu Leerzeichen werden.

```text
{
  "requestTarget": "/search?q=C%2B%2B+%26+systems",
  "pathname": "/search",
  "queryPairs": [["q", "C++ & systems"]]
}
```

Deine Testsuite sollte eine kompakte Matrix abdecken, keine große Menge zufälliger Strings:

- Leerzeichen, Literal-Plus, Prozentzeichen, Ampersand, Gleichheitszeichen, Fragezeichen und Hash.
- Slash und kodierter Slash in einer Kennung, die ein einzelnes Segment sein soll.
- Leere Strings, fehlende optionale Felder und wiederholte Query-Namen.
- Einen Unicode-Wert und einen Wert, dessen Prozentsequenzen wie eine zweite Decodierstufe aussehen.
- Eine vollständige, in ein Feld kopierte URL, das eigentlich eine Kennung erwartet.

Verwende Property-based Tests, wenn das in deinem Team bereits üblich ist. Verstecke die benannten Fälle aber nicht hinter generierten Beispielen. Sie dokumentieren, warum die Grenze existiert. Bei einer Regression ist `encoded slash stays inside project_id` viel nützlicher als eine Seed-Nummer.

Führe dieselben Integrationstests über den Weg aus, den der Produktionsverkehr nimmt. Ein direkter Test gegen einen Anwendungsprozess zeigt nicht, ob ein Proxy `%2F ablehnt`, doppelte Slashes umschreibt oder bei doppelten Query-Werten einen anderen Wert auswählt. Wenn die Produktionskante nicht lokal eingebunden werden kann, nutze eine Staging-Route mit derselben Konfiguration und mache den Test zu einem Release-Check.

## Gib Agenten strukturierte Argumente, keine Freiheit zum URL-Bau

Ein Agent sollte eine Aktion auswählen und typisierte Argumente liefern. Die Implementierung der Aktion sollte HTTP-Methode, erlaubte Origin, Routenvorlage, Query-Grammatik, Header und Kodierung bestimmen. Wenn der Agent eine vollständige URL übergeben darf, werden all diese Kontrollen zu einer mehrdeutigen Zeichenkette zusammengezogen.

Ein enger Aktionsvertrag kann so aussehen:

```json
{
  "name": "get_project_builds",
  "input": {
    "project_id": "north/ops",
    "branch": "release+candidate",
    "limit": 25
  }
}
```

Der Aktionscode sollte `project_id` als Kennung validieren, als einzelnes Pfadsegment kodieren, `branch` in einen Query-Builder geben, `limit` als Ganzzahl im erlaubten Bereich prüfen und die URL aus einer festen Origin erstellen. Das Modell muss weder ein Bearer-Token sehen noch entscheiden, wohin ein Ampersand gehört.

Diese Trennung verhindert auch Origin-Verwechslungen. Eine Zeichenkette, die mit `https://other.example` beginnt, gehört nicht in ein Kennungsfeld. Wenn eine Aktion tatsächlich eine vom Benutzer angegebene URL abruft, muss sie eine eigene Aktion mit schriftlich festgehaltener Allowlist, DNS- und Redirect-Regeln sowie einem klaren Zweck sein. Schmuggle keine beliebige Abruffunktion über ein Feld namens `callback` oder `file` ein.

Sei vorsichtig bei APIs, die Filtersprache in Query-Parametern akzeptieren. Ein Feld wie `filter=status:open AND owner:me` hat zwei Grammatiken: die URL-Serialisierung und die Filtersprache selbst. URL-Kodierung hält den Filter in einem Query-Wert. Sie macht den Filter nicht sicher. Parse oder beschränke die innere Sprache separat oder biete typisierte Filterfelder an.

Vermeide Geheimnisse in URLs. Query-Strings landen in Zugriffslogs, in bestimmten Situationen im Browserverlauf, in Telemetrie und in Fehlerberichten. Anmeldedaten gehören in den von der API vorgesehenen Autorisierungsmechanismus. Ein kodiertes API-Token wird nicht sicher, nur weil es in einem Query-String steht.

## Eine Genehmigung ist nützlich, kann aber keine fehlerhafte Anfrage reparieren

Menschliche Autorisierung und korrekte Serialisierung lösen unterschiedliche Probleme. Eine Genehmigung kann bestätigen, dass ein erkannter Agent-Prozess ein bestimmtes Geheimnis verwenden darf. Sie zeigt aber nicht, ob eine Prozentsequenz bei einem nachgelagerten Router zu einem Slash wird oder ob ein doppelter Parameter nach dem Parsen die Berechtigungen verändert.

Diese Unterscheidung ist wichtig, wenn jemand behauptet, Prompts oder Genehmigungskarten machten eine strenge Anfragekonstruktion überflüssig. Das stimmt nicht. Wer `https://api.example.test/projects/%252Fadmin` prüft, müsste jeden Decoder auf dem Anfrageweg gedanklich simulieren. Das ist keine angemessene Sicherheitskontrolle, besonders wenn ein Agent in einer Sitzung viele Aufrufe ausführen kann.

Setze deterministische Prüfungen vor die Genehmigungsgrenze:

- Akzeptiere strukturierte Felder statt eines fertig zusammengesetzten Anfrageziels.
- Fixiere Origin, Methode und Routenvorlage in der Aktionsdefinition.
- Serialisiere jedes Pfadsegment und jeden Query-Wert genau einmal.
- Lehne doppelte oder fehlerhafte Felder ab, die der Endpunkt nicht definiert.
- Teste das endgültige Ziel über den bereitgestellten Anfrageweg.

Sallyport kann Anmeldedaten aus einem MCP-fähigen Agenten heraushalten und eine Genehmigung für einen Agent-Prozess oder für jede Verwendung eines ausgewählten Schlüssels verlangen. Diese menschliche Kontrolle funktioniert am besten, wenn die Aktionsschicht bereits eine eindeutige Anfrage erzeugt.

Auch Logs brauchen beide Ebenen. Zeichne die genehmigte Aktion und ihre sicheren Argumente auf und bewahre anschließend eine bereinigte Darstellung des tatsächlichen Anfrageziels sowie des HTTP-Ergebnisses auf. Bei einem sensiblen Query-Feld kannst du den Wert ausblenden, aber den Parameternamen und genug Struktur behalten, um versehentliche Duplikate zu erkennen. Protokolliere niemals Autorisierungs-Header, nur weil eine Anfrage fehlgeschlagen ist.

## Routennormalisierung kann einen korrekten Client aushebeln

Selbst eine perfekt serialisierte Client-Anfrage kann sich an Infrastrukturgrenzen ändern. Proxies, Load-Balancer, Web Application Firewalls und Anwendungsserver haben jeweils eigene Regeln für doppelte Slashes, Punktsegmente, kodierte Trennzeichen und ungültige Escapes. Du brauchst einen Routenvetrag für die gesamte Kette.

Angenommen, der Client sendet diesen Pfad:

```text
/files/reports%2F2025%2Fnotes
```

Wenn die API eine undurchsichtige Datei-ID erwartet, lautet der gewünschte Anwendungsparameter `reports/2025/notes`. Ein Proxy, der vor der Weiterleitung decodiert, kann jedoch `/files/reports/2025/notes` senden. Ein Router für `/files/:id` könnte das ablehnen. Ein anderer Router könnte `/files/:folder/:year/:name` auswählen und einen anderen Handler aufrufen. Keines dieser Ergebnisse beweist, dass die Client-Kodierung fehlerhaft war.

Treffe für jede sensible Route eine Entscheidung. Du kannst kodierte Slashes an der Kante ablehnen und dokumentieren, dass IDs keine Slashes enthalten dürfen. Du kannst sie bis zum Handler erhalten und diese Eigenschaft testen. Oder du gestaltest den Endpunkt so um, dass undurchsichtige Daten außerhalb des Pfads liegen. Was du nicht tun solltest: das Verhalten versionsabhängigen Standardeinstellungen überlassen und es anschließend als Sicherheitsgrenze bezeichnen.

Achte auch auf Redirects. HTTP-Clients folgen ihnen möglicherweise automatisch, und eine umgeleitete URL kann einen anders normalisierten Pfad oder Query-String enthalten. Für Aktionen mit Anmeldedaten muss festgelegt sein, ob Redirects erlaubt sind, ob die Ziel-Origin übereinstimmen muss und ob Autorisierungs-Header bei einem Origin-Wechsel entfernt werden. Das ist ein eigenes Thema, trifft aber häufig im selben Anfragecode auf URL-Fehler.

Wenn du mehrere Dienste betreibst, teste Meinungsverschiedenheiten absichtlich. Sende denselben kodierten Pfad in einer Nichtproduktionsumgebung über die Kante und direkt an die Anwendung. Unterscheiden sich die geparsten Pfadwerte, behebe das, bevor du einem Agenten Zugriff auf die Route gibst. Ein geteilter Parser ist auch ohne Angreifer ein Betriebsproblem.

## Eine Regressionstestsuite sollte die beabsichtigte Bedeutung bewahren

URL-Tests sollen nicht eine einzige bevorzugte Schreibweise der Escapes erzwingen. Sie sollen die Beziehung zwischen Aktionsargumenten und der serverseitigen Bedeutung einer Anfrage bewahren, auch wenn sich Bibliotheken und Infrastruktur ändern.

Schreibe Assertions auf drei Ebenen. Unit-Tests prüfen, dass ein Pfadsegment-Encoder `a/b` in ein einzelnes kodiertes Segment umwandelt und dass die Query-Serialisierung `&` innerhalb eines Werts erhält. Aktionstests erfassen Methode, Origin, Pfad und Query-Paare, die an einen Echo-Handler gesendet werden. Integrationstests laufen über eine produktionsähnliche Kante und prüfen, ob der Handler die erwartete Route und die erwarteten Parameter erhält.

Wenn ein API-Vertrag unklar ist, schreibe die Mehrdeutigkeit auf und beseitige sie. „Unterstützt Suchtext in der URL“ ist kein Vertrag. Lege fest, ob ein leeres `q` akzeptiert wird, ob `q=a+b` ein Plus oder ein Leerzeichen bedeutet, ob wiederholte `tag`-Werte erlaubt sind und ob `%2F` in IDs zulässig ist. Diese Details sind nicht nebensächlich, sobald ein Agent beliebigen menschlichen Text in Aufrufe umwandeln kann.

Überdecke einen fehlschlagenden Test nicht, indem du die Eingabe früher decodierst. Früheres Decodieren lässt den Fall oft normal aussehen und verschiebt die Mehrdeutigkeit in eine weniger sichtbare Schicht. Bewahre rohen Benutzertest als Daten, bis der komponentenspezifische Serializer ihn verarbeitet. Prüfe anschließend, was der Empfänger tatsächlich geparst hat.

Sallyports Aktivitätsjournal erleichtert den Vergleich zwischen einem genehmigten Agent-Aufruf und dem zurückgegebenen Ergebnis. Die Audit-Kette kann mit `sp audit verify` offline geprüft werden. Nutze diese Spur zur Untersuchung einer Abweichung, nicht als Ersatz für die Entscheidung, was dein Endpunkt akzeptiert.

Der erste Test, den ich hinzufügen würde, ist beinahe banal: eine Kennung mit `/`, ein Query-Wert mit `+` und `&` sowie ein Prozentzeichen, das wörtlich erhalten bleiben muss. Wenn deine Aktion für diese Eingaben nicht exakt angeben kann, was der Server erhält, ist sie noch nicht bereit, Benutzertest von einem Agenten anzunehmen.
