Skip to content

Commit 8e209b7

Browse files
REST API: Switch to denylist approach for stripping internal schema keywords.
Use a targeted denylist of the three known WordPress-internal keys (sanitize_callback, validate_callback, arg_options) instead of an allowlist based on rest_get_allowed_schema_keywords(). The allowlist approach was too aggressive — it stripped legitimate JSON Schema keywords like $schema, $ref, allOf, not, and definitions that are valid but not in WordPress's supported subset.
1 parent c22d026 commit 8e209b7

1 file changed

Lines changed: 7 additions & 46 deletions

File tree

src/wp-includes/rest-api/endpoints/class-wp-rest-abilities-v1-list-controller.php

Lines changed: 7 additions & 46 deletions
Original file line numberDiff line numberDiff line change
@@ -216,63 +216,24 @@ private function normalize_schema_empty_object_defaults( array $schema ): array
216216
}
217217

218218
/**
219-
* Recursively removes non-JSON-Schema keywords from a schema.
219+
* Recursively removes WordPress-internal keywords from a schema.
220220
*
221221
* Ability schemas may include WordPress-internal properties like
222222
* `sanitize_callback`, `validate_callback`, and `arg_options` that are
223223
* used server-side but are not valid JSON Schema keywords. This method
224-
* strips any key not in the list returned by rest_get_allowed_schema_keywords(),
225-
* plus `required`.
224+
* removes those specific keys so they are not exposed in REST responses.
226225
*
227226
* @since 6.9.0
228227
*
229228
* @param array<string, mixed> $schema The schema array.
230-
* @return array<string, mixed> The schema with only valid JSON Schema keywords.
229+
* @return array<string, mixed> The schema without WordPress-internal keywords.
231230
*/
232231
private function strip_internal_schema_keywords( array $schema ): array {
233-
$allowed_keywords = rest_get_allowed_schema_keywords();
234-
$allowed_keywords[] = 'required';
235-
$allowed_keywords = array_flip( $allowed_keywords );
236-
237-
$schema = array_intersect_key( $schema, $allowed_keywords );
238-
239-
// Sub-schema maps: keys are user-defined, values are sub-schemas.
240-
foreach ( array( 'properties', 'patternProperties' ) as $keyword ) {
241-
if ( isset( $schema[ $keyword ] ) && is_array( $schema[ $keyword ] ) ) {
242-
foreach ( $schema[ $keyword ] as $key => $child_schema ) {
243-
if ( is_array( $child_schema ) ) {
244-
$schema[ $keyword ][ $key ] = $this->strip_internal_schema_keywords( $child_schema );
245-
}
246-
}
247-
}
248-
}
249-
250-
// Single sub-schema: items.
251-
if ( isset( $schema['items'] ) ) {
252-
if ( wp_is_numeric_array( $schema['items'] ) ) {
253-
foreach ( $schema['items'] as $index => $item_schema ) {
254-
if ( is_array( $item_schema ) ) {
255-
$schema['items'][ $index ] = $this->strip_internal_schema_keywords( $item_schema );
256-
}
257-
}
258-
} elseif ( is_array( $schema['items'] ) ) {
259-
$schema['items'] = $this->strip_internal_schema_keywords( $schema['items'] );
260-
}
261-
}
232+
unset( $schema['sanitize_callback'], $schema['validate_callback'], $schema['arg_options'] );
262233

263-
// Single sub-schema: additionalProperties (when not boolean).
264-
if ( isset( $schema['additionalProperties'] ) && is_array( $schema['additionalProperties'] ) ) {
265-
$schema['additionalProperties'] = $this->strip_internal_schema_keywords( $schema['additionalProperties'] );
266-
}
267-
268-
// Array-of-schemas keywords.
269-
foreach ( array( 'anyOf', 'oneOf' ) as $keyword ) {
270-
if ( isset( $schema[ $keyword ] ) && is_array( $schema[ $keyword ] ) ) {
271-
foreach ( $schema[ $keyword ] as $index => $sub_schema ) {
272-
if ( is_array( $sub_schema ) ) {
273-
$schema[ $keyword ][ $index ] = $this->strip_internal_schema_keywords( $sub_schema );
274-
}
275-
}
234+
foreach ( $schema as $key => $value ) {
235+
if ( is_array( $value ) ) {
236+
$schema[ $key ] = $this->strip_internal_schema_keywords( $value );
276237
}
277238
}
278239

0 commit comments

Comments
 (0)