All checks were successful
Container-Image bauen und veröffentlichen / build-and-push (push) Successful in 5m15s
171 lines
16 KiB
Markdown
171 lines
16 KiB
Markdown
# Code- und Barrierefreiheitsprüfung
|
||
|
||
Stand: 09.09.2026. Repository: syncova-policies. Geprüfter Commit: 0134dd6.
|
||
|
||
## Ergebnis und Prüfgrenzen
|
||
|
||
Die Anwendung enthält sinnvolle Grundlagen: lokal eingebundene Inter-Schrift, lang="de", einen Sprunglink zum Hauptinhalt, beschriftete Suchfelder, echte Links zu Datenschutzerklärungen, Radix-Dialoge mit Titeln und bereinigte Markdown-Ausgabe. Vollständige technische oder rechtliche Barrierefreiheit ist damit noch nicht belegt. Es bestehen konkrete Fehler in Statusmeldungen und Formularsemantik sowie Risiken bei kleinen Ansichten und der Veröffentlichung von Pflichtseiten.
|
||
|
||
Durchgeführt: statische Prüfung zentraler öffentlicher Komponenten, Admin-Formulare, Dialog-/Tab-Bausteine, Authentifizierung, Benutzer- und Seiten-APIs, CSS, Lockdatei und Workflow; Sichtung der fünf letzten Commits; Abgleich mit aktuellen offiziellen Quellen. TypeScript: node node_modules/typescript/bin/tsc --noEmit --incremental false, Exit-Code 0.
|
||
|
||
Nicht durchgeführt: produktiver Browser-/Screenreadertest, vollständiger EN-301-549-Test, Kontrastmessung gerenderter Zustände, Prüfung der aktuell in der Datenbank veröffentlichten Texte, Sichtung/Übersetzungsprüfung des alangu-Videos, Prüfung exportierter PDF-Dateien oder Penetrationstest. Aus CSS abgeleitete Layout-/Fokusrisiken sind deshalb ausdrücklich noch zu reproduzieren. Keine Änderungen an Anwendung, Datenbank oder Deployment.
|
||
|
||
## Priorität 1: Sicherheit
|
||
|
||
### S1 – Veraltete, von kritischen Sicherheitslücken betroffene Next.js-Version
|
||
|
||
Fundstelle: package-lock.json:4233, package.json (Next-Abhängigkeit).
|
||
|
||
Die Lockdatei fixiert Next.js 15.5.3. Die App verwendet den App Router mit Server Components. Diese Kombination liegt im betroffenen Bereich der RSC-Sicherheitslücke CVE-2025-66478. Ein Versionsbereich mit ^ in package.json behebt dies bei npm ci nicht.
|
||
|
||
Vorschlag: Next.js und die zugehörigen React-Abhängigkeiten gemeinsam auf einen kompatiblen, aktuell gepatchten Stand aktualisieren und die Lockdatei neu erzeugen. Das Sicherheitsrelease vom 25.08.2026 nennt 15.5.24 für die 15.5-Reihe. Anschließend Authentifizierung, Middleware, Build und Docker-Image prüfen. Die zusätzlichen August-Lücken haben eigene Einsatzbedingungen; ihre konkrete Ausnutzbarkeit wurde hier nicht behauptet oder getestet.
|
||
|
||
Quellen: [RSC-Sicherheitsmeldung](https://nextjs.org/blog/CVE-2025-66478), [Sicherheitsrelease August 2026](https://nextjs.org/blog/august-2026-security-release).
|
||
|
||
### S2 – Sperren und Rollenänderungen widerrufen vorhandene Berechtigungen nicht
|
||
|
||
Fundstellen: lib/auth.ts:29 und :55, lib/require-admin.ts:10, app/api/users/[id]/route.ts:16.
|
||
|
||
isActive wird bei der Anmeldung geprüft. Danach kommt die Rolle aus dem JWT; geschützte APIs vertrauen der Sitzung. Wird ein Konto gesperrt, gelöscht oder herabgestuft, kann dessen bereits ausgestellte Sitzung weiterhin autorisiert werden.
|
||
|
||
Vorschlag: Vor privilegierten Datenzugriffen den aktuellen Benutzerzustand aus der Datenbank prüfen. Für Passwortwechsel bzw. „alle Sitzungen abmelden“ eine Sitzungsversionsnummer oder eine widerrufbare Sitzung verwenden. Die Middleware allein ersetzt diese Prüfung nicht.
|
||
|
||
Abnahme: Konto anmelden, anschließend sperren/herabstufen/löschen, alte Sitzung gegen Lese- und Schreib-APIs verwenden. Die jeweils entzogenen Rechte müssen sofort abgewiesen werden.
|
||
|
||
### S3 – ADMIN und SUPER_ADMIN haben faktisch dieselben Benutzerverwaltungsrechte
|
||
|
||
Fundstellen: app/api/users/route.ts:53 und :96; app/api/users/[id]/route.ts:16, :60 und :107.
|
||
|
||
Ein ADMIN darf SUPER_ADMIN-Konten anlegen, Rollen ändern und andere Konten löschen. Wenn SUPER_ADMIN eine höhere Vertrauensstufe darstellen soll, fehlt die serverseitige Abgrenzung.
|
||
|
||
Vorschlag: Berechtigungsmatrix festlegen und zentral durchsetzen; Rollenwerte und Eingabetypen serverseitig validieren. Den letzten aktiven Super-Admin gegen versehentliche Sperrung/Herabstufung/Löschung schützen. Falls beide Rollen absichtlich gleichberechtigt sind, das Rollenmodell vereinfachen.
|
||
|
||
## Priorität 2: Barrierefreiheit und Bedienbarkeit
|
||
|
||
### A1 – Dynamische Fehler werden nicht zuverlässig angekündigt
|
||
|
||
Fundstellen: app/auth/signin/page.tsx:75; components/policy-form.tsx:317; components/policy-defaults-form.tsx:196; components/site-pages-form.tsx:232; components/policy-list.tsx:76.
|
||
|
||
Die Fehlermeldungen sind normale div-Elemente ohne Live-Region oder gezielte Fokusführung. Im Policy-Formular werden Fehler gesetzt und Tabs gewechselt, der Fokus wird aber nicht zur Zusammenfassung bzw. zum betroffenen Feld geführt.
|
||
|
||
Vorschlag: Fehlermeldungen programmatisch ankündigen, beispielsweise über eine geeignete Live-Region. Eine fokussierbare Fehlerzusammenfassung mit Links zu den Feldern vorsehen und beim Navigieren zum Fehler zuerst den passenden Tab öffnen. Feldfehler über aria-describedby verknüpfen. Keine unnötigen Doppelansagen durch gleichzeitigen Fokus und mehrere Live-Regionen.
|
||
|
||
Bezug: WCAG 3.3.1, 3.3.3 und 4.1.3. Die genaue Ansage mit NVDA prüfen.
|
||
|
||
### A2 – Die Suche meldet „keine Treffer“ nicht über ihre Statusregion
|
||
|
||
Fundstelle: components/policy-list.tsx:159.
|
||
|
||
Die Statusregion existiert nur bei mehr als null Treffern. Gerade beim Wechsel auf null Treffer wird sie entfernt. Auch das erneute Einfügen einer bereits gefüllten Live-Region ist nicht zuverlässig.
|
||
|
||
Vorschlag: Die Statusregion dauerhaft rendern; darin Laden, Trefferzahl, null Treffer und Ladefehler sinnvoll abbilden. Beim Zurücksetzen den Fokus ins Suchfeld zurückführen, weil die Zurücksetzen-Schaltfläche verschwindet.
|
||
|
||
Bezug: WCAG 4.1.3; Fokusführung ergänzend prüfen.
|
||
|
||
### A3 – Pflichtfelder werden nur teilweise programmatisch ausgezeichnet
|
||
|
||
Fundstellen: components/markdown-input.tsx:44 und :61; components/policy-form.tsx:331.
|
||
|
||
required steuert im MarkdownInput nur ein Sternchen, nicht required oder aria-required an der Textarea. Die Bedeutung des Sternchens wird nicht am Formular erläutert. aria-invalid allein erklärt nicht den Fehler.
|
||
|
||
Vorschlag: Eine sichtbare Pflichtfelderläuterung, aria-required und verknüpfte Hinweise/Fehler ergänzen. Native required-Validierung nur einsetzen, wenn das Verhalten in ausgeblendeten Tabs und bei geerbten Standardtexten berücksichtigt ist. Tablisten im Markdown-Editor mit dem jeweiligen Feldnamen beschriften.
|
||
|
||
Bezug: WCAG 1.3.1, 3.3.2, 4.1.2.
|
||
|
||
### A4 – Dialoge sind nicht durchgängig für schmale bzw. niedrige Ansichten ausgelegt
|
||
|
||
Fundstellen: components/policy-form.tsx:298; components/user-management.tsx:314; components/ui/dialog.tsx:61.
|
||
|
||
Tabs und Veröffentlichungsschalter stehen ohne responsiven Umbruch nebeneinander. Das Formular „Neuer Benutzer“ verwendet den Dialog ohne maximale Höhe und ohne scrollbaren Bereich. Der gemeinsame Dialog-Baustein begrenzt nur die Breite. Viele Fehlermeldungen stehen außerdem außerhalb des scrollbaren Formularbereichs.
|
||
|
||
Vorschlag: Die Kopfzeile unterhalb des sm-Breakpoints untereinander setzen. Alle Dialoge auf die verfügbare dynamische Viewporthöhe begrenzen, etwa max-height: calc(100dvh - 2rem), und Inhalt samt Fehlern erreichbar scrollen lassen. Speichern/Abbrechen müssen erreichbar bleiben. Nicht einfach Überlauf verstecken.
|
||
|
||
Bezug: WCAG 1.4.4, 1.4.10, 1.4.12 und 2.1.1. Risiko aus dem Code; Reproduktion bei 320 CSS-Pixeln, 200 % Textvergrößerung, 400 % Zoom und geöffneter Bildschirmtastatur steht aus.
|
||
|
||
### A5 – Fokus nach programmgesteuert geöffneten Dialogen ist nicht ausdrücklich abgesichert
|
||
|
||
Fundstellen: app/admin/page.tsx:291 und :496; components/ui/dialog.tsx:58; components/defaultable-field.tsx:49 und :55.
|
||
|
||
Die Dialoge werden über State und Menüaktionen geöffnet, nicht über einen zugehörigen DialogTrigger. Eine explizite Rückgabe des Fokus an einen dauerhaft vorhandenen Auslöser ist nicht implementiert. Auch „Abweichen“ und „Standard übernehmen“ ersetzen das fokussierte Element.
|
||
|
||
Vorschlag: Auslöserreferenz bzw. ein sinnvolles Ersatzziel speichern und über onCloseAutoFocus wieder fokussieren. Beim Moduswechsel das neue Eingabefeld bzw. dessen Auslöser fokussieren. Verschachtelte Benutzer-/Löschdialoge gesondert testen. Keinen globalen pointer-events-Reset als Ersatz für korrektes Dialogverhalten verwenden.
|
||
|
||
Bezug: WCAG 2.4.3 und 2.1.2. Radix bringt Grundlagen mit; das konkrete Zusammenspiel ist noch im Browser zu prüfen.
|
||
|
||
### A6 – Pflichtseiten lassen sich versehentlich vollständig entfernen
|
||
|
||
Fundstellen: app/api/site-pages/route.ts:31; components/site-footer.tsx:17; components/legal-page.tsx:36.
|
||
|
||
Ein leer gespeicherter Text ist erlaubt, der Footer entfernt den Link und die Seite antwortet mit 404. Fehlende Felder einer PUT-Anfrage werden ebenfalls zu leeren Strings. So kann eine unvollständige Aktualisierung mehrere Seiten entfernen.
|
||
|
||
Vorschlag: Entwurf und veröffentlichte Fassung trennen. Vor Veröffentlichung Pflichtinhalte prüfen; unvollständige PUT-Nutzdaten ablehnen oder ausdrücklich PATCH-Semantik implementieren. Eine vorhandene Erklärung nicht wegen eines leeren Entwurfs vom Netz nehmen. Links zu Leichter Sprache und Gebärdensprache zusätzlich gut sichtbar im Kopfbereich platzieren; der Footer allein ist nicht pauschal ein Gesetzesverstoß.
|
||
|
||
Rechtsbezug: Erklärung nach § 14 LBGG; ergänzende Angebote nach § 13 Absatz 3 LBGG in Verbindung mit § 4 BITV 2.0.
|
||
|
||
### A7 – Die vorhandene Erklärungsvorlage verweist auf die falsche Stelle
|
||
|
||
Fundstelle: components/site-pages-form.tsx:103.
|
||
|
||
Die Vorlage nennt die Schlichtungsstelle nach § 16 BGG. Für dieses kommunale Portal in Schleswig-Holstein ist das Landesverfahren über die Beschwerdestelle für barrierefreie Informationstechnik maßgeblich. Auch lib/site-pages.ts:40 sollte die landesrechtliche Grundlage nennen.
|
||
|
||
Vorschlag: Neue Vorlage dieses Pakets verwenden, aktuelle Kontaktdaten und überprüften Status eintragen. Keine vollständige Vereinbarkeit allein aus einem erfolgreichen automatisierten Test ableiten.
|
||
|
||
Quelle: [Beschwerdestelle Schleswig-Holstein](https://www.inklusion.sh/unsere-aufgaben/beschwerdestelle/).
|
||
|
||
### A8 – Videoverfügbarkeit, Videoinhalt und Barrierefreiheit werden zu pauschal beschrieben
|
||
|
||
Fundstellen: components/legal-page.tsx:65 und :92; components/site-pages-form.tsx:140 und :302.
|
||
|
||
Die Beschreibung behauptet Erläuterungen zum gesamten Portal. Die bisher mitgeteilte Videobezeichnung bezieht sich dagegen auf die Barrierefreiheitserklärung; der tatsächliche Inhalt wurde nicht gesichtet. Der Admin-Hinweis behauptet pauschal, ein Player-Anbieter bringe Untertitel mit. Das ist nicht aus dem Code ableitbar. Eine externe Untertitel-URL wird beim iframe nicht verwendet.
|
||
|
||
Vorschlag: Videotitel und Inhaltsbeschreibung pflegbar machen. Abdeckung von Portalinhalt, Navigation, Erklärung und weiteren Angeboten prüfen und gegebenenfalls das Video ergänzen. Sichtbaren Direktlink und entsprechende Textalternative anbieten. Tastaturbedienung, Fokus, Untertitel und gegebenenfalls Audiodeskription am konkreten Medium prüfen; Anforderungen hängen von dessen Ton-/Bildinhalt ab.
|
||
|
||
Quelle: [§ 4 BITV 2.0](https://www.gesetze-im-internet.de/bitv_2_0/__4.html). Ein ergänzender deutscher Text ersetzt fehlende DGS-Inhalte nicht.
|
||
|
||
### A9 – Markdown kann die Seitenstruktur und den Umbruch verschlechtern
|
||
|
||
Fundstellen: lib/markdown.ts:6 und :73; components/legal-page.tsx:49; app/globals.css:165.
|
||
|
||
Die Seite erzeugt bereits eine h1. Beliebige Markdown-h1, Überschriftensprünge, Bilder ohne sinnvollen Alternativtext und breite Tabellen werden redaktionell nicht geprüft. Für Tabellen und lange Links fehlen gezielte Umbruchregeln. Im Fehler-Fallback werden Listen zu visuellen Aufzählungspunkten statt semantischen Listen.
|
||
|
||
Vorschlag: Hinweise/Validierung im Editor, kontextgerechte Überschriftenebenen, Alt-Text-Prüfung und Tabellenüberschriften. Lange Links umbrechen; tatsächlich zweidimensionale Tabellen in einem beschrifteten, tastaturbedienbaren Scrollbereich darstellen. Einen semantikerhaltenden Fallback einsetzen. Die Vorlagen beginnen mit h2, weil die Seite den Haupttitel bereits liefert.
|
||
|
||
Bezug: WCAG 1.1.1, 1.3.1 und 1.4.10. Konkrete veröffentlichte Inhalte sind zusätzlich zu prüfen.
|
||
|
||
### A10 – Kleine Beschriftungs- und Lesbarkeitsdetails
|
||
|
||
Fundstellen: app/auth/signin/page.tsx:119; components/user-management.tsx:390; app/globals.css:165.
|
||
|
||
„Passwort anzeigen“ bleibt auch bei sichtbarem Passwort unverändert. Beschriftung zwischen Anzeigen/Verbergen wechseln oder einen korrekt benannten Toggle mit aria-pressed verwenden.
|
||
|
||
Die öffentlichen Markdown-Texte sind weiterhin text-sm, also normalerweise 14 px. Das ist kein automatischer WCAG-Verstoß. Für längere Texte würde ich entsprechend dem Wunsch nach Standardschrift text-base mit etwa 1,6 Zeilenhöhe verwenden; kompakte Abstände können erhalten bleiben. Leichte Sprache zusätzlich hinsichtlich Darstellung und Verständlichkeit prüfen lassen.
|
||
|
||
## Weitere Verbesserungen
|
||
|
||
- components/policy-list.tsx:167: Das bedingungslos angezeigte Badge „DSGVO-konform“ entfernen oder durch eine sachliche Bezeichnung ersetzen. Ein nichtleerer Datensatz ist keine nachgewiesene Rechtsprüfung.
|
||
- lib/site-pages.ts:133: Medien-URLs mit URL-Normalisierung validieren. Eine mit / und anschließendem Backslash beginnende Adresse kann vom Browser als fremder Host interpretiert werden. Explizite Regeln für lokale Dateien und erlaubte externe Hosts verwenden; Nutzername/Passwort in URLs ablehnen.
|
||
- components/site-pages-form.tsx:278: Der Hinweis „Andere Videoportale sind gesperrt“ ist zu weitgehend. Beliebige HTTPS-Adressen sind als native Medienquelle erlaubt; nur die iframe-Auswahl ist auf alangu beschränkt.
|
||
- lib/site-pages.ts:84: Ein Auftragsverarbeitungsvertrag allein beweist nicht die Zulässigkeit einer Einbindung. Den tatsächlichen Datenfluss des Players mit dem DSB bewerten; keine pauschale Anbieterbewertung in Codekommentaren.
|
||
- lib/auth.ts:7 und mehrere API-Routen: Gemeinsamen Prisma-Client statt separater Instanzen in vielen Modulen nutzen.
|
||
- Benutzer-/Seiten-APIs: Typen, Längen, gültige Rollen und E-Mail-Normalisierung serverseitig einheitlich validieren.
|
||
- .gitea/workflows/publish-image.yml: Vor dem Image-Publish TypeScript, Linting, gezielte Funktionstests und automatisierte Accessibility-Tests einbauen. Im geprüften Repository fehlen eine entsprechende Test-Suite und Playwright/axe-Abhängigkeiten.
|
||
|
||
## Rechtlicher Maßstab
|
||
|
||
Für das Portal des Amts Leezen sind die landesrechtlichen Vorschriften Schleswig-Holsteins maßgeblich: insbesondere LBGG §§ 11–16, BfWebV SH und die über § 13 Absatz 3 LBGG herangezogene BITV 2.0. Die aktuelle Landeshandreichung nennt EN 301 549 V3.2.1 mit WCAG 2.1 A/AA und weiteren anwendbaren Anforderungen. WCAG 2.2 ist ein sinnvolles zusätzliches Entwicklungsziel, hier aber nicht pauschal als bereits verbindlicher Ersatz angesetzt.
|
||
|
||
Eine Erklärung, dass Barrieren bestehen, ersetzt deren Beseitigung nicht. Status, konkrete Barrieren, Alternativen, Bewertungsmethode, Datum, Feedback und Beschwerdeweg müssen sachlich zutreffen. Nicht geprüft bedeutet nicht automatisch nicht vereinbar, und kein automatisierter Befund bedeutet nicht vollständig vereinbar.
|
||
|
||
Quelle: [Aktuelle Landeshandreichung](https://www.schleswig-holstein.de/DE/landesregierung/ministerien-behoerden/PBISH/Service/checklisten/downloads/mindestanforderungen_websites.pdf?__blob=publicationFile&v=4).
|
||
|
||
## Empfohlene Abnahme
|
||
|
||
1. Sicherheitspatches und wirksamen Sitzungswiderruf umsetzen.
|
||
2. Formulare, Suche, Dialoge und Veröffentlichungsschutz korrigieren.
|
||
3. Öffentlich und administrativ mit Tastatur, NVDA/Firefox oder NVDA/Chrome sowie VoiceOver/Safari prüfen: Öffnen, Escape, Fokus zurück, verschachtelte Dialoge, Dropdowns, Tabs, Fehler und Speichern.
|
||
4. Reflow, Textvergrößerung, Textabstände und Hoch-/Querformat prüfen; Hell/Dunkel, Hover, Fokus und Fehlermeldungen messen. Für normalen Text mindestens 4,5:1, großen Text 3:1, relevante UI-Grenzen und Zustände 3:1.
|
||
5. Tatsächlich veröffentlichte Inhalte, alangu-Player und gegebenenfalls bereitgestellte PDFs gesondert prüfen.
|
||
6. Konformitätsstatus anhand aller anwendbaren Anforderungen festlegen, Vorlagen finalisieren und Erklärung mindestens jährlich sowie nach relevanten Änderungen überprüfen.
|
||
|
||
Referenz für technische Kriterien: [WCAG 2.1](https://www.w3.org/TR/WCAG21/). Die Prüfliste ist ein Arbeitsplan und kein Prüfprotokoll mit bestandenen Ergebnissen.
|