fix: Guards laufen auch im permission_callback vor permission() und im try des execute_callback – Lese-Tools antworten bei gesperrter Fähigkeit mit der Ablehnung statt internal_error, werfende Guards ergeben internal_error
Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
This commit is contained in:
co-authored by
Claude Sonnet 5.5
parent
74cf0c54a8
commit
b92545a8fe
@@ -63,7 +63,7 @@ final class AbilityRegistrar
|
||||
'description' => $ability->description(),
|
||||
'category' => self::CATEGORY,
|
||||
'input_schema' => $ability->inputSchema(),
|
||||
'permission_callback' => static function (mixed $input = null) use ($ability): bool|WP_Error {
|
||||
'permission_callback' => static function (mixed $input = null) use ($ability, $guards): bool|WP_Error {
|
||||
if (! Access::currentUserMayUse()) {
|
||||
return Errors::forbidden('Dein Benutzerkonto ist für LIT MCP nicht freigegeben.');
|
||||
}
|
||||
@@ -73,7 +73,12 @@ final class AbilityRegistrar
|
||||
}
|
||||
|
||||
try {
|
||||
return $ability->permission(is_array($input) ? $input : []);
|
||||
$input = is_array($input) ? $input : [];
|
||||
// Before permission(): a read tool whose loader breaks must answer with the guard's refusal
|
||||
// (e.g. the Divi release lock), not with internal_error from its own permission check.
|
||||
$refusal = self::guardRefusal($guards, $ability, $input);
|
||||
|
||||
return $refusal ?? $ability->permission($input);
|
||||
} catch (Throwable $e) {
|
||||
Log::failure($ability->name(), $e);
|
||||
return Errors::internal();
|
||||
@@ -81,14 +86,15 @@ final class AbilityRegistrar
|
||||
},
|
||||
'execute_callback' => static function (mixed $input = null) use ($ability, $guards): mixed {
|
||||
$input = is_array($input) ? $input : [];
|
||||
foreach ($guards as $guard) {
|
||||
$refusal = $guard->refusal($ability, $input);
|
||||
|
||||
try {
|
||||
// Second line of defence: the permission callback already asked the guards. A refusal there
|
||||
// stops the call, so a write is logged as refused only once.
|
||||
$refusal = self::guardRefusal($guards, $ability, $input);
|
||||
if ($refusal !== null) {
|
||||
return $refusal;
|
||||
}
|
||||
}
|
||||
|
||||
try {
|
||||
$result = $ability->execute($input);
|
||||
foreach ($guards as $guard) {
|
||||
$result = $guard->annotate($ability, $result);
|
||||
@@ -125,6 +131,22 @@ final class AbilityRegistrar
|
||||
return $args;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param list<AbilityGuard> $guards
|
||||
* @param array<string, mixed> $input
|
||||
*/
|
||||
private static function guardRefusal(array $guards, Ability $ability, array $input): ?WP_Error
|
||||
{
|
||||
foreach ($guards as $guard) {
|
||||
$refusal = $guard->refusal($ability, $input);
|
||||
if ($refusal !== null) {
|
||||
return $refusal;
|
||||
}
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
/**
|
||||
* A partial write stays partial_write; otherwise the first ToolRefusal anywhere in the cause chain is the answer
|
||||
* (e.g. the Divi release lock, spec §5.5); a rolled-back write is write_failed; anything else internal_error.
|
||||
|
||||
Reference in New Issue
Block a user