WPAgently-Dokumentation

Developer-/Staging-Modus: Power vollständig verstehen

Die meisten brauchen diesen Modus nie. Wenn du direkten Datei-, Datenbank- oder PHP-Zugriff brauchst, lies die Grenzen vor der Freischaltung vollständig.

_Die mit Skills 0.4.86 gebündelte Dokumentation beschreibt den Release-Satz aus Companion 0.4.117, Power 0.6.35 sowie CLI und Skills 0.4.86. Power bleibt ein getrenntes Opt-in-Plugin mit serverseitigen Gates, echten Administrator-Zugangsdaten und lokaler Freischaltung. Der Kandidatenlader führt nur bereits P2-verifizierte MU-Kandidaten kurzzeitig aus. Vor jeder manuellen Aktivierung prüft ein signierter lokaler Loopback den exakten Site-Ursprung, die Antwortzeit, den HTTP-Status und den Antwortkörper. Ein Lauf gilt erst nach wp_loaded als gesund. exit(), normale PHP-Fatals, alte oder beschädigte Markierungen und parallele Läufe deaktivieren alle Candidates fail-closed. Der Lader ist keine PHP-Sandbox und verspricht weder Datenbank- oder Netzwerk-Rollback noch Schutz bei SIGKILL, OOM, Segfault oder späten Shutdown-Fehlern. Nicht unterstützte Power-Erweiterungen, insbesondere Code-Snippets-Single-Use, stehen in der Produktroadmap._

Ed25519-Signaturen beziehen sich ausschließlich auf die beiden Plugin-Artefakte. CLI und Skills 0.4.86 sind diesem Dokumentationsstand über ihre gebündelte Version und den deterministischen Build zugeordnet, nicht über Plugin-Signaturen.

Kurz vorweg, ehrlich: Die allermeisten brauchen diesen Modus nie. Für Blogartikel und Landing-Pages ist der geprüfte Kern (wp-agent-companion) alles, was du benötigst. Der Power-Modus ist ein separates, mächtiges Werkzeug für Entwickler-Aufgaben, es ist standardmäßig vollständig abgeschaltet, und du schaltest es nur bewusst und Stück für Stück frei. Diese Doku erklärt es vollständig und beschönigt die Risiken nicht. Wenn dir Begriffe wie Datenbank, PHP oder Staging nichts sagen, ist das dein Signal, dass du hier nicht weiterlesen musst.

Kernaussage in einem Absatz

Der Power-Modus ist ein zweites, komplett separates WordPress-Plugin (wp-agent-power) für direkten Zugriff auf Dateien, Datenbank und die Ausführung von PHP-Code, standardmäßig vollständig deaktiviert. Er ist in Stufen aufgeteilt, von harmlos bis maximal riskant. P0 (nur Lesen) kann bereits vertrauliche Daten sehen. P1 (eng begrenzte Wartungsbefehle) und P2 (Dateien schreiben) sind Schreibzugriff, und P2 ist bereits gleichbedeutend mit dem Ausführen von Code auf deiner Website. P3 (in die Datenbank schreiben) begrenzt Änderungen mit Pflichtbackup, Dry-Run, Zeilenlimit, Sperren, Read-back und Rollback. Diese Schutzmechanismen sind keine allgemeine Zusage, dass Datenbankänderungen auf Live-Seiten sicher sind. P4/P5 (beliebiger PHP-Code) ist die weitreichendste Stufe. Er führt beliebigen Programmcode auf deiner Website aus, ist nur für Testkopien gedacht und benötigt auf einer erkannten Live-Seite eine zusätzliche, zeitlich begrenzte Freigabe. Die getrennt schaltbaren BACKUP- und RESTORE-Kanäle sichern und restaurieren Datenbank und Dateien vollständig über REST, auch ohne SSH oder lokalen WordPress-Pfad.

Öffentliche JSON-Verträge im Release-Satz

Dieser Abschnitt beschreibt den Release-Satz Companion 0.4.117, Power 0.6.35 sowie CLI und Skills 0.4.86. Die Power-Factory-Map umfasst 26 Abilities, und der Testkatalog umfasst 30 Power-Verträge. Clients lesen immer die Ability-Schemas der tatsächlich installierten Version und dürfen keine älteren offenen JSON-Formen nachbilden.

Alle öffentlichen Power-Schemas sind geschlossen und begrenzt. Unbekannte Eingabefelder enden mit HTTP 400. Fehlt ein früher optionaler Provider-Ausgabewert, erscheint er als explizites null, nicht als still ausgelassenes Feld. Eine nicht darstellbare Provider-Ausgabe wird mit HTTP 500 abgelehnt und nicht gekürzt. Wo eine ältere Power-Ability einen offenen JSON-Wert benötigte, verwendet sie exakt den Integritätsumschlag {json,sha256,bytes} ohne Feld format. bytes und sha256 binden die UTF-8-JSON-Bytes. Er erlaubt nur JSON-Objekte oder -Listen bis 4 MiB, 32 Ebenen und 20.000 Einträgen.

Die Backup- und Restore-Pfade bleiben davon bewusst verschieden und sind vollständig typisiert. db-select liefert seine redigierte Liste unter rows im genannten JSON-Umschlag, während columns eine begrenzte Namensliste bleibt. db-tables liefert geschlossene Tabellen-, Spalten- und DDL-Metadaten. db-export liefert columns sowie rows als begrenzte Matrix aus Base64-Zellen oder null, mit batch_sha256. db-import akzeptiert und bestätigt dieselbe Struktur je begin, batch, commit oder abort. file-catalog liefert geschlossene Komponenten-, Verzeichnis- und Dateieinträge. file-export und file-import übertragen Inhalte ausschließlich als begrenztes content_base64 zusammen mit SHA-256 und Offset. Diese Formen ersetzen keine Backup-Gates, Verschlüsselung, Freigabe oder die serverseitige Drift-Prüfung.

1. Für wen ist dieser Modus

Der normale, geprüfte Kern des Toolkits (wp-agent-companion, Blogartikel- und Landing-Page-Abläufe) deckt fast alle Aufgaben ab und prüft seine eigenen Ergebnisse. Der Power-Modus ist ausschließlich für Aufgaben gedacht, für die es keine passende, geprüfte Funktion gibt: eine Konfigurationsdatei ansehen, den Wert einer einzelnen Einstellung prüfen, eine gezielte Datenbankzeile lesen oder ändern, eine Plugin-Liste für eine Fehlersuche ziehen, einen begrenzten WP-CLI-Wartungsbefehl ausführen, oder (im Ausnahmefall, nur auf einer Testkopie) beliebigen PHP-Code zur Fehlersuche laufen lassen. Jedes Ergebnis aus diesem Kanal ist ausdrücklich ungeprüft (verified: false, channel: "power" in der CLI-Ausgabe), weil es an den Prüfungen der normalen Abläufe (Render-Check, SEO-Persistenz, Idempotenz) vorbeigeht.

2. Zwei getrennte Vertrauensbereiche

Der geprüfte Kern und der Power-Modus sind bewusst zwei getrennte Plugins mit getrennten Zugangspunkten (MCP-Endpunkten):

  • Kern: <site>/wp-json/wp-agent/mcp (immer installiert, wp-agent-companion).
  • Power: <site>/wp-json/wp-agent-power/mcp (separates Zusatzplugin wp-agent-power, nicht vorinstalliert).

Der Power-Zugangspunkt steht nicht automatisch in der Assistenten-Konfiguration. Der normale Verbindungsaufbau (wp-agent init) trägt nur den Kern-Server ein, das ist ein bewusster sicherer Standard. Nach der serverseitigen Aktivierung erzeugt wp-agent power init den Power-Block für ~/.codex/config.toml beziehungsweise .mcp.json. Dafür sind echte Administrator-Zugangsdaten statt der Editor-Bot-Zugangsdaten der normalen Abläufe nötig. Der Befehl verweigert eine heimliche Remote-Aktivierung und prüft, ob der serverseitige Master-Schalter wirklich offen ist. Details stehen in docs/agents/codex-power.md.

Der Kern (wp-agent-companion) enthält keinerlei Roh-Zugriffs-Code. Ein automatisierter Test (power-firewall.mjs) beweist, dass die normalen Abläufe nichts aus dem Power-Code laden und der Kern keine Datei aus wp-agent-power einbindet. disable-power.php kennt ausschließlich den Plugin-Namen und deaktiviert das Power-Plugin über die WordPress-Kern-Schnittstelle, ohne Power-Code zu laden.

3. Die Stufen im Überblick

