--- type: issue id: "0055" status: done created: 2026-08-16 milestone: M2 priority: medium area: security related: - "docs/issues/0054-ki-geraeuschunterdrueckung-element-call.md" - "docs/adr/0018-ki-geraeuschunterdrueckung-clientseitig-opt-in.md" - "docs/adr/0001-gitlab-kanonisch-push-mirror.md" gitlab_iid: "39" --- # Issue-0055: `@sorb`-Scope in ThreadNet-Web ist nirgends auf rohana festgelegt ## Problem / Motivation `ThreadNet-Web` bezieht `@sorb/threadnet-call-embedded` aus der Gitea-npm-Registry auf rohana, aber **im Repo steht nirgends, dass der Scope `@sorb` dorthin zeigt**. Es gibt weder eine `.npmrc` im Wurzelverzeichnis noch in `apps/web`, und die CI setzt auch keine. Dass es trotzdem funktioniert, liegt allein am Lockfile: `pnpm-lock.yaml` pinnt die vollständige Tarball-URL ``` tarball: https://rohana.axion1337.de/api/packages/sorb/npm/%40sorb%2F…tgz ``` und die CI installiert mit `--frozen-lockfile`. Solange niemand die Version anhebt, wird der Scope nie aufgelöst — und der fehlende Eintrag fällt nicht auf. Aufgefallen beim Anheben auf `0.19.2-threadnet.8` (#0054): `pnpm install` griff nach `registry.npmjs.org` und brach ab. ``` ERR_PNPM_FETCH_404 GET https://registry.npmjs.org/@sorb%2Fthreadnet-call-embedded: Not Found ``` Die Anhebung ging nur durch, weil der Scope für diesen einen Aufruf von Hand gesetzt wurde: ```sh env 'npm_config_@sorb:registry=https://rohana.axion1337.de/api/packages/sorb/npm/' pnpm install ``` Das steht in keiner Doku. Der nächste, der die Abhängigkeit anhebt, läuft in dieselbe Wand. ## Warum das mehr ist als eine Unbequemlichkeit Der heutige Fehlschlag ist **laut** — 404, Abbruch, niemand baut versehentlich etwas Falsches. Laut ist gut. Der Punkt ist, dass die Lautstärke von einer Bedingung abhängt, die uns nicht gehört: `@sorb/threadnet-call-embedded` existiert auf `registry.npmjs.org` **derzeit nicht**. Registriert dort jemand diesen Namen, löst genau derselbe Befehl nicht mehr mit 404 auf, sondern **erfolgreich — gegen ein fremdes Paket**. Das ist das Muster, das als *dependency confusion* bekannt ist, und die Bedingung dafür ist nicht „ein Angriff auf uns", sondern „jemand legt einen Scope-Namen an". Der Schutz besteht heute ausschließlich darin, dass ein Name auf einer fremden Registry noch frei ist. Verschärfend: Der Fehler träfe genau den Moment, in dem jemand eine Version anhebt — also den Moment, in dem eine neue Abhängigkeit ohnehin erwartet wird und ein Download nicht auffällt. Das ist dieselbe Klasse wie die Befunde aus #0054 und der Backup-Reihe: **die Absicherung besteht nicht, sie ergibt sich nur zufällig aus dem aktuellen Zustand.** Sie „meldet" auch nichts — sie funktioniert stillschweigend, bis sie es nicht mehr tut. ## Acceptance - Eine **eingecheckte** `.npmrc` in `ThreadNet-Web` bindet den Scope fest: `@sorb:registry=https://rohana.axion1337.de/api/packages/sorb/npm/` - Ein Anheben der Version funktioniert im **frischen Klon ohne Zusatzschritte** — das ist der eigentliche Nachweis, nicht das Vorhandensein der Datei. - Gegenprobe: Ein Lauf mit absichtlich unerreichbarer rohana **scheitert**, statt auf `registry.npmjs.org` auszuweichen. - Die anderen Repos der Gruppe sind auf denselben Befund geprüft: Wer sonst noch `@sorb`-Pakete zieht, hat dieselbe Lücke (`threadnet-call` selbst publisht nur, konsumiert aber ggf. auch). - Der Auslieferungsweg in `ThreadNet-Web:docs/axion1337-fork.md` nennt den Befehl zum Anheben. ## Notes **Kein Token nötig.** Der Lesezugriff auf die Registry ist anonym möglich (nachgeprüft: der Paket-Index antwortet ohne Authentifizierung). Die `.npmrc` enthält damit **nur eine URL und kein Geheimnis** und kann bedenkenlos eingecheckt werden — das ist der Grund, warum hier kein Secrets-Handling dranhängt. **Warum nicht einfach dokumentieren:** Ein Satz in der Fork-Doku würde den 404 erklären, aber die `registry.npmjs.org`-Auflösung nicht verhindern. Nach der Lehre aus #0053 und dem Bindmount-Fall ist Aufschreiben hier ausdrücklich **nicht** die Maßnahme: die Regel war beide Male vorhanden und hat den Fehler nicht verhindert. Wirksam ist die eingecheckte Datei, weil sie den falschen Weg gar nicht erst offen lässt. **Verwandt, aber getrennt:** Bei der Gelegenheit ist aufgefallen, dass der Job `publish_npm` in `threadnet-call` `allow_failure: true` trägt — ein *scheiternder* Publish färbt die Pipeline nicht rot. Das ist ein eigener Befund und gehört nicht in dieses Issue; hier nur notiert, damit er nicht verloren geht. ## Erledigt 2026-08-18 — `.npmrc` eingecheckt, Auflösung nachgewiesen Umgesetzt in `ThreadNet-Web` (`be323ed`): `.npmrc` im Wurzelverzeichnis bindet `@sorb` an die Registry auf rohana. **Ein Fund beim Umsetzen, der den naiven Fix stillschweigend zunichtegemacht hätte:** Upstreams `.gitignore` enthält `/.npmrc` (Zeile 7) — sinnvoll, wo die Datei Tokens trägt. Die Datei wäre also lokal geblieben, ohne dass irgendetwas gemeldet hätte; CI und frischer Klon hätten weiter gegen npmjs aufgelöst. Die Ausnahme steht jetzt ausdrücklich mit Begründung im `.gitignore` (`!/.npmrc`), damit der nächste Upstream-Merge die Lücke nicht wieder aufmacht. Dieselbe Klasse wie die Befunde aus #0054: gemeldeter Erfolg ohne Wirkung. **Abnahme — in einem isolierten Baum gemessen, nicht angenommen:** | Prüfung | Ergebnis | |---|---| | ohne `.npmrc` (heutiger Zustand) | löst gegen `registry.npmjs.org` auf, bricht ab | | mit `.npmrc`, frische Auflösung ohne Zusatzschritt | Tarball von `rohana.axion1337.de`, Integrity identisch mit dem Repo-Lockfile | | Gegenprobe rohana unerreichbar | `ERR_PNPM_META_FETCH_FAIL`, **kein** Ausweichen auf npmjs, kein Lockfile | | `pnpm install --frozen-lockfile` (CI-Weg) | grün, Lockfile unverändert | **Andere Repos geprüft:** `threadnet-call` *publisht* das Paket nur und schreibt die Scope-Zeile in seiner CI bereits selbst (`.gitlab-ci.yml`); es konsumiert keine `@sorb`-Pakete. In `gitops` kommt der Scope nicht vor. `ThreadNet-Web` war der einzige Konsument — die Lücke ist damit vollständig geschlossen, nicht nur an einer Stelle. **Dokumentiert:** `ThreadNet-Web:docs/axion1337-fork.md` hat jetzt einen Abschnitt „Element Call anheben" mit dem Weg ohne Umgebungsvariable, dem `--dir`-statt-`--filter` Fallstrick und der Prüfung, dass die Tarball-URL auf rohana zeigt. **Nicht angefasst:** `allow_failure: true` auf `publish_npm` — eigener Befund, liegt weiterhin bei sorb.