LimeSurvey 7.0.4 → 7.0.5

Eine technische Analyse

Kopfdaten und Grenzen

Release
7.0.4 → 7.0.5
Commits
11
Geänderte Dateien
50
Analyse
11.08.2026
Schwerpunkt
Berechtigungsfix und Oberflächenkorrekturen

Nicht untersucht

Verglichen wurden die Tags 7.0.4+260620 und 7.0.5+260623 mit git log --oneline 7.0.4+260620..7.0.5+260623, git diff --stat 7.0.4+260620..7.0.5+260623 sowie dateiweisen Diffs; beide Tags sind im Repository vorhanden und mussten nicht ersetzt werden. Nicht ausgewertet wurden die binären Übersetzungskataloge unter locale/ und die Vorlagendatei locale/_template/limesurvey.pot, sodass über inhaltliche Änderungen an Oberflächentexten keine Aussage getroffen wird. Ebenfalls nicht auf Auswirkungen untersucht wurden die Änderungen an der Testinfrastruktur unter tests/, weil dieser Code in einer Produktivinstallation nicht ausgeführt wird; ob die dortige Anmeldelogik nach der Umstellung noch fehlerfrei arbeitet, bleibt damit offen. Die ausgelieferten JavaScript-Bündel wurden nur über den Vergleich ihrer Dateiinhalte und der inhaltsabhängigen Dateinamen geprüft, nicht durch einen eigenen Build. Für die Kurzliste mussten keine Einträge wegen der Höchstzahl entfallen. Indirekte Effekte über transitive Drittabhängigkeiten sind durch reine Diff-Analyse grundsätzlich nicht vollständig erfassbar.

Executive Summary

7.0.5 ist ein kleines Korrekturrelease, dessen inhaltliches Gewicht fast vollständig in einem einzigen Commit liegt: der Bedingungseditor prüft erstmals Schreibrechte und die Zusammengehörigkeit von Umfrage, Gruppe und Frage, wodurch eine Rechteumgehung im Administrationsbereich geschlossen wird. Daneben stehen eine Reihe von Oberflächenkorrekturen am globalen Seitenmenü, an der Kontoseite und an der Navigationsleiste, die für Theme-Entwickler ausgeliefertes Markup verändern. Am ehesten unbemerkt kaputt geht die Menüdarstellung im globalen Seitenmenü: die Symboltypen werden anders ausgewertet als zuvor, und der Menütitel wird nicht mehr maskiert ausgegeben. Am ehesten unbemerkt ungelöst bleibt dagegen der in 7.0.4 eingeführte falsche Tabellenname für archivierte Antworttabellen, denn die bereits fehlerhaft geschriebenen Datensätze korrigiert dieses Release nicht. Datenbankmigrationen enthält das Release keine.

  1. Berechtigungsprüfung im Bedingungseditor
  2. Zentrale Auflösung von Umfrage-, Gruppen- und Frage-ID
  3. Korrekter Tabellenname für archivierte Antworttabellen
  4. Globales Seitenmenü rendert sofort und misst korrekt
  5. Symboltypen und Titelausgabe im Seitenmenü
  6. Stabile Schlüssel für Unterfragen und Antworten im Editor

Upgrade-Empfehlung

✅ Sofort installieren

Das Release schließt eine Rechteumgehung im Bedingungseditor, die jedem Konto mit Lesezugriff auf eine beliebige Umfrage offensteht, und erfordert dafür keine Anpassung an der Installation. Wer unter 7.0.4 Umfragen deaktiviert hat, sollte vor dem nächsten Datenbank-Integritätscheck die in A3 beschriebene Prüfung durchführen. Eigener Code, der die öffentlichen Methoden des Bedingungseditors aufruft, muss vorher gegen die geänderten Signaturen geprüft werden.

Lesepfad

ZielgruppeRelevante Kennungen und Kapitel
AdministratorenA1, A2, A3, A4, A5, A6, Weitere relevante Änderungen, Empfehlung
Plugin-EntwicklerA1, A2, Empfehlung
Theme-EntwicklerA4, A5, Weitere relevante Änderungen, Empfehlung
IntegratorenA1, A2, A3, Empfehlung
API-Nutzerkeine Kennung, Empfehlung

Die wichtigsten Änderungen

A1: Berechtigungsprüfung im Bedingungseditor

Sicherheitsrelevanz
Hoch
Handlungsdruck
Mittel
Bruchverhalten
Fehler zur Laufzeit
Betroffen
Administratoren, Plugin-Entwickler, Integratoren