StufeNameFähigkeitVoraussetzung zum AktivierenAuf erkannter Live-Seite
P0LesenDatei lesen, Verzeichnis begrenzt auflisten, ein SELECT, eine Option lesen, Plugins/Themes auflisten, UmgebungsinfoGültige Lizenz + Master-SchalterErlaubt (mit Schwärzung)
P1Enge WP-CLI-BefehleFeste Liste begrenzter Wartungs- und LesebefehleGültige Lizenz + Master + Stufen-Flag P1Erlaubt (begrenzt und wiederholbar)
P2Dateien ändernSchreiben, exakt bearbeiten und löschen unter wp-content/themes/ und wp-content/mu-plugins/ mit AbsturzschutzGültige Lizenz + Master + Stufen-Flag P2 + Umgebungs-ErlaubnisGesperrt ohne WP_AGENT_POWER_ALLOW_WRITE_ON_PRODUCTION
P3In die DB schreibenEin UPDATE/DELETE beziehungsweise ein Options-Set/-Delete, mit Schutz-RahmenGültige Lizenz + Master + Stufen-Flag P3Erlaubt (keine harte Umgebungssperre, siehe Abschnitt 6)
P4/P5Beliebiger Codeexec-php sowie eine geprüfte Code-Snippets-Oberfläche, beides Code-Ausführung (RCE)Gültige Lizenz + Master + Stufen-Flag P4 + wp-config-Konstante WP_AGENT_POWER_ALLOW_CODE_EXECUTIONAuf Produktion gesperrt ohne exakt gebundene Einmalfreigabe
BACKUPRemote-BackupDatenbank und reguläre Dateien katalogisieren und in geprüften Häppchen exportierenGültige Lizenz + Master + Stufen-Flag BACKUPErlaubt, enthält ungeschwärzte Secrets und braucht Verschlüsselung
RESTORERemote-RestoreGeprüfte Datenbanktabellen und Dateikomponenten über Staging atomar wiederherstellenGültige Lizenz + Master + Stufen-Flag RESTOREErlaubt, destruktiv und separat freizuschalten

Jede Stufe hat ihr eigenes, unabhängig schaltbares Flag (ein „Flag“ ist hier schlicht ein Ein/Aus-Schalter in der WordPress-Datenbank). Eine gültige WPAgently-Lizenz und der Master-Schalter (wp_agent_power_enabled) sind bei jeder Stufe zusätzlich Pflicht; der Master ist der eine, oberste Not-Aus. Eine frische oder nicht lizenzierte Installation meldet null Fähigkeiten an, keine Kategorie und keinen eigenen Zugangspunkt: „Registrierung als Gate“ heißt, eine nicht freigeschaltete Stufe existiert serverseitig schlicht nicht (WordPress antwortet mit 404 „nicht gefunden“ statt mit einer Ablehnung zur Laufzeit), selbst für ein gültiges Administrator-Passwort.

4. Die Stufen im Detail

P0, reines Lesen (sechs Fähigkeiten)

  • read-file (rel_path): liest eine Datei innerhalb des WordPress-Verzeichnisses. Pfad-Tricks, absolute Pfade, Laufwerksbuchstaben, Null-Bytes und Symlinks als Ziel sind gesperrt, ebenso bestimmte Backup- und Audit-Verzeichnisse. Dateien über 1 MiB werden abgelehnt, nicht beschnitten. Eine Sperrliste verweigert Datenbank-Dumps (.sql/.sqlite/.db), Schlüsselmaterial (.pem/.key/.p12/…), die .env-Familie und bekannte Geheimnis-Dateien komplett, weil eine Schwärzung sie strukturell nicht sicher machen könnte. Der verbleibende Inhalt wird zweifach geschwärzt: define()-Konstanten mit Namen wie KEY, SALT, SECRET, PASSWORD, NONCE, zusätzlich eine allgemeine Muster-Schwärzung über den gesamten Text.
  • list-directory (rel_path, optional max_depth, max_entries): listet reguläre Dateien und Verzeichnisse deterministisch innerhalb von ABSPATH auf. Die Tiefe ist auf 0 bis 5, die Anzahl auf 1 bis 500 Einträge begrenzt. Symlinks werden nicht verfolgt. Geschützte Backup-, Firewall- und Audit-Verzeichnisse bleiben ausgeschlossen.
  • db-select (query): führt genau ein SELECT aus (also eine reine Lese-Abfrage). Mehrere Anweisungen, SQL-Kommentare und alle schreibenden Schlüsselwörter werden abgelehnt, ebenso INTO OUTFILE/DUMPFILE/LOAD_FILE. Zustandsbehaftete oder absichtlich teure Funktionen wie SLEEP(), BENCHMARK(), GET_LOCK() und die verwandten Lock-, Replikationswarte- und Locking-Service-Funktionen sind ebenfalls gesperrt. Das gilt auch für LAST_INSERT_ID(expr), Zuweisungen mit @variable := ..., SELECT ... INTO @variable und sperrende Klauseln wie FOR UPDATE, FOR SHARE und LOCK IN SHARE MODE. Zusätzlich zur Schwärzung der Ausgabe sperrt eine Prüfung des Abfragetexts jede Referenz auf user_pass, user_activation_key, session_tokens, auf wp_users/wp_usermeta als Ganzes und auf option_value beziehungsweise ein *-Select auf wp_options. Das schließt Umgehungen über Alias, SUBSTRING/CONCAT oder UNION an der Wurzel aus. Zeilenlimit 200. Die redigierten Zeilen stehen im geschlossenen {json,sha256,bytes}-Umschlag rows, nicht mehr als offenes Array.
  • get-option (name): liest eine einzelne WordPress-Option. Werte mit einem geheim aussehenden Namen (auth, api, secret, token, password, key, salt, plus die konkret belegten Container monsterinsights_site_profile und wp_woocommerce_api_keys) werden geschwärzt, auch rekursiv in Arrays.
  • list-extensions: listet installierte Plugins und Themes mit Version und Aktiv-Status, ohne Geheimnisse.
  • env-info: meldet WP-/PHP-Version, erkannten Umgebungstyp, aktives Theme und den vollständigen Power-Status (aktiv, welche Stufen offen sind, der Zähler, ob Schreiben auf Produktion beziehungsweise Code-Ausführung per Konstante erlaubt ist).

P1, enge WP-CLI-Befehle (eine Fähigkeit: wp-cli)

Eine feste, vollständig verankerte Liste erlaubter Befehle: rewrite flush, transient delete <name>, plugin list, theme list, option get <name>. Alles andere wird hart abgelehnt, insbesondere globale Cache-Flushes, cron, db, eval, eval-file, search-replace, user, core, config, shell, server, package sowie jede verändernde plugin-/theme-Unteraktion. cache flush ist ausdrücklich gesperrt, weil es Netzwerk-, Objekt- oder Full-Page-Caches außerhalb des gesicherten Site-Zustands verändern kann. cron event run <hook> ist ebenfalls nicht erlaubt, weil ein beliebiger Hook externe Systeme oder andere nicht gesicherte Zustände verändern kann. Die Ausführung läuft ohne dazwischenliegende Shell, sodass keine Shell-Sonderzeichen ausgenutzt werden können. Unter Windows wird eine native wp.exe direkt gestartet. Ein gefundener Launcher wp.bat oder wp.cmd wird niemals dem Windows-Kommandointerpreter übergeben. Power verwendet ihn nur, wenn daneben wp-cli.phar liegt, und startet stattdessen den gefundenen PHP-Interpreter mit diesem PHAR als festem Argument. Neben php-cgi.exe wird ein benachbartes php.exe automatisch erkannt. Das deckt unter anderem Local-Installationen ab, ohne Pfade oder Argumente mit Prozentzeichen, Ausrufezeichen, Anführungszeichen, Leerzeichen oder Umlauten durch cmd.exe zu schicken. Weil anonyme proc_open-Pipes unter Windows kein verlässliches nichtblockierendes Lesen bieten, erfasst Power Standardausgabe und Fehlerausgabe dort über automatisch gelöschte temporäre Dateihandles mit denselben harten Größen- und Zeitgrenzen. Fehlt das benachbarte PHAR oder der PHP-CLI-Interpreter, wird WP-CLI mit konkreter Diagnose fehlersicher als nicht verfügbar gemeldet. Die Ausgabe von option get wird zweistufig geschwärzt.

Mit background:true beziehungsweise --background lässt sich dasselbe geprüfte Kommando als persistenter WordPress-Cron-Auftrag einreihen. Der erste Aufruf liefert sofort eine nicht erratbare Job-ID. Mit job_id beziehungsweise --job-id liest ausschließlich derselbe Administrator Status und Ergebnis. Der Worker prüft Lizenz, Master, P1-Flag, Administratorrecht und Allowlist direkt vor der Ausführung erneut. Ausgabe wird vor der Speicherung auf gültiges UTF-8 begrenzt und mit denselben Regeln geschwärzt wie beim synchronen Pfad. Pro Site sind höchstens 20 wartende, laufende oder abrufbare Jobs gespeichert. Aktive Jobs werden nie verdrängt. Bei voller Kapazität weicht nur das älteste abgeschlossene Ergebnis. Ergebnisse laufen nach einer Stunde ab. Ein Auftrag gilt nach 15 Minuten ohne Start beziehungsweise nach zwei Minuten im laufenden Zustand als fehlgeschlagen. Wenn DISABLE_WP_CRON aktiv ist, startet Power keinen internen Cron-Request, sondern überlässt den persistent geplanten Event dem externen Scheduler des Betreibers.

P2, Dateien ändern und Erweiterungsarchive vorprüfen (sieben Fähigkeiten)

Alle drei Fähigkeiten arbeiten ausschließlich unter wp-content/themes/ und wp-content/mu-plugins/, mit vollem Pfad-Schutz. write-file ersetzt den vollständigen Inhalt. edit-file verlangt die zuvor gelesene SHA-256-Prüfsumme und ersetzt exakten Text. Mehrere Treffer werden ohne replace_all:true abgelehnt. delete-file verlangt dieselbe Konfliktprüfung und zusätzlich confirm:true. Ein serverseitiger Kurzzeit-Lock verhindert parallele Änderungen derselben Datei auch über direkte REST- oder MCP-Aufrufe.

