2026-08-18 12:00:00 +00:00
---
type : issue
id : "0099"
status : open
created : 2026-08-06
milestone : M1
priority : medium
projekt : threadnet-web
gitlab_iid : "12"
related : []
---
# Upstream-Sicherheitsfixes lassen sich nicht mergen — kein gemeinsamer Vorfahre
> Adoptiert aus [threadnet-web#12](https://git.lab/axion1337.chat/ThreadNet-Web/-/issues/12) (2026-08-18, ADR-0019). Kommentare und Verlauf bleiben dort; kanonisch ist ab jetzt diese Datei.
Am 2026-08-06 gemessen (`docs/axion1337-fork.md` , Abschnitt 4): **Dieses Repo hat keine Upstream-Historie.** Element Web 1.12.17 kam am 2026-05-10 als kompletter Baum herein — in `3da3635` , im selben Commit wie das erste eigene Feature.
Damit gibt es **keinen gemeinsamen Vorfahren mit `element-hq/element-web`** . `git merge upstream/develop` ist nicht möglich; erzwungen kollidiert praktisch jede Datei.
## Warum das ein Sicherheitsthema ist, kein Build-Thema
Element veröffentlicht Sicherheitsfixes als neue Version. Bei einem normalen Fork zieht man sie mit einem Merge. Bei uns bedeutet dasselbe: neuen Upstream-Stand beschaffen und **unsere 12 Patches von Hand neu auftragen** — davon sieben in der Medien-Pipeline, wo Element gerade auf MVVM umbaut.
Das ergibt eine unangenehme Kette mit gitops#22 (Advisory-Monitoring): Wir würden von einer Lücke erfahren und wären trotzdem langsam. Die Zeitspanne zwischen „bekannt" und „gepatcht" ist das, was zählt — und sie ist hier strukturell zu lang.
⚠️ Verschärfend: Verschiebt Element beim MVVM-Umbau eine der `viewmodels/` -Dateien, entsteht **kein Konflikt** — unsere Zeilen sind schlicht weg, und Git meldet nichts.
## Was zu tun ist
1. **Zuerst messen, nicht bauen:** Auf welchem Stand ist Upstream inzwischen, und sind seit 1.12.17 Sicherheitsfixes für Element Web erschienen? Das beantwortet, ob das dringend ist oder Vorsorge.
2. `element-hq/element-web` als zweiten Remote aufnehmen und den Tag von 1.12.17 holen. Damit lässt sich ein Update wenigstens **als Diff** betrachten, statt blind zu kopieren.
3. Prüfen, ob sich ein gemeinsamer Vorfahre nachträglich herstellen lässt — ein Graft/Replace des Import-Commits auf den passenden Upstream-Tag. Wenn das trägt, sind künftige Updates wieder ein Merge.
4. Falls nicht: einen Ablauf schreiben, wie unsere 12 Patches auf einen neuen Stand aufgetragen werden — mit dem Funktionstest für ClamAV als Abnahme (verschlüsselte Datei senden, abgelehnte empfangen).
Zusammenhang: gitops#22 (Advisory-Monitoring) ist die Erkennung, dieses Issue die Reaktionsfähigkeit. Das eine nützt wenig ohne das andere.
*Gefunden am 2026-08-06 beim Vermessen der Merge-Reibung (Arbeitspaket 3 aus #7).*
2026-08-19 12:00:00 +00:00
## Schritt 1 beantwortet 2026-08-19 — es ist keine Vorsorge mehr
Das Issue verlangt ausdrücklich „zuerst messen, nicht bauen". Gemessen:
**Wir sind neun Veröffentlichungen zurück.** Import war Element Web **1.12.17**
(2026-05-10); Upstream steht bei **v1.12.26** (2026-08-18).
**Und es gibt eine offene Sicherheitsmeldung, die uns betrifft:**
| | |
|---|---|
| Advisory | `GHSA-wrcp-5v3v-3j6v` |
| veröffentlicht | **2026-07-20** (vier Wochen offen) |
| Einstufung | medium |
| Inhalt | Ein bösartiger Homeserver kann über die Startseite HTML in Element Web einschleusen |
| betroffen | ** < v1.12.22** — wir laufen 1.12.17 |
Die übrigen fünf Advisories im Projekt betreffen ältere Stände (≤ 1.11.x) und damit
nicht uns.
### Einordnung — real, aber schmal
Der Angriff setzt voraus, dass der Client auf einen **bösartigen Homeserver** zeigt.
Unsere Nutzer verwenden ihn gegen den eigenen Server, und Föderation ist seit
[ADR-0021 ](../adr/0021-foederation-geschlossen.md ) geschlossen.
**Aber die Voraussetzung ist erreichbar:** `disable_custom_urls` ist in
`element-values.yaml` **nicht gesetzt** , Element erlaubt damit standardmäßig die
Eingabe eines beliebigen Homeservers — und die Desktop-Builds werden verteilt. Wer
sich mit unserem Client woanders anmeldet, ist ungeschützt.
### Was daraus folgt
Die Antwort auf „dringend oder Vorsorge" lautet: **weder Panik noch Vorsorge, sondern
überfällig.** Ein Fork ohne Merge-Weg hat vier Wochen gebraucht, um eine bekannte
Lücke *nicht* zu schließen — genau die Zeitspanne, die dieses Issue als das
eigentliche Problem benennt.
**Sofort möglich, unabhängig vom Merge-Problem:** `disable_custom_urls: true` setzen.
Das nimmt dem Advisory für unsere Nutzer die Voraussetzung, kostet eine Zeile und
berührt die Fork-Frage nicht. Ob fremde Homeserver überhaupt erlaubt sein sollen, ist
ohnehin eine eigene Entscheidung — bei geschlossener Föderation liegt „nein" nahe.
Schritte 2– 4 (Upstream als Remote, Graft-Versuch, Patch-Auftragsverfahren) bleiben
offen; sie sind der eigentliche Umfang dieses Issues.
2026-08-19 12:00:00 +00:00
## Teilmaßnahme 2026-08-19 — `disable_custom_urls` an beiden Stellen
Entscheidung sorb. Gesetzt in **zwei** Dateien, nicht in einer:
| Ort | Datei | Commit | wirksam |
|---|---|---|---|
| Web-Client | `gitops:apps/production/custom-configs/element-values.yaml` | `d809b3f` | mit dem Flux-Abgleich (Element liest `config.json` beim Laden) |
| Desktop-Client | `ThreadNet-Web:apps/desktop/axion1337/config.json` | `e1e9a19` | **erst mit dem nächsten Desktop-Build** |
Die zweite Stelle wäre fast durchgerutscht: Der Desktop-Client lädt seine **eigene**
`config.json` , nicht die des Web-Deployments — derselbe Fallstrick wie beim
Themes-Rollout. Eine Änderung nur in `element-values.yaml` hätte den Web-Client
abgesichert und genau die Builds unberührt gelassen, die verteilt werden.
### Was sie bewirkt — und was nicht
Im Code unserer Fassung nachgelesen, nicht angenommen:
- `ServerPicker.tsx` : Der ** „Bearbeiten"-Knopf am Servernamen wird nicht gerendert**.
Über die Oberfläche ist der Homeserver damit nicht mehr wechselbar.
- `ErrorUtils.tsx` : Bei 401/403 benennt die Fehlermeldung den Server, statt generisch
zu bleiben — kleiner Nebengewinn für den Alltag.
⚠️ **Es ist eine Oberflächen-Sperre, keine technische.** `MatrixChat.tsx` übernimmt
`hs_url` weiterhin aus der URL — in zwei Registrierungs-Flüssen (mobile Registrierung,
Bestätigungslink) und **ohne** `disable_custom_urls` zu prüfen. Ein präparierter Link
bleibt davon unberührt.
**Für `GHSA-wrcp-5v3v-3j6v` heißt das:** Der bequeme, sichtbare Weg zu einem fremden
Homeserver ist weg, alle Wege sind es nicht. Die Maßnahme senkt die Wahrscheinlichkeit,
dass jemand versehentlich oder auf Zuruf woanders landet — sie ist **kein Ersatz** für
das Upstream-Update. Das bleibt Schritt 2– 4 dieses Issues.
2026-08-19 12:00:00 +00:00
## Schritt 2 erledigt 2026-08-19 — und er widerlegt eine Annahme des Issues
`upstream` (`element-hq/element-web` ) ist als zweiter Remote eingerichtet, `v1.12.17`
und `v1.12.26` sind flach geholt. Der Remote ist **lokale Konfiguration** und wird
nicht mitcommittet; die Einrichtung steht jetzt in
`ThreadNet-Web:docs/axion1337-fork.md` (Commit `fa5dcc5` ).
### Der Import ist NICHT der Tag `v1.12.17`
Das Issue und die Fork-Doku behaupteten beide, „Element Web 1.12.17 kam am 2026-05-10
als kompletter Baum herein". Die erste Messung mit dem neuen Remote widerspricht dem:
| Messung | Ergebnis |
|---|---|
| `git diff --stat v1.12.17 3da3635` | **783 Dateien, +17443 / − 10371** |
| nur bei uns vorhanden | **115 Dateien** |
| nur im Tag vorhanden | 62 Dateien |
Entscheidend sind zwei davon, weil sie datieren:
`apps/web/src/viewmodels/room/timeline/event-tile/body/TextualBodyViewModel.tsx` und
`packages/shared-components/src/core/roving/RovingTabIndex.tsx` — im Tag `v1.12.17`
**nicht vorhanden** , in `v1.12.26` **und in unserem Import vorhanden** . Der
`CHANGELOG.md` unseres Imports endet bei 1.12.17 (2026-04-30), weil er erst beim
Release fortgeschrieben wird.
**Die Grundlage ist also ein `develop`-Stand nach dem 1.12.17-Release** (1.12.18 kam am
2026-05-12), nicht der Tag. Ausgerechnet MVVM-Dateien belegen es — der Bereich, vor dem
dieses Issue ohnehin warnt.
### Folge für Schritt 3
**Ein Graft auf `v1.12.17` wäre falsch.** Er würde jede künftige Zusammenführung auf
eine erfundene Ahnenreihe stellen und Konflikte an Stellen erzeugen, wo keine sind —
oder schlimmer: keine erzeugen, wo welche gehören. Wer den gemeinsamen Vorfahren
nachträglich herstellen will, muss den passenden ** `develop` -Commit** finden. Das
braucht die **volle** Upstream-Historie; ein flacher Tag-Fetch reicht dafür nicht.
Schritt 3 bleibt damit offen, aber er hat jetzt eine tragfähige Fragestellung statt
einer falschen Zielmarke.
### Unverändert: die Advisory-Bewertung
Der Basisstand liegt zwischen 1.12.17 und 1.12.18 und damit klar unter 1.12.22 —
`GHSA-wrcp-5v3v-3j6v` trifft uns weiterhin. Die Korrektur ändert die Dringlichkeit
nicht, nur den Weg dorthin.
2026-08-19 12:00:00 +00:00
## Schritt 3 beantwortet 2026-08-19 — der Graft trägt, und er halbiert das Problem
Volle Upstream-Historie geholt (70.330 Commits, `.git` wächst von 87 MB auf 603 MB).
Damit ließ sich der gesuchte gemeinsame Vorfahre **suchen statt raten** .
### Die Basis ist `deadd548` vom 2026-05-08
Verfahren: für jeden `develop` -Commit im Fenster 25.04.– 16.05. die Zahl der gegen
unseren Import abweichenden Dateien messen — der Basis-Commit ist das Minimum. Gemessen
wurde **der Baum, nicht das Datum** .
Ergebnis: `deadd548` („Update storybook", #33437 ) mit **43 abweichenden Dateien** —
gegenüber **783** beim fälschlich angenommenen Tag `v1.12.17` . Und diese 43 zerfallen
sauber:
| Art | Anzahl | Was es ist |
|---|---|---|
| nur Modus (755 → 644) | **31** | der Import-Bug, den die Fork-Doku als historisch beschreibt |
| echte Inhaltsänderung | 12 | davon 5 + 1 Test = **unser Discord-Feature** , dazu `LICENSE` und `CHANGELOG.md` |
| Hinzufügungen | 2 | unsere Feature-Dateien |
Der inhaltliche Rest ist also **genau das, was wir damals selbst geändert haben** . Damit
ist `deadd548` der Stand, aus dem der Fork genommen wurde.
### Der Graft wurde getestet, nicht behauptet
In einem **Wegwerf-Klon** (Produktions-Repo unberührt, danach entfernt):
| | |
|---|---|
| vor dem Graft | `git merge-base main upstream/develop` → **leer** |
| nach `git replace --graft 3da3635 deadd548` | gemeinsamer Vorfahre ** `deadd548` ** |
| `git merge upstream/develop` | läuft durch, **32 konfliktbehaftete Dateien** |
⚠️ Ein Zwischenergebnis war wertlos und wäre fast als Erfolg durchgegangen: Der erste
Merge-Versuch gegen `v1.12.26` meldete „0 Konflikte" — er war aber **gar nicht
gelaufen** („refusing to merge unrelated histories"), weil der Tag flach geholt war und
nur einen einzigen Commit trägt. Erst die Gegenprobe auf `MERGE_HEAD` deckte das auf.
### Was die 32 Konflikte sind
**Nur vier sind Quellcode** — und es sind genau unsere Patches:
`MImageBody.tsx` , `MAudioBody.tsx` (ClamAV), `SdkConfig.ts` (Branding),
`RoomListItemViewModel.ts` (Discord-Feature). Der Rest sind
`.github/workflows/*` (die Fork-Doku führt sie als „gering — eigene Strecke") und
`pnpm-lock.yaml` , das ohnehin regeneriert wird.
### Was das ändert
Aus „neuen Upstream-Baum beschaffen und **zwölf Patches von Hand neu auftragen** " wird
„**vier Quellcode-Konflikte in einem normalen Drei-Wege-Merge** lösen".
Wichtiger noch ist die stille Gefahr, die verschwindet. Das Issue warnt: *„Verschiebt
Element beim MVVM-Umbau eine der `viewmodels/` -Dateien, entsteht kein Konflikt — unsere
Zeilen sind schlicht weg, und Git meldet nichts."* Mit echtem gemeinsamem Vorfahren
**meldet Git es** . Das ist der eigentliche Gewinn, nicht die Zahl.
### Entscheidung, die noch aussteht
Der Graft ist eine **lokale** Ersetzung (`refs/replace/*` ). Damit alle davon haben,
müsste er mitgeliefert werden — `git push origin 'refs/replace/*'` und ein Hinweis in
der Doku, dass Klone `git fetch origin 'refs/replace/*:refs/replace/*'` brauchen.
Alternative: kein Graft, sondern beim nächsten Update einmalig einen echten
Merge-Commit mit `--allow-unrelated-histories` bauen und ab dann normal weitermergen.
Beides ändert die Historie-Wahrnehmung des Repos dauerhaft und ist damit
**ADR-pflichtig** . Entscheidung sorb steht aus; Schritt 4 (Auftragsverfahren für die
Patches) wird bei Weg 1 oder 2 weitgehend gegenstandslos.
2026-08-19 12:00:00 +00:00
## Weg B belegt 2026-08-19 — mit einer wichtigen Präzisierung
Entscheidung sorb: **Weg B** ([ADR-0022 ](../adr/0022-upstream-anschluss-durch-einmaligen-merge.md )).
### `--allow-unrelated-histories` allein genügt NICHT
Gemessen gegen `v1.12.26` , im selben Klon, nacheinander:
| Vorgehen | Konflikte |
|---|---|
| nur `--allow-unrelated-histories` , ohne Vorfahren | **1757** |
| mit lokal gesetztem Graft auf `deadd548` | **32** |
Ohne gemeinsamen Vorfahren macht Git einen Zwei-Wege-Vergleich — das ist die „praktisch
jede Datei konfliktet"-Lage aus der Fork-Doku. **Der Graft ist also kein Gegenentwurf zu
Weg B, sondern sein Werkzeug:** lokal setzen, den Merge damit rechnen lassen, committen —
der Merge-Commit trägt danach die echten Eltern, und der Graft kann weg. Ab dann läuft
die Abstammung über den Merge-Commit selbst. Das steht so nicht in ADR-0022s
Optionsbeschreibung und ist hier nachgetragen.
⚠️ **Stolperstein auf dem Weg dahin:** Die mit `--depth=1` geholten Tags tragen eine
`.git/shallow` -Grenze. `v1.12.26` war dadurch auf **einen** Commit abgemauert, obwohl
derselbe Commit über `develop` vollständig vorlag — jeder Merge-Versuch scheiterte mit
„refusing to merge unrelated histories", was wie ein Grundproblem aussah und keines war.
`git fetch --unshallow upstream` löst es.
### Die 32 Konflikte, vollständig aufgeschlüsselt
| Menge | Was | Auflösung |
|---|---|---|
| 24 | `.github/workflows/*` | unsere Fassung behalten — eigene CI-Strecke |
| 1 | `pnpm-lock.yaml` | regenerieren |
| 1 | `apps/web/package.json` | **Produktentscheidung, siehe unten** |
| 6 | Quellcode, je 1– 2 Konfliktblöcke | von Hand |
**Der Fund, der die ganze Übung rechtfertigt:** Upstream hat
`RoomListItemAccessibilityWrapper` in `RoomListItemWrapper` **umbenannt** . Weil es jetzt
einen echten Vorfahren gibt, **meldet Git den Konflikt** — genau der Fall, den dieses
Issue als stille Gefahr beschreibt („unsere Zeilen sind schlicht weg, und Git meldet
nichts").
### Warum ich hier gestoppt habe
`apps/web/package.json` enthält zwei Zeilen, die keine Konfliktauflösung sind, sondern
Entscheidungen:
```
ours: "@sorb/threadnet-call-embedded": "0.19.2-threadnet.12"
upstream: "@element-hq/element-call-embedded": "0.22.0"
ours: "matrix-js-sdk": "github:matrix-org/matrix-js-sdk#d19cb751..."
upstream: "matrix-js-sdk": "42.2.0"
```
Die erste falsch aufgelöst, und die **gesamte KI-Geräuschunterdrückung (#0054 ) ist
stillschweigend weg**. Die zweite betrifft den Git-Ref-Pin, der seinerzeit ein Build-Fix
war (Abschnitt 3 der Fork-Doku) — ob Upstreams 42.2.0 ihn erübrigt, zeigt nur ein Build.
Beides ist ohne Build und ohne den ClamAV-Funktionstest nicht absicherbar. Der Merge ist
damit **vorbereitet und vermessen, aber nicht ausgeführt** ; das Experiment wurde
abgeräumt (kein Zweig, kein Replace-Ref, Arbeitsverzeichnis sauber). Die Upstream-Historie
bleibt im Klon liegen, damit der nächste Anlauf nicht wieder 600 MB holen muss.
**Nächster Schritt:** den Merge als eigenes Vorhaben fahren, mit Build und
ClamAV-Abnahme als Voraussetzung — nicht als Nebenschritt.