Hintergrund: Der Bedingungseditor übernahm die Parameter qid und gid bisher ungeprüft aus der Anfrage und führte die verändernden Teilaktionen aus, ohne eine eigene Rechteprüfung vorzunehmen. Die einzige Hürde war die im Aktionsrahmen verankerte Mindestprüfung auf Leserecht für die in surveyid genannte Umfrage. Damit genügte ein Konto mit Lesezugriff auf eine beliebige Umfrage, um über eine passend gebaute URL Bedingungen einer fremden Frage zu löschen. Das Release führt die Hilfsmethode checkPermission ein, ruft sie vor jeder Teilaktion auf und leitet Umfrage-, Gruppen- und Frage-ID nur noch über die in A2 beschriebenen zentralen Methoden auf, die die Zusammengehörigkeit der drei Kennungen erzwingen.

Konsequenz: Verändernde Teilaktionen verlangen jetzt das Recht surveycontent in der Stufe update, lesende Ansichten das Recht in der Stufe read; Konten mit reinem Leserecht erhalten dort eine 403-Antwort statt der bisherigen Ausführung. Wird eine Frage-ID zu einer anderen Umfrage als der in der URL genannten angegeben, endet die Anfrage mit 400. Praktisch relevant ist zusätzlich, dass mehrere öffentliche Methoden ihre Parameter verloren haben: index nimmt nur noch die Teilaktion entgegen, createNavigatorUrl gar keine Parameter mehr, und getCopyForm überschreibt die übergebenen Werte für Frage und Gruppe mit denen aus der Anfrage. Eigener Code, der diese Methoden mit Argumenten aufruft, bricht sichtbar. Aufrufe des Bedingungseditors ohne qid enden nun mit 400 statt mit der bisherigen Weiterleitung samt Hinweismeldung; die einzige davon ausgenommene Teilaktion ist das Zurücksetzen der Umfragelogik, das vorgezogen und mit einer eigenen Rechteprüfung versehen wurde.

Vorher / Nachher:

// Vorher (application/controllers/admin/ConditionsAction.php)
// Delete entry if this is delete
case "delete":
    $this->surveyCondition->deleteCondition($qid, $p_cid);
    $this->redirectToConditionStart($qid, $gid);
    break;
// Nachher
// Delete entry if this is delete
case "delete":
    $this->checkPermission($qid, 'update');
    $this->surveyCondition->deleteCondition($qid, $p_cid);
    $this->redirectToConditionStart($qid, $gid);
    break;
// Neu (application/controllers/admin/ConditionsAction.php)
private function checkPermission(int $qid, string $permission = 'read')
{
    if (!$qid) {
        throw new CHttpException(400, gT("Your request is invalid.", 'unescaped'));
    }
    $question = Question::model()->findByPk($qid);
    if (!Permission::model()->hasSurveyPermission($question->sid, 'surveycontent', $permission)) {
        throw new CHttpException(403, gT("Access denied!", 'unescaped'));
    }
}

Nach dem Update testen: Mit einem Konto, das auf einer Umfrage nur surveycontent in der Stufe read besitzt, den Bedingungseditor einer Frage öffnen und dort das Löschen aller Bedingungen auslösen — die Antwort muss 403 lauten. Anschließend mit einem berechtigten Konto eine Bedingung anlegen, bearbeiten, kopieren und löschen sowie über das Umfragemenü die Umfragelogik zurücksetzen, um zu prüfen, dass die vorgezogene Behandlung dieser Teilaktion samt Bestätigungsdialog weiterhin funktioniert. Zuletzt eine URL aufrufen, in der surveyid auf die eigene und qid auf eine fremde Umfrage zeigt, und auf die Antwort 400 prüfen.

Betroffene Dateien: application/controllers/admin/ConditionsAction.php, application/core/LSYii_Application.php, tests/functional/backend/GetGroupAndQuestionIdPermissionTest.php, phpunit.xml

Relevante Commits: c3fc519a92

CVE / Advisory: Eine CVE-Kennung ist im Repository nicht auffindbar. Die Commit-Nachricht nennt Issue #20544 („Read-only survey user can delete conditions“), der mitgelieferte Funktionstest trägt die Annotation @group security, und phpunit.xml nimmt ihn in die Testsuite security auf.

A2: Zentrale Auflösung von Umfrage-, Gruppen- und Frage-ID

Sicherheitsrelevanz
Mittel
Handlungsdruck
Niedrig
Bruchverhalten
Fehler zur Laufzeit
Betroffen
Administratoren, Plugin-Entwickler, Integratoren

Hintergrund: Die Anwendungsklasse besaß bisher nur getSurveyId, das den Parameter aus der Anfrage las, auf Ganzzahligkeit prüfte und das Vorhandensein der Umfrage bestätigte. Für Gruppen und Fragen gab es kein Gegenstück, weshalb jeder Controller diese Prüfung selbst erledigen musste — oder eben nicht. Das Release ergänzt getGroupId und getQuestionId, zieht die Ganzzahlprüfung in die private Methode checkInteger und lässt beide neuen Methoden zusätzlich prüfen, dass die aufgelöste Gruppe beziehungsweise Frage zu der Umfrage gehört, die aus der Anfrage hervorgeht. Im selben Commit wurde die Ausnahmeerzeugung der Dateizugriffsprüfung is_file angefasst.

