Bevor du einen PR für den Plugin-Katalog öffnest, schau dir an, was sein Gate wirklich prüft


Du schreibst ein Plugin, öffnest den PR, den der Katalog verlangt, und CI kommt mit einer Zeile zurück: Der gepinnte sha ist kein Commit-Objekt im Repo. Du starrst auf dein YAML – du hast den sha direkt von der GitHub-Seite kopiert. Du hast den sha eines Tag-Objekts kopiert, und das Gate will den sha eines Commit-Objekts. In deinem PR sehen die beiden identisch aus.

Der Plugin-Katalog ist ein Ort, an dem andere Menschen Code auf ihren eigenen Maschinen ausführen, deshalb ist sein Gate keine Zeremonie – es ist eine echte Supply-Chain-Prüfung. Hier steht, was es verifiziert, warum es sich um etwas so Harmloses wie eine Umbenennung kümmert, und was du vor dem Einreichen bestätigen solltest.

Wo das Gate lebt und wann es läuft

Die Zulassung ist ein eigener Workflow, der nur bei PRs anspringt, die plugin-catalog/** berühren, damit er bei unabhängiger Arbeit nie rot werden kann. Er hat zwei Jobs:

  • structural – eine günstige Schema-Prüfung, die keine Hermes-Installation braucht: Sie validiert das gesamte Verzeichnis plugin-catalog/ und wahrt die Invariante, dass alles darin gültig bleibt.
  • pinned-source-validate – das eigentliche Supply-Chain-Gate: der von dir gepinnte sha muss in deinem Repo erreichbar sein, und das Plugin genau an diesem Commit muss hermes plugins validate bestehen.

Mit anderen Worten: Das Gate klont dein Repository wirklich, checkt den von dir deklarierten Commit aus und führt dort die Validierung aus. Es liest nicht dein YAML und glaubt dir aufs Wort – es liest den Code an diesem Commit.

Drei Felder werden einzeln geprüft

Jedes Feld in deinem Eintrag, das ein PR steuern kann, wird geprüft, bevor es verwendet wird:

Feld Was das Gate verlangt
repo eine https-URL ohne Leerzeichen
sha 40 Hex-Zeichen, die git cat-file -t als Commit-Objekt meldet
subdir ein reiner relativer Pfad, der nach der Auflösung noch innerhalb des gepinnten Clone liegt

Diese sha-Typprüfung ist die Quelle des Fehlers oben: Ein Tag-Objekt ist eine völlig legitime Objekt-ID, aber es schält sich zu einem Commit, den der Reviewer nie als Pin deklariert hat, also lehnt das Gate es ab. Die subdir-Prüfung funktioniert genauso – kein literaler Vergleich, sondern eine echte Pfadauflösung mit anschließendem Confinement-Test, und genau das macht .. als Ausbruch nutzlos.

Warum es sich um eine Umbenennung kümmert

Die Liste der geänderten Einträge nutzte --diff-filter=AM – nur hinzugefügt und geändert. Der Haken: Eine umbenannte Eintragsdatei landet nie in dieser Liste, und das Gate prüft nur Einträge auf der Liste. Benenne einen gefährlichen Eintrag um, und er rutscht komplett unter dem Gate durch.

Der Filter liest jetzt AMRT: hinzugefügt, geändert, umbenannt und typgeändert (etwa eine Datei, die zu einem Symlink wird) erreichen alle die Liste. Daneben gibt es eine gröbere Regel: Jeder Symlink irgendwo im Clone muss innerhalb des Clone bleiben. Diese Prüfung durchläuft absichtlich das gesamte Checkout – find steigt nicht in ein symlinktes Verzeichnis ab, also könnte ein Verzeichnis-zu-Verzeichnis-Link einen Ausbruch verbergen, den ein nur auf das Plugin-Verzeichnis beschränkter Scan nie sehen würde.

Damit Plugin-Code sich nicht auf das Gate stützen kann

Das ist der interessanteste Teil des Designs, denn das Gate führt Code aus, den du nicht überprüft hast (hermes plugins validate führt vom Plugin deklarierte Installationslogik aus):

  • Einträge werden von fd 3 gelesen, nicht von stdin. Plugin-Code, der die Standardeingabe leer saugt, würde die verbleibende Eintragsliste verschwinden lassen und die Validierung stillschweigend überspringen. Der Wechsel auf einen anderen Dateideskriptor schließt diese Tür.
  • Der Parser läuft unter python3 -I, setzt seine Variablen pro Eintrag zurück und schlägt bei jedem Parse-Fehler fail-closed mit einem Sentinel fehl.
  • Eintrags-PRs müssen reine Daten sein: Sie dürfen plugin-catalog/** und contributors/emails/** berühren und nichts anderes. Der Grund ist unverblümt – der Durchsetzungscode des Gates selbst (der Workflow, der Validator, das gebaute Hermes) wird aus dem PR-Checkout aufgelöst, also könnte ein Eintrags-PR, der auch Tooling ändert, genau das Gate entschärfen, das ihn prüft.

Die überstrenge Regel, die eingegrenzt wurde

Die erste Version des Fixes war zu weit gefasst: Sie verlangte „nur Daten“, sobald sich irgendetwas unter plugin-catalog/ änderte. Nach diesem Maßstab wären 29 der letzten 134 gemergten Katalog-PRs gescheitert – die mit den Attributions-E-Mail-Maps – und jeder README-, Policy- oder CI-PR, der das Verzeichnis berührte, wäre ebenfalls rot geworden.

Die Regel ist jetzt eingegrenzt: Sie läuft einmal, nur wenn sich tatsächlich ein Eintrags-YAML geändert hat, und sie erlaubt contributors/emails/** neben plugin-catalog/**. Derselbe PR hat außerdem den Full-History-Clone gestrichen: Der Diff ist der Merge-Commit von GitHub gegen seinen ersten Parent mit nur zwei Stufen Tiefe, und die Rename-Erkennung funktioniert weiterhin.

Eine Checkliste vor dem Einreichen

  • Pinne einen Commit-sha, keinen Tag-sha. Nimm die vollständige 40-stellige ID von der Dateiseite oder der Commit-Liste, nicht von der Releases/Tags-Seite.
  • Halte subdir als reinen relativen Pfad – kein ./, kein .., keine absoluten Pfade, keine Variablen.
  • Ein PR, eine Aufgabe. Ein Katalog-Eintrags-PR berührt plugin-catalog/** (plus Attributions-Maps) und nichts anderes – kein Tooling, keine Workflows, keine Docs.
  • Führe den Validator lokal aus. Es ist dasselbe Skript wie in CI: python3 scripts/validate_plugin_catalog.py plugin-catalog/.
  • Stelle sicher, dass das Plugin am gepinnten Commit besteht. Das Gate klont dein Repo und checkt diesen Commit aus, also ist „die neueste Version besteht, die gepinnte aber nicht“ ein sehr häufiges rotes Kreuz.

Wie der Katalog als Nutzer funktioniert (Stöbern, Installieren, Offline-Installation), steht im Plugin-Katalog-Leitfaden; die Einstiegspunkte auf Befehls-Ebene stehen in der hermes plugins-Referenz. Wenn dich stattdessen die Vertrauensgrenze auf der MCP-Seite interessiert, ist der MCP-Katalog-Sicherheitsleitfaden das passende Gegenstück.

Das Fazit

Die Design-Philosophie passt in einen Satz: Alles, was ein PR steuern kann, wird vor der Verwendung geprüft, und es wird nach der Auflösung geprüft, nicht wörtlich. Umbenennungen, .., Tag-Objekte, Symlinks, sogar leergezogene stdin – nichts davon ist „ein Bug im Code“; alles davon ist „der Checkpoint stand an der falschen Stelle“.

Für Plugin-Autoren ist das Erfreuliche, dass die Regeln stabil sind. Pinne ein Commit-Objekt, halte den Subdir relativ, mach eine Sache pro PR – und das Gate verifiziert in deinem Namen, dass das, was der Reviewer gesehen hat, und das, was ein Nutzer installiert, dasselbe Artefakt sind.