From e005d83a3d1987b37a3558fa9acb7e66e213e196 Mon Sep 17 00:00:00 2001 From: Jan-Niclas Loosen Date: Sat, 26 Sep 2026 17:26:04 +0200 Subject: [PATCH] docs: S7 exception applies only to pages not yet in Divi's saved form Co-Authored-By: Claude Opus 5.5 (1M context) --- docs/guides/divi-attrs.md | 2 +- docs/guides/divi-placeholders.md | 2 +- docs/guides/divi-shortform.md | 2 +- docs/superpowers/notes/2026-09-26-etappe-2-entscheidungen.md | 1 + docs/superpowers/specs/2026-09-25-wp-lit-mcp-design.md | 4 ++-- readme.txt | 4 +++- 6 files changed, 9 insertions(+), 6 deletions(-) diff --git a/docs/guides/divi-attrs.md b/docs/guides/divi-attrs.md index 5d9d919..9de9be7 100644 --- a/docs/guides/divi-attrs.md +++ b/docs/guides/divi-attrs.md @@ -41,7 +41,7 @@ Unbekannte Pfade → Fehler `invalid_attr_path` mit Vorschlägen unter `fields`. geprüft und vom Plugin neu erzeugt (siehe `divi-placeholders`). Eigenes HTML, CSS und HTML-Attribute (`…decoration.attributes`, `…advanced.html`, `…css…`) nur mit dem Recht `unfiltered_html`, sonst „nur im Divi-Builder“. Unberührte Teile der Seite bleiben byte-identisch; Ausnahme: Divi selbst normalisiert Seiten -mit eigenen HTML-/Attribut-Feldern beim Speichern (Warnung in der Antwort, neue `tree_rev` gilt). +mit eigenen HTML-/Attribut-Feldern beim Speichern – nur wenn die Seite noch nicht in Divis eigener gespeicherter Form vorliegt (z. B. importierter oder direkt in die Datenbank geschriebener Inhalt); über den Divi-Builder oder LIT MCP gespeicherte Seiten betrifft das praktisch nie (Warnung in der Antwort, neue `tree_rev` gilt). ## Pfade finden diff --git a/docs/guides/divi-placeholders.md b/docs/guides/divi-placeholders.md index 16fb767..571e0e9 100644 --- a/docs/guides/divi-placeholders.md +++ b/docs/guides/divi-placeholders.md @@ -43,5 +43,5 @@ dynamischen Quellen; Zusatztexte davor/danach (`before`/`after`) werden bereinig Verweis mitten im Text, eigenes HTML-Ausgeben von Feldern) lehnt das Werkzeug mit `validation_failed` ab. Unberührte Teile der Seite bleiben dabei byte-identisch. Ausnahme: Divi selbst normalisiert Seiten mit -eigenen HTML-/Attribut-Feldern beim Speichern; die Antwort warnt dann, dass der Inhalt beim Speichern +eigenen HTML-/Attribut-Feldern beim Speichern – nur wenn die Seite noch nicht in Divis eigener gespeicherter Form vorliegt (z. B. importierter oder direkt in die Datenbank geschriebener Inhalt); über den Divi-Builder oder LIT MCP gespeicherte Seiten betrifft das praktisch nie; die Antwort warnt dann, dass der Inhalt beim Speichern verändert wurde, und die neue `tree_rev` gilt. diff --git a/docs/guides/divi-shortform.md b/docs/guides/divi-shortform.md index 45333ad..8ca822a 100644 --- a/docs/guides/divi-shortform.md +++ b/docs/guides/divi-shortform.md @@ -102,7 +102,7 @@ HTML, das WordPress in Beiträgen erlaubt, Links ohne `javascript:`. `$variable`-Verweise werden geparst, geprüft und vom Plugin neu erzeugt. Eigenes HTML, CSS und HTML-Attribute eines Moduls (`…decoration.attributes`, `…advanced.html`, `…css…`) ändert nur, wer das Recht `unfiltered_html` hat – sonst „nur im Divi-Builder“. Unberührte Teile der Seite bleiben byte-identisch; Ausnahme: Divi selbst normalisiert Seiten mit eigenen HTML-/Attribut-Feldern -beim Speichern (dann Warnung, dass der Inhalt beim Speichern verändert wurde – mit der neuen `tree_rev` +beim Speichern – nur wenn die Seite noch nicht in Divis eigener gespeicherter Form vorliegt (z. B. importierter oder direkt in die Datenbank geschriebener Inhalt); über den Divi-Builder oder LIT MCP gespeicherte Seiten betrifft das praktisch nie (dann Warnung, dass der Inhalt beim Speichern verändert wurde – mit der neuen `tree_rev` weiterarbeiten). ## Warnungen diff --git a/docs/superpowers/notes/2026-09-26-etappe-2-entscheidungen.md b/docs/superpowers/notes/2026-09-26-etappe-2-entscheidungen.md index ba4441a..0bca87c 100644 --- a/docs/superpowers/notes/2026-09-26-etappe-2-entscheidungen.md +++ b/docs/superpowers/notes/2026-09-26-etappe-2-entscheidungen.md @@ -11,6 +11,7 @@ Getroffen bei der Planung von Etappe 2a/2b, 2026-09-26. Kosten, falls falsch, je - D7 (Ruling R1): Divi-Speichern ohne kses für Benutzer ohne `unfiltered_html` (sonst re-serialisiert WordPress jeden Block) ist nur vertretbar, weil keine client-gelieferte Zeichenkette ungeprüft in `post_content` gelangt. Klartext-Werte werden je Feldtyp bereinigt (Text: `sanitize_text_field`; Rich-Text: `wp_kses_post`, abhängig von `unfiltered_html`; URLs: `esc_url_raw`, geroutet über die Attribut-Metadaten des Katalogs, ersatzweise über den Schlüsselnamen). Werte mit `$variable(...)$` – ob aus Platzhaltern erzeugt oder unverändert aus `lit/divi-node-get` zurückgegeben – werden geparst, streng geprüft (zulässiger Typ, existierende gcid-/gvid-/Dynamic-Content-Quellennamen, whitelisted Settings-Schlüssel, String-Settings bereinigt) und vom Plugin neu serialisiert; alles andere wird mit `validation_failed` abgelehnt. Code-Modul ungefiltert nur mit `unfiltered_html`. – Kosten: jeder neue Schreibpfad muss bereinigen, streng validieren und neu serialisieren (Review-Pflicht). - R2 (Ruling): Ob Divi das Dynamic-Content-`before`/`after` bei der Ausgabe selbst escaped, wird in Etappe 2b im Divi-Quellcode verifiziert; die Bereinigung aus D7 gilt unabhängig vom Ergebnis (Verteidigung in der Tiefe). – Kosten: keine (Verifikation, keine Abhängigkeit der Sicherheit vom Ergebnis). - R2 – Befund (Etappe 2b, Divi 5.2.1, Ruling S5): (1) Dynamic-Content-`before`/`after` wird bei der Ausgabe nur dann mit `wp_kses` gefiltert, wenn der **Autor des Beitrags** kein `unfiltered_html` hat (`DynamicContentElements::get_wrapper_element`); auf Seiten eines Administrators gelangt es ungefiltert ins HTML – ein Redakteur könnte dort Skript platzieren. (2) `enable_html: on` gibt Meta-Werte mit `wp_kses_post` statt `esc_html` aus; Divi entfernt `enable_html` beim Speichern nur für Benutzer ohne `unfiltered_html` (`Security::sanitize_dynamic_content_fields`, `wp_insert_post_data` Priorität 10). (3) `custom_meta_` wird ohne `is_protected_meta`-Prüfung ausgegeben (`DynamicContentOptionPostMetaKey`). (4) Farbverweis-Namen gehen unescaped in `var(--)` (`StyleLibrary\Utils::resolve_dynamic_variable`). (5) Divis eigene `wp_insert_post_data`-Filter sind kein kses und laufen auch bei unserem Speichern ohne kses: `AttributeSecurity` (Priorität 5, immer, sobald `"decoration":{` und `"attributes":{` vorkommen) und `HtmlSecurity` (Priorität 6, bei `htmlBefore`/`htmlAfter`/`elementType`) parsen und re-serialisieren die **ganze Seite**; `DynamicContentFixes` (Priorität 10). Folgen: die Plugin-Prüfung aus D7 ist die eigentliche Garantie (VariableRef: nur ganze, geprüfte, neu serialisierte Verweise; `before`/`after` ohne unzulässiges HTML, kein `enable_html`, keine Meta-Schlüssel mit `_`); Divis Filter bleiben aktiv (Verteidigung in der Tiefe) und werden nie entfernt; Schreiben auf `*.decoration.attributes` und `*.advanced.html` nur mit `unfiltered_html` („nur im Divi-Builder“, Ruling S7), und auf Seiten mit diesen Feldern gilt die Byte-Invarianz nicht (Divi normalisiert beim Speichern). – Kosten: keine (Dokumentation; die Sicherheit hängt nicht von Divis Ausgabe ab). + - Nachtrag (Etappe 2b, Task 10, live): Divis `AttributeSecurity`/`HtmlSecurity` serialisieren mit `serialize_blocks()`; Bytes ändern sich nur, wenn die Seite noch nicht in dieser Form gespeichert war (z. B. Import, direkter DB-Schreibzugriff). Über Builder oder LIT MCP gespeicherte Seiten bleiben außerhalb der Änderung byte-identisch (E2E `divi-write-smoke.sh` 8a/8b). - D8: Spalten neuer Zeilen über `module.decoration.sizing.desktop.value.flexType` (`"1_3,2_3"` → `8_24`, `16_24`; Fünftel und 1/7, 1/9, 1/10, 1/11 nativ), wie Divi 5.2 (Flex-Layout ist Standard). – Kosten: falls der Builder anders speichert, nur Composer anpassen. - D9: Platzhalter stehen allein im Wert; Überschriften der Kurzform ohne `level` werden `h2`. – Kosten: eingebettete dynamische Texte nur über die Vollform. - D10: `lit/divi-replace-text` je Inhaltstyp; Probelauf liefert `tree_rev` je Seite, Ausführung nur auf diesen `targets` mit `confirm: true`; ersetzt nur sichtbaren Text (`…innerContent`, Schlüssel text/title/label/alt/titleText), nie URLs oder Stile. – Kosten: Text mit HTML-Entitäten muss so gesucht werden, wie er gespeichert ist. diff --git a/docs/superpowers/specs/2026-09-25-wp-lit-mcp-design.md b/docs/superpowers/specs/2026-09-25-wp-lit-mcp-design.md index fc03b61..fe3b4a8 100644 --- a/docs/superpowers/specs/2026-09-25-wp-lit-mcp-design.md +++ b/docs/superpowers/specs/2026-09-25-wp-lit-mcp-design.md @@ -18,7 +18,7 @@ Ein WordPress-Plugin, das auf jeder Kundenseite einen MCP-Server bereitstellt, m 1. Ein claude.ai-Connector verbindet sich per OAuth mit einer Kundenseite; nur freigegebene Rollen können Tools nutzen. 2. Von Claude erzeugte oder geänderte Divi-Inhalte sind im Visual Builder vollständig bearbeitbar (manuell verifiziert pro Etappe). -3. Unangetastete Inhalte bleiben byte-identisch erhalten. Dokumentierte Ausnahme (Ruling S7): Divi selbst normalisiert Seiten mit eigenen HTML-/Attribut-Feldern beim Speichern (`AttributeSecurity`/`HtmlSecurity` auf `wp_insert_post_data`, laufen für jeden Benutzer und werden nie entfernt); die Antwort meldet das als Warnung „Inhalt beim Speichern verändert“ (`CHANGED_ON_SAVE`), die Seite bleibt gültig (E2E `divi-write-smoke.sh` Abschnitt 8). +3. Unangetastete Inhalte bleiben byte-identisch erhalten. Dokumentierte Ausnahme (Ruling S7): Divi selbst normalisiert Seiten mit eigenen HTML-/Attribut-Feldern beim Speichern (`AttributeSecurity`/`HtmlSecurity` auf `wp_insert_post_data`, laufen für jeden Benutzer und werden nie entfernt) – aber nur, wenn die Seite noch nicht in Divis `serialize_blocks()`-Form gespeichert war (z. B. Import, direkter DB-Schreibzugriff); über Builder oder LIT MCP gespeicherte Seiten bleiben unverändert (E2E Abschnitt 8a); die Antwort meldet das als Warnung „Inhalt beim Speichern verändert“ (`CHANGED_ON_SAVE`), die Seite bleibt gültig (E2E `divi-write-smoke.sh` Abschnitt 8). 4. Eine neue Website (Seitenstruktur, Menü, Einstellungen, Designsystem aus einem CD) lässt sich im Dialog aufsetzen. 5. Jede Änderung ist nachvollziehbar (Protokoll) und umkehrbar (Revision bzw. Snapshot). @@ -171,7 +171,7 @@ Seiten, Theme-Builder-Layouts (`et_header_layout`, `et_body_layout`, `et_footer_ - Parst `post_content` in einen Baum (auf Basis von `parse_blocks`, mit Sonderbehandlung für `divi/global-layout` und `globalModule`, die `parse_blocks` laut Divi-Code nicht korrekt verarbeitet). - Serialisiert den Baum zurück; unveränderte Knoten werden aus ihrem Original-Markup übernommen. -- **Invariante:** Parse → Serialize ohne Änderung ergibt byte-identischen Inhalt. Fremde/unbekannte Blöcke bleiben unberührt. Ausnahme beim Speichern (nicht im BlockTree): Divi selbst normalisiert Seiten mit eigenen HTML-/Attribut-Feldern beim Speichern (`module.decoration.attributes`, `module.advanced.html` mit `htmlBefore`/`htmlAfter`/`elementType`) – Divi parst und serialisiert dann die ganze Seite neu; gemeldet als `CHANGED_ON_SAVE` (Ruling S7). +- **Invariante:** Parse → Serialize ohne Änderung ergibt byte-identischen Inhalt. Fremde/unbekannte Blöcke bleiben unberührt. Ausnahme beim Speichern (nicht im BlockTree): Divi selbst normalisiert Seiten mit eigenen HTML-/Attribut-Feldern beim Speichern (`module.decoration.attributes`, `module.advanced.html` mit `htmlBefore`/`htmlAfter`/`elementType`) – Divi parst und serialisiert dann die ganze Seite neu, was nur Bytes ändert, die noch nicht in Divis `serialize_blocks()`-Form vorlagen; gemeldet als `CHANGED_ON_SAVE` (Ruling S7). - Reines PHP ohne WordPress-Abhängigkeit (vollständig unit-testbar). - Attributwerte mit `$variable(...)$` werden beim Serialisieren wie von Divi erwartet escaped (`"` → `"`). - Umsetzung (D3): eigener Tokenizer mit dem Delimiter-Muster von `WP_Block_Parser` statt `parse_blocks()` – ohne WordPress testbar, jedes Byte zwischen den Delimitern bleibt erhalten. Knoten mit `globalModule`, `divi/global-layout` oder nicht dekodierbaren Attributen sind undurchsichtig: sie werden nicht betreten, nur als Ganzes verschoben, dupliziert oder gelöscht (Bearbeitung ab Etappe 3). Unbalanciertes Markup wird abgelehnt, nie repariert. diff --git a/readme.txt b/readme.txt index e67342e..9ce5b29 100644 --- a/readme.txt +++ b/readme.txt @@ -47,7 +47,9 @@ Einstellungen: Einstellungen → LIT MCP (Verbindung, Zugriff, Tools, Protokoll, von Modulen schreibt LIT MCP nur für Benutzer mit unfiltered_html. Divis eigene Speicherfilter bleiben aktiv. * Unberührte Teile einer Divi-Seite bleiben beim Speichern byte-identisch. Ausnahme: Divi selbst normalisiert - Seiten mit eigenen HTML-/Attribut-Feldern beim Speichern (die Antwort warnt dann, dass der Inhalt beim + Seiten mit eigenen HTML-/Attribut-Feldern beim Speichern, sofern sie noch nicht in Divis eigener + gespeicherter Form vorliegen (z. B. importierter Inhalt); über den Divi-Builder oder LIT MCP gespeicherte + Seiten betrifft das praktisch nie (die Antwort warnt dann, dass der Inhalt beim Speichern verändert wurde). * Nach jeder Divi-Änderung wird der statische CSS-Cache von Divi für die Seite geleert. * Uploads über LIT MCP: nur Bilder (jpg, png, gif, webp, avif), PDF, MP4, MP3; Größenlimit einstellbar