Konsequenz: Plugins und eigene Controller können die drei Kennungen künftig zentral auflösen lassen und erhalten die Konsistenzprüfung dabei geschenkt. Eine gültige, aber nicht existierende Umfrage-ID führt jetzt zu 404 mit der Meldung „Survey not found.“ statt zur bisherigen Meldung „Your request is invalid.“. Außerdem gibt getSurveyId den Wert nicht mehr als Ganzzahl zurück, weil die Umwandlung mit intval entfallen ist; da der Zwischenspeicher über is_int geprüft wird, greift er nicht mehr, und jeder Aufruf liest den Parameter erneut und lädt die Umfrage erneut aus der Datenbank. Code, der den Rückgabewert mit === gegen eine Ganzzahl vergleicht, schlägt fehl. In is_file erhält CHttpException als dritten Parameter die Zeichenkette 'unescaped', während die Nachbarzeilen sie als Maskierungsmodus an gT übergeben; der dritte Parameter ist dort der Fehlercode und wird an den Konstruktor von Exception durchgereicht, der eine Ganzzahl erwartet. Diese Zeile greift nur bei eingeschaltetem Debug-Modus, weil das Auslösen der Ausnahme sonst unterdrückt bleibt.

Vorher / Nachher:

// Vorher (application/core/LSYii_Application.php)
$intSurveyId = intval($surveyId);
if (strval($intSurveyId) !== strval($surveyId)) {
    if ($throwError) {
        throw new CHttpException(400, gT('Your request is invalid.'));
    }
    return false;
}
$surveyId = intval($surveyId);
// Nachher
if (!$surveyId || !self::checkInteger($surveyId, $throwError)) {
    return false;
}
$survey = Survey::model()->findByPk($surveyId);
if (!$survey) {
    if ($throwError) {
        throw new CHttpException(404, gT('Survey not found.', 'unescaped'));
    }
    return false;
}
self::$surveyId = $surveyId;
// Neu (application/core/LSYii_Application.php)
$surveyId = self::getSurveyId($throwError);
if ($surveyId && $surveyId != $question->sid) {
    if ($throwError) {
        throw new CHttpException(400, gT('Your request is invalid.', 'unescaped'));
    }
    return false;
}
/* We can set self::$surveyId according to question */
self::$surveyId = $question->sid;
self::$questionId = $questionId;
return self::$questionId;
// Vorher (application/core/LSYii_Application.php)
throw new CHttpException(403, "Disable for security reasons.");
// Nachher
throw new CHttpException(403, "Disable for security reasons.", 'unescaped');

Neuer Bezugswert: Wer bisher auf die Ganzzahligkeit des Rückgabewerts von getSurveyId gebaut hat, muss den Wert künftig selbst umwandeln; eine im Diff erkennbare alternative Quelle für den bereits umgewandelten Wert gibt es nicht.

Nach dem Update testen: Eine Umfrage-URL mit einer numerischen, aber nicht vergebenen Umfrage-ID aufrufen und prüfen, dass eine 404-Antwort erscheint. Anschließend mit eingeschaltetem Debug-Modus einen Dateizugriff außerhalb des Upload-Verzeichnisses provozieren — etwa über eine Theme-Ressource mit Pfadwechsel — und im Fehlerprotokoll nachsehen, ob eine 403-Seite oder ein Typfehler entsteht.

Betroffene Dateien: application/core/LSYii_Application.php

Relevante Commits: c3fc519a92

CVE / Advisory: Wie bei A1 ist keine CVE-Kennung auffindbar; die Sicherheitsmarkierung ergibt sich aus der Aufnahme des zugehörigen Tests in die Testsuite security in phpunit.xml.

Sammel-Commit c3fc519a92; umfasst die 2 vorstehenden Einträge.

A3: Korrekter Tabellenname für archivierte Antworttabellen

Sicherheitsrelevanz
Keine
Handlungsdruck
Hoch
Bruchverhalten
Kein Bruch
Betroffen
Administratoren, Integratoren

Hintergrund: Beim Deaktivieren einer Umfrage benennt LimeSurvey die Antworttabelle um und legt in archived_table_settings einen Datensatz an, der den neuen Tabellennamen und die Liste der verschlüsselten Felder festhält. In 7.0.4 wurde die physische Tabelle nach old_responses_<sid>_<zeitstempel> umbenannt, der Datensatz aber unter old_survey_<sid>_<zeitstempel> eingetragen — Umbenennung und Registrierung liefen also auseinander. Beide Zeilen stammen aus demselben Commit vom 18.06.2026, der ausschließlich in 7.0.4 enthalten ist; die fehlerhafte Registrierung betrifft damit nur Umfragen, die unter 7.0.4 deaktiviert wurden.

