Dieser Artikel ist Teil der Friends4-Enzyklopädie und wird nach Wiki-Prinzipien gepflegt, geprüft und versioniert.
Friends4 – Full-Stack Security & Quality Audit (Juli 2026)
Scope: Node.js/Express/MySQL/Socket.io/EJSBackend, Android (Kotlin) und iOSApp (SwiftUI). Ausgeklammert (auf Wunsch): kompletter gameworld/MoorhuhnBereich und das emergencyFeuerwehr3DSpiel. Methode: Manuelle CodeDurchsicht (Auth und Zahlungsnahe Bereiche zuerst), gezielte statische Suche, npm audit, KonfigReview. Gesamtbild: Die Codebase ist überdurchschnittlich reif und sicherheitsbewusst (SessionRotation, bcrypt cost 12, 2FA/TOTP, globales CSRF, Origin/SecFetchPrüfung, perRouteRateLimits, Sing
Friends4 – Full-Stack Security & Quality Audit (Juli 2026)
**Scope:** Node.js/Express/MySQL/Socket.io/EJS-Backend, Android- (Kotlin) und iOS-App (SwiftUI).
**Ausgeklammert (auf Wunsch):** kompletter `gameworld`/Moorhuhn-Bereich und das `emergency`-Feuerwehr-3D-Spiel.
**Methode:** Manuelle Code-Durchsicht (Auth- und Zahlungs-nahe Bereiche zuerst), gezielte statische Suche, `npm audit`, Konfig-Review.
> **Gesamtbild:** Die Codebase ist überdurchschnittlich reif und sicherheitsbewusst
> (Session-Rotation, bcrypt cost 12, 2FA/TOTP, globales CSRF, Origin/Sec-Fetch-Prüfung,
> per-Route-Rate-Limits, Single-Use-Reset-Tokens, IDOR-Schutz auf Model-Ebene,
> `0` npm-Vulnerabilities, iOS-Keychain, `EncryptedSharedPreferences`). Die meisten
> Befunde betreffen **Streaming-Zugriffskontrolle, Deploy-Hygiene und Härtung** –
> nicht fehlende Grundkontrollen.
---
Prioritätsübersicht (Maßnahmenkatalog)
| # | Befund | Datei/Ort | Einstufung |
|---|--------|-----------|------------|
| 1 | `/api/streaming/*` (chunk/start/end) ohne jede Authentifizierung; schreibt bis 5 MB/Chunk auf Platte | `src/server.js:1528`, `src/api/streamingChunkRoutes.js:35` | **Kritisch** |
| 2 | MediaMTX Publish-Auth per Default auskommentiert → offenes RTMP-Relay; Reads immer erlaubt | `.mediamtx.yml` | **Hoch** |
| 3 | DB-Dumps (`friends4.sql`, `jarvis.sql` …) + `app-debug.apk` (7 MB) im Git eingecheckt | Repo-Root | **Hoch** |
| 4 | CSP mit `'unsafe-inline'` (script+style); `JSON.stringify` in `<script>` ohne `</script>`-Escaping | `src/server.js:900`, diverse EJS | **Hoch** |
| 5 | CSRF-Bypass für „native" Requests allein per fälschbarem Header | `src/middleware/security.js:137,264` | **Hoch** |
| 6 | Upload-Validierung nur per Client-MIME, kein Magic-Byte-/Reprocessing | `src/routes/index.js`, `src/routes/communityApi.js` | **Mittel** |
| 7 | Socket.io-Auth nur beim Connect, kein Re-Validate bei Logout/Expiry | `src/bootstrap/socketBootstrap.js`, `src/sockets/*` | **Mittel** |
| 8 | Legacy-Android `kotlin/*.kt` + `*_plain`-Fallback speichern Session/E2EE-Keys im Klartext | `friends4-android-app/kotlin/*`, `.../SessionManager.kt:31`, `E2EE.kt:187` | **Mittel** |
| 9 | Kein Certificate-Pinning (Android/iOS) | `RetrofitClient.kt`, `ApiService.kt`, iOS `URLSession` | **Mittel** |
| 10 | `friends4://` Deep-Links auf `exported=true` ohne App-Links-Verifikation | `AndroidManifest.xml:40+` | **Mittel** |
| 11 | 2FA-Secret & Reset-Token im Klartext at rest in DB | `userModel.js` `two_factor_secret`, `password_reset_tokens` | **Mittel** |
| 12 | Kein dediziertes Rate-Limit auf DM-Versand (nur Worldchat + globaler Limiter) | `src/controllers/messageController.js` | **Mittel** |
| 13 | `unsafe-eval` per Env optional zuschaltbar – in Prod verifizieren | `src/server.js` CSP | **Niedrig** |
| 14 | Sehr geringe Testabdeckung (Solo-Dev) | `tests/` | **Niedrig** |
| 15 | DSGVO: Audit-Logging bei Datenzugriff, Löschketten prüfen | diverse | **Niedrig** |
| 16 | Doppelter Root-`AndroidManifest.xml` / toter Code | `friends4-android-app/AndroidManifest.xml` | **Niedrig** |
---
1. Sicherheit – Detailbefunde
1.1 Auth / Session (überwiegend gut)
- **Positiv:** Kein JWT, sondern serverseitige Sessions (`express-session` + `express-mysql-session`), `__Host-`-Cookie-Prefix in Prod, `httpOnly`, `secure`, `sameSite=lax`, `rolling`, Session-**Regeneration** bei jedem Login/Registrierung/2FA (`establishAuthenticatedSession`, `authController.js:310`). bcrypt cost **12**. Starke Passwortregeln (≥12 Zeichen, Mixed, keine Whitespaces). Login/Reset/Register mit generischen Fehlermeldungen → keine User-Enumeration. Passwort-Reset über eigene `password_reset_tokens`-Tabelle mit `used_at` (Single-Use) und 1 h Ablauf. Bei Passwortänderung werden andere Sessions revoked (`revokeOtherSessionsForUser`).
- **Befund 11 (Mittel):** `two_factor_secret` (TOTP) und der Reset-Token liegen **im Klartext** in der DB. Bei DB-Leak sind 2FA umgehbar und Reset-Links (1 h) missbrauchbar.
*Fix:* TOTP-Secret mit app-seitigem Schlüssel (z. B. `crypto`-AES-GCM, Key aus `.env`/KMS) verschlüsseln; Reset-Token nur als `sha256`-Hash speichern und beim Einlösen gegen den Hash prüfen.
1.2 Streaming-Zugriffskontrolle
- **Befund 1 (Kritisch):** `app.use('/api/streaming', express.raw({limit:'5mb'}), streamingChunkRoutes)` – die Routen `POST /chunk/:streamKey`, `/start/:streamKey`, `/end/:streamKey` haben **keine** `requireLogin`/Ownership-Prüfung. Wer einen `streamKey` kennt oder rät, kann Chunks einschleusen, HLS-Konvertierung starten und fremde Streams beenden. Zusätzlich unauth. Schreiben von bis zu 5 MB je Request nach `STREAMS_DIR` → Disk-/CPU-DoS (ffmpeg-Start).
*Fix:* `requireLogin` + Prüfung, dass der eingeloggte User Eigentümer des zum `streamKey` gehörenden aktiven Streams ist (Abgleich gegen `streamingStateModel`/`videoworldModel`); `streamKey` als unguessbares Secret behandeln; Chunk-Endpoint zusätzlich rate-limiten.
- **Befund 2 (Hoch):** In `.mediamtx.yml` ist `externalAuthenticationURL` (Publish-Auth gegen `/rtmp-auth`) **auskommentiert**; laut Kommentar sind **Reads immer erlaubt**. Damit kann jeder auf beliebige `streamKeys` publizieren (offenes Relay) und jeder HLS-Stream ist ohne Zugriffskontrolle abrufbar.
*Fix:* `externalAuthenticationURL` aktivieren und im Backend Publish **und** Read gegen aktive, autorisierte Streams prüfen (v. a. für nicht-öffentliche/FSK18-Streams).
1.3 SQL-Injection (gut)
- Durchgehend `mysql2` mit `?`-Platzhaltern. Dynamische Fragmente (`${placeholders}` = `?,?,?`, `${orderBy}`, `${moderationCreatedAtColumn}`, `${whereConditions.join}`) sind aus **Whitelists/festen Strings** abgeleitet (geprüft in `videosearchService.js`, `searchExploreController.js`, `adminController.js`). **Kein** String-konkatenierter User-Input in Queries gefunden.
*Empfehlung:* Als Regressionsschutz die dynamischen Spalten-/Sortier-Whitelists zentral kapseln und per Unit-Test absichern.
1.4 XSS / EJS
- **Positiv:** User-Content (`renderEmojiText`, `renderForumPostText`, `renderTextWithEmojisAndLinks`) wird konsequent **zuerst HTML-escaped** und danach nur kontrolliertes Emoji-/Link-Markup injiziert (`emojiRenderer.js`). Admin-Rich-Content läuft über `sanitize-html` mit strikter Allow-List (`htmlSanitizer.js`).
- **Befund 4 (Hoch):** Effektive CSP enthält `script-src 'unsafe-inline'` **und** `style-src 'unsafe-inline'` (`server.js:900+`). Die strengere manuelle CSP in `securityHeaders.js` (`script-src 'self'`) wird von der Helmet-CSP überschrieben. Damit würde jede eingeschleuste Inline-Injection ausgeführt. Verschärfend: zahlreiche Templates betten Server-Daten via `<script>const x = <%- JSON.stringify(...) %></script>` ein (`lernworld.ejs`, `settings.ejs`, `club-detail.ejs`, `admin/*`). `JSON.stringify` escaped **nicht** `</script>`, `<!--`, `U+2028/2029` → bei user-beeinflussten Strings Script-Breakout möglich.
*Fix:* Auf Nonce-basierte CSP umstellen (`'unsafe-inline'` entfernen, Inline-Skripte mit `res.locals.cspNonce` versehen). JSON-Embeds über einen Helper serialisieren, der `<`→`<`, `>`→`>`, `&`→`&`, `U+2028/2029` ersetzt (oder in `<script type="application/json">` + `JSON.parse` mit escaptem Inhalt).
1.5 CSRF (gut, mit einer Lücke)
- **Positiv:** Global verdrahtet (`attachCsrfToken` + `enforceCsrfForSensitiveRoutes`, `server.js:1201`), Double-Submit + session-gebundene Tokens mit `timingSafeEqual`, Alter/Anzahl-Begrenzung. Zusätzlich `enforceSameOriginForSessionWrites` (Origin/Referer + `Sec-Fetch-Site`).
- **Befund 5 (Hoch):** `isNativeAppRequest()` hebt die CSRF-Prüfung allein anhand der **fälschbaren** Header `x-friends4-client-app: friends4-native` + Platform-Header auf (`security.js:264`). Cross-Origin-Browser-Requests können zwar keine Custom-Header ohne CORS-Preflight setzen – aber der Rückhalt ist dann nur noch der Origin/Sec-Fetch-Check, der bei **fehlenden** Origin/`Sec-Fetch-Site`-Headern (ältere Clients/Sonderfälle) durchlässt.
*Fix:* Native Clients zusätzlich über ein pro-Session-gebundenes Token/Signaturheader authentifizieren, nicht nur über einen statischen Marker; `enforceSameOriginForSessionWrites` bei fehlendem Origin **und** fehlendem `Sec-Fetch-Site` für state-changing Requests eher blocken (fail-closed).
1.6 HTTP-Security-Header (gut)
- Helmet aktiv: `X-Content-Type-Options`, `X-Frame-Options: SAMEORIGIN`, `Referrer-Policy`, `Permissions-Policy` (camera/mic/payment/usb `none`, geo `self`), HSTS (nur Prod, `max-age=1J; includeSubDomains; preload`), CSP mit differenzierten `frame-src`/`connect-src`/`img-src`. Einzige Schwäche: `'unsafe-inline'`/optional `'unsafe-eval'` (siehe 1.4, Befund 13).
1.7 Rate-Limiting (gut, mit Lücke)
- Dedizierte Limiter: Login 5/15 min (Key = IP+User), Register 5/h, Forgot-PW 3/30 min, Verification 4/15 min, Report 10/15 min, Community-Aktionen 8/10 min, globaler Limiter 900/15 min, Heartbeat 120/min. Worldchat-Privatnachrichten nutzen `communityActionLimiter`.
- **Befund 12 (Mittel):** Direktnachrichten (`messageController`) haben kein eigenes Limit außer dem globalen 900/15 min → Spam/Massenversand möglich.
*Fix:* Eigenen DM-Send-Limiter (z. B. 20–30/min je User) einführen.
1.8 File-Uploads (Mittel)
- **Positiv:** Größenlimits (Event-Cover 900 KB, Avatar 5 MB, Paid-Image 20 MB, Media 50 MB), `fileFilter` per MIME-Allow-List, Dateinamen **serverseitig** generiert (`Date.now()+randomHex`) → **kein Path-Traversal**.
- **Befund 6 (Mittel):** Der MIME-Typ stammt ausschließlich aus dem vom Client gesetzten Multipart-Header; es findet **kein Content-Sniffing (Magic Bytes)** und **kein Bild-Reprocessing/Re-Encoding** statt. Ein Polyglot/als `image/*` deklariertes SVG/HTML kann Payloads schmuggeln, die später ausgeliefert werden.
*Fix:* Nach Upload echten Content-Type per Magic-Bytes (`file-type`) prüfen und Bilder mit `sharp` re-encodieren (strippt EXIF/Payloads); SVG entweder verbieten oder `sanitize`-en; Auslieferung mit `Content-Type: image/*` + `X-Content-Type-Options: nosniff` (vorhanden) und ggf. `Content-Disposition: attachment` für Nicht-Bild.
1.9 Autorisierung / IDOR (gut)
- Ownership konsequent auf Model-Ebene: DM (`getConversation`, `getDirectMessageForParticipant`, `editDirectMessage` mit `sender_id/receiver_id`-Scoping), Stories mit Freundschafts- **und** Block-Prüfung, Bild-/Kommentar-Löschung mit `403` bei Fremdbesitz, Admin-Routen über gestufte `require*`-Middlewares inkl. Rollen-Trainings-Compliance. Keine offensichtliche „nur-ID-tauschen"-IDOR gefunden.
*Empfehlung:* Für neue Endpunkte das Muster „Query filtert immer nach `session.userId`" als Konvention/Lint festhalten.
1.10 Secrets-Management
- **Positiv:** Keine hartcodierten Secrets in `src/` gefunden – alles via `process.env`. `.env` und `.session-secret` sind in `.gitignore`. Nur `.env.example` eingecheckt. Android/iOS enthalten keine eingebetteten Server-Secrets.
- **Befund 3 (Hoch):** Eingecheckt sind **DB-Dumps** (`friends4.sql`, `2706.sql`, `A.Sql`, `G.sql`, `jarvis.sql`, `games.sql`, `qr.sql`) und `app-debug.apk` (7 MB). `friends4.sql` enthält 8 `INSERT`-Statements (keine bcrypt-Hashes gesichtet → vermutlich Schema/Seed, **verifizieren**). Selbst reine Schema-Dumps erleichtern Angreifern die Aufklärung; das APK bläht das Repo auf und kann Klartext-Strings/Endpunkte offenlegen.
*Fix:* Dumps und `.apk` aus dem Repo entfernen (ggf. `git filter-repo`, da Historie), in `.gitignore` aufnehmen; Schema-Migrationen (`migrations/`) sind die einzige Quelle der Wahrheit. Falls Personendaten enthalten → als Datenschutzvorfall behandeln.
1.11 WebRTC/WHIP-Streaming
- In `.mediamtx.yml` sind `webrtc/rtsp/srt` **deaktiviert**, API/Metrics nur auf `127.0.0.1` – gut. Verbleibendes Risiko ist die fehlende Publish/Read-Auth (Befund 2) und die offenen Chunk-Routen (Befund 1).
1.12 DSGVO
- **Positiv:** Datenexport vorhanden (`exportMyDataFromSettings`, JSON-Download), Account-Löschung (Sysadmin-gestützt), Einwilligungen werden versioniert erfasst (`updateUserOnboardingSafetyConsents`, `privacyConsentVersion`), 2FA/E2EE vorhanden, Betrieb in DE.
- **Befund 15 (Niedrig):** Kein durchgängiges **Audit-Logging bei sensiblen Datenzugriffen**; Verschlüsselung sensibler Felder at rest nur punktuell (siehe 1.1). Lösch-**Kaskaden** (Nachrichten, Medien, Forum, Referrals) auf Vollständigkeit prüfen (Right-to-Erasure).
*Fix:* Zentrales, unveränderliches Audit-Log für Admin-Zugriffe auf Nutzerdaten; dokumentierte Lösch-Checkliste je Datendomäne.
1.13 Dependencies / Deployment
- `npm audit --omit=dev`: **0 Vulnerabilities**; sinnvolle `overrides` für transitive Pakete. `prestart`/`check:syntax`/`security:check`/`typecheck` in CI vorhanden.
- **Prüfen (Niedrig):** Läuft PM2 **nicht** als root? Sind Stacktraces/`error.ejs` in Prod generisch (kein Debug-Leak)? `NODE_ENV=production` gesetzt? `allowUnsafeEvalInCsp` in Prod aus (Befund 13)?
---
2. Code-Qualität & Architektur
- **Positiv:** Saubere Schichtung (`routes`/`controllers`/`services`/`models`/`middleware`/`bootstrap`), zentrales Error-Rendering (`renderError.js`), Bootstrap-Fabriken (`appFactory`, `*Bootstrap`), ESLint inkl. `eslint-plugin-security`, `tsc`-Typecheck.
- **Verbesserung:** `dashboardLegacyController.js` ist mit >15 000 Zeilen ein Monolith → schrittweise in Domänen-Module (analog `src/modules/`) auslösen. N+1-Potenzial in Feed/Forum/Messenger prüfen (Batch-Loads statt Loop-Queries; die vorhandenen `IN (${placeholders})`-Batch-Queries sind der richtige Ansatz). DB-Indizes auf häufig gefilterte Spalten (`user_id`, `created_at`, `stream_key`, `receiver_id`) verifizieren (in Stories/DM bereits vorhanden).
- **Befund 14 (Niedrig):** Sehr wenige automatisierte Tests (`tests/*.test.js`). Priorität: Auth, CSRF, IDOR-Model-Funktionen, Rate-Limits als Regressionstests.
3. Performance
- Redis vorhanden (`@socket.io/redis-adapter`, `redis`) → Multi-Instance-Socket.io grundsätzlich vorbereitet (`config/socketAdapter.js`, `scripts/validate-multi-instance.sh`); **in Prod aktiv?** verifizieren. `compression` aktiv. Runtime-Cache (`services/runtimeCache.js`) vorhanden. Empfehlung: Feed-/häufige Queries in Redis cachen, Bild-CDN + `sharp`-Varianten (deckt zugleich Befund 6 ab), Lazy-Loading in Feed/Stories/Forum bestätigen.
4. GEO / SEO (sehr gut)
- Vorhanden: `robots.txt` (inkl. explizite Freigabe für GPTBot/ChatGPT-User/OAI-SearchBot/ClaudeBot/anthropic-ai; sensible Pfade `Disallow`), `llms.txt` (klare, zitierbare Faktenabsätze, offizielle URLs), `sitemap.xml` + `sitemap-geo.xml` + `sitemap-index.xml` (dynamisch), Schema.org-JSON-LD (Organization/SoftwareApplication/FAQPage in `seo-feature-landing.ejs`), dynamische Meta/OG-Tags, Geo-Landingpages je Stadt, SSR via EJS (crawlbar). Impressum/Datenschutz als echte Seiten.
- **Hinweis:** Beim Absichern der CSP/JSON-Embeds (Befund 4) die JSON-LD-Blöcke nicht escapen-brechen (dedizierter JSON-LD-Serializer). Core Web Vitals separat messen.
5. Mobile Apps
- **Positiv:** Android `usesCleartextTraffic="false"` + `network_security_config` (`cleartextTrafficPermitted="false"`), aktuelle App nutzt `EncryptedSharedPreferences`/`MasterKey` (`SessionManager.kt`). iOS speichert Session im **Keychain** (`AuthService.swift`, `kSecAttrService`). Die meisten Activities `exported="false"`.
- **Befund 8 (Mittel):** Zwei Ebenen: die flache **Legacy**-`friends4-android-app/kotlin/*.kt` speichert Session in **Klartext**-`SharedPreferences("friends4_prefs", MODE_PRIVATE)` (`LoginActivity.kt`, `SplashActivity.kt`, `Dashboard`/`WorldChat`). Zudem fallen `SessionManager` (`:31`) und `E2EE` (`:187`) bei fehlendem Keystore auf `*_plain`-Prefs zurück → **E2EE-Privatschlüssel/Session im Klartext** (Root-/Backup-Angriff). Die `kotlin/`-Dateien liegen zwar außerhalb der Gradle-SourceSets (vermutlich toter Code), sollten aber entfernt werden.
*Fix:* Legacy-`kotlin/`-Ordner und doppelten Root-`AndroidManifest.xml` (Befund 16) löschen; `*_plain`-Fallback für Session/E2EE-Keys entfernen bzw. hart abbrechen (kein Persistieren sensibler Daten ohne Keystore).
- **Befund 9 (Mittel):** Kein Certificate-Pinning (weder `CertificatePinner` bei OkHttp/Retrofit noch iOS-Pinning). Defense-in-Depth gegen MITM bei untergeschobener CA fehlt.
*Fix:* OkHttp `CertificatePinner` mit Backup-Pins bzw. iOS `URLSessionDelegate`-Pinning; Rotation über Config planen.
- **Befund 10 (Mittel):** `DashboardActivity`/`SplashActivity` sind `exported="true"` und behandeln `friends4://`-Deep-Links (feed/forum/profile/me …) über ein **Custom-Scheme** ohne verifizierte App-Links (`autoVerify`). Andere Apps können das Scheme registrieren/abfangen (Intent-Hijacking) und Deep-Link-Parameter sind unsigniert.
*Fix:* Auf verifizierte **App-Links** (`https://` + `assetlinks.json`, `android:autoVerify="true"`) bzw. iOS **Universal Links** umstellen; Deep-Link-Parameter serverseitig validieren, keine sicherheitsrelevanten Aktionen allein aus dem Link ableiten.
- **Play-Store-Historie (2.3.3):** Der frühere Guideline-Verstoß betraf irreführende/nicht-funktionale Screenshots/Features. Analoge Lücken hier: sicherstellen, dass alle in den Stores beworbenen Features (Videoworld/Streaming) real erreichbar und die Deep-Links (Befund 10) nicht auf tote Ziele zeigen.
---
Empfohlene Reihenfolge der Umsetzung
1. **Sofort (Kritisch/Hoch):** Streaming-Chunk-Routen absichern (#1) → MediaMTX-Auth aktivieren (#2) → DB-Dumps/APK aus Git entfernen (#3) → CSP `unsafe-inline` entfernen + JSON-Embeds escapen (#4) → CSRF-Native-Bypass härten (#5).
2. **Kurzfristig (Mittel):** Upload-Reprocessing (#6), Socket-Re-Auth (#7), Android-Legacy/Plain-Fallback entfernen (#8), DM-Rate-Limit (#12), 2FA/Reset-Token verschlüsseln (#11).
3. **Mittelfristig (Mittel/Niedrig):** Cert-Pinning (#9), App-Links (#10), Audit-Logging/Löschketten (#15), Testabdeckung (#14), Monolith-Refactoring, Prod-Härtung (PM2-User, `unsafe-eval` aus, Redis-Adapter aktiv).
---
Offene Punkte / benötigter Zugriff (statt zu raten)
- **`.env`/Prod-Konfig:** Ist `NODE_ENV=production`, `ALLOW_SAME_SITE_SESSION_WRITES`, `allowUnsafeEvalInCsp`, `EMAIL_VERIFICATION_REQUIRED` korrekt gesetzt? (nur `.env.example` einsehbar)
- **nginx/Reverse-Proxy-Config:** TLS-Version, HSTS-Doppelung, Weiterleitung `/api/streaming` und MediaMTX-Ports, ob `/private-uploads` wirklich geschützt ist.
- **DB-Schema in Prod:** Sind Indizes auf `stream_key`, `receiver_id`, `created_at` vorhanden? Enthalten die eingecheckten Dumps echte Personendaten?
- **PM2/Deployment:** Läuft der Prozess unter unprivilegiertem User? Sind Logs/Stacktraces nach außen nicht erreichbar?
- **Redis in Prod:** Ist der Socket.io-Redis-Adapter im Multi-Instance-Betrieb aktiv?
Hinweise
Nutze diesen Artikel als Orientierung. Bei Regeln gilt die aktuell freigegebene Version und bei Unsicherheit die Moderation.
Warnungen
Melde veraltete, missverständliche oder sicherheitsrelevante Informationen direkt über das Formular.
Diskussion
Die Diskussionsseite ist für konkrete Verbesserungsvorschläge gedacht: fehlende Abschnitte, unklare Formulierungen, Quellenhinweise oder Korrekturen. Sie ist keine Chatfunktion.
Bilder und Downloads
Optionale Medien, Grafiken oder Downloads können über strukturierte Daten und zukünftige Dateifreigaben ergänzt werden.