<!-- Testrunde 3, 31.07.2026, zehn neue Personas der Zielgruppe.
     Durchschnitt 3.81/10 (Runde 1: 3.80, Runde 2: 4.20).
     Noten: 2.4, 2.6, 3.4, 3.4, 3.7, 4.2, 4.3, 4.3, 4.6, 5.2
     ja 0 / vielleicht 0 / nein 10.
     Diesmal lagen alle zehn Protokolle vollstaendig vor.
     83 Funde wurden gegengeprueft: 55 BESTAETIGT, 13 TEILWEISE,
     15 GESCHMACKSFRAGE, 0 vollstaendig WIDERLEGT. -->

# CloakDrop — Urteil aus Testrunde 3

*Hinweis zur Datenlage: Anders als in den beiden Vorrunden lagen mir diesmal **alle zehn Protokolle vollständig** vor. Alle Nennungszählungen unten sind echte Personenzahlen, keine Hochrechnungen. Zusätzlich wurden 83 einzelne Funde in einer Gegenprobe am Code nachgestellt. In Abschnitt 6 steht nur, was diese Gegenprobe mit BESTÄTIGT oder TEILWEISE bewertet hat.*

---

## 1. Die Note

| | Runde 1 | Runde 2 | Runde 3 | R2 → R3 |
|---|---|---|---|---|
| **Gesamt** | 3,80 | 4,20 | **3,81** | **−0,39** |
| Vertraulichkeit (6 P.) | 4,08 | 4,50 | **4,38** | −0,12 |
| Tempo (4 P.) | 3,38 | 3,75 | **2,95** | **−0,80** |
| "würde nutzen: ja" | 0 von 10 | 0 von 10 | **0 von 10** | ±0 |
| "vielleicht" | 0 | 1 | **0** | −1 |

**Spanne Runde 3: 2,4 bis 5,2.** Median 3,95. Zum Vergleich: Runde 1 lag bei 3,0–5,5, Runde 2 bei 3,0–6,5.

Verteilung der zehn Noten:

| Bereich | Anzahl | Wer |
|---|---|---|
| unter 3,0 | 2 | Achleitner 2,4 · Lindqvist 2,6 |
| 3,0 – 3,9 | 3 | Brandtner 3,4 · Lena K. 3,4 · Wehrle 3,7 |
| 4,0 – 4,9 | 4 | Vollmer 4,2 · Reinhardt 4,3 · Vosskamp 4,3 · Reissner 4,6 |
| 5,0 und mehr | 1 | Reithinger 5,2 |

Die Rechnung: 2,6 + 4,3 + 4,2 + 4,3 + 3,4 + 5,2 + 3,4 + 3,7 + 4,6 + 2,4 = **38,1 : 10 = 3,81**. Das ist nach der zweiten Runde ein Rückschritt und liegt praktisch wieder auf dem Stand von Runde 1 (3,80). Ich beschönige das nicht: **sieben Änderungen, alle davon wurden bemerkt, und die Note ist um 0,39 gefallen.**

Die Aufteilung nach Segmenten habe ich selbst vorgenommen — die Runde-3-Daten kamen nicht vorsortiert. Vertraulichkeit sind die sechs mit einer beruflichen Geheimhaltungspflicht oder einem eigenen Bedrohungsmodell (Reinhardt, Vosskamp, Wehrle, Vollmer, Reithinger, Reissner), Tempo die vier, die einfach eine Datei loswerden wollen (Lindqvist, Brandtner, Lena K., Achleitner).

Dabei kommt die eigentliche Bewegung heraus: **die Prüfergruppe hält sich (−0,12), die Praxisgruppe bricht ein (−0,80).** Das ist kein Zufall und es hat einen benennbaren Grund. In den Runden 1 und 2 lautete der Vorwurf: *„Es verschickt nichts."* In Runde 3 lautet er: *„Es verschickt nichts — und der Teil, der fertig sein soll, verliert mir stillschweigend Dateien."* Wer die Kryptografie würdigen kann, hat etwas, das den Ausfall abfedert. Wer nur Dateien verschickt, hat nichts mehr.

Und der obere Rand ist weg. In Runde 2 gab es eine 6,5 vom Pentester. Sein Gegenstück in dieser Runde ist Jonas Reithinger, Maintainer einer Krypto-Bibliothek, und er kommt auf 5,2 — obwohl er die Kryptografie stärker lobt als irgendjemand vorher. Er sagt selbst, warum:

> *„Die Dokumentation ist auffallend ehrlich und rettet einen ganzen Punkt; das Marketing der Startseite verschweigt die Kernbeschränkung 4100 Pixel lang und kostet ihn wieder."* (Reithinger, 5,2)

---

## 2. Was die Änderungen seit Runde 2 gebracht haben

Ich gehe die sieben einzeln durch. „bemerkt" zählt Personen, nicht Erwähnungen.

| Änderung | bemerkt | Befund |
|---|---|---|
| Passwort auf dem Drop | **10/10** | Vollständig angekommen — und hat den meistgefundenen Defekt der Runde eingeschleppt. |
| Herkunftscheck im Einfügefeld | **9/10** | Reiner Gewinn. Kein einziger Kritikpunkt. |
| Schlüssellängenprüfung | **2/10 als solche** | Wirkt bei allen, erkannt haben sie zwei. |
| help.html entfernt | **0/10** | Beschwerde ist verschwunden, Lob gibt es dafür nicht. |
| Übersicht ohne Metadaten | **4/10** | Ein Vorwurf weg, zwei neue in der Gegenrichtung. |
| HSTS auf ein Jahr | **2/10** | In Runde 2 das meistgenannte Einzelthema, jetzt unsichtbar. |
| LICENSE / README | **2/10, beide negativ** | Wird nicht als Fortschritt gewertet, sondern als Widerspruch. |

### Das Passwort: durchgekommen, und trotzdem ein Rückschlag

Alle zehn haben es gefunden, alle zehn haben es getestet, alle zehn beschreiben es als richtig gebaut. Das ist die einzige Änderung mit voller Trefferquote in drei Runden.

> *„Was mich positiv überrascht hat, ehrlich gesagt mehr als erwartet: das Passwort. Falsches Passwort — nach 0,2 Sekunden ‚That password does not open this drop', und ich blieb in der Maske. Richtiges Passwort, 0,3 Sekunden, Dateien da. Genau so muss das sein."* (Lindqvist, 2,6)

> *„Das ist genau mein Ablauf, seit Jahren, nur ohne 7-Zip."* (Reinhardt, 4,3, über den Satz „Send the password by another route — on the phone, in person, anywhere but the same message")

> *„Passwort auf einen Drop, PBKDF2-SHA256 mit 310.000 Runden — das ist genau das, was ich will, denn ohne Passwort steht der Schlüssel im Klartext im Link und liegt damit in jedem Chatverlauf."* (Reissner, 4,6)

In Runde 2 war das Passwort Maßnahme Nummer 2 und die wichtigste Erkenntnis der Runde. Es ist gebaut, es ist richtig gebaut, und es hat **niemanden** von „nein" auf „vielleicht" gebracht. Der Grund ist arithmetisch: es beseitigt einen Blocker der zweiten Kategorie, während der Blocker der ersten Kategorie unverändert steht.