Jeder benannte P2/P3-Pfad verwendet seinen eigenen Snapshot, Audit-Eintrag vor der Mutation und exakten Read-back. Schreibende PHP-Änderungen durchlaufen vorher php -l. Danach werden Startseite und wp-admin geprüft. Nur wenn der Pfad weiterhin seinen exakten eigenen Zwischenzustand nachweisen kann, darf er die dokumentierte Wiederherstellung versuchen und diese prüfen. Bei Fremddrift, Lock-Verlust oder unsicherem Read-back bleibt der aktuelle Zustand erhalten und der Vorgang endet mit recovery_required. Der Größendeckel beträgt 2 MiB. Auf erkannter Produktion sind alle sieben Fähigkeiten serverseitig gar nicht angemeldet, außer die wp-config-Konstante WP_AGENT_POWER_ALLOW_WRITE_ON_PRODUCTION ist gesetzt.

Die drei zusätzlichen Fähigkeiten prepare-extension-archive, inspect-extension-archive und revoke-extension-archive bilden eine sichere Vorprüfung für ein lokales Plugin- oder Theme-ZIP. Der Dateiinhalt läuft über einen fünf Minuten gültigen Einmal-Endpunkt. Das 256-Bit-Token steht nur im Header und liegt serverseitig nur als SHA-256-Hash vor. Der Server streamt höchstens 32 MiB in ein zufälliges Verzeichnis außerhalb von ABSPATH und wp-content. Er akzeptiert ausschließlich echten ZIP-Inhalt. Die Prüfung extrahiert nichts. Sie lehnt Traversal-Pfade, absolute Pfade, doppelte Namen, Symlinks, Spezialdateien, verschlüsselte Einträge, mehr als 2.000 Einträge, einzelne Dateien über 32 MiB, insgesamt mehr als 128 MiB entpackte Daten und auffällige Kompressionsverhältnisse ab. Genau ein Paketordner und genau eine eindeutige Plugin-Hauptdatei beziehungsweise eine Theme-style.css mit gültigem WordPress-Namen sind Pflicht. Nach Erfolg oder Ablehnung wird die temporäre Kopie gelöscht. Ein geplanter Aufräumjob entfernt auch abgebrochene Sitzungen.

Die siebte Fähigkeit verify-manual-extension-install ist rein lesend. Sie vergleicht eine zuvor manuell installierte aktive oder inaktive Erweiterung vollständig mit der kurzlebigen, privaten Datei-Attestierung aus der Archivprüfung. Sie installiert, aktiviert, deaktiviert, löscht oder verändert nichts und benötigt trotzdem den offenen P2-Gate sowie Administratorrechte.

Dieser Pfad installiert und aktiviert absichtlich nichts. WordPress kann eine beliebige Drittanbieter-Erweiterung nicht mit einer allgemeinen, belastbaren Transaktionsgarantie installieren. Eine Plugin-Aktivierung würde außerdem fremden PHP-Code ausführen. Deshalb meldet die Vorprüfung ausdrücklich installable:false und dry_run_only:true. Ein Backup und die Phrase SCHREIBEN sind für diese reine Vorprüfung nicht nötig. Sobald später ein transaktional abgesicherter Installationspfad gebaut wird, muss er beides vor der Mutation erzwingen. Bis dahin bleibt die eigentliche Installation eine bewusste Administratoraktion unter Plugins > Installieren beziehungsweise Design > Themes.

P3, in die Datenbank schreiben (zwei Fähigkeiten: db-write, option-write)

Zusätzlich zur Entitätssperre muss jedes db-write-Ziel zum geprüften Tabellenkatalog der aktuellen Site gehören. Alle Site-Options- und Multisite-Netzwerktabellen bleiben gesperrt. Ein Site-Admin kann dadurch weder eine andere Multisite-Site noch eine verschachtelte WordPress-Installation beschreiben. Blockierende, zustandsbehaftete und absichtlich teure SQL-Primitiven sind auch im Schreibpfad verboten. Der Vorschau-Deckel und die Erkennung von WHERE, ORDER BY und LIMIT unterscheiden Struktur von Stringliteralen und gequoteten Bezeichnern.

db-write führt genau ein UPDATE oder DELETE aus, eine WHERE-Bedingung ist Pflicht, gefährliche SQL-Konstrukte und Unterabfragen sind gesperrt, ebenso mehrere Anweisungen und SQL-Kommentare. Geschützte Bereiche (wp_users, wp_usermeta, die komplette Options-Tabelle, Firewall-/Security-Tabellen, die Audit-Tabelle, alle wp_agent-eigenen Tabellen, MySQL-Systemschemata) sind hart gesperrt, sowohl als Zieltabelle als auch als Referenz irgendwo im Statement. Ein Zeilen-Deckel (Standard 500, vorab per Vorschau-SELECT geprüft) verhindert Massenschäden. Vorschau statt Ausführung ist der Standard (Dry-Run): ohne dry_run:false liefert die Fähigkeit nur eine geschwärzte Vorschau der betroffenen Zeilen. Der echte Schreibvorgang verlangt bei UPDATE und DELETE einen erkennbaren Primärschlüssel sowie eine transaktionsfähige Tabelle. Unter einem serverseitigen Tabellen-Lock erfasst er die Zielzeilen in einer Datenbanktransaktion. MySQL und MariaDB verwenden dabei FOR UPDATE. SQLite verwendet die Transaktion und die feste Primärschlüsselmenge. Ein konkurrierender Fremdschreibvorgang führt dort im Konfliktfall zu einem fail-closed Datenbankfehler. Mutiert wird anschließend ausschließlich diese feste Primärschlüsselmenge, nicht erneut die möglicherweise veränderte WHERE-Bedingung. LIMIT ohne ORDER BY wird als nicht deterministisch abgelehnt. Vor dem Commit müssen die tatsächlich geänderten beziehungsweise gelöschten Zeilen exakt zum Read-back passen. Abweichungen rollen die Transaktion zurück. Nach dem Commit folgt derselbe Gesundheitsprüfung-plus-verifizierter-Wiederherstellung-Mechanismus wie bei P2. Nur ein belegter Erfolg erhöht den internen Epoch-Zähler.

option-write setzt oder löscht genau eine Option, ausschließlich über die offiziellen WordPress-Funktionen (nie über rohes SQL). Eine Sperrliste schützt den eigenen Master-/Stufen-/Zähler-Schalter, siteurl/home (Umgebungserkennung), Firewall-Konfiguration sowie eine feste Liste sicherheitskritischer Optionen (admin_email, new_admin_email, users_can_register, default_role, template, stylesheet, current_theme, active_plugins, active_sitewide_plugins, upload_path, upload_url_path). Zusätzlich: ein neuer Wert darf kein Array/Objekt sein, und eine bereits serialisiert gespeicherte Option (etwa wp_user_roles) darf weder überschrieben noch gelöscht werden. Ein serverseitiger Lock schützt dasselbe Optionsziel. Die Fähigkeit beachtet den echten Rückgabewert von update_option() beziehungsweise delete_option() und liest den rohen persistierten Wert erneut. Filter-Vetos, veränderte Werte und spätere Drift führen zu Fehler und Compare-and-Swap-Rollback, nicht zu einem falschen written:true oder Epoch-Sprung. Ein identischer Wert beziehungsweise das Löschen einer nicht vorhandenen Option bleibt ein ausdrücklicher No-Op. Dry-Run-Standard, geschwärzter Vergleich, derselbe Gesundheitsprüfung-plus-Wiederherstellung-Mechanismus.

Der Compare-and-Swap vergleicht die gespeicherten Bytes, nicht die möglicherweise groß- und kleinschreibungsunempfindliche Datenbank-Kollation. Meldet die WordPress-API false, beansprucht WPAgently den inzwischen sichtbaren Zustand nicht als eigene Mutation und rollt eine mögliche fremde Gewinneränderung nicht zurück.

P4/P5, beliebige Code-Ausführung (vier Fähigkeiten)

Führt einen übergebenen PHP-Codeblock im WordPress-Laufzeitkontext aus und liefert Rückgabewert, Ausgabe und Fehler. Das ist das Ausführen beliebigen Codes ohne inhaltliche Einschränkung, und zwar mit Absicht: einzuschränken, was beliebiger Code tun darf, wäre technisch nicht durchsetzbar. Die gesamte Sicherheit liegt deshalb vor der Ausführung, in einem fünffachen Registrierungs-Gate:

  1. Plugin aktiv.
  2. Gültige WPAgently-Lizenz.
  3. Server-Master-Schalter an.
  4. Stufen-Flag wp_agent_power_stage_p4 an.
  5. wp-config-Konstante WP_AGENT_POWER_ALLOW_CODE_EXECUTION strikt === true (kein „truthy“-Vergleich, ein String "true" oder "1" zählt nicht).

Fehlt eine der fünf Bedingungen, ist exec-php schlicht nicht angemeldet (404, kein Tool). Die Konstante ist der entscheidende Faktor gegen eine reine Web-Eskalation: sie liegt in einer Datei auf dem Server und ist über keinen Web-/Ability-Weg setzbar, ein geleaktetes Administrator-Passwort kann sie nicht setzen. Zusätzlich verlangt die Fähigkeit manage_options und prüft das Gate zur Laufzeit ein zweites Mal. Auf erkannter Produktion ist zusätzlich ein gültiger, nicht abgelaufener Produktions-Override-Token nötig (siehe Abschnitt 6). Vor jeder Ausführung wird ein manipulationssicherer Audit-Eintrag geschrieben; ohne erfolgreichen Log-Eintrag läuft der Code nicht. Wichtig: exec-php erzeugt serverseitig keinen umkehrbaren Vorzustand und stellt nichts automatisch wieder her. Beliebiger Code kann unbegrenzt viele Seiteneffekte auslösen, deshalb ist die Gesundheitsprüfung danach nur ein Absturz-Hinweis, keine Garantie.

