Skip to content

Commit

Permalink
Interactivity API: Prevent non-object state from being added (#59886)
Browse files Browse the repository at this point in the history
Ensure that `state` is an object when creating a store for a given namespace.

`store.state` is expected to be an object. There is code that prevents state from being updated if it is not an object, but it's possible to set the state to a non-object initially, which breaks subsequent updates.

Co-authored-by: sirreal <[email protected]>
Co-authored-by: DAreRodz <[email protected]>
  • Loading branch information
3 people authored and getdave committed Mar 18, 2024
1 parent 37a8390 commit 50c9d80
Show file tree
Hide file tree
Showing 5 changed files with 22 additions and 7 deletions.
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
<?php
/**
* HTML for testing the directive `data-wp-bind`.
* HTML for testing the `store` function.
*
* @package gutenberg-test-interactive-blocks
*/
Expand All @@ -9,6 +9,7 @@
?>

<div data-wp-interactive="test/store">
<div data-wp-text="state.0" data-testid="state-0"></div>
<div
data-testid="non-plain object"
data-wp-text="state.isNotProxified"
Expand Down
11 changes: 7 additions & 4 deletions packages/e2e-tests/plugins/interactive-blocks/store/view.js
Original file line number Diff line number Diff line change
Expand Up @@ -3,18 +3,21 @@
*/
import { store, getElement } from '@wordpress/interactivity';

// A non-object state should never be allowed.
store( 'test/store', { state: [ 'wrong' ] } );

const { state } = store( 'test/store', {
state: {
0: 'right',
get isNotProxified() {
const { ref } = getElement();
return state.elementRef === ref;
}
},
},
callbacks: {
init() {
const { ref } = getElement();
state.elementRef = ref; // HTMLElement
}
}
} )
},
},
} );
1 change: 1 addition & 0 deletions packages/interactivity/CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@

### Bug Fixes

- Prevent non-objects from being set in store state. ([#59886](https://github.com/WordPress/gutenberg/pull/59886))
- Ensure that stores are available for subscription before hydration. ([#59842](https://github.com/WordPress/gutenberg/pull/59842))
- Ensure scope is restored when catching exceptions thrown in async generator actions. ([#59708](https://github.com/WordPress/gutenberg/pull/59708))

Expand Down
5 changes: 4 additions & 1 deletion packages/interactivity/src/store.ts
Original file line number Diff line number Diff line change
Expand Up @@ -274,7 +274,10 @@ export function store(
if ( lock !== universalUnlock ) {
storeLocks.set( namespace, lock );
}
const rawStore = { state: deepSignal( state ), ...block };
const rawStore = {
state: deepSignal( isObject( state ) ? state : {} ),
...block,
};
const proxiedStore = new Proxy( rawStore, handlers );
rawStores.set( namespace, rawStore );
stores.set( namespace, proxiedStore );
Expand Down
9 changes: 8 additions & 1 deletion test/e2e/specs/interactivity/store.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@
*/
import { test, expect } from './fixtures';

test.describe( 'data-wp-bind', () => {
test.describe( 'store', () => {
test.beforeAll( async ( { interactivityUtils: utils } ) => {
await utils.activatePlugins();
await utils.addPostWithBlock( 'test/store' );
Expand All @@ -22,4 +22,11 @@ test.describe( 'data-wp-bind', () => {
const el = page.getByTestId( 'non-plain object' );
await expect( el ).toHaveText( 'true' );
} );

test( 'Ensures that state cannot be set to a non-object', async ( {
page,
} ) => {
const element = page.getByTestId( 'state-0' );
await expect( element ).toHaveText( 'right' );
} );
} );

0 comments on commit 50c9d80

Please sign in to comment.