[wp-trac] [WordPress Trac] #56494: REST API: Fix and test rest_default_additional_properties_to_false
WordPress Trac
noreply at wordpress.org
Fri Sep 30 08:18:49 UTC 2022
#56494: REST API: Fix and test rest_default_additional_properties_to_false
--------------------------------------+------------------------------
Reporter: anna.bansaghi | Owner: (none)
Type: defect (bug) | Status: new
Priority: normal | Milestone: Awaiting Review
Component: REST API | Version: 6.0.2
Severity: normal | Resolution:
Keywords: has-patch has-unit-tests | Focuses:
--------------------------------------+------------------------------
Comment (by anna.bansaghi):
Hi @TimothyBJacobs, thanks for the review, and raising the need of
discussion of passing the `$types` parameter.
I was thinking about it and experimenting with the function a lot, and
finally I came up with two possible solutions.
It became a bit long write-up, but it helped me to organize the
information around MR Trees for this function.
= Insignificant changes
At the beginning I would like to mention the least important changes:
- moved the `array` section before the `object`
- that moved the "Task" at the very end of the function
Now the code can be split into 3 parts from "bigger" to "smaller" data
structure:
- recursive call on forest
- recursive call on tree
- performing the "Task" on the current node
= Interesting changes
The schema traversal can be implemented in two ways (hence the two
solutions), depending on how we define the schema being "object".
==== Object definition
The schema is an object if:
- has `type ⊃ 'object'`
Or we can say more permissive that if:
- has `type ⊃ 'object'`, or
- has `properties` keyword set, or
- has `patternProperties` keyword set, or
- has `additionalProperties` keyword set with `array` value
Similarly a schema is an array if:
- has `type ⊃ 'array'`, or
- has `items` keyword set (not supporting the `tuple` form)
==== "Task" to be performed
In this sense the "Task" can be implemented in two ways:
{{{#!php
<?php
if ( ! isset( $schema['additionalProperties'] ) && in_array( 'object',
$types, true ) ) {
$schema['additionalProperties'] = false;
}
}}}
or
{{{#!php
<?php
if ( ! isset( $schema['additionalProperties'] ) &&
( in_array( 'object', $types, true ) ||
isset( $schema['properties'] ) ||
isset( $schema['patternProperties'] ) ) ) {
$schema['additionalProperties'] = false;
}
}}}
==== Solution 1
It is more similar to the original function, uses the first object
definition, and the first "Task" will be performed.
[https://github.com/WordPress/wordpress-develop/pull/3170]
The schema traversal might stop at some nodes, and leave sub-trees
unvisited without performing the "Task", because the nodes are checked
against the `type`, and are discarded from further traversal if they do
not have, or have wrong `type`.
==== Solution 2
It uses the more liberal second object definition, and the second "Task"
will be performed.
[https://github.com/WordPress/wordpress-develop/pull/3374]
The schema will be fully traversed, the "Task" will be performed on all
nodes, because there are no guards in the "Forest part" nor in the "Tree
part".
= Solution 1
This implementation is very similar to the original one, in which we
assume that we work on valid schema.
For this function the schema is valid if:
- has `type`
- has correct `type` (if it is `array` then it has `items`, and if it is
`object` then it has some properties).
**If the schema is invalid, then**
==== 1. Throw notice & discard schema from further traversal
In the **original implementation** if the schema had no `type`, then the
schema was discarded from further traversal because of the first line
`$type = (array) $schema['type'];`, and a run-time notice was thrown: `PHP
Notice: Undefined index: type in /home/anna/html/wp60/wp-includes/rest-
api.php on line 3041`
In the **proposed implementation** I replaced the built-in notice with the
`_doing_it_wrong()` notice.
==== 2. Discard schema from further traversal
In the **original implementation** if the schema had no correct `type`,
then the schema was discarded from further traversal because of these
guards:
{{{#!php
<?php
if ( in_array( 'object', $type, true ) ) {
...
}
}}}
{{{#!php
<?php
if ( in_array( 'array', $type, true ) ) {
...
}
}}}
This remained as is in the **proposed implementation**.
==== 3. Skip performing "Task" on schema
In the **original implementation** if the schema had no `type ⊃ 'object'`
then the "Task" was skipped. For example the "Task" was not performed on
this schema:
{{{#!php
<?php
[
'properties' => [...]
]
}}}
The "Task" remained as is in the **proposed implementation**.
= Solution 2
While in Solution 1 the schema might have sub-trees unvisited because of a
missing or wrong `type`, in this solution the traversal is full. The
"Task" will be performed on all nodes of the schema.
The test cases have some minor differences, but they seem to be
progressive rather than regressive.
**I came up with this solution because I think**
==== Validation
Both solutions are silent about a wrong `type` keyword, but they handle
this situation differently (discarding vs. keeping nodes). This function
in general should not be responsible for validation, it is done in another
function where the schema is validated against the value, and notices will
be thrown.
==== Sanitization
Looking at the `rest-api.php` code it seems to me that we do not sanitize
the schema (well, besides this very function where we set the
`additionalProperties` to `false`), and discarding nodes from performing
the "Task" on them seems like a (silent to the user) sanitization.
==== Completeness
If we assume that this function actually should have control over some
aspects of validation or sanitization, then I would expect to control all
aspects.
For example schema mistakes like these are also remain silent, and we just
do not know the reason why some parts of the schema is unvisited:
{{{#!php
<?php
[
'type' => 'array',
'properties' => [...]
]
}}}
or the reverse:
{{{#!php
<?php
[
'type' => 'object',
'items' => [...]
]
}}}
After some experiments around building a fully featured validation and/or
sanitization into this function, I chose the other way: let's expand the
object definition instead, and there is no reason to think about
validation nor sanitization anymore in this function.
But I am not aware of all aspects of these changes, and would like to
understand if the first solution is desired.
-----
If the first solution is the correct one, then it might be worth to
continue reading about how supporting `anyOf`, `allOf`, `noneOf` changes
the definition of a valid schema.
Now having base `type` on `anyOf`, `allOf`, `oneOf` schemas allows not
having `type` on children. But we need to perform the "Task" on children
too, so they have to have `type`. And that is when and why we pass the
`$types` in the "Forest part": the children will inherit the `type` from
their parent.
--
Ticket URL: <https://core.trac.wordpress.org/ticket/56494#comment:4>
WordPress Trac <https://core.trac.wordpress.org/>
WordPress publishing platform
More information about the wp-trac
mailing list