Aktueller Produktionsvertrag: Der frühere wiederverwendbare Produktions-Override ist abgeschafft und bleibt nicht kompatibel. exec-php sowie alle Code-Snippets-Mutationen brauchen auf erkannter Produktion eine neue Einmalfreigabe. Der Server bindet sie an Blog, Administrator, genaue Aktion, Payload- und Intent-Hash, Ablauf und Nonce, protokolliert das Prägen und Verbrauchen getrennt ohne Secret und verbraucht sie atomar vor Code oder Mutation. Die CLI verlangt eine zweite Bestätigung, verwaltet das Ticket nur kurz in einer Datei mit Modus 0600 und übergibt oder protokolliert es nie über argv oder stdout.

Die drei zusätzlichen Fähigkeiten list-code-snippets, get-code-snippet und mutate-code-snippet verwenden die öffentliche PHP-API des Plugins Code Snippets. Die Liste enthält bewusst keinen Quellcode. Das gezielte Lesen liefert Code, Typ und einen Konflikthash. Die Mutation kann lokale, nicht netzwerkweite PHP-, HTML-, CSS- und JavaScript-Snippets erstellen, ändern, aktivieren, deaktivieren oder weich löschen. Erstellen und Ändern lassen das Snippet immer inaktiv. Aktive Snippets müssen vor Änderung oder Löschung deaktiviert werden. Ein Wechsel zwischen unterschiedlichen Snippet-Typen verlangt den Quellcode ausdrücklich erneut. Jede Mutation verlangt eine aktionsspezifische exakte Bestätigung, einen frischen Hash bei bestehenden Snippets, Intent und Backup-Referenz, schreibt das Audit vor dem Provider-Aufruf, liest anschließend den echten Providerzustand neu und erhöht den Epoch-Zähler. PHP-Code durchläuft vor dem Speichern und Aktivieren php -l. Nach jeder Aktivierung folgt die Zustandsprüfung. Bei Fehler oder ungültigem Provider-Read-back darf der Pfad nur bei weiterhin beweisbarem eigenen Zwischenzustand die dokumentierte Wiederherstellung versuchen. Andernfalls bleibt der aktuelle Zustand für die manuelle Prüfung erhalten und der Vorgang liefert recovery_required.

Die Spezialisierung ist keine niedrigere Vertrauensstufe. Ein erfolgreich aktiviertes Snippet wird ab folgenden WordPress-Abläufen mit den Rechten der Website ausgeführt beziehungsweise als HTML, CSS oder JavaScript in die Ausgabe eingebunden. Deshalb liegen auch die beiden Lesewege hinter demselben vollständigen P4-Gate. Snippet-Code kann außerdem Zugangsdaten enthalten. Netzwerkweite und geteilte Multisite-Snippets, Single-Use-Snippets und an Code-Snippets-Pro-Bedingungen gebundene Snippets bleiben geschlossen. Eingaben und vorhandene Providerdaten mit ungültigem UTF-8 werden unverändert abgelehnt, damit der Konflikthash keine unterschiedlichen Bytefolgen zusammenfasst. Der Vertragstest deckt zusätzlich fehlerhafte Provider-Rückgaben und verifizierte Rollbacks ab. Der vollständige Lebenszyklus aller vier unterstützten Typen ist praktisch gegen eine echte lokale Installation von Code Snippets 3.9.6 geprüft.

BACKUP und RESTORE, vollständige Remote-Sicherung

Die eigenständige BACKUP-Stufe (wp_agent_power_stage_backup) trägt den Sicherungsweg über REST für Kunden ohne SSH- oder WP-CLI-Zugriff. db-tables liefert zuerst den Discovery-Katalog und materialisiert den gewählten Umfang danach in einer einzigen Datenbank-Lesetransaktion. MySQL und MariaDB verwenden REPEATABLE READ mit konsistentem Snapshot und halten die Tabellenschemas über Metadatensperren stabil. SQLite fixiert seine MVCC-Lesesicht beim ersten Zugriff. db-export liest anschließend ausschließlich aus den unveränderlichen privaten Snapshot-Dateien, seitenweise mit fortsetzbarem Cursor und Prüfsumme. Änderungen zwischen zwei HTTP-Anfragen können dadurch keine gemischte Sicherung mehr erzeugen. Snapshot-ID und zufälliges Token sind an Administrator und Blog gebunden, werden nur im privaten lokalen Fortschritt gespeichert und nie in einer URL übertragen. Nach Erfolg löscht der Client die Sitzung. Abgelaufene oder abgebrochene Sitzungen werden serverseitig bereinigt. Der Server begrenzt eine Sitzung auf 512 Tabellen, 512 MiB und 120 Sekunden, höchstens drei gleichzeitige Sitzungen und zwei Stunden Lebensdauer. Tabellen ohne Primärschlüssel werden nur bis 10.000 Zeilen in einem einzelnen stabilen Read unterstützt, größere werden fail-closed abgelehnt. file-catalog erfasst reguläre Dateien in den erlaubten WordPress-Komponenten und file-export überträgt sie größenbegrenzt mit SHA-256-Prüfsummen. Links, Spezialdateien, geschützte Verzeichnisse und mehr als 20.000 Dateien werden abgelehnt.

Der Dateiweg erzeugt kein unbeschränktes Serverarchiv. Er überträgt höchstens 512 KiB pro Chunk, prüft jeden Chunk und die vollständige Datei mit SHA-256 und erstellt das tar.gz erst im lokalen Backup-Verzeichnis. Der Katalog und der Datei-Restore akzeptieren zusammen höchstens 20.000 Dateien und Verzeichnisse.

Vor einem lokalen tar.gz-Restore werden die normale und die ausführliche Archivliste zeilenweise verarbeitet, niemals vollständig in den Speicher gelesen. Ein Archiv darf höchstens 200.000 Einträge, 4 KiB pro Pfad, 64 KiB pro Ausgabzeile und 128 MiB Auflistungsausgabe haben. Überschreitungen werden vor dem Entpacken abgelehnt.

Der entscheidende Unterschied zu db-select ist die verlustfreie Ausgabe ohne Schwärzung. Passwort-Hashes, Session-Tokens und geheime Optionen können im Export enthalten sein. Die gültige WPAgently-Lizenz, das eigene standardmäßig ausgeschaltete Flag, die manage_options-Pflicht und die clientseitige Verschlüsselungs-Pflicht schützen diesen Weg. Sobald eine vertrauliche Tabelle enthalten ist (wp_options, wp_users, wp_usermeta), bricht der Backup-Client ohne Passphrase ab. Ein Notausgang existiert nur über die bewusst gesetzte Option allowUnencryptedSensitive.

Bei verschlüsselten REST-Backups sind die JSONL-Zeilen mit AES-256-GCM gebunden. Fortschrittsdatei, fertiges Tabellenartefakt und Manifest tragen zusätzlich einen lokal aus der Passphrase mit scrypt abgeleiteten HMAC. Der CLI-Client prüft diese Dateiauthentifizierung vor einem Restore, bevor er eine Servermutation anfordert. Passphrase und abgeleiteter Schlüssel verlassen den lokalen Client nicht.

Der getrennte RESTORE-Kanal (wp_agent_power_stage_restore) akzeptiert nur zuvor lokal vollständig geprüfte Manifeste. Datenbankimporte landen zuerst in sitzungsgebundenen Staging-Tabellen. Erst ein vollständiger Commit ersetzt die gewählten Zieltabellen. Dateiimporte landen in benachbarten Staging-Verzeichnissen und werden erst nach Größen- und Prüfsummenabgleich ausgetauscht. Ein Fehler löst Rollback oder eine harte Ablehnung aus. --tables begrenzt REST-Datenbank-Restores tatsächlich auf die gewählten Tabellen. Bei monolithischen lokalen Dumps wird die Option ausdrücklich abgelehnt, weil sie dort nicht verlustfrei umgesetzt werden kann.

Ein restaurierbares REST-Datenbankbackup verwendet das versionierte Schemaformat wpagent-engine-ddl-v2. Es enthält die engine-native Tabellendefinition, separate SQLite-Indizes, Constraints, die MySQL-Typmetadaten der offiziellen SQLite-Integration und den Auto-Increment-Zustand. Ältere reine Datenartefakte werden vor jedem Server-Schreibaufruf abgelehnt. Dasselbe gilt bereits beim Backup für Views, virtuelle Tabellen, nicht transaktionale MySQL-Tabellen und Tabellen mit Triggern. WPAgently lässt solche Objekte nicht stillschweigend aus und bezeichnet ein später nicht restaurierbares Artefakt nicht als vollständiges Datenbankbackup.

Fehlende Tabellen dürfen nur nach ausdrücklicher Restore-Freigabe innerhalb des aktuellen Site-Präfixes angelegt werden. Andere Multisite-Blogs und erkannte verschachtelte WordPress-Installationen bleiben ausgeschlossen. Bei vorhandenen Tabellen muss das vollständige Schema zu Beginn und unmittelbar vor dem Commit exakt zum Backup passen. Fremdschlüsselprüfungen werden nur auf derselben Datenbankverbindung pausiert, ihr Zustand wird jeweils zurückgelesen und auf jedem Fehlerpfad wieder aktiviert. Vor dem Erfolg prüft Power alle Constraints, deren Kind- oder Elterntabelle vom Restore betroffen ist.