Schlimmer: die Funktion hat einen Defekt mitgebracht, den fünf Personen unabhängig gefunden haben. „Copy link again" lässt bei passwortgeschützten Drops das Salz weg und liefert einen toten Link (§6, Fund C). Er trifft ausgerechnet die Nutzer, die sich die Mühe eines Passworts machen.

> *„Der einzige Rettungsweg produziert eine Leiche, und zwar ausgerechnet bei den Drops, die man am sorgfältigsten behandelt hat."* (Reissner, 4,6)

Und zwei Personen haben nachgesehen, was das Passwortfeld verlangt: nichts.

> *„Ich habe danach zehn weitere falsche Passwörter eingegeben. Alle zehn in 2,0 Sekunden. Keine Verzögerung, keine Sperre, kein Hinweis. Dann habe ich einen Drop mit dem Passwort ‚1' angelegt — die App hat das anstandslos angenommen, kein Wort dazu — und ihn im vierten Versuch nach 1,29 Sekunden geraten."* (Reinhardt, 4,3)

Die Gegenprobe hat das nachgestellt: `#pw-input` hat `minLength=-1`, `required=false`, `pattern=''`, zehn Fehlversuche in 2,53 Sekunden ohne jede Bremse.

### Der Herkunftscheck: die sauberste Änderung der Runde

Neun von zehn haben ihn ausgelöst, und **kein einziger hat etwas daran auszusetzen.** Drei haben zusätzlich eine Ähnlichkeitsdomain probiert, was in Runde 2 niemand getan hat.

> *„Die Eingabeprüfung fängt sogar die Tippfehler-Domain: cloakdrop.com.example.net wurde erkannt und beim Namen genannt. Das fällt bei den meisten durch."* (Reinhardt, 4,3)

> *„‚That link points at cloakdrop.com.angreifer.de, not at this site.' — heute folgenlos, aber vorausgedacht."* (Vollmer, 4,2)

> *„Es nennt die Domain beim Namen. Bei einem Google-Drive-Link: ‚drive.google.com'. Das hat mich ehrlich beeindruckt."* (Lena K., 3,4)

In Runde 2 war das Fund F mit zwei Findern. Jetzt ist es ein Lobpunkt mit neun. Das ist die beste Aufwand-Wirkung-Bilanz der sieben Änderungen.

### Die Schlüssellängenprüfung: wirkt bei allen, erkannt haben sie zwei

Alle bekommen jetzt bei einem abgeschnittenen Link „That is not a key" statt der falschen Meldung. Als *Änderung* erkannt haben es zwei — und einer davon ist genau der, dem sie gehört:

> *„Längenprüfung des Schlüssels VOR importKey (3196-3206): ein auf 16 Byte gekürzter Link würde sonst still als AES-128 durchgehen. Diese Falle schließt fast niemand, hier ist sie geschlossen und begründet auskommentiert."* (Reithinger, 5,2)

Das ist der Runde-2-Fund der Doktorandin, sauber abgearbeitet. Die Lehre daraus ist dieselbe wie bei HSTS: eine gut behobene Schwachstelle ist unsichtbar. Sie verhindert Kritik, sie erzeugt kein Lob.

### help.html: die Beschwerde ist weg

In Runde 2 war das der „teuerste Fehler der Runde" — sechzehn beworbene Artikel, die es nicht gab. In Runde 3 erwähnt **niemand** help.html. Drei Tester zählen ausdrücklich neun ausgelieferte Seiten und finden auf keiner davon eine Attrappe. Das ist ein voller Erfolg, den man nicht messen kann, weil er nur aus einer Abwesenheit besteht.

### Die Übersicht: ein Vorwurf weg, zwei neue in die Gegenrichtung

Die Runde-2-Kritik (die Liste verrät Anzahl, Größe und Datum auf einem fremden Rechner) taucht kein einziges Mal mehr auf. Stattdessen loben zwei Personen genau die Zurückhaltung:

> *„Die Übersicht ‚Stored in this browser' weist selbst darauf hin, dass Anzahl, Größe und Datum für jeden sichtbar sind, der hinter einem steht — ein Hinweis, den kein Marketingtexter geschrieben hätte."* (Wehrle, 3,7)

> *„Die Übersicht kann keine Dateinamen zeigen, weil das Verzeichnis mitverschlüsselt ist — das habe ich in der Datenbank nachgesehen."* (Achleitner, 2,4)

Dafür kommen zwei neue Beschwerden aus der anderen Richtung, beide bestätigt: die Zeile über dem Speicher nennt weder Anzahl noch Größe, man muss erst „Manage" drücken (Wehrle), und mehrere Drops sind in der Liste nicht auseinanderzuhalten, weil dort nur Datum und Restlaufzeit stehen (Lindqvist). Netto ist das ein Fortschritt — die schwerere Kritik ist weg —, aber kein Punktgewinn.

### HSTS und LICENSE/README: unsichtbar beziehungsweise gegen euch verbucht

HSTS war in Runde 2 mit **sechs** unabhängigen Nennungen das meistgenannte Einzelthema. Jetzt steht es bei zwei Personen in einer Aufzählung von Kopfzeilen, ohne dass jemand die Zahl nennt. Erledigt und vergessen.

Bei LICENSE/README ist es schlechter. Zwei Personen haben es gesehen, beide werten es negativ:

> *„Die AGPL-3.0 liegt als LICENSE im Baum, aber index.html trägt keine einzige Copyright-Zeile und keinen Lizenzhinweis. Die README sagt ‚Licence: GNU AGPL v3', /opensource sagt ‚The client isn't open source today'. Beides gleichzeitig ist kein Zustand."* (Reithinger, 5,2)

> *„AGPL-Text im Baum, aber nichts zu klonen, keine Signatur, keine reproduzierbaren Builds. Für ‚don't trust, verify' fehlt damit der wichtigste Schritt."* (Reissner, 4,6)

Die übrigen acht schreiben, als sei nichts passiert: kein öffentlicher Quelltext. Eine Lizenzdatei ohne Repository liest sich für diese Zielgruppe nicht als halber Schritt, sondern als angekündigter, aber nicht eingelöster Schritt.

---

## 3. Die Vertrauensfrage

**Zum dritten Mal in Folge: kein einziger Fund gegen den Anbieter.** Und diesmal ist die Prüfung schärfer ausgefallen als in beiden Vorrunden zusammen.

Was tatsächlich gemacht wurde:

- **Netzwerkmitschnitt: 10 von 10.** Übereinstimmend null Fremdanfragen. Konkrete Zahlen aus den Protokollen: 3 Anfragen (Lena K., ganzer Test), 7 (Reinhardt), 14 über neun Unterseiten (Vollmer), 19 über acht Unterseiten (Achleitner), rund 50 Seitenaufrufe über drei Testläufe (Reissner) — restlos alle an cloakdrop.com.
- **CSP aktiv angegriffen**, nicht nur gelesen: Vosskamp hat fetch nach außen, fetch auf die eigene Herkunft, sendBeacon, WebSocket, Blob-Worker und ein externes Bild einzeln probiert — jedes Mal blockiert. Reithinger hat per DOM ein Inline-Skript eingehängt: `window.__pwn` blieb undefined.
- **Ausgeliefertes gegen Quelltext gehasht** — das ist neu und hat es in keiner Vorrunde gegeben. Reissner: index.html live 427.560 Byte, SHA-256 `e9d050c9…`, Repo identisch; dasselbe für limits.html und verify.html.
- **Rundlauf byte-exakt in drei Engines**: Chromium 147, Firefox 148, WebKit 26.4, jeweils SHA-256-identisch (Vosskamp). Sieben weitere haben in einer Engine geprüft, ausnahmslos identisch.
- **IV-Konstruktion gelesen** von drei Fachleuten, alle drei mit demselben Ergebnis: 8 Byte Zufallspräfix plus 32-Bit-Zähler, Manifest auf 0, Chunks ab 1, keine Wiederholung möglich.

