Ein Blog

Posts mit Tag "api-design"

How-to API nicht designen: String.split(String pattern) schneidet per default leere Strings am Ende des Ergebnis-Arrays weg (und nur da):

var arr = "a.b.".split("\\."); // ["a", "b"]

Dieses Verhalten alleine produziert schon Bugs (tatsächlich habe ich dadurch einen gefunden, der seit ~3 Jahren unbemerkt war).

Es wird aber noch besser! Wenn man das nicht will, also wenn man will, dass es sich so wie in jeder anderen Sprache verhält, muss man eine Überladung verwenden, die einen limit-Parameter hat. Warum limit? Na das ist die maximale Anzahl an Tokens für einen Match:

If n is non-positive then the pattern will be applied as many times as possible and the array can have any length. If n is zero [Anm. d. Red.: default] then the pattern will be applied as many times as possible, the array can have any length, and trailing empty strings will be discarded.

Das intuitive Verhalten wäre also:

var arr = "a.b.".split("\\.", -1); // ["a", "b", ""]

Wiedermal ein schöner API-gotcha, dieses Mal in C#/.NET:

Path.GetTempFileName() gibt den Pfad zu einer einzigartigen Datei zurück. Dabei wird diese Datei auch gleich (ohne Inhalt) angelegt. Sehr praktisch also! Man kann die Datei auch direkt löschen, wenn man weiß, dass man sie nicht mehr braucht.

Brauchst du etwas mehr, als eine einzige Datei? Es gibt praktischerweise auch Path.GetTempPath()!

Moment — diese Funktion gibt keinen eindeutigen Ordnernamen zurück, sondern den Pfad zum Temp-Verzeichnis des Nutzers. Wer gerade seinen Code refactored hat und davon ausging, dass man den Ordner sicher löschen kann, hat also vielleicht Pech. Wenn man darin dann Dateien mit gleichen Namen anliegt, wiegt sich auch in falscher Sicherheit. Nicht schön.

Dies ist ein schönes Beispiel, dass man Seiteneffekte immer deutlich machen soll. Schon bei GetTempFileName hätte man noch irgendwo ein Create in den Namen tun müssen, damit man sieht, dass diese Funktion auch tatsächlich noch etwas anderes macht, als einen String zurückzugeben. Alternativ hätte man auch bei GetTempPath noch einen anderen Namen finden können.

Naming things is hard.

Was mir bei einer CTF-Challenge gerade aufgefallen ist und wahrscheinlich dort für die Exploitation wichtig ist, ich aber bisher nicht auf dem Schirm hatte:

Bei JavaScripts str.replaceAll(a, b) werden in b Steuerzeichen wie $ interpretiert, auch, wenn a keine RegExp ist:

console.log("a_b_c_d_e".replaceAll("_", "$`"))
// 'aaba_bca_b_cda_b_c_de'

Kommt b also vom User, können damit Werte injiziert werden.

Für mich ist das unerwartetes Verhalten, da für mich nicht offensichtlich ist, dass der replacement-String in jedem Fall interpretiert wird. Gibt es für sowas Linter-Regeln?

How to design better APIs. Geht um HTTP-basierte-APIs.

Es gibt einen RFC für standardisierte Error-Responses von HTTP-APIs: RFC 9457 Problem Details.

Grundidee ist, dass der Server im Fehlerfall mit dem Content-Type application/problem+json antwortet. Das JSON im Body hat dann einige standardisierte Felder und welche, die die API selbst noch hinzufügen kann, bspw. mehr Kontext zum Fehler.

Es gibt einen Draft, um eine HTTP-API als deprecated zu markieren: The Deprecation HTTP Header Field

Baut auf RFC8594 auf, mit dem man bereits singalisieren konnte, wann eine HTTP-API abgeschaltet wird.