Eine Schema-Abweichung zwischen begin und Commit ist Drift und wird fail-closed mit HTTP 409 abgelehnt, bevor eine destruktive Live-Mutation beginnt.

Eine zuvor fehlende SQLite-Tabelle bleibt während begin und aller Batches vollständig unsichtbar. Erst der Commit legt Tabelle, Indizes und Typmetadaten innerhalb derselben SQLite-Transaktion an, kopiert die geprüften Daten, stellt die Sequenz wieder her und liest Schema, Indizes, Fremdschlüssel und Sequenz zurück. Ein Fehler rollt die gesamte Anlage zurück. Eine zwischenzeitlich von einem anderen Prozess angelegte Tabelle wird ohne Änderung abgelehnt und bei einem Abort niemals gelöscht.

MySQL und MariaDB können Schema-DDL nicht in dieselbe Datentransaktion aufnehmen. Dort legt Power ausdrücklich autorisierte fehlende InnoDB-Ziele erst unmittelbar vor der Datentransaktion bei pausierten und zurückgelesenen Fremdschlüsselprüfungen an. Jede erfolgreiche Anlage wird im Sitzungsjournal vermerkt. Die folgende Transaktion sperrt und prüft alle Zielschemas erneut, leert vorhandene Ziele in einer getrennten Phase, schreibt danach Eltern-, Kind- und zyklische Tabellen unabhängig von der Manifestreihenfolge und verifiziert alle betroffenen Fremdschlüssel. Ein kontrollierter Fehler entfernt ausschließlich neu angelegte, weiterhin leere und schemaidentische Zieltabellen als zusammenhängende Gruppe. Wegen der nicht transaktionalen MySQL-DDL kann eine harte Prozessunterbrechung im engen Zeitraum zwischen Tabellenanlage und Journalaktualisierung eine leere Zieltabelle hinterlassen. Die Wiederaufnahme behandelt sie als fremde parallele Anlage und endet geschlossen mit HTTP 409, statt sie ohne eindeutigen Eigentumsnachweis zu löschen.

Bei vorhandenen MySQL- und MariaDB-Tabellen prüft Power den gesicherten Auto-Increment-Wert vor jeder Live-Mutation gegen die IDs im Staging. Ein unmöglicher Wert wird abgelehnt, solange die Live-Daten unverändert sind. Das abschließende ALTER TABLE bleibt wegen der MySQL-DDL-Semantik ein Schritt nach dem atomaren Daten-Commit. Scheitert ausschließlich dieser Schritt oder sein Read-back, meldet Power deshalb keinen angeblich zurückgerollten Restore mehr. Die Sitzung wird idempotent als angewendet gespeichert und die Antwort enthält eine ausdrückliche finalization_warnings-Warnung zum unbestätigten Auto-Increment-Zustand.

5. Weitere Sicherheitsmechanismen (stufenübergreifend)

  • Geschützte Bereiche (protected-entities.php): der Modus kann sein eigenes Gate nicht umschreiben. Jede Option mit dem Präfix wp_agent_power_ (Master, alle Stufen-Flags, der Zähler, kurzlebige Produktions-Freigaben) ist über option-write/db-write unerreichbar. siteurl/home (Grundlage der Umgebungserkennung) ebenso, damit eine echte Live-Seite sich nicht als Testkopie tarnen lässt.
  • Umgebungserkennung (wpagent_power_environment()): drei Kategorien, production/staging/local (Live-Seite, Testkopie, lokal). Fail-safe: jeder nicht eindeutig als lokal erkannte Host gilt als production. Lokal wird über Loopback-Adressen und reservierte Entwickler-Endungen (.local, .test, .example, .invalid) erkannt, sonst über wp_get_environment_type() beziehungsweise die Konstante WP_ENVIRONMENT_TYPE.
  • Block-Markup-Schutz, CVE-2026-7465 (block-guard.php): verhindert, dass ein Datenbank- oder Datei-Schreibvorgang einen bösartigen, angreifer-kontrollierten Gutenberg-Block in post_content einschleust (der bei einer verwundbaren Block-Registrierung zur Code-Ausführung würde). Für db-write streng (nur eine feste Liste bekannter Inhaltsblöcke, core/html und core/freeform immer verboten), für P2-Dateischreibvorgänge bewusst lockerer, weil P2 ohnehin schon vollen Theme-Code-Zugriff hat.
  • Absturzschutz und Gesundheitsprüfung: P2 prüft geschriebene PHP-Inhalte mit php -l und hält den vorherigen Dateistand fest. P3 hält die betroffenen Datenbankzeilen fest. Beide prüfen danach Startseite und wp-admin über Loopback. Nur der jeweilige Pfad darf eine dokumentierte Wiederherstellung versuchen, solange sein eigener Zwischenzustand exakt beweisbar bleibt. P4/P5 haben wegen der unbegrenzten Seiteneffekte beliebigen Codes keine Wiederherstellungszusage. Fremddrift, Lock-Verlust und unsichere Zustände bleiben für die manuelle Prüfung erhalten und liefern recovery_required. Wichtig, ehrlich benannt in Abschnitt 9: das ist ein Absturz-Erkenner, keine Sicherheitsgarantie.
  • Audit-Log (audit.php): ein serverseitiges, fail-closed Log im stabilen Power-Kontrollverzeichnis. Dieses liegt standardmäßig in einem installationsspezifischen Ordner neben ABSPATH, also außerhalb von ABSPATH und allen restaurierbaren Komponenten. Wenn der Hosting-Anbieter dort kein Schreiben erlaubt, verwendet Power den Fallback wp-content/.wp-agent-power-control/audit/. Beide Orte erhalten Apache-, IIS- und PHP-Guards. Nginx wertet diese portablen Dateien nicht selbst aus. Bei einer WordPress-Unterverzeichnisinstallation unter Nginx muss WP_AGENT_POWER_CONTROL_DIR deshalb auf einen Ort außerhalb des Document-Roots zeigen oder der direkte Zugriff in der Hosting-Konfiguration gesperrt sein. WP_AGENT_POWER_CONTROL_DIR kann einen anderen absoluten Ort aus wp-config.php fest vorgeben. Sequenz, Verkettung, SHA-256 und die HMAC mit einem audit-spezifischen, PHP-geguardeten Schlüssel müssen stimmen. Ein separater, ebenfalls signierter und auf 4 KiB begrenzter Tail-Zustand bindet die letzte Sequenz, den letzten Hash und die Dateimetadaten. Im normalen Append-Pfad werden dieser Zustand und der tatsächliche Dateischwanz in konstanter Zeit gegengeprüft. Fehlt der Tail-Zustand, wird er erst nach einer vollständigen authentifizierten Prüfung der Kette neu aufgebaut. Beschädigte, übergroße oder verlinkte Zustandsdateien schließen den Pfad. Die Schlüsseldatei liegt mit Modus 0600 im selben nicht restaurierbaren Kontrollordner, wird bei der Deinstallation zusammen mit der forensischen Historie erhalten und nie in Audit, Checkpoint oder Tail-Zustand geschrieben. Eine bestehende v2-Historie wird vor der Migration vollständig mit ihrem bisherigen, aus den WordPress-Sicherheitskonstanten abgeleiteten Schlüssel verifiziert. Erst danach wird genau dieser Schlüssel persistent übernommen. Spätere WordPress-Salt-Rotationen und eine erneute Plugin-Installation unterbrechen die Audit-Kette deshalb nicht. Fehlt die persistente Schlüsseldatei nach einer Salt-Rotation oder ist sie beschädigt, wird keine neue Historie begonnen. Die Operation wird fail-closed abgelehnt. Manipulation, Löschen, Umordnen, ungültiges UTF-8 und eine einseitige Kürzung führen ebenfalls zur fail-closed-Ablehnung. Neue Einträge speichern weder Login noch Host im Klartext, sondern nur lokale HMAC-Pseudonyme. Eine vorhandene alte v1-Kette wird vor der Übernahme vollständig geprüft und mit dem ersten v2-Checkpoint verankert. Weil v1 noch keine HMAC besaß, belegt diese Verankerung nur den beim Upgrade vorgefundenen Zustand, nicht die Authentizität einer davor möglicherweise mit Dateizugriff vollständig neu berechneten v1-Historie. Vor jeder P1-/P2-/P3-/P4-Schreiboperation wird ein Eintrag geschrieben, bevor die Operation läuft. Seit dem 13.07.2026 gilt das auch für BACKUP, dort nur für Metadaten und nie für Zeilendaten. Reine P0-Lesevorgänge bleiben unprotokolliert. Die lokale Grenze bleibt: Wer Audit, Checkpoint, Tail-Zustand und Schlüsseldatei gemeinsam kontrolliert, kann diese rein lokale Spur entfernen oder ersetzen. P4/P5 kann alle lokalen Kontrollartefakte lesen oder verändern. Gegen diesen Vertrauensbruch hilft nur ein externes oder unveränderbares Logziel.
  • Stabiler Restore-Zustand: Restore-Sitzungen und der installationsweite .session.lock liegen im selben Control-Root wie das Audit, außerhalb aller Komponenten, die RESTORE austauschen darf. Ein vollständiger Uploads-Austausch kann diesen Zustand deshalb nicht entfernen. Frühere Sitzungen unter Uploads werden unter einer exklusiven Sperre in den Control-Root migriert. Das Warten auf eine zentrale Sperre ist auf etwa 500 ms begrenzt. Danach lehnt Power die Operation fail-closed ab, statt unbegrenzt einen PHP-Worker zu blockieren.
  • Not-Aus-Schalter: serverseitig deaktiviert die Kern-Fähigkeit wp-agent/disable-power das gesamte wp-agent-power-Plugin über die WordPress-Kern-Schnittstelle; ab dem nächsten Request ist keine Power-Fähigkeit mehr angemeldet. Clientseitig bricht die Umgebungsvariable WP_AGENT_POWER_DISABLED jeden wp-agent power-Befehl sofort ab; das schützt nur die CLI, nicht einen direkten Web-Aufruf.
  • Advisory-Locks: lock.mjs verhindert clientseitig zwei parallele wp-agent power-Läufe derselben Site. Zusätzlich besitzen P2-Dateimutationen einen atomaren, kurzlebigen Server-Lock pro Zielpfad. P3 besitzt entsprechende Server-Locks pro Tabelle beziehungsweise Optionsname und ergänzt den Tabellen-Lock bei db-write um eine Datenbanktransaktion mit Row-Locks. Diese Server-Locks erfassen auch direkte REST- und MCP-Aufrufe desselben Plugins. Fremdcode muss sie nicht respektieren, weshalb P3 zusätzlich exakte Read-backs und Compare-and-Swap-Rollbacks verwendet. Locks ersetzen weder das Gate noch das Pflichtbackup.