Konsequenz: Der Fehler ist rein korrigierend behoben, bestehende Installationen müssen nichts anpassen. Die unter 7.0.4 geschriebenen Datensätze bleiben jedoch falsch, und das hat zwei greifbare Folgen. Erstens löscht der Datenbank-Integritätscheck alle Einträge aus archived_table_settings, zu denen keine gleichnamige Tabelle existiert; der falsch benannte Eintrag verschwindet dort also endgültig, samt der darin gespeicherten Liste verschlüsselter Felder. Zweitens sucht der Import archivierter Antworten in der Dateneingabe den Datensatz über den Tabellennamen; findet er ihn nicht, bleibt die Liste der verschlüsselten Felder leer. Wer unter 7.0.4 eine Umfrage mit verschlüsselten Antwortfeldern deaktiviert hat, sollte den betroffenen Datensatz vor dem nächsten Integritätscheck korrigieren.

Vorher / Nachher:

// Vorher (application/models/services/SurveyDeactivate.php)
$this->archiveTable($iSurveyID, $userID, "old_survey_{$surveyDate}", 'response', $DBDate, json_encode(Response::getEncryptedAttributes($iSurveyID)));
// Nachher
$this->archiveTable($iSurveyID, $userID, "old_responses_{$surveyDate}", 'response', $DBDate, json_encode(Response::getEncryptedAttributes($iSurveyID)));

Nach dem Update testen: In archived_table_settings nach Datensätzen mit tbl_type = 'response' suchen, deren tbl_name mit old_survey_ beginnt, und prüfen, ob dazu jeweils eine Tabelle mit demselben Namen existiert oder ob stattdessen eine gleich datierte Tabelle mit dem Präfix old_responses_ vorliegt. Anschließend eine Testumfrage mit verschlüsseltem Antwortfeld aktivieren, eine Antwort erfassen, die Umfrage deaktivieren und kontrollieren, dass der neue Datensatz denselben Namen trägt wie die umbenannte Tabelle.

Betroffene Dateien: application/models/services/SurveyDeactivate.php

Relevante Commits: 56bcfb18f7

A4: Globales Seitenmenü rendert sofort und misst korrekt

Sicherheitsrelevanz
Keine
Handlungsdruck
Niedrig
Bruchverhalten
Stiller Fehler
Betroffen
Administratoren, Theme-Entwickler

Hintergrund: Das globale Seitenmenü der Umfrage- und Gruppeneinstellungen baute sein Grundgerüst erst auf, nachdem die Menüdaten per AJAX eingetroffen waren; bis dahin blieb der Platzhalter im Layout leer und die Seite sprang beim Nachladen um. Die Breitenberechnung des Ziehgriffs rechnete außerdem mit der absoluten Seitenkoordinate des Mauszeigers abzüglich vier Pixeln, ging also stillschweigend davon aus, dass die Seitenleiste am linken Rand des Dokuments beginnt. Zusätzlich verwies die Paketdefinition auf eine CSS-Datei, die im Paketverzeichnis nicht vorhanden ist.

Konsequenz: Das Menügerüst und die Ereignisbehandlung entstehen jetzt sofort, die geladenen Menüpunkte werden nachträglich eingespielt; der Layoutsprung beim Seitenaufbau entfällt. Der Ziehgriff berechnet die Breite aus der Position der Seitenleiste und dem beim Anfassen gemerkten Abstand zum rechten Rand, wodurch das Ziehen auch dann korrekt funktioniert, wenn die Leiste nicht bündig am linken Rand des Dokuments sitzt. Für Theme-Entwickler ist das ausgelieferte Markup an drei Stellen anders: die Höhenbegrenzung des Griffcontainers ist entfallen, die Schaltfläche trägt zusätzlich die Klasse resize-btn, und die Komponente hängt sich in das Elternelement des Platzhalters statt in den Container. Eigene Regeln, die auf der bisherigen Struktur aufsetzen, greifen unverändert weiter oder eben nicht mehr, ohne dass ein Fehler sichtbar wird. Die Klasse resize-btn ist im Diff nirgends mit Formatierung hinterlegt; sie steht als Ansatzpunkt bereit.

Vorher / Nachher:

