Widen front component crossing attributes to aria-*, data-* and draggable (#22614)
Only a closed allow-list of props crossed the front-component worker→host boundary (`id, className, style, title, tabIndex, role, aria-label, aria-hidden, data-testid`), so arbitrary `aria-*`/`data-*` attributes and `draggable` never reached the host DOM. That breaks headless UI libraries (Radix, cmdk, react-aria) that drive styling/state through those attributes. This widens the crossing set to all `aria-*`, all `data-*`, and `draggable`: - `draggable` becomes an enumerated remote property (it's a DOM IDL property React may set as a property, bypassing `setAttribute`, so it can't ride the prefix path). - Arbitrary `aria-*`/`data-*` are forwarded in the worker by patching `setAttribute`/`removeAttribute` through remote-dom's attribute channel, only for names not already synced as observed attributes. Security: only inert `aria-*`/`data-*`/`draggable` cross, and they still route through the host `filterProps` guards (non-function `on*` dropped, `javascript:` URLs denied) — nothing bypasses them. The enumerated `aria-label`/`aria-hidden`/`data-testid` keep their existing path. <!-- This is an auto-generated description by cubic. --> <a href="https://cubic.dev/pr/twentyhq/twenty/pull/22614?utm_source=github" target="_blank" rel="noopener noreferrer" data-no-image-dialog="true"><picture><source media="(prefers-color-scheme: dark)" srcset="https://www.cubic.dev/buttons/review-in-cubic-dark.svg"><source media="(prefers-color-scheme: light)" srcset="https://www.cubic.dev/buttons/review-in-cubic-light.svg"><img alt="Review in cubic" src="https://www.cubic.dev/buttons/review-in-cubic-dark.svg"></picture></a> <!-- End of auto-generated description by cubic. -->
This commit is contained in:
@@ -60,4 +60,33 @@ describe('filterProps', () => {
|
||||
|
||||
expect(result.href).toBe('https://twenty.com');
|
||||
});
|
||||
|
||||
it('should forward arbitrary aria-* and data-* attributes', () => {
|
||||
const result = filter(
|
||||
{
|
||||
'aria-selected': 'true',
|
||||
'aria-activedescendant': 'item-2',
|
||||
'data-state': 'open',
|
||||
'data-count': '3',
|
||||
},
|
||||
'div',
|
||||
);
|
||||
|
||||
expect(result['aria-selected']).toBe('true');
|
||||
expect(result['aria-activedescendant']).toBe('item-2');
|
||||
expect(result['data-state']).toBe('open');
|
||||
expect(result['data-count']).toBe('3');
|
||||
});
|
||||
|
||||
it('should forward the draggable attribute', () => {
|
||||
expect(filter({ draggable: 'true' }, 'div').draggable).toBe('true');
|
||||
expect(filter({ draggable: true }, 'div').draggable).toBe(true);
|
||||
});
|
||||
|
||||
it('should still drop a non-function on* handler smuggled as a data-adjacent prop', () => {
|
||||
const result = filter({ onClick: 'alert(1)', 'data-state': 'open' }, 'div');
|
||||
|
||||
expect('onClick' in result).toBe(false);
|
||||
expect(result['data-state']).toBe('open');
|
||||
});
|
||||
});
|
||||
|
||||
Reference in New Issue
Block a user