6. Sicherheits-Rahmung: begrenzte Schutzmechanismen für P0 bis P3, Testkopie-first für P4/P5

P0 ist lesend, P1 auf begrenzte und wiederholbare Wartungs- und Lesebefehle beschränkt. P2 und P3 verlangen Pflicht-Backup und dokumentierte, pfadspezifische Recovery-Prüfungen, sind aber keine allgemeine Live-Sicherheitszusage. Beliebiger PHP-Code (P4/P5, die eigentliche Code-Ausführungs-Stufe) bleibt „Testkopie-first“ mit gesonderter, getippter Einmal-Freigabe.

Konkret bedeutet das:

  • P0 bis P1: keine Umgebungssperre, auf jeder Umgebung nutzbar (P0 ist rein lesend, P1 ist auf begrenzte und wiederholbare Wartungs- und Lesebefehle beschränkt).
  • P2: eine harte Sperre auf erkannter Produktion. Die Fähigkeit wird auf production gar nicht angemeldet, außer der Betreiber setzt die wp-config-Konstante WP_AGENT_POWER_ALLOW_WRITE_ON_PRODUCTION bewusst.
  • P3: bewusst keine harte Umgebungssperre. Stattdessen begrenzen Pflicht-Backup, Dry-Run-Standard, Zeilen-Deckel, geschützte Bereiche, SQL-Sperrliste und DB-Fehler-Schutz mit Zurückrollen den Pfad. Es gibt keine Einmal-Freigabe. Diese Mechanismen ersetzen weder eine Testkopie noch eine fachliche Prüfung und sind keine allgemeine Live-Sicherheitszusage.
  • P4/P5: bleibt Testkopie-first. Auf erkannter Produktion verweigert exec-php und jede Code-Snippets-Mutation ohne eine serverseitig geprägte Einmal-Freigabe. Sie ist 15 Minuten gültig, wird vor der Aktion atomar verbraucht und ist an Blog, Administrator, exakte Aktion, Payload- und Intent-Hash sowie Nonce gebunden. Nur der Secret-Hash wird gespeichert. Die Mint-Route ist keine Ability, erscheint also nicht in der Toolliste. Die CLI prägt und übermittelt das Ticket ausschließlich für die bestätigte Aktion, nie über argv oder stdout. Das schützt vor versehentlichen Produktionsläufen, nicht vor einem vollständig kompromittierten Administrator-Zugang.
  • BACKUP: keine Umgebungssperre (rein lesend, verändert die Seite nicht), aber eine eigene Vertraulichkeits-Sperre über das separate Flag.

7. Freischaltung: das Mehrfach-Gate

Eine Power-Fähigkeit läuft nur, wenn alle für ihre Stufe zutreffenden Bedingungen gleichzeitig erfüllt sind:

  1. Server-Master-Schalter (wp_agent_power_enabled, Betreiber-Hoheit). Standard aus. Administratoren schalten ihn unter Werkzeuge > WPAgently Power oder per WP-CLI ein.
  2. Stufen-Flag je Stufe (wp_agent_power_stage_p1/p2/p3/p4/backup/restore, jeweils eigener Schalter, Standard aus), gilt immer zusätzlich zum Master. Das Backend zeigt jede Stufe einzeln mit Risikohinweis. Beim Ausschalten des Masters werden alle Stufen geschlossen.
  3. wp-config-Konstanten (nur für P2 auf Produktion und für P4/P5): WP_AGENT_POWER_ALLOW_WRITE_ON_PRODUCTION beziehungsweise WP_AGENT_POWER_ALLOW_CODE_EXECUTION, ausschließlich per Dateizugriff auf wp-config.php setzbar, außerhalb der Reichweite jeder Ability.
  4. Lokale Unlock-Session (clientseitig, CLI): wp-agent power unlock verlangt vorher eine Zusammenfassung (Seite, erkannte Umgebung, aktive Stufen) und danach entweder die getippte Phrase ENTSPERREN oder, nicht-interaktiv, das Flag --i-approve-power. Standard-Zeitlimit 30 Minuten (--ttl-minutes), vorzeitig beendbar mit wp-agent power lock. Die Session ist an die Ziel-Seite gebunden, Dateirechte 0600, enthält keine Zugangsdaten.
  5. Pro-Aktion-Bestätigung für jede schreibende oder ausführende Stufe, mit jeweils eigener Phrase, damit ein blindes Durchtippen der einen nicht die andere mit bestätigt:
    • P2/P3: Phrase SCHREIBEN (oder --i-approve-power). Ohne Bestätigung bleibt es bei P3 automatisch beim sicheren Dry-Run.
    • P4/P5 (exec-php): eigene, stärkere Phrase CODE-AUSFUEHREN (bewusst ohne Umlaut geschrieben, weil es ein exakt abzutippendes Token ist, kein Fließtext). Kein Dry-Run-Fallback: ohne Bestätigung und ohne interaktives Terminal wird komplett verweigert.
    • Code-Snippets-Mutationen über MCP: je nach Aktion CREATE CODE SNIPPET, UPDATE CODE SNIPPET, ACTIVATE CODE SNIPPET, DEACTIVATE CODE SNIPPET oder TRASH CODE SNIPPET. Die serverseitige Fähigkeit verlangt zusätzlich Intent, Backup-Referenz und bei bestehenden Snippets den frischen Konflikthash.
    • Produktions-Code bestätigen: eine dritte, eigene Phrase PRODUKTION-CODE-AUSFUEHREN. Die CLI prägt danach nur für diese Aktion automatisch eine Einmalfreigabe.

Die Phrasen sind technische Bedienbestätigungen. Sie sind keine elektronische Signatur und kein Nachweis einer rechtlich informierten Einwilligung; siehe Abschnitt 14.

  1. Pflicht-Backup vor P2, P3 und P4/P5 (siehe Abschnitt 8), rein clientseitig erzwungen. P1 ist auf eine feste Liste begrenzter, wiederholbarer Wartungs- und Lesebefehle beschränkt und verlangt kein Backup. Beliebige Cron-Hooks sind deshalb nicht Teil von P1.

Alle Befehle außer lock brauchen echte Administrator-Zugangsdaten (WP_AGENT_ADMIN_USER/WP_AGENT_ADMIN_APP_PASSWORD oder --admin-user/--admin-app-password), niemals den Editor-Bot der geprüften Abläufe. Serverseitig verlangt jede Power-Fähigkeit manage_options, der Bot-Nutzer hat das absichtlich nicht und wird mit 403 abgewiesen.

8. Pflicht-Backup vor P2, P3 und P4/P5

Für P2 (write-file, edit-file, delete-file), P3 (db-write, option-write) und P4/P5 (exec-php) erzwingt die CLI vor der eigentlichen Aktion ein frisches Backup, sonst wird die Operation verweigert, ohne dass irgendetwas geschrieben wird:

  • P2 verlangt eine Dateisicherung. P3 verlangt eine Datenbanksicherung. P4/P5 verlangt Datenbank und Dateien. Fehlt das passende Backup, erzeugt --backup-now es automatisch. Mit WP_AGENT_WP_PATH nutzt die CLI den lokalen Snapshot-Weg. Ohne lokalen WordPress-Pfad nutzt sie den vollständigen REST-Weg.
  • P4/P5 verlangt zusätzlich, dass das Backup innerhalb der aktuellen Unlock-Session entstanden ist (Zeitstempel nach dem Session-Start), strenger als P2/P3, weil bei beliebiger Code-Ausführung jeder ältere Snapshot potenziell bereits überholt ist.
  • Ein Backup zählt nur als „frisch“, wenn sein Manifest tatsächlich Datenbank- oder Dateiinhalt als eingeschlossen meldet, nicht schon bei der bloßen Existenz eines Backup-Ordners.
  • Ein REST-Backup ist wiederherstellbar. Vor dem Schreiben prüft die CLI Manifest, Prüfsummen, Zielseite und Datenbank-Engine. Anschließend verwendet sie den separat freigeschalteten RESTORE-Kanal.

