Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Footnotes: Fix recursion into updating attributes when attributes is not an object #53257

Merged
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
9 changes: 9 additions & 0 deletions packages/core-data/src/entity-provider.js
Original file line number Diff line number Diff line change
Expand Up @@ -221,6 +221,15 @@ export function useEntityBlockEditor( kind, name, { id: _id } = {} ) {
);

function updateAttributes( attributes ) {
// Only attempt to update attributes, if attributes is an object.
if (
! attributes ||
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Note: this falsy check is required because typeof null equals object so this catches if somehow this function is called with null.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I haven't tested this yet, but just staring at it makes me think it's a safe change regardless since we're destructuring attributes below.

Copy link
Contributor Author

@andrewserong andrewserong Aug 2, 2023

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yeah, one of the things that tripped me up when looking at this function is that when passed anything other than an object, the destructuring winds up converting (or attempting to convert) to an object.

Hopefully it's a fairly safe-ish change! The thing that took me the longest was testing it out in a custom block 😅

Thanks for taking a look!

Array.isArray( attributes ) ||
Copy link
Member

@ramonjd ramonjd Aug 2, 2023

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Will attributes ever be an Array?

Edit: okay, I just re-read the PR description. Ignore me.

typeof attributes !== 'object'
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I just had to double check for my own benefit: this could catch null but I think it'd be okay since { ...null } returns {}

👍🏻

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thanks for the extra checking!

The problem for null values is a little edge-casey, but an example would be if a block had an attribute that's a list of items where null is a valid value. For a somewhat contrived example, if we update the attributes in the test block's block.json to the following:

  "attributes": {
    "items": {
      "type": "array",
      "items": {
        "type": "string"
      },
      "default": [
        "apples",
        null,
        "oranges"
      ]
    }
  },

Then, if we remove the ! attributes check in this if block so that we don't handle null / falsy values, then when { ...null } turns the value into an empty object, the attributes are mutated.

Before / after:

image

In practice, I think it's fairly unlikely that there'll be blocks out in the wild affected by it, but good to catch null values here and return as-is just in case. I reckon a good next step will be to add some extra tests like Ella mentioned so that we're covering these sorts of edge cases 🙂

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thanks for taking the time to double check!

) {
return attributes;
}

attributes = { ...attributes };

for ( const key in attributes ) {
Expand Down