Dein Profil-Export hat deine Cloud-Zugangsdaten mitgeschickt


Du verbringst einen Morgen damit, ein Profil zu optimieren, und gibst es dann an ein Teammitglied weiter: hermes profile export work -o work.tar.gz, mit einer Notiz, die besagt: „Pack das aus, dann hast du mein Setup“. Sie packen es aus — und haben tatsächlich dein Setup, plus deine Zugangsdaten für die private Registry. Das tar.gz enthält skills/, config.yaml, state.db und ein .docker/config.json, an das du nie gedacht hättest.

Das war kein hypothetisches Leck. hermes profile export schloss einige Credential-Dateien aus, verfehlte aber die Credential-Verzeichnisse auf Betriebssystemebene vollständig. Zwei PRs, die am 3. und 4. Oktober gemergt wurden (#132201 und #132654), haben die Lücke geschlossen. Hier steht, was geleakt ist, warum, und was du heute prüfen solltest.

Stufe eins: .ssh, .aws, .gnupg, .kube

Ein früherer Fix (#130525) hielt bereits Hermes’ eigene Secrets — auth.json, .env, OAuth-Tokens — aus den Exporten heraus. Doch PROFILE_CREDENTIAL_PATHS deckte nur den home/-Teilbaum des Subprozesses ab; die Speicher des Betriebssystems standen schlicht nicht auf der Liste.

#132201 fügte die erste Charge hinzu: .ssh, .aws, .gnupg, .kube und direnvs .envrc. Außerdem behob er zwei benachbarte Probleme:

  • Verschachtelung. Ein aus einem echten Home kopiertes Skill-Verzeichnis kann skills/some-skill/.ssh/id_rsa enthalten. Der alte Export-Filter traf nur Namen auf oberster Ebene, sodass ein verschachteltes .ssh trotzdem mitgeschickt wurde. Nach dem Fix werden diese Speicher in jeder Tiefe verworfen.
  • Distribution-Installationen. Wenn du eine Profil-Distribution installierst, werden diese Pfade als benutzereigen markiert (profile_path_is_private), sodass ein Paket sie nicht mehr auf deiner Maschine platzieren oder überschreiben kann.

Stufe zwei: .docker, .azure, .config/gh, .config/gcloud

Einen Tag später bemerkte #132654, dass vier Schwesterverzeichnisse noch ungeschützt waren — und zwar genau die Verzeichnisse, deren Schreiben agent/file_safety.py bereits verweigert. Hermes weigerte sich, sie zu schreiben, und exportierte sie dann fröhlich:

Verzeichnis Was dort üblicherweise liegt
.docker/config.json Registry-Auth für private Images
.azure/accessTokens.json Azure CLI Access Tokens
.config/gh/hosts.yml Dein GitHub CLI Token
.config/gcloud/ Google Cloud ADC und credentials.db

Die @file-Kontextreferenz machte es schlimmer: Wenn du in einer Nachricht @file:~/.config/gcloud/application_default_credentials.json schriebst, wurde das Token in den Kontext expandiert. Und das Gateway leitet nicht vertrauenswürdigen Remote-Text durch die @file-Expansion, sodass es dieselbe Leck-Klasse war.

Warum es passierte: drei von Hand kopierte Listen

Die Grundursache war nicht ein vergessenes Verzeichnis. Dieselbe Liste war an drei Stellen von Hand kopiert worden, und die Kopien waren auseinandergelaufen:

  1. die Write-Denylist in agent/file_safety.py;
  2. der Export-Filter in hermes_cli/profiles.py;
  3. die @file-Verweigerungsliste in agent/context_references.py.

Der Fix gibt der Liste eine einzige Quelle. agent/file_safety.py enthält jetzt:

HOME_CREDENTIAL_DIRS = (".ssh", ".aws", ".gnupg", ".kube",
                        ".docker", ".azure", ".config/gh", ".config/gcloud")

build_write_denied_prefixes leitet sich davon ab, und hermes_cli/profiles.py packt dasselbe Tupel in _OS_CREDENTIAL_STORES aus, sodass die Export-Listen (PROFILE_CREDENTIAL_PATHS, _EXPORT_CREDENTIAL_FILES) nicht mehr von der Write-Denylist abweichen können. agent/context_references.py importiert es ebenfalls, wodurch @file .config/gcloud hinzugewann.

Der Export-Filter änderte außerdem, wie er abgleicht — über abschließende Pfadkomponenten statt über Namen auf oberster Ebene:

def _export_credential_entries(directory: str, contents: list) -> set:
    """Entries of *directory* that are an _EXPORT_CREDENTIAL_FILES store: matched on trailing path
    components, so ``.config/gh`` drops at any depth while the rest of ``.config`` ships."""

So werden .config/gh und .config/gcloud verworfen, wo immer sie liegen, während unabhängige Inhalte wie .config/other, .config/ghost oder .config/gh.bak weiterhin normal exportiert werden.

Schon einmal exportiert? Geh von einer Offenlegung aus

Dieser Fix schützt nur Exporte, die danach erstellt wurden. Wenn du ein Profil-Archiv geteilt hast — mit einem Teammitglied, einem File-Host oder einem Git-Repo —, ist die richtige Antwort die Rotation, nicht das Löschen der Datei:

  1. Schau zuerst nach, was tatsächlich im Archiv steckt (ein tar.gz lässt sich ohne Auspacken auflisten):
tar -tzf work.tar.gz | grep -E '(^|/)\.(ssh|aws|gnupg|kube|docker|azure|envrc)/|\.config/(gh|gcloud)/'
  1. Alles, was passt, ist verbrannt: SSH-Keys ersetzen, AWS-/Azure-/gcloud-Zugangsdaten rotieren, das GitHub-Token widerrufen und neu ausstellen, das Passwort der privaten Registry ändern.
  2. Erneut exportieren, prüfen, dass er sauber ist (derselbe grep liefert nichts), und dann diesen teilen.

Kurz zusammengefasst

  • hermes profile export erzeugt ein tar.gz und schließt jetzt Hermes’ eigene Credential-Dateien plus acht Credential-Verzeichnisse auf Home-Ebene aus;
  • die Lücke bestand, weil drei von Hand kopierte Listen auseinandergelaufen waren; der Fix macht eine Liste maßgeblich und gleicht über abschließende Pfadkomponenten ab;
  • die @file-Expansion und Distribution-Installationen teilen sich diese Liste, sodass alle drei gemeinsam nachgezogen wurden;
  • der Fix wirkt in die Zukunft — rotiere alles, was du bereits verschickt hast.

Für das größere Bild der Isolation siehe die drei Outbound-Lanes für die Secret-Redaktion und ein Profil umbenennen, ohne seine Identität zu verlieren; die vollständige Subcommand-Oberfläche steht in der hermes profile-Referenz.