Files
WP-LIT-MCP/src/Integrations/WordPress/Render/PageRenderSummaryAbility.php
T
janniclas.loosenandClaude Opus 5.5 b24eb6c3e9 fix(security): no direct HTTP access to plugin PHP files (audit L2, cheap part)
Every file under src/ exits outside WordPress (defined('ABSPATH') ||
exit; after the use block, so a direct request no longer prints a fatal
error with the server path); divi-convert-fixture.php gets the WP_CLI
guard the other integration scripts have. A unit test keeps both true;
PSR1 side-effects warning excluded for the guard.

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01At3V6SjwbMe6eMzVGW3Hs7
2026-09-26 19:32:12 +02:00

114 lines
4.0 KiB
PHP
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
<?php
declare(strict_types=1);
namespace LIT\MCP\Integrations\WordPress\Render;
use LIT\MCP\Abilities\Ability;
use LIT\MCP\Abilities\Annotations;
use LIT\MCP\Integrations\WordPress\PostGuard;
use LIT\MCP\Integrations\WordPress\Schema\ContentSchema;
use WP_Error;
defined('ABSPATH') || exit;
/**
* lit/page-render-summary: what a visitor sees in the content area – headings, texts, links,
* images with alt text and obvious problems (spec §5.7, §8.1). Works for every managed entry,
* Divi or not; the content is rendered in-process, header/footer/Theme Builder are not included.
*/
final class PageRenderSummaryAbility implements Ability
{
public function __construct(private ContentSchema $schema, private PageRenderer $renderer)
{
}
public function name(): string
{
return 'lit/page-render-summary';
}
public function label(): string
{
return 'Gerenderte Seite (Auszug)';
}
public function description(): string
{
return 'Rendert den Inhaltsbereich eines Eintrags wie im Frontend und liefert einen strukturierten Auszug: '
. 'Überschriften, Texte, Links, Bilder mit Alt-Text und Warnungen (Hierarchie-Sprünge, fehlende '
. 'Alt-Texte). Header, Footer und Theme-Builder-Vorlagen sind nicht enthalten. Das Rendern führt die '
. 'üblichen Inhalts-Filter aus (Shortcodes, Divi, andere Plugins) und kann dabei Netzwerkanfragen '
. 'auslösen, z. B. oEmbed-Abrufe, die von WordPress oder anderen Plugins zwischengespeichert werden. '
. 'Gut zum Prüfen nach Änderungen.';
}
public function inputSchema(): array
{
return [
'type' => 'object',
'properties' => ['post_id' => ['type' => 'integer', 'minimum' => 1]],
'required' => ['post_id'],
'additionalProperties' => false,
];
}
public function outputSchema(): ?array
{
return [
'type' => 'object',
'properties' => [
'post_id' => ['type' => 'integer'],
'url' => ['type' => 'string'],
'headings' => ['type' => 'array', 'items' => ['type' => 'object']],
'texts' => ['type' => 'array', 'items' => ['type' => 'string']],
'links' => ['type' => 'array', 'items' => ['type' => 'object']],
'images' => ['type' => 'array', 'items' => ['type' => 'object']],
'warnings' => ['type' => 'array', 'items' => ['type' => 'string']],
'notes' => ['type' => 'array', 'items' => ['type' => 'string']],
],
'required' => ['post_id', 'headings', 'texts', 'links', 'images', 'warnings', 'notes'],
];
}
public function annotations(): array
{
return Annotations::read();
}
public function isDirect(): bool
{
return true;
}
public function permission(array $input): bool|WP_Error
{
$post = PostGuard::editable($input['post_id'] ?? null, $this->schema, 'post_id', true);
return $post instanceof WP_Error ? $post : true;
}
public function execute(array $input): mixed
{
$post = PostGuard::editable($input['post_id'] ?? null, $this->schema, 'post_id', true);
if ($post instanceof WP_Error) {
return $post;
}
$notes = [
'Nur der Inhaltsbereich; Header, Footer und Theme-Builder-Vorlagen fehlen.',
'Das Rendern führt Plugin-/Theme-Filter aus (Shortcodes, oEmbed u. Ä.) und kann dabei '
. 'Netzwerkanfragen auslösen, die von WordPress oder anderen Plugins zwischengespeichert werden.',
];
if ((string) $post->post_password !== '') {
$notes[] = 'Der Eintrag ist passwortgeschützt; Besucher sehen zuerst das Passwortformular.';
}
return array_merge(
['post_id' => (int) $post->ID, 'url' => (string) get_permalink($post)],
RenderSummary::fromHtml($this->renderer->render($post)),
['notes' => $notes]
);
}
}