> *„Das ist nicht ‚wir versprechen, nichts zu senden', das ist ‚der Browser lässt uns nicht, selbst wenn wir wollten'. Genau diese Sorte Beweis verlange ich, und genau diese Sorte bekomme ich sonst nirgends. Bitwarden Send kann das nicht behaupten, Proton Drive kann es nicht behaupten, Tresorit kann es nicht behaupten."* (Vosskamp, 4,3)

> *„Ich lese also tatsächlich den Code, der bei mir läuft. Das kann sonst niemand in diesem Feld von sich behaupten."* (Reissner, 4,6)

> *„Für jemanden, der wie ich sonst nur Datenschutzerklärungen zu lesen bekommt, ist das eine andere Art von Aussage."* (Reinhardt, 4,3)

**Die Kryptografie hält. Was nicht hält, liegt wieder daneben — und diesmal an drei Stellen:**

1. **Kein AAD.** Kein einziger `encrypt`-Aufruf bindet Drop-ID, Frist oder Dateiindex ans Chiffrat. Reithinger hat `expires` auf heute plus zehn Jahre gesetzt, der Drop öffnete wieder und meldete stolz „opens for 3650 more days". Gegenprobe: reproduziert. Das ist derselbe Fund wie in Runde 1, nur jetzt mit der Codeursache. Heute folgenlos, weil es die eigene Datenbank ist. Am Tag des Relay-Starts ist eine Frist ohne kryptografische Bindung genau nichts wert.

2. **Das Passwort ist ungeschützt gegen schlechte Passwörter.** Siehe §2. Der eingewickelte Schlüssel und das Salz stehen beide im Link, also darf ein Angreifer offline und unbegrenzt probieren — und die App nimmt „1" an.

3. **Der Löschknopf sagt die Unwahrheit.** Das ist der schwerste Vertrauensschaden der Runde und er kommt nicht aus der Kryptografie, sondern aus der Buchführung. Nach einem Abbruch liegen verschlüsselte Bruchstücke im Browser, die Übersicht meldet „nothing stored", und über die Oberfläche kommt man nicht heran (§6, Fund A).

> *„Ein Löschknopf, der nicht löscht, ist bei mir kein Schönheitsfehler, sondern ein Haftungsthema."* (Reinhardt, 4,3)

> *„Für einen normalen Nutzer ist das ein Speicherleck. Für mich ist es der Grund, das Ding nicht anzufassen. Ein Werkzeug, das mir sagt ‚nichts gespeichert', während 128 Megabyte auf der Platte liegen, hat mein Vertrauen verspielt — auch wenn es nicht böse gemeint ist."* (Vosskamp, 4,3)

**Eine Korrektur gegenüber Runde 1**, die zu eurem Vorteil ausfällt: der NEL-Kanal an `a.nel.cloudflare.com` existiert weiterhin und ist von `connect-src` nicht erfasst — aber privacy.html benennt ihn inzwischen wörtlich, samt der Angabe, dass erfolgreiche Aufrufe gar nicht gemeldet werden. Der Vorwurf aus Runde 1 („während die Startseite behauptet, es gehe nie ein Byte hinaus") gilt nicht mehr.

---

## 4. Der Aufwand

Hier ist das Produkt weiterhin unangefochten, und zwar in beiden Segmenten.

**Klicks bis zum kopierten Link:** vier. Achleitner hat sie gezählt und ausdrücklich als sein Kriterium benannt.

> *„Vier Fingertipps vom Aufruf bis zum Link in der Zwischenablage. Das ist genau meine Schmerzgrenze und schlägt alles, was ich sonst auf dem Handy habe."* (Achleitner, 2,4 — der niedrigsten Note der Runde)

**Durchsatz, zehn unabhängige Messungen: 141 bis 253 MB/s.**

| Wer | Gemessen |
|---|---|
| Lindqvist | 450 MB in 1,8 s (253 MB/s) · 185 MB in 1,2 s · Download 450 MB in 10,5 s |
| Vollmer | 230 MB in 1,0 s (237 MB/s) · Entschlüsseln + Download 5,0 s |
| Reithinger | 220 MB, 208 MB/s · Download 5,4 s |
| Wehrle | 184 KB in 0,28 s · 42 MB in 0,37 s · 470 MB in 1,8 s |
| Brandtner | 51 MB in unter 1 s · 600 Dateien / 420 MB in 4,1 s · 450 MB in 2,0 s |
| Vosskamp | 300 MB in 2,1 s, entschlüsselt in 7,3 s · 43 MB in Firefox in 0,9 s |
| Reissner | 121 MB in 0,86 s (141 MB/s) · Download 3,06 s |
| Reinhardt | 15 MB in 0,1 s · 320 MB in acht Dateien in 1,9 s |
| Lena K. | Seite in 0,32 s · 48 MB in 0,37 s |
| Achleitner | 2,4 MB in 0,25 s |

Sätze zum Aufheben:

> *„Das ist schneller, als WeTransfer meine Datei überhaupt vom Rechner liest."* (Lindqvist)
> *„Das ist schneller, als mein 7-Zip ein 15-MB-Archiv packt."* (Reinhardt)
> *„Das ist nicht ‚schnell für eine Website', das ist schneller, als mein Faxgerät die erste Zeile eingezogen hat."* (Wehrle)
> *„Google Drive fängt in der Zeit nicht mal an."* (Lena K.)

**Wo Leute ausgestiegen sind, nach Häufigkeit:**

1. **Der zweite Browserkontext — 10 von 10.** Alle zehn haben denselben Test gemacht, alle zehn sind dort stehengeblieben. Drei schreiben ausdrücklich, dass sie es nicht glauben wollten: Lindqvist hat danach den Quelltext durchsucht, Achleitner hat es dreimal wiederholt, Vosskamp hat es in einem dritten Kontext gegengeprüft. Für sieben von zehn ist das der Punkt, an dem der Test faktisch endet und nur noch Fundament bewertet wird.

2. **Der Hänger bei vollem Speicher — 4 Nennungen, alle mit Stoppuhr.** Lindqvist 103,6 s bei 95 %. Reissner 150 s bei 54 %. Wehrle 191 s bei 62 %. Achleitner rund 240 s bei 43 %. In keinem Fall eine Meldung, in keinem Fall ein Abbrechen-Knopf.

> *„Auf einem Parkplatz mit wartendem Kunden ist das unbrauchbar. Ich weiß nicht, ob es läuft, ob es hängt, ob ich neu laden soll."* (Achleitner, 2,4)
> *„Wenn mir das bei einer Abgabe um 23 Uhr passiert, bin ich raus."* (Lindqvist, 2,6)

3. **„Download all" — 8 Nennungen, mit Wartezeiten von 15 bis 90 Sekunden** auf Dateien, die nie kommen. Brandtner hat 90 Sekunden gewartet, Vollmer 61, Reithinger 15, Lena K. 30.

4. **Die Startseite verstehen — 20 Sekunden ohne Erkenntnis.** Lena K. hat gemessen, wo die entscheidende Einschränkung überhaupt steht: im ersten Bildschirm kein einziges Mal, der erste Treffer bei y=4246 von 5750 Pixeln in einer zugeklappten FAQ-Antwort, der zweite im Fußtext bei y=5501 in 14 Pixel Schrift. Die Gegenprobe hat das nachgemessen und bestätigt.

> *„Was heißt denn ‚relay'? Ich bin Soziologiestudentin. Ich weiß nicht, was ein Relay ist."* (Lena K., 3,4)

5. **Der Link nach dem Verschlüsseln ist auf dem iPhone nicht im Bild.** Brandtner landet 463 Pixel unterhalb des Linkfelds und hält es für einen Absturz. Gegenprobe: `top:-463`, außerhalb des Viewports, exakt reproduziert.

6. **Die Umschalter „Cloak files" / „Open a link" sind 37 Pixel hoch.** Apple nennt 44. Brandtner hat sich zweimal vertippt. Der „Manage"-Knopf ist mit 24 Pixeln noch kleiner.

---

## 5. Wahrheitsprüfung

**Was gehalten hat — jeweils mehrfach unabhängig nachgemessen:**

- *„Nothing is uploaded — you will see no request at all."* — **10/10 bestätigt.** Zum dritten Mal die bestbelegte Aussage des Projekts.
- *„Filenames encrypted inside the manifest."* — bestätigt. Reithinger hat die ersten Bytes des Manifests gelesen (`7f db 9a 02 …`), Achleitner hat in der Datenbank nachgesehen.
- *„4 MB chunks — plaintext plus a 16-byte tag."* — auf das Byte bestätigt. Chunk-Overhead exakt 16 Byte, kein Padding, kein Header, keine Version.
- *„Tampering fails loudly."* — **für Chunks bestätigt, für das Manifest nicht** (siehe unten).
- **„500 MB … the same number the app enforces"** — **jetzt wahr.** Das ist der wichtigste eingelöste Punkt aus Runde 2: dort hatte der Pentester `disabled` entfernt und 525 MB durchlaufen lassen. In dieser Runde haben **sechs** Personen dasselbe versucht (Vollmer, Vosskamp, Reithinger, Wehrle, Reissner, Lena K.) und alle sechs sind an der Prüfung im Code hängengeblieben: *„A drop carries up to 500.0 MB. This selection is 520.0 MB."*
- **Ablauf löscht wirklich** — von acht Personen mit zurückdatiertem Datensatz geprüft, jedes Mal: Link tot, Bruchstücke beim nächsten Start weg.
- **Löschen räumt Datensatz und alle Chunks in einer Transaktion** — mehrfach nachgezählt, unter anderem von 242.067.344 Byte auf 0.

**Was nicht hält:**

1. **limits.html:1910 „Delete a drop early — Yes, from the app"** und **1909 „Encrypted chunks after expiry — Removed next time you open the app".** Für Waisen-Bruchstücke gilt beides nie. Reinhardt hat den Widerspruch benannt, die Gegenprobe hat ihn wörtlich bestätigt. Das ist die einzige Stelle in dieser Runde, an der die Seite etwas über gelöschte Daten behauptet, das messbar falsch ist — und es ist dieselbe Kategorie wie help.html in Runde 2.

> *„Wenn eine Seite sonst so peinlich genau ist, fällt eine Aussage, die nicht stimmt, doppelt auf."* (Reinhardt, 4,3)

2. **glossary.html:1944/1946: „7 days from creation" und „refuses past seven days".** Die App bietet 1 Stunde / 24 Stunden / 7 Tage, Vorauswahl 24 Stunden. **Drei unabhängige Finder** (Lena K., Reissner, Achleitner). Schlimmer noch: das Glossar enthält nach Zählung der Gegenprobe **null** Treffer für „password", „PBKDF2", „310,000", „1 hour", „24 hours" — obwohl das Passwort das Hauptmerkmal dieses Release ist und das Glossar über sich selbst schreibt *„Where a term has a number behind it, the number is here too"*.

> *„Wer das Glossar liest und Freitag ein Angebot schickt, glaubt es hält bis nächste Woche. Es ist Samstag tot."* (Achleitner, 2,4)

3. **limits.html verschweigt die größte Einschränkung des Builds.** Das Wort „relay" kommt dort genau einmal vor — in der Fußzeile, die auf jeder Seite gleich ist. Die Tabelle nennt 500 MB, „Files in one drop — No limit" und „Server involved — None" als Verkaufsargument. Dass der Empfänger nichts bekommt, steht in der Tabelle nicht und in keiner der sieben FAQ-Antworten.

> *„Auf der Seite, die ‚Limits' heißt und die ich als erstes aufmache, wenn ich wissen will, ob etwas für mich passt, steht diese Einschränkung überhaupt nicht. Das finde ich unfair, und es ist der einzige Punkt, an dem ich dem sonst sehr ehrlichen Ton der Seite nicht mehr traue."* (Brandtner, 3,4)

4. **„Tampering fails loudly" gilt für das Manifest nicht.** Reithinger hat ein Bit im gespeicherten Manifest gekippt und bekam *„The key doesn't fit — Check that the link was copied completely"*. Der Schlüssel war korrekt. Beim Chunk macht die App es richtig und unterscheidet den GCM-Fehler sauber; beim Manifest fängt ein einziges catch alles ab.

> *„Sie schiebt einen Angriff auf mein Copy-Paste."* (Reithinger, 5,2)

5. **Die Fehlermeldung „That is not a key" schiebt die Ursache dem Chatprogramm zu**, obwohl der Link im „Copy link again"-Fall von der App selbst zerstört wurde.

> *„Die App hat mir den Link kaputtgemacht und schiebt es dann meinem Chatprogramm in die Schuhe."* (Brandtner, 3,4)

6. **about.html:1981, dritte Runde in Folge:** *„The glossary and the are the honest version of an origin story."* Zwei Finder (Vosskamp, Brandtner). Der Satz stand schon im Runde-2-Bericht.

> *„Kleinigkeit, aber es steht auf der Seite, die mir erklären will, wie sorgfältig hier gearbeitet wird."* (Brandtner, 3,4)

7. **index.html trägt keine Lizenz- und keine Copyright-Zeile**, während README.md AGPL v3 nennt und opensource.html sagt, der Client sei nicht offen. Gegenprobe: `grep -ci 'agpl|copyright|licen[cs]e' index.html` = 0.

8. **Der Code-Kommentar untertreibt den Größenverrat.** In index.html steht, wer das Browserprofil öffnen kann, kenne „die Größe auf 4 MB genau". Gemessen: eine 1.200-Byte-Datei erzeugt eine Chunk-Zeile mit 1.216 Byte, eine 2.400.000-Byte-Datei eine mit 2.400.016 Byte — also die exakte Klartextlänge plus 16 Byte, je Datei einzeln. Derselbe zu freundliche Satz steht auch in privacy.html.

9. **Acht eingebettete App-Store-Icons fremder Marken** im Kanal-Band, deklariert als `data:image/png`, tatsächlich JPEG (`/9j/` = `FF D8 FF`, mit Exif- und JFIF-Strukturen). Der MIME-Fehler ist ein harter Fakt; die Lizenzfrage bei AGPL-Weitergabe ist eine naheliegende Folgerung.

10. **Kein einziges Vorkommen von DSGVO, Auftragsverarbeitung, Artikel 9, Haftung oder Gewährleistung auf den neun Produktseiten.** Wehrle hat danach gesucht, die Gegenprobe hat es mit einer Regex über alle Seiten nachgezählt: genau ein Treffer, und der stammt aus dem Wort „reliably". (Impressum und Datenschutzseite waren wie in allen Runden ausgenommen, und dort steht es.)

---

## 6. Neue Fehler, die mehrere unabhängig gefunden haben

Hier steht nur, was die Gegenprobe mit **BESTÄTIGT** oder **TEILWEISE** bewertet hat. Bei TEILWEISE sage ich dazu, was an der ursprünglichen Beschreibung ungenau war. Nennungen sind Personen.

### A. Abbruch beim Verschlüsseln hinterlässt Chiffrat, das die App weder sieht noch löschen kann — 10 Nennungen, BESTÄTIGT

**Alle zehn.** Neun melden es als eigenständigen Fund (Lindqvist, Reinhardt, Vollmer, Vosskamp, Brandtner, Reithinger, Lena K., Reissner, Achleitner), der zehnte (Wehrle) als Folge des Hängers. Das ist der meistgefundene Defekt aller drei Runden.

**Ursache, zeilengenau bestätigt:** `saveChunk` schreibt in der Schleife ab index.html:2763, `saveTransfer` erst danach in 2795. Alle drei Lesewege der Oberfläche — `speicherZeile()` 2874, `dropsAnzeigen()` 2887, `abgelaufeneEntfernen()` 2599 — gehen über `idbAlleDrops()` 2576, und das liest ausschließlich den Store `transfers`. Was keinen Datensatz hat, wird nie gesehen und nie gelöscht. Der Aufräumzweig in 2765-2768 greift nur im `catch`, also nur bei einem Speicherfehler, nicht bei einer Navigation.

**Gemessen in der Gegenprobe:** 400-MB-Datei, Neuladen bei 49 % → 56 Chunks, 224.000.896 Byte, null Transfer-Datensätze, `usage` 224.011.273. `#storeline` bleibt versteckt, die erzwungen geöffnete Übersicht meldet „nothing stored", drei App-Neustarts ändern nichts.

Die Zahlen der Tester decken sich, weil alle dieselbe Chunk-Größe treffen (4.000.016 Byte pro Stück): 32 MB nach 120 ms Abbruch (Reissner), 64 MB (Vollmer bei 28 %), 104 MB (Reinhardt, Reithinger), 112 MB (Vollmer bei 35 %), 128.000.512 Byte (Vosskamp, zusätzlich physisch im Profilordner nachgewiesen), 180 MB (Lena K.), 188 MB (Lindqvist), 192 MB (Brandtner, Achleitner), 260 MB (Reissner nach dem Hänger), 540 MB (Brandtner nach drei Abbrüchen).

> *„Die App verrechnet sich um Faktor 4700 bei der einzigen Zahl, die sie über meinen Speicher behauptet."* (Lindqvist, 2,6)

> *„Das ist der einzige Fund, bei dem die Software dem Nutzer etwas antut, das er nicht sieht und nicht rückgängig machen kann."* (Vollmer, 4,2)

> *„Schreibt mir, wenn das Relay steht — aber schreibt mir vorher, wenn die Waisen-Chunks weg sind."* (Vosskamp, 4,3)

### B. „Download all" liefert nur zehn Dateien und meldet es nicht — 8 Nennungen (4× BESTÄTIGT, 4× TEILWEISE)

Ort: index.html:3253-3258. Der Klick ruft `manifest.files.forEach(downloadFile)`, jede Datei löst ein eigenes `a.click()` aus. Chromium deckelt automatische Downloads bei zehn. Die App bemerkt das nicht, weil `a.click()` nichts wirft.

**Gemessen:** 40 Dateien → 10 Downloads nach 10 s, unverändert 10 nach 30 s und nach 60 s. Kein ZIP, keine Fehleransicht, keine Konsolenausgabe, alle 40 Knöpfe stehen wieder auf „Download".

BESTÄTIGT bei Vollmer (10 von 60), Brandtner (10 von 40, 20 von 600, in der Safari-Engine 10 von 20), Reithinger (10 von 20, 10 von 60), Lena K. (10 von 40).

**TEILWEISE bei Lindqvist, Reinhardt, Wehrle und Achleitner — und zwar zu euren Ungunsten.** Diese vier haben den Defekt als „N einzelne Downloads statt eines Archivs" beschrieben und dabei angenommen, dass wenigstens alle N ankommen: Lindqvist rechnet mit „12 Dateien = 12 Downloads = 12 Rückfragen", Reinhardt mit „33 Anlagen, 33 Downloadfragen", Wehrle mit 60 Röntgenbildern. Das trifft nicht zu. Es kommen zehn, der Rest fällt lautlos weg. Die vier haben den Fund also **zu freundlich** beschrieben, nicht zu scharf. Bei vier Dateien (Achleitner) kommen tatsächlich alle vier an, weil sie unter dem Deckel liegen — daher seine mildere Fassung.

Die präziseste Beschreibung kommt von Brandtner, und sie benennt genau die richtige Härte:

> *„Meine Kundin hält sich für fertig und hat die Hälfte. Bei Hochzeitsbildern merkt man das erst Monate später. Für mich ist das der schlimmste Befund von allen, schlimmer als das fehlende Relay, weil das Relay ehrlich als ‚in Arbeit' gekennzeichnet ist und dieser Fehler nicht."* (Brandtner, 3,4)

> *„Halb angekommene Dateien ohne Meldung sind für ein Produkt, dessen ganzer Stolz ‚fails loudly' ist, die falsche Sorte Fehler."* (Reithinger, 5,2)

### C. „Copy link again" liefert bei Passwort-Drops einen toten Link — 5 Nennungen, BESTÄTIGT

Ort: index.html:3157. Der Link wird aus `t` und `k` neu zusammengesetzt, `params.s` — das Salz — fehlt. Gefunden von Vollmer, Brandtner, Reithinger, Wehrle und Reissner, alle fünf mit derselben Zeile und alle fünf mit ausgelesener Zwischenablage.

**Gemessen:** `#copy-btn` auf dem Fertig-Schirm liefert den vollständigen Link inklusive `&s=`. `#copy-again` in der Empfangsansicht liefert ihn ohne. Der so kopierte Link ergibt beim Öffnen „That is not a key". Reithingers Rechnung stimmt: ohne `s` überspringt die App den Passwortzweig und landet in der Längenprüfung, wo die Hülle 12 Byte IV + 32 Byte Schlüssel + 16 Byte Tag = 60 Byte hat statt der verlangten 32.

Die Verschärfung, die zwei unabhängig nennen: die Empfangsansicht bewirbt genau diesen Knopf als einzigen Rettungsweg, weil der Link aus der Adresszeile entfernt wurde.

> *„Genau die Funktion, die ein sicherheitsbewusster Nutzer benutzt, hat den kaputten Rettungsweg."* (Reissner, 4,6)

> *„Ich hätte eine halbe Stunde gebraucht, um zu begreifen, dass ich nichts falsch gemacht habe. Das ist die Art von Fehler, wegen der ich Dienste wechsle."* (Brandtner, 3,4)

### D. Bei vollem Browserspeicher hängt das Verschlüsseln ohne Meldung und ohne Abbruch — 4 Nennungen, BESTÄTIGT

Gefunden von Lindqvist, Wehrle, Reissner und Achleitner. Gemessene Wartezeiten: 103,6 s bei 95 % · 150 s bei 54 % · 191 s bei 62 % · rund 240 s bei 43 %.

**Gegenprobe, zwei unabhängige Läufe:** drei Drops zu 480 MB laufen sauber durch (1440 von 1970 MB belegt), der vierte steht nach 203,9 s ohne Endzustand. Zweiter Lauf mit 450-MB-Drops: der vierte steht 240 s. `#st-progress` enthält null Bedienelemente — es gibt buchstäblich keinen Knopf, den man drücken könnte.

Der bittere Teil: für genau diesen Fall liegt in index.html:2769-2778 eine fertig geschriebene Meldung („This browser has no room left … Delete an older drop under Manage") samt Aufräumroutine. Sie wurde in **vier unabhängigen Läufen der Gegenprobe kein einziges Mal erreicht.**

> *„Im Code gibt es einen hübschen Zweig für ‚This browser has no room left' mit Aufräumen — der wird nie erreicht, weil die IndexedDB-Transaktion weder fertig wird noch scheitert. Kein Timeout, kein Ausweg."* (Reissner, 4,6)

> *„Das ist der Unterschied zwischen gut gedacht und fertig."* (Achleitner, 2,4)

**Präzisierung der Gegenprobe:** irgendwann löst es sich doch auf — aber über den allgemeinen `catch` in 2719-2723 mit einem `alert('Encryption failed: …')`, nicht über die vorbereitete Meldung. Und es bleiben Waisen-Chunks zurück, weil der Fehler außerhalb des try/catch fällt. Fund D erzeugt also zuverlässig Fund A. Zwei Defekte, die einander füttern — Achleitners Formulierung.

### E. Die Empfangsansicht widerspricht sich auf einem Bildschirm — 2 Nennungen, BESTÄTIGT

Brandtner und Achleitner, unabhängig, mit denselben Zeilen. Der feste Vorspann in index.html:2091 lautet *„This drop was cloaked in this browser. The key came in with the link — decryption happens right here, with nothing fetched from anywhere."* Direkt darunter erscheint im Fehlerfall *„Nothing here to open."* `showError()` ruft `setMode('receive')` und blendet den Vorspann nicht aus.

Gilt laut Gegenprobe genauso für den abgeschnittenen und den abgelaufenen Link, weil alle drei über dieselbe Funktion laufen. Der Fund stand schon in Runde 1 im Bericht.

> *„Zwei Sätze, die sich direkt widersprechen, übereinander auf einem Handybildschirm."* (Brandtner, 3,4)
> *„Mein Kunde ruft mich an und fragt, was er falsch gemacht hat."* (Achleitner, 2,4)

### F. Das Glossar widerspricht der App bei der Ablauffrist — 3 Nennungen, BESTÄTIGT

Lena K., Reissner und Achleitner. glossary.html:1944 sagt „7 days from creation", 1946 „refuses past seven days". Die App bietet 1 h / 24 h / 7 Tage mit Vorauswahl 24 Stunden (index.html:2417-2424), limits.html:1905 sagt es korrekt.

> *„Eine der beiden Seiten lügt mich an, und ich weiß nicht welche."* (Lena K., 3,4)

### G. Zurück verliert den fertigen Link endgültig, der Drop bleibt liegen — 2 Nennungen, BESTÄTIGT

Lena K. und Wehrle. Nach dem Verschlüsseln existiert der Schlüssel nur im Eingabefeld. Ein Klick auf Zurück führt zur Marketingseite, Vorwärts bringt ein leeres Feld zurück. Es gibt keine Liste erzeugter Links — die Gegenprobe hat den ganzen Seitentext auf „recent" durchsucht: kein Treffer. Der Drop liegt weiter in der Datenbank (transfers=1, chunks=1, 640.016 Byte) und ist nur noch löschbar, nicht mehr öffenbar.

Vollmer hat dieselbe Baustelle über den Vorwärts-Knopf gefunden, Vosskamp und Reissner über die Empfangsansicht (Neuladen wirft kommentarlos auf die Startseite, Zurück tut dort gar nichts). Insgesamt haben also **fünf Personen** an der Verlaufsbehandlung etwas gefunden, an vier verschiedenen Stellen.

> *„Auf dem Handy drücke ich Zurück reflexartig."* (Lena K., 3,4)

### H. Ein Zeilenumbruch im eingefügten Link wird nicht repariert, sondern abgeschnitten — 2 Nennungen, BESTÄTIGT

Vollmer und Reithinger. index.html:3411 schneidet am ersten Whitespace ab (`frag.split(/\s/)[0]`), statt Whitespace zu entfernen. Ein Passwort-Link ist gemessen 145 Zeichen lang — genau die Länge, bei der jeder Mailclient umbricht. Je nach Bruchstelle kommt „That doesn't look like a CloakDrop link" oder „That is not a key".

### I. Der gelöschte Drop gibt den belegten Platz nicht frei — 2 Nennungen, BESTÄTIGT

Vollmer und Wehrle, mit Messungen, die sich bis auf wenige Byte decken. Nach dem Löschen: transfers=0, chunks=0, chunkBytes=0 — `usage` bleibt bei 230.027.042 Byte, auch acht Sekunden später. Beide ordnen die Ursache selbst richtig ein (Chromiums träge Buchführung, nicht die App), und die Gegenprobe teilt das: `idbDropDel` löscht sauber über einen Schlüsselbereich. Trotzdem sieht der Nutzer eine Zahl, die nicht stimmt.

### J. Passwort ohne Mindestanforderung und ohne Bremse — 2 Nennungen (1× BESTÄTIGT, 1× TEILWEISE)

Reinhardt (BESTÄTIGT) und Reithinger (TEILWEISE). Siehe §2 und §3 für die Messungen.

**Was an Reithingers Fassung ungenau war:** er hat die fehlende Passwortgüte mit der Wahl der Ableitungsfunktion in einen Fund gepackt. Der erste Teil ist ein bestätigter Defekt. Der zweite — PBKDF2-SHA256 statt Argon2id — ist eine Geschmacksfrage: 310.000 Runden ist der von OWASP genannte Wert, er steht offen auf limits.html, und WebCrypto kann Argon2 nicht. Seine Messung von 102 ms je Ableitung war auf die Millisekunde richtig.

### K. Kein AAD: Frist, Drop-ID und Größe sind unauthentifizierte Klartextfelder — 1 Nennung, BESTÄTIGT

Nur Reithinger. Ich ziehe den Fund trotzdem hoch, weil der Beweis sauber ist und weil er derselbe ist wie Fund 1 aus Runde 1 — nur diesmal mit der Codeursache. Beide `crypto.subtle.encrypt`-Aufrufe (2759, 2791) laufen ohne `additionalData`. Gegenprobe: `expires` auf jetzt plus zehn Jahre gesetzt, Link öffnet normal und meldet „opens for 3650 more days".

> *„In dem Moment, in dem das Relay existiert, ist eine Frist ohne kryptografische Bindung genau nichts wert, und dann fällt das auf."* (Reithinger, 5,2)

### L. Kein Abbrechen und keine Warnung während des Verschlüsselns — 2 Nennungen, BESTÄTIGT

Lena K. (kein Abbrechen-Knopf) und Lindqvist (kein `beforeunload`). Gegenprobe: `#st-progress` enthält null Knöpfe und null Links, `grep beforeunload` ergibt null Treffer. Damit ist Neuladen der einzige Ausweg — und genau das erzeugt Fund A.

### Weitere bestätigte Einzelfunde, kurz

Alle mit sauberem Beweis, je 1 Finder:

- **about.html:1981** — der kaputte Satz aus Runde 2, immer noch da (Vosskamp und Brandtner, also 2 Finder).
- **Manifest-Manipulation wird als Copy-Paste-Fehler gemeldet** (Reithinger) — siehe §5.
- **Bidi-Steuerzeichen im Dateinamen** gelangen ungefiltert in die Empfangsliste, während Chromium den geschriebenen Dateinamen entschärft: angezeigter und gespeicherter Name gehen auseinander (Reithinger).
- **Erstellungsdatum in UTC** statt lokal, über `toISOString()` — ein Drop nach Mitternacht MESZ zeigt den Vortag (Reithinger).
- **Der Link bleibt nach „Start a new drop" im DOM stehen** (Reithinger).
- **JPEG-Daten unter `data:image/png`**, dazu fremdes App-Store-Artwork im Baum (Reithinger).
- **Keine Lizenz- und Copyright-Zeile in index.html** (Reithinger).
- **Dieselbe Datei lässt sich kommentarlos mehrfach in einen Drop legen** (Lindqvist); an derselben Stelle schließt der Entfernen-Knopf über einen veralteten Index.
- **Drops sind in der Übersicht nicht auseinanderzuhalten** (Lindqvist).
- **Die Speicherzeile nennt weder Anzahl noch Größe** (Wehrle) — der Gegeneinwand steht in index.html:1991-1992 und ist begründet, der Praxiseinwand bleibt.
- **Keine Anzeige des verbleibenden Platzes** (Achleitner) und **das Kontingent reicht nur für rund vier volle Drops** (Reinhardt; gemessen 1,82 bis 2,24 GB, schwankend).
- **Falsche Fehlerursache, wenn ein Drop in einem anderen Tab gelöscht wurde** (Reithinger): es kommt „Browsers may clear storage on their own", obwohl der Nutzer selbst gelöscht hat.
- **limits.html verschweigt die Browser-Bindung** (Brandtner) — siehe §5.
- **Segmentknöpfe 37 px, Link nach dem Cloaken außerhalb des iPhone-Bildschirms** (Brandtner).

### Zwei Funde, die nur zur Hälfte tragen

- **Brandtner: „Die 7-Tage-Frist dürfte auf dem iPhone nicht halten."** Messbar bestätigt ist nur, dass die WebKit-Engine `navigator.storage.estimate` gar nicht kennt. Die ITP-Löschung nach sieben Tagen ohne Interaktion war ohne echtes iOS-Gerät nicht prüfbar. Sie kennzeichnet den Fund selbst als Vermutung — das ist redlich und deshalb steht er hier.
- **Brandtner: „Große Dateien werden komplett im Arbeitsspeicher zusammengesetzt."** Der Codeteil ist exakt bestätigt (alle entschlüsselten Puffer liegen gleichzeitig im Heap, bevor der Blob entsteht). Der behauptete Absturz auf iOS bleibt unbelegt, ebenfalls von ihr selbst als Vermutung gekennzeichnet.

### Was sich in der Gegenprobe nicht halten ließ

Das gehört in diesen Bericht, auch wenn es kurz ist. **Von 83 geprüften Funden wurde kein einziger vollständig widerlegt** — das hat es in keiner der drei Runden gegeben und ist ein Qualitätsmerkmal dieses Testfelds. Aber drei Dinge sind unterwegs zerbröselt und ich nenne sie:

**Vosskamps NEL-Fund war in einem entscheidenden Punkt falsch.** Sie schreibt, der Report-Kanal an `a.nel.cloudflare.com` sei „ein Kanal, den die Seite nirgends erwähnt". Er ist real und er unterliegt `connect-src` tatsächlich nicht — aber privacy.html beschreibt ihn wörtlich, inklusive der Angabe, dass nur fehlgeschlagene Verbindungen gemeldet werden und dass nie eine Datei, ein Dateiname oder ein Schlüssel darin steht. Der Vorwurf der Verschleierung fällt weg, die technische Beobachtung bleibt.

**Vosskamps Firefox-Favicon-Meldung ließ sich nicht nachstellen.** In Firefox 148.0.2 auf der ausgelieferten Seite: null Konsolenmeldungen, die CSP enthält `img-src 'self' data:`, `/favicon.ico` antwortet mit 200. Die zitierte Fehlermeldung passt exakt zur PIN-Wand davor, die eine andere, engere CSP ohne `img-src` setzt und kein Favicon einbindet. Sie hat vermutlich die Zugangswand gemessen, nicht das Produkt. Endgültig ausschließen lässt es sich nicht, weil die FaviconLoader-Meldung in einer Konsole liegt, die das Prüfwerkzeug nicht mitliest.

**Drei Zeilenangaben waren daneben.** Lindqvist verweist für die Empfangsfunktion auf 2461-2473, dort steht die Passwort-Ableitung; die Funktion liegt bei 3148-3261. Er verweist für `replaceState` auf 2460-2461 statt 3157-3158 — rund 700 Zeilen daneben. Reinhardt nennt 1913 und 1925 für die Hero-Zeile und das Kanal-Band, die tatsächlich in 1916 und 1929 stehen. Die zitierten Texte selbst waren in allen drei Fällen wörtlich richtig. Nicht dramatisch, aber wenn ihr die Funde abarbeitet, sucht nach dem Text, nicht nach der Zeile.

**Nicht in dieser Liste, weil es „gefällt mir nicht" ist und nicht „kaputt":** das fehlende Relay (an sechs Stellen offen angesagt), der 500-MB-Deckel (dokumentierte Produktentscheidung, im Code durchgesetzt), der fehlende öffentliche Quelltext (auf opensource.html eingeräumt), die Wahl PBKDF2 statt Argon2id (OWASP-Wert, auf limits.html genannt) und der fehlende anonyme Meldeweg. Die Fakten stimmen in allen fünf Fällen — es sind Bewertungen dokumentierter Entscheidungen, keine Defekte. Fünfzehn der 83 Funde fielen in diese Kategorie.

---

## 7. Was die Note am stärksten heben würde

Sortiert nach Wirkung. „Nennungen" sind Personen von zehn.

| # | Maßnahme | Nennungen | Ohne Relay machbar? | Aufwand |
|---|---|---|---|---|
| 1 | **Relay bauen** | 10/10 als Blocker, 8× auf Platz 1 | nein, per Definition | groß |
| 2 | **Waisen-Chunks aufräumen** und die Übersicht ehrlich rechnen lassen | **10** | **ja** | klein bis mittel |
| 3 | **„Download all" als ZIP** — oder wenigstens Zähler und Fehlermeldung | **8** | **ja** | mittel |
| 4 | **„Copy link again" das Salz mitgeben** | **5** | **ja** | 1 Zeile |
| 5 | **Hänger bei vollem Speicher abfangen + Abbrechen-Knopf** | 4 (+2 für den fehlenden Abbruch) | **ja** | mittel |
| 6 | **Öffentliches Repository + reproduzierbare Builds** | 6 (davon 3 als Blocker) | **ja** | groß |
| 7 | **Browser-Bindung nach oben ziehen**, limits.html ergänzen | 10/10 sind darüber gestolpert; 4 nennen die Platzierung ausdrücklich | **ja** | klein |
| 8 | **Passwortgüte und eine Bremse** beim Setzen | 2 | **ja** | klein |
| 9 | **Glossar auf den Stand der App bringen** (Frist, Passwort, PBKDF2) | 3 | **ja** | ~1 Stunde |
| 10 | **Vorspann im Fehlerfall ausblenden** (Fund E) | 2 | **ja** | 1 Zeile |
| 11 | **Ein Satz zu Auftragsverarbeitung und Rechtsraum** | 3 (Reinhardt, Vollmer, Wehrle) | **ja** für den Satz | klein / die Sache selbst groß |
| 12 | **AAD über Frist, Drop-ID und Index** | 1 | **ja** | klein bis mittel |
| 13 | **Deckel über 500 MB** | 4 (Lindqvist, Vollmer, Brandtner, Wehrle) | teilweise — der Browserspeicher begrenzt mit | mittel |
| 14 | **Empfangs- oder Zustellnachweis** | 2 (Wehrle, Achleitner) | nein | braucht Relay |
| 15 | **Missbrauchsantwort vor dem Relay** | 4 nennen Firefox Send namentlich | nein, aber jetzt zu entscheiden | Konzept |
| 16 | **Kleinkram:** about.html:1981, toISOString, JPEG unter PNG, Lizenzzeile, Bidi-Filter, Segmentknöpfe auf 44 px, Scrollposition nach dem Cloaken, doppelte Dateien | je 1–2 | **ja** | ein Nachmittag |

**Die Zeilen 2 bis 5 sind die eigentliche Nachricht dieser Runde.** Sie stehen alle in dem Teil, den ihr für fertig haltet, sie sind alle ohne Relay behebbar, und sie haben zusammen **27 Nennungen**. Zum Vergleich: in Runde 2 hatte der meistgenannte behebbare Punkt (HSTS) sechs.

Punkt 4 ist eine Zeile Code mit fünf Nennungen — das beste Aufwand-Wirkung-Verhältnis dieser Runde, so wie HSTS es in Runde 2 war.

**Punkt 6 ist neu in dieser Schärfe.** Drei Personen nennen das fehlende Repository ausdrücklich als Blocker gleichrangig mit dem Relay, und einer sagt es als Bedingung hin:

> *„Erneut ansehen würde ich es an dem Tag, an dem ‚git clone' funktioniert."* (Reithinger, 5,2)

> *„Ich sehe es mir wieder an, wenn drei Dinge stehen: das Relay, der veröffentlichte Quelltext, und eine Antwort auf die Metadaten-Frage."* (Vosskamp, 4,3)

Vosskamps dritter Punkt ist der, den bisher niemand gestellt hat und der beim Relay-Bau sofort relevant wird: ein Relay hinter Cloudflare sieht IP-Adresse, Zeitpunkt und Blob-Größe. *„Inhalt interessiert einen staatlichen Akteur weniger als die Frage, wer am Dienstagabend 41 Megabyte von einer Behörden-IP abgeholt hat."* Wer das Relay entwirft, sollte die Antwort vorher wissen, nicht nachher.

**Ehrliche Einschätzung zur erreichbaren Note.** Wenn ihr die Punkte 2 bis 5 und 7 bis 10 vollständig abarbeitet und kein Relay baut, erwarte ich **4,3 bis 4,8** und weiterhin kein „ja". Das ist niedriger als die Prognose aus Runde 2 (4,7 bis 5,2), und der Grund ist unangenehm: die Runde-3-Tester haben härter geprüft als die Runde-2-Tester, und ein Testfeld, das härter prüft, findet in dieser Software noch etwas. Die Prognose gilt nur, wenn beim Abarbeiten nichts Neues eingeschleppt wird — das Passwort hat gezeigt, dass das nicht selbstverständlich ist.

---

## 8. Die härteste Wahrheit

Sieben Änderungen, alle bemerkt, Note von 4,20 auf 3,81 gefallen. Das liegt nicht daran, dass die Änderungen schlecht waren — sechs von sieben sind sauber gemacht und der Herkunftscheck ist die beste Einzeländerung aus drei Runden. Es liegt daran, dass **sechs von sieben an der Hälfte gearbeitet haben, die schon funktionierte**, und dass die siebte, das Passwort, einen neuen Defekt mitgebracht hat, den fünf Leute gefunden haben.

Der eigentliche Bruch ist aber ein anderer, und er ist wichtiger als die Zahl. In den Runden 1 und 2 lautete der Vorwurf: *„Es verschickt nichts, aber was da ist, ist ehrlich und gut gebaut."* In Runde 3 lautet er zum ersten Mal: *„Es verschickt nichts — und der fertige Teil verliert mir stillschweigend Dateien und sagt mir die Unwahrheit über gelöschte Daten."*

Vier Defekte tragen das:

- Der Abbruch hinterlässt Chiffrat, das die App nicht sieht. **Zehn von zehn.**
- „Download all" liefert zehn Dateien und meldet die fehlenden nicht. **Acht von zehn.**
- „Copy link again" zerstört Passwort-Links. **Fünf von zehn.**
- Der volle Speicher lässt die App stumm hängen. **Vier von zehn.**

Alle vier haben dieselbe Signatur: **die Software verliert etwas und sagt es nicht.** Das ist genau die Sorte Fehler, die ein Produkt nicht haben darf, dessen einziges Alleinstellungsmerkmal die nachprüfbare Ehrlichkeit ist. Ihr habt sie an der teuersten Stelle. Und es steht sogar in eurem eigenen Quelltext, dass ihr es besser wisst: für den vollen Speicher liegt eine fertige Meldung bereit, die in vier Testläufen nie erreicht wurde, und auf der Startseite steht „Tampering fails loudly", während das Manifest genau das nicht tut.

Der Preis dafür ist konkret. Reinhardt hätte das Produkt gemocht — sie schreibt zwei Absätze darüber, warum sie wiederkommen will. Vosskamp hat die CSP als besser als alles bezeichnet, was sie kennt. Reissner hat die ausgelieferten Bytes gegen den Quelltext gehasht und war beeindruckt. Alle drei begründen ihr „nein" am Ende **nicht** mit dem fehlenden Relay allein, sondern mit dem Datenrest, den die App leugnet. Beim Relay verzeihen sie euch, weil es angesagt ist. Beim Löschknopf nicht, weil er behauptet, fertig zu sein.

Die gute Nachricht steckt in derselben Zahl. Drei Runden, 83 gegengeprüfte Funde, **kein einziger Fund gegen die Kryptografie und kein einziger Fund gegen den Anbieter.** Zehn Leute haben es diesmal ernsthaft versucht, einer davon mit einem Byte-Vergleich der ausgelieferten Datei. Das Fundament trägt und es trägt nachweislich. Die vier Defekte oben sind Tagesarbeit, nicht Konzeptarbeit — und wenn sie weg sind, steht ihr vor genau einer Frage, und die ist immer noch dieselbe wie in Runde 1: das Relay.