Wichtig: dieses Pflicht-Backup ist eine reine CLI-Disziplin. Die serverseitigen Fähigkeiten selbst kennen keine Backups (Backups liegen auf deinem Rechner) und erzwingen serverseitig nichts. Ein direkter Web-Aufruf am Backup-Mechanismus vorbei wird davon nicht gebremst, siehe Abschnitt 9.

9. Die ehrlichen Grenzen (nicht beschönigt)

Diese Grenzen sind im Code selbst so dokumentiert und wurden im adversarialen Security-Review bestätigt. Sie gehören zwingend in jede Kundenkommunikation, unverändert:

  1. P2 ist bereits die Code-Ausführungs-Grenze, nicht erst P4/P5. Eine PHP-Datei nach wp-content/mu-plugins/ zu schreiben heißt, beliebigen Code auszuführen. Deshalb ist P2 auf erkannter Produktion gesperrt. Die für P4/P5 gedachte Konstante schützt vor allem den Angreifer ohne Dateizugriff (etwa nur mit einem geleakten Administrator-Passwort); ein Angreifer mit bereits vorhandenem P2-Zugriff kann über ein mu-plugin dieselbe Konstante setzen und so auf P4/P5 eskalieren, was ihm aber gegenüber dem bereits vorhandenen P2-Zugriff nichts Neues gewährt.
  2. Backup, Unlock-Session und die getippte Pro-Aktion-Bestätigung sind Betriebs-Disziplin, keine Sicherheitskontrolle. Sie laufen ausschließlich in der CLI. Ein direkter Web-Aufruf gegen den Power-Zugangspunkt (etwa mit einem geleakten Administrator-Passwort) umgeht sie vollständig; das wurde im adversarialen Review live bestätigt. Die eigentliche Sicherheitsgrenze ist ausschließlich der serverseitige Gate (gültige Lizenz, Master-Schalter, Stufen-Flags, manage_options, Produktions-Sperre, geschützte Bereiche).
  3. Die Gesundheitsprüfung ist ein Absturz-Erkenner, keine Sicherheitsgarantie. Sie prüft nur, ob Startseite und wp-admin mit HTTP unter 500 antworten. Ein bösartiger, aber nicht abstürzender Schreibvorgang (zum Beispiel eine versteckte Hintertür in mu-plugins) gilt als „gesund“ und wird nicht zurückgerollt. Das wurde im Review live demonstriert.
  4. Die Schwärzung ist Best-Effort, keine Garantie. Sie basiert auf Namensmustern. db-export (die BACKUP-Stufe) liefert ausdrücklich gar keine Schwärzung, das ist eine bewusste, aber vertraulichkeitskritische Entscheidung, siehe Abschnitt 4.
  5. P4/P5 kann jede eigene Schranke abschalten. Läuft einmal beliebiger Code, kann er das Audit-Log löschen oder fälschen, den Not-Aus deaktivieren und jeden geschützten Bereich umschreiben. Kein Laufzeit-Mechanismus kann das verhindern. Die gesamte Sicherheit von P4/P5 liegt vor der Ausführung, nicht während oder danach.
  6. Ein bereits laufender exec-php-Aufruf lässt sich nicht mitten in der Ausführung stoppen. PHP-Requests sind synchron; eine Plugin-Deaktivierung in einem parallelen Request wirkt erst ab dem nächsten Request.
  7. Prompt-Injection bleibt ein reales Risiko innerhalb einer offenen Session. Über db-select oder read-file gelesener Inhalt ist Daten, keine Anweisung. Ein manipulierter Assistent kann innerhalb einer offenen Session jede gerade freigeschaltete Fähigkeit missbrauchen, begrenzt durch die serverseitigen Gates der jeweiligen Stufe (Zeilen-Deckel, geschützte Bereiche, Schwärzung), nicht durch eine zusätzliche Intelligenzprüfung pro Aktion.

10. Bedrohungsmodell: was hält, was (bisher) nicht

Zwei adversariale Security-Reviews liegen vor, hier zusammengefasst aus den Report-Dateien und mit dem aktuellen Code abgeglichen:

P0 bis P3 (Stand 10.07.2026): der Gate-Selbstschutz hielt jedem direkten Angriff stand (kein Weg, den Master-Schalter zu setzen, eine Stufe freizuschalten oder die Umgebungserkennung herunterzustufen, weder mit geleaktem Administrator-Zugang noch prompt-injiziert). Elf konkrete Lücken wurden gefunden (F1 bis F11), darunter drei „hoch“: db-select ließ sich über Alias/SUBSTRING/UNION umgehen und leakte Passwort-Hashes (F1), read-file leakte jedes Secret, das nicht als define() vorlag (F2), und option-write erlaubte das Schreiben sicherheitskritischer Optionen wie wp_user_roles oder active_plugins (F3). Im aktuellen Code findet sich zu jedem dieser Funde eine im Kommentar ausdrücklich referenzierte Gegenmaßnahme. Diese Zuordnung ist eine Codeinspektion, kein erneuter adversarialer Testlauf; es existiert kein aktualisierter, auf den heutigen Code datierter Findings-Report, der die Fixes bestätigt, und die Checkliste hat weiterhin ausschließlich unangehakte Kästchen.

P4/P5 (Stand 11.07.2026): Der damals geprüfte vierfache Gate aus Master, Stufen-Flag, wp-config-Konstante und Produktions-Override hielt vollständig, auch live gegen ein simuliertes geleaktes Administrator-Passwort. Ein Fund (F8-Regression) wurde per Commit f456bfc behoben und im aktuellen Code gegengelesen und bestätigt. Seit Version 0.6.1 kommt die gültige Companion-Lizenz als eigene Registrierungsbedingung hinzu.

BACKUP (db-tables/db-export): wurde nach den anderen Reviews gebaut und von diesen daher nicht erfasst. Ein eigener, adversarialer Review liegt seit dem 13.07.2026 vor, mit vier bestätigten Befunden, alle LOW, keiner über eine von WordPress verteidigte Vertrauensgrenze. Die umgesetzten Härtungen und die offenen Produktentscheidungen fasst Abschnitt 16 zusammen.

11. CLI-Kurzreferenz

wp-agent power status                                    Umgebung, Server-Master, Session
wp-agent power init                                      Power-MCP-Konfiguration erzeugen
wp-agent power unlock [--ttl-minutes 30] [--i-approve-power]
wp-agent power lock

wp-agent power read-file --path <rel>                     P0
wp-agent power list-directory --path <rel>                 P0
wp-agent power get-option --name <opt>                     P0
wp-agent power db-select --query "<SELECT ...>"             P0
wp-agent power list-extensions                              P0
wp-agent power env-info                                     P0

wp-agent power wp-cli --command "<allowlist-kommando>"      P1
wp-agent power wp-cli --command "<allowlist-kommando>" --background
wp-agent power wp-cli --job-id <id>                          P1, Jobstatus und Ergebnis

wp-agent power write-file --path <rel> --content-file <datei> \
  [--i-approve-power] [--backup-now]                        P2
wp-agent power edit-file --path <rel> --expected-sha256 <hash> \
  --search-file <datei> --replacement-file <datei> \
  [--replace-all] [--i-approve-power] [--backup-now]        P2
wp-agent power delete-file --path <rel> --expected-sha256 <hash> \
  [--i-approve-power] [--backup-now]                        P2
wp-agent power inspect-extension-archive --archive <datei.zip> \
  --type <plugin|theme>                                     P2, nur Vorprüfung
wp-agent power db-write --query "<UPDATE/DELETE ... WHERE ...>" \
  [--i-approve-power] [--backup-now]                        P3 (Dry-Run-Default)
wp-agent power option-write --name <opt> [--value <json>] [--delete] \
  [--i-approve-power] [--backup-now]                        P3 (Dry-Run-Default)

wp-agent power exec-php --code-file <datei> --intent "<text>" \
  [--i-approve-power] [--i-approve-production-code] \
  [--backup-now]                                            P4/P5 (kein Dry-Run)

Gemeinsame Optionen: --site, --admin-user, --admin-app-password, --session-dir, --json. Vollständige Flag-Referenz inklusive Backup-Optionen: wp-agent power --help.

12. Wann diesen Modus nutzen, wann nicht

Nutzen: gezielte Diagnose ohne passende Kern-Fähigkeit, ein begrenzter WP-CLI-Wartungs- oder Lesebefehl, eine einzelne, gut eingegrenzte Datei- oder Datenbankänderung mit Backup und Review, ausnahmsweise beliebiger PHP-Code zur Fehlersuche ausschließlich auf einer Testkopie oder lokal.

Nicht nutzen: für Blogartikel und Landing-Pages (dafür wp-article/wp-landing über den geprüften Kern), für beliebigen Code auf einer als Produktion erkannten Website ohne zwingenden Grund und ohne die Produktions-Ausführung bewusst doppelt zu bestätigen, für den vollständigen, unredigierten Datenbank-Export außerhalb eines tatsächlichen Backup-Vorhabens, und niemals, um den Server-Master-Schalter „nur damit eine Aufgabe funktioniert“ einzuschalten.

13. Reifegrad und Auslieferungsstatus

