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_rsaenthalten. Der alte Export-Filter traf nur Namen auf oberster Ebene, sodass ein verschachteltes.sshtrotzdem 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:
- die Write-Denylist in
agent/file_safety.py; - der Export-Filter in
hermes_cli/profiles.py; - die
@file-Verweigerungsliste inagent/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:
- 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)/'
- 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.
- Erneut exportieren, prüfen, dass er sauber ist (derselbe grep liefert nichts), und dann diesen teilen.
Kurz zusammengefasst
hermes profile exporterzeugt 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.