// Vorher (assets/packages/globalsidepanel/src/components/GlobalSidemenu.js)
init() {
    this.actions.getMenus().then(() => {
        this.controlActiveLink();
        this.render();
        this.attachEventListeners();
        this.mounted();
    }).catch((error) => {
// Nachher
init() {
    this.render();
    this.attachEventListeners();

    this.actions.getMenus().then(() => {
        this.controlActiveLink();
        this.update();
        this.mounted();
    }).catch((error) => {
// Vorher (assets/packages/globalsidepanel/src/components/GlobalSidemenu.js)
<div class="resize-handle ls-flex-column" style="height: 100%; max-height: ${this.getWindowHeight}px">
    <button
        id="resize-handle-btn"
        class="btn"
// Nachher
<div class="resize-handle ls-flex-column" style="height: 100%;">
    <button
        id="resize-handle-btn"
        class="btn resize-btn"
// Vorher (assets/packages/globalsidepanel/src/components/GlobalSidemenu.js)
this.sideBarWidth = e.pageX - 4;
// Nachher
const sidebar = document.getElementById('sidebar');
const left = sidebar ? sidebar.getBoundingClientRect().left : 0;
this.sideBarWidth = (e.clientX - left) + (this.grabOffset || 0);

Konfiguration: In application/config/packages.php entfällt im Paket des globalen Seitenmenüs der css-Schlüssel ersatzlos; das Paket besteht danach nur noch aus der Skriptangabe und der Abhängigkeit. Der entfallene Schlüssel verwies auf build.min/css/main.css; unter assets/packages/globalsidepanel/build.min/ liegt in beiden Tags ausschließlich js/globalsidepanel.js, die Datei existierte also nicht. Wer eine eigene, angepasste Fassung dieser Konfigurationsdatei einsetzt und den Schlüssel dort belässt, behält die Anforderung an eine nicht vorhandene Datei.

// Vorher (application/config/packages.php)
'css'        => [
    'build.min/css/main.css'
],

Nach dem Update testen: Die globalen Umfrageeinstellungen und die Einstellungen einer Umfragegruppe öffnen und beobachten, ob das Gerüst der Seitenleiste bereits vor dem Eintreffen der Menüpunkte steht. Danach den Ziehgriff an verschiedenen Stellen anfassen und prüfen, ob die Leiste dem Zeiger ohne Sprung folgt. Abschließend im Netzwerkprotokoll des Browsers kontrollieren, dass keine Anforderung mehr auf eine nicht vorhandene Stylesheet-Datei des Seitenmenü-Pakets läuft.

Betroffene Dateien: assets/packages/globalsidepanel/src/components/GlobalSidemenu.js, assets/packages/globalsidepanel/src/globalsidepanelmain.js, assets/packages/globalsidepanel/build.min/js/globalsidepanel.js, application/config/packages.php

Relevante Commits: 2faf7a34c6

A5: Symboltypen und Titelausgabe im Seitenmenü

Sicherheitsrelevanz
Niedrig
Handlungsdruck
Niedrig
Bruchverhalten
Stiller Fehler
Betroffen
Administratoren, Theme-Entwickler

Hintergrund: Die Menüpunkte des globalen Seitenmenüs stammen aus den Menüeinträgen der Datenbank und tragen neben Titel und Symbol auch einen Symboltyp. Bisher setzte die Komponente für den Typ fontawesome das Präfix fa fa- und für den Typ remix das Präfix ri- vor den gespeicherten Symbolnamen und erkannte daneben den Typ iconClass in gemischter Schreibweise. Die neue Fassung ersetzt die Kette aus Bedingungen durch eine Verzweigung, in der remix und iconclass denselben Zweig teilen und der gespeicherte Wert unverändert als Klassenattribut ausgegeben wird; der Typ image kommt neu hinzu. Zugleich entfiel die Maskierung des Menütitels.

Konsequenz: Menüeinträge vom Typ remix, deren gespeicherter Symbolname bisher ohne das Präfix ri- auskam, erhalten dieses Präfix nicht mehr und zeigen kein Symbol; ein Fehler wird dabei nicht ausgegeben. Der Typ iconClass in gemischter Schreibweise trifft in der neuen Verzweigung auf keinen Zweig mehr und liefert eine leere Ausgabe. Neu unterstützt wird der Typ image, der den gespeicherten Wert als Bildquelle mit fester Breite einsetzt. Sicherheitlich bedeutsam ist, dass der Menütitel jetzt unmaskiert in die Seite geschrieben wird: Enthält ein Menüeintrag Markup, wird es ausgeführt statt angezeigt. Menüeinträge lassen sich nur mit dem globalen Recht settings bearbeiten, sodass die Ausnutzbarkeit auf Konten mit dieser Berechtigung beschränkt bleibt; einen Angriffsweg für Konten ohne dieses Recht zeigt der Diff nicht.

Vorher / Nachher:

// Vorher (assets/packages/globalsidepanel/src/components/Sidemenu.js)
if (iconType === 'fontawesome') {
    iconClass = `fa fa-${esc(menuItem.menu_icon)}`;
} else if (iconType === 'remix') {
    iconClass = `ri-${esc(menuItem.menu_icon)}`;
} else if (iconType === 'iconClass') {
    iconClass = esc(menuItem.menu_icon);
}

return `<i class="${iconClass}"></i>`;
// Nachher
switch (menuItem.menu_icon_type || 'fontawesome') {
    case 'fontawesome':
        return `<i class="fa fa-${esc(menuItem.menu_icon)}">&nbsp;</i>`;
    case 'image':
        return `<img width="32px" src="${esc(menuItem.menu_icon)}" />`;
    case 'iconclass':
    case 'remix':
        return `<i class="${esc(menuItem.menu_icon)}">&nbsp;</i>`;
    default:
        return '';
}
// Vorher (assets/packages/globalsidepanel/src/components/Sidemenu.js)
<span>${esc(menuItem.menu_title)}</span>
// Nachher
<span>${menuItem.menu_title}</span>

Neuer Bezugswert: Das Präfix der Symbolklasse liefert die Komponente für den Typ remix nicht mehr; es muss künftig vollständig im gespeicherten Symbolnamen des Menüeintrags stehen.

Nach dem Update testen: In der Verwaltung der Menüeinträge alle Einträge mit dem Symboltyp remix heraussuchen und prüfen, ob deren Symbolname bereits die vollständige Klasse enthält; danach die globalen Umfrageeinstellungen öffnen und kontrollieren, dass jeder Menüpunkt sein Symbol zeigt. Zusätzlich einen Menüeintrag mit spitzen Klammern im Titel anlegen und nachsehen, ob der Titel als Text erscheint oder als Markup wirksam wird.

Betroffene Dateien: assets/packages/globalsidepanel/src/components/Sidemenu.js, assets/packages/globalsidepanel/build/js/globalsidepanel.js, assets/packages/globalsidepanel/build.min/js/globalsidepanel.js

Relevante Commits: 2faf7a34c6

Sammel-Commit 2faf7a34c6; umfasst die 2 vorstehenden Einträge.

A6: Stabile Schlüssel für Unterfragen und Antworten im Editor

Sicherheitsrelevanz
Keine
Handlungsdruck
Niedrig
Bruchverhalten
Stiller Fehler
Betroffen
Administratoren

Hintergrund: Der React-Fragen-Editor bildete die Schlüssel seiner Listenelemente bisher aus der Datenbank-Kennung des jeweiligen Kindelements. Neu angelegte Unterfragen und Antworten tragen zunächst eine temporäre Kennung, die beim Speichern durch die echte ersetzt wird; der Schlüssel wechselte dadurch mitten in der Bearbeitung, React ersetzte das Element, und der Eingabefokus ging verloren. Der Editor vergibt jedem neuen Kindelement nun zusätzlich einen zufällig gebildeten Anwendungsschlüssel, der über den Wechsel der Kennung hinweg stabil bleibt und bevorzugt als Listenschlüssel verwendet wird. Die clientseitigen Prüfschemata für Antwort- und Unterfragen-Aktualisierungen lassen das neue Feld ausdrücklich zu.

Konsequenz: Wer beim Anlegen mehrerer Unterfragen oder Antworten den Fokus verlor, sobald der Speichervorgang zurückkam, sollte durchtippen können. Die Änderung greift allerdings nur dort, wo das Editor-Bündel aus den Quellen neu gebaut wird: Die im Repository abgelegten Bündel unter editor/build/static/js sind in beiden Tags inhaltsgleich, und ihre inhaltsabhängigen Dateinamen sind unverändert. Ob das offizielle Auslieferungspaket den Editor beim Bauen neu erzeugt, ist aus dem Repository nicht bestimmbar, da dort kein Einstiegsdokument des Editors versioniert ist. Für Installationen, die neu bauen, bleibt ein Rest: Die Array-Fragetypen reichen das Merkmal, das den Fokus beim Bearbeiten des Fragetitels schützen soll, unter abweichender Schreibweise an ihre Unterkomponenten weiter, sodass es dort immer undefiniert ankommt und die Schutzbedingung wirkungslos bleibt. Ob die Erweiterung der Prüfschemata bedeutet, dass der Anwendungsschlüssel auch an die Server-Schnittstelle übertragen wird und wie diese darauf reagiert, ist dem Diff nicht zu entnehmen, weil er keine serverseitige Änderung enthält.

Vorher / Nachher:

// Vorher (editor/src/components/QuestionTypes/MultipleChoice/MultipleChoice.js)
key={`${subQuestion.qid}-multiple-choice`}
draggableId={`${subQuestion.qid}-multiple-choice`}
// Nachher
key={`${subQuestion.appKey || subQuestion.qid}-multiple-choice`}
draggableId={`${subQuestion.appKey || subQuestion.qid}-multiple-choice`}
// Neu (editor/src/hooks/useQuestionChildren.js)
newChild.appKey = `${entityType}-${APP_KEY_PREFIX}${RandomNumber()}`
// Neu (editor/src/components/QuestionTypes/ArrayQuestion/ArrayQuestion.js)
istitleFocused={isTitleFocused}
// Neu (editor/src/components/QuestionTypes/ArrayQuestion/ArrayColumnsTitles/ArrayColumnsTitles.js)
isTitleFocused,

Nach dem Update testen: Im Fragen-Editor eine Frage vom Typ Mehrfachauswahl anlegen, mehrere Unterfragen unmittelbar hintereinander erfassen und beobachten, ob der Fokus im zuletzt angelegten Feld bleibt, während die Speicheranfragen zurückkommen. Dasselbe mit einem Array-Fragetyp wiederholen und dabei zusätzlich prüfen, ob der Fokus beim Bearbeiten des Fragetitels dort bleibt, wenn im Hintergrund eine neue Zeile angelegt wird. Vor dem Test durch Vergleich der Dateinamen unter editor/build/static/js klären, ob die eingesetzte Installation ein neu gebautes Editor-Bündel enthält.

Betroffene Dateien: editor/src/hooks/useQuestionChildren.js, editor/src/components/QuestionTypes/MultipleChoice/MultipleChoice.js, editor/src/components/QuestionTypes/ArrayQuestion/ArrayQuestion.js, editor/src/helpers/Buffer/operationsScheme/subQuestion/subquestionUpdateJoi.js, editor/src/helpers/constants/constants.js

Relevante Commits: 9e11b46c27

Weitere relevante Änderungen

Dateibrowser für Beschriftungssätze initialisiert
Themes und Rendering · Kein Bruch · Administratoren
Die Beschriftungssatz-Verwaltung ruft vor dem Aufbau des HTML-Editors nun initKcfinder() auf, sodass die Ressourcenverwaltung dort wieder Dateien anzeigt. (82e87c989b)

Semantische Überschriften auf der Kontoseite
Themes und Rendering · Stiller Fehler · Theme-Entwickler
Drei Seitenüberschriften sind von div auf h2 umgestellt, weshalb eigene Regeln mit Selektoren auf die früheren div-Elemente dort nicht mehr greifen. (f03b8d7a32)

Tabellenbeschriftungen für die Menülisten ergänzt
Themes und Rendering · Kein Bruch · Theme-Entwickler
Die beiden Menüübersichten geben jetzt ein caption-Element in der Tabelle aus, das eigene Tabellenlayouts einplanen müssen. (688f054261)

Navigationsleiste erbt die Textfarbe nicht mehr
Themes und Rendering · Stiller Fehler · Theme-Entwickler
Die Deklaration color: inherit entfällt bei Hover und Tastaturfokus, sodass Verweise in der Navigationsleiste dort die Farbe der Grundregel zeigen. (af75b15a76)

Zuordnung nach Bereich

BereichVollständig beschriebenWeitere Einträge
SicherheitA1, A2, A5
DatenbankA3
RemoteControl APIkeine
Survey Runtimekeine
Themes und RenderingA4, A5ja
Plugin-KompatibilitätA1, A2
Performancekeine

Nicht betroffene Bereiche

Die JSON-RPC-Schnittstelle in application/helpers/remotecontrol/remotecontrol_handle.php erscheint nicht im Diff; Änderungen an ihren Methoden oder Rückgabewerten sind daher nicht erkennbar.

Der Diff enthält keine Datei unterhalb von application/helpers/update/, und die interne Datenbank-Versionsnummer bleibt unverändert. Das Release bringt damit keine Migration mit; die Zeile steht im folgenden Auszug als unveränderter Kontext zwischen den beiden geänderten Versionswerten.

// Vorher (application/config/version.php)
$config['versionnumber'] = '7.0.4';
$config['dbversionnumber'] = 708;
$config['buildnumber'] = '';
$config['updatable'] = true;
$config['templateapiversion']  = 3;
$config['assetsversionnumber'] = '30492';
// Nachher
$config['versionnumber'] = '7.0.5';
$config['dbversionnumber'] = 708;
$config['buildnumber'] = '';
$config['updatable'] = true;
$config['templateapiversion']  = 3;
$config['assetsversionnumber'] = '30493';

Die Ereignisverwaltung unter application/libraries/PluginManager/ und die Ausdrucksauswertung unter application/helpers/expressions/ sind im Diff nicht enthalten; über Änderungen an Plugin-Ereignissen, ihren Parametern oder der Ausdruckssyntax lässt sich daraus nichts ableiten.

Der Diff enthält keine Datei unterhalb von themes/survey/. Die Theme-Änderungen dieses Release liegen unter themes/admin/Sea_Green/ und in assets/admin_themes/Sea_Green/.

Weder composer.json noch composer.lock erscheinen im Diff; die PHP-Mindestanforderung und der Bestand an PHP-Abhängigkeiten sind dort nicht angefasst worden.

Die Command-Schnittstelle unter application/libraries/Api/Command/V1/, die der React-Editor anspricht, ist im Diff nicht enthalten; die in A6 beschriebene Erweiterung betrifft ausschließlich die clientseitigen Prüfschemata.

Empfehlung

Administratoren

Vor dem Update: In archived_table_settings nach Datensätzen mit tbl_type = 'response' und einem tbl_name suchen, der mit old_survey_ beginnt, und die Treffer zusammen mit dem Datenbankbestand sichern (A3). Prüfen, ob eigene Erweiterungen die öffentlichen Methoden des Bedingungseditors mit Argumenten aufrufen (A1). Die Menüeinträge mit dem Symboltyp remix daraufhin durchsehen, ob ihr Symbolname die vollständige Klasse enthält (A5).

Unmittelbar nach dem Update: Den Bedingungseditor mit einem berechtigten und einem nur lesenden Konto prüfen (A1), die globalen Umfrageeinstellungen auf vollständige Menüsymbole und ein sprungfreies Seitenmenü kontrollieren (A4, A5) und in der Beschriftungssatz-Verwaltung die Ressourcenverwaltung öffnen. Den Datenbank-Integritätscheck erst starten, nachdem die falsch benannten Archivdatensätze bereinigt oder gesichert sind, weil er Einträge ohne passende Tabelle löscht (A3).

Rollback: Das Release legt keine Konfigurationsdatei an und bringt keine Migration mit — die interne Datenbank-Versionsnummer ist oben vor und nach dem Update mit demselben Wert zitiert. Eine Rückkehr zu 7.0.4 ist auf Dateiebene möglich, ohne dass am Schema etwas zurückzunehmen wäre. Unter 7.0.5 beim Deaktivieren von Umfragen geschriebene Archivdatensätze tragen den korrigierten Tabellennamen; ob 7.0.4 mit diesen Datensätzen umgehen kann, ist aus dem Diff nicht bestimmbar und vor einem Rollback in einer Testumgebung zu prüfen (A3).

Plugin-Entwickler

Eigenen Code gegen die geänderten Signaturen im Bedingungseditor prüfen, insbesondere Aufrufe von index, createNavigatorUrl und getCopyForm (A1). Wo bisher Umfrage-, Gruppen- oder Frage-ID selbst aus der Anfrage gelesen und geprüft wurden, lohnt der Wechsel auf LSYii_Application::getSurveyId, getGroupId und getQuestionId, weil diese die Zusammengehörigkeit der Kennungen mit erzwingen (A2). Vergleiche des Rückgabewerts von getSurveyId mit === gegen eine Ganzzahl müssen angepasst werden, und wer den geänderten Statuscode 404 für nicht existierende Umfragen abfängt, sollte die eigene Fehlerbehandlung nachziehen (A2).

Theme-Entwickler

Eigene Regeln für das globale Seitenmenü gegen das geänderte Markup prüfen: entfallene Höhenbegrenzung, die neue Klasse resize-btn und der verschobene Einhängepunkt (A4). Die Darstellung der Menüsymbole nach dem Update sichten, weil der Symboltyp anders ausgewertet wird (A5). Auf der Kontoseite und in den beiden Menüübersichten sind Überschriften von div auf h2 umgestellt und Tabellenbeschriftungen hinzugekommen, und in der Navigationsleiste entfällt die Farbvererbung bei Hover und Fokus — die drei zugehörigen Einträge stehen unter Weitere relevante Änderungen.

Integratoren

Automatisierte Abläufe, die URLs des Bedingungseditors aufrufen, müssen künftig surveyid, gid und qid konsistent zueinander setzen; Aufrufe ohne qid enden mit 400 statt mit einer Weiterleitung (A1). Fehlerbehandlungen, die auf dem bisherigen Statuscode für nicht existierende Umfragen aufsetzen, sind auf 404 umzustellen (A2). Wer archivierte Antworttabellen aus archived_table_settings ausliest, muss damit rechnen, dass unter 7.0.4 geschriebene Datensätze einen Namen tragen, zu dem keine Tabelle existiert (A3).

API-Nutzer

Für Nutzer der RemoteControl-Schnittstelle enthält dieses Release keine vollständig beschriebene Änderung; die zugehörige Datei erscheint nicht im Diff.