P0 bis P5 sowie BACKUP und RESTORE sind im Release-Satz von Power 0.6.35 umgesetzt. Der Testkatalog umfasst 30 Power-Verträge. Remote-Backups umfassen Datenbank und Dateien und sind über denselben geprüften Kanal wiederherstellbar. Drei P2-Abilities laden Plugin- und Theme-ZIPs kurzlebig hoch, prüfen sie begrenzt und widerrufen die Sitzung. Sie extrahieren, installieren, aktualisieren, überschreiben oder aktivieren keine Erweiterung. Die eigentliche Installation bleibt eine bewusste Administratoraktion in WordPress. Der Kandidatenlader führt nur zuvor P2-verifizierte MU-Kandidaten opt-in und zeitlich begrenzt aus. Vor jeder manuellen Aktivierung prüft ein signierter lokaler Loopback ausschließlich die gebundene WordPress-Antwort. Ein Lauf wird erst nach dem WordPress-Meilenstein wp_loaded als gesund markiert. exit(), PHP-Fatals, alte oder beschädigte Markierungen und parallele Läufe deaktivieren alle Candidates. Das ersetzt keine PHP-Sandbox und kann keine Datenbank- oder Netzwerkeffekte zurückrollen. Die Code-Snippets-Oberfläche deckt keine Single-Use- oder Pro-Bedingungen ab. Dieser zukünftige Pfad steht ausdrücklich in der Produktroadmap. Power bleibt opt-in. Plugin, serverseitige Gates, echte Administrator-Zugangsdaten und lokale Freischaltung sind getrennte Voraussetzungen.

Beim Löschen des Power-Plugins entfernt WordPress alle Master- und Stufenfreigaben, ungenutzte Produktions-Einmal-Freigaben, den Epoch-Zähler, persistente WP-CLI-Jobs und ihre Cron-Ereignisse, verwaiste Datei- und Datenbank-Locks und den Update-Cache. Das Audit-Log, sein privater Authentifizierungsschlüssel und vorhandene Restore-Sitzungen bleiben bewusst erhalten, damit die Deinstallation keine forensischen Nachweise oder einen möglicherweise noch benötigten Wiederherstellungszustand zerstört. Sie müssen nach der betrieblichen Prüfung bei Bedarf separat gelöscht werden.

14. Rechtsstand

Die EULA wurde am 30.07.2026 anhand des aktuellen Power-Codes, der dokumentierten Sicherheitsgrenzen und aktueller Primärquellen überarbeitet. Die getippten Phrasen sind als Bedienungsdisziplin eingeordnet, nicht als Signatur oder Haftungsverzicht. Die frühere Produktfrage nach einer zusätzlichen BACKUP-Konstante ist in Abschnitt 16.3 geschlossen. Verbleibende rechtliche Launch-Aufgaben betreffen die nachweisbare Einbeziehung von AGB und EULA, den Widerrufsprozess und die Kündigungsabwicklung im Polar-Checkout. Für Kunden ist ausschließlich die mitgelieferte EULA maßgeblich.

15. Rechtliches

Die verbindliche rechtliche Rahmung gehört ausschließlich in die EULA (docs/legal/eula.md, Ziffer 6 „Optionaler Power-Modus“), nicht in dieses technische Dokument. Diese Doku beschreibt den technischen Ist-Zustand, damit Kunden-Doku, Marketing und EULA auf derselben Tatsachengrundlage aufbauen. Die interne KI-Prüfung ist keine anwaltliche Beratung oder Compliance-Zertifizierung; siehe Abschnitt 14.

16. BACKUP-Security-Review (13.07.2026): Umsetzung, Empfehlungen, Installation

Am 13.07.2026 wurde die BACKUP-Stufe (db-tables/db-export) erstmals einem eigenen, adversarialen Security-Review unterzogen. Ergebnis: vier bestätigte Befunde, alle LOW, keiner überschreitet eine von WordPress verteidigte Vertrauensgrenze. Die damaligen Codebefunde sind behoben. Die optionale zusätzliche Gating-Härtung wurde am 29.07.2026 als Produktentscheidung geschlossen.

16.1 Umgesetzte Härtungen (im Code)

  • Metadaten-Audit für db-tables/db-export (Befund 2). Beide Fähigkeiten schreiben jetzt vor ihrer Operation einen fail-closed, verketteten Eintrag in dasselbe Audit-Log wie die Schreib-Stufen. Protokolliert werden ausschließlich Metadaten (Fähigkeit, Tabellenname, Paging-Modus, der opake Cursor und das Zeilen-Limit). Zeilendaten oder Secrets landen nie im Log. Kann der Eintrag nicht geschrieben werden, läuft der Export nicht. Damit hinterlässt auch die einzige unredigierte Lesefläche eine forensische Spur.
  • Vertraulichkeits-Hinweis in der Tool-Beschreibung (Befund 2). Die Tool-Annotation von db-export (und analog db-tables) trug bisher nur readonly: true, was ein Assistent als harmlos lesen konnte. Ergänzt wurde ein title-Hinweis, der die Vertraulichkeit (verlustfreier, unredigierter Export inklusive Passwort-Hashes und Secret-Options) bis in die Toolliste trägt.

16.2 Tabellen-Isolation in geteilten Datenbanken (Befund 4)

Der ursprüngliche reine Präfixfilter ist ersetzt. Der Katalog erkennt verschachtelte WordPress-Installationen an ihrem eigenen Satz aus options, posts und postmeta und schließt ihren gesamten Präfix aus. Auf Multisite bleiben globale Tabellen für normale Site-Administratoren ebenfalls ausgeschlossen. Die Integrationstests legen eine fremde Installation unter wp_tenant_ an und belegen sowohl für den Katalog als auch für den direkten Export die Ablehnung.

Die verbleibende technische Grenze ist ausdrücklich dokumentiert. Eine fremde Nicht-WordPress-Anwendung, die in derselben Datenbank absichtlich Tabellen innerhalb des WordPress-Präfix-Namensraums anlegt, besitzt keine WordPress-Strukturmarker und ist nicht automatisch als fremd erkennbar. Solche Tabellen müssen einen eigenen, nicht verschachtelten Präfix verwenden. Diese Präfixtrennung ist eine Betriebsanforderung für gemeinsam genutzte Datenbanken.

16.3 Gating-Entscheidung für Remote-Backups (Befund 1, entschieden am 29.07.2026)

Anders als P2 (Umgebungssperre) und P4/P5 (zusätzlich die wp-config-Konstante WP_AGENT_POWER_ALLOW_CODE_EXECUTION) besitzt BACKUP keine Dateisystem-Konstante. Sein Registrierungs-Gate prüft die gültige WPAgently-Lizenz, den Master-Schalter und das Backup-Flag. Ein dauerhaft gesetztes Backup-Flag bedeutet damit, dass jedes manage_options-Credential auf einer lizenzierten Site die ganze Datenbank im Klartext ziehen darf.

Die zusätzliche, nur per Dateizugriff setzbare wp-config-Konstante WP_AGENT_POWER_ALLOW_DB_EXPORT wird nicht eingebaut. Sie würde zwar verhindern, dass ein geleaktes Admin-App-Passwort allein das Backup-Gate öffnet, würde aber zugleich den vorgesehenen REST-Backup-Weg für Kunden ohne SSH-, WP-CLI- oder Hosting-Dateizugriff unbenutzbar machen.

Diese Abwägung ist vertretbar, weil ein manage_options-Credential unter dem nativen WordPress-Modell bereits Plugins installieren und damit die Site vollständig kompromittieren kann. BACKUP überschreitet deshalb keine zusätzliche WordPress-Vertrauensgrenze. Der unredigierte Export bleibt trotzdem eng abgesichert: gültige WPAgently-Lizenz, Master-Schalter, eigenes Default-aus-Flag, manage_options, keine Registrierung bei geschlossenem Gate, Pflichtverschlüsselung für sensible Tabellen und fail-closed Metadaten-Audit. Das Backup-Flag sollte nur für den Sicherungslauf geöffnet und anschließend wieder geschlossen werden.

16.4 Installation und Aktivierung des wp-agent-power-Plugins

Das Power-Plugin ist ein zweites, getrenntes WordPress-Plugin und nicht im Companion-Paket enthalten. Es wird als eigenes Paket wp-agent-power-<version>.zip ausgeliefert. Installation und Freischaltung:

  1. Das ZIP dist/wp-agent-power-<version>.zip unter Plugins > Installieren > Plugin hochladen einspielen und aktivieren. Ordner- und Dateiname wp-agent-power/wp-agent-power.php müssen erhalten bleiben, sonst greift der Not-Aus wp-agent/disable-power nicht.
  2. Nach der Aktivierung ist weiterhin nichts freigeschaltet. Eine frische Installation meldet null Fähigkeiten an und öffnet keinen Zugangspunkt (Registrierung als Gate). Es passiert erst dann etwas, wenn der Master-Schalter und die jeweilige Stufe bewusst gesetzt werden.
  3. Unter Werkzeuge > WPAgently Power den Hauptschalter und BACKUP aktivieren. RESTORE bleibt getrennt aus, bis wirklich eine Wiederherstellung ansteht. Alternativ per WP-CLI:
wp option update wp_agent_power_enabled 1
wp option update wp_agent_power_stage_backup 1

Zum Ausschalten genügt der Hauptschalter im Backend. Dabei werden auch alle Stufen-Flags geschlossen. Per WP-CLI geht das mit wp option delete wp_agent_power_enabled.

16.5 Release-Nachweis

Der vollständige Power-Testlauf enthält power-code-snippets.mjs, backup-rest.mjs, die Security-Regressionstests aller Stufen, den Audit-Chain-Test und den Advisory-Lock-Test. Der Projekt-Build nimmt wp-agent-power, diesen Power-Skill und die Agenten-Dokumentation in die Release-Artefakte auf. Vor jeder Veröffentlichung müssen npm run test:power und node scripts/build.mjs erneut erfolgreich laufen.