Automattic / Automattic/jetpack-crm
Metabox data attributes are escaped as one string, so their values carry literal quotes
- Dominant language
- PHP
- Stars
- 13
- Forks
- 8
- Avg merge
- 1d 10h
- Merged PRs (30d)
- 13
Description
`zeroBSCRM_do_meta_box_html()` builds a string of `data-*` attributes and then runs `esc_attr()` over the whole string rather than over each value, at `includes/ZeroBSCRM.MetaBox.php:839`:
```php
echo '
```
`$dataAttrStr` is already markup by then, built a few lines up at `:826`:
```php
$dataAttrStr .= 'data-' . str_replace( '_', '-', $capKey ) . '="' . $capVStr . '"';
```
So every quote in it becomes `"`. What the renderer builds:
```
data-tab="zbs-contact-files-metabox"data-can-hide="1" data-areas="normal,side" data-can-accept-tabs="" ...
```
and what it echoes:
```
data-tab="zbs-contact-files-metabox"data-can-hide="1" data-areas="normal,side" ...
```
The browser reads those as unquoted attribute values, decodes the character references, and hands back values with literal quote characters in them: `data-areas` is `"normal,side"` rather than `normal,side`.
There is a second thing wrong on the same line. `$extraAttrs . $dataAttrStr` has no separator between the two, so on a tab pane `data-tab` and `data-can-hide` run together. An unquoted attribute value only ends at whitespace, so the parser folds the second attribute into the first one's value.
## What actually breaks
Less than you would think, and I want to be honest about which part is real.
The `data-can-*` and `data-areas` attributes are written and never read. I grepped `js/`, `includes/`, `admin/` and the root, and nothing anywhere consumes them. That half of this is inert, and only matters if someone later writes JS against them and can't work out why the values are wrong.
The one that looks live is `data-tab` on a tab pane, set at `:850`. Semantic UI's tab module is initialised on metabox tab groups at `js/ZeroBSCRM.admin.metabox.manager.js:101`, and it matches panes to menu items by `data-tab`. The menu item's own `data-tab` is escaped correctly, in `zeroBSCRM_do_meta_box_htmlTabHead()` at `:699`, so the two values don't agree: the menu item says `zbs-contact-files-metabox` and the pane says `"zbs-contact-files-metabox"data-can-hide="1"`. Tab groups are reachable, several metaboxes set `can_become_tab => true` (Contacts `:848`, `:1155`, `:2275`, Quotes `:976`), and they're formed by dragging one metabox onto another in the edit screen.
I have confirmed the PHP output exactly, by running the concatenation and `esc_attr()` through WP. I have **not** confirmed the broken-tab symptom in a browser, so treat that last paragraph as a strong reading of the code rather than a reproduction. Worth doing before anyone spends time on it.
## Fix
Escape values, not attribute strings. Something like: give `$dataAttrStr` and `$extraAttrs` an `esc_attr()` on each value where they are built, at `:826` and `:850`, and echo them unescaped with a `phpcs:ignore` saying so. Put the missing space back between the two while you're in there.
## Origin
Long-standing. The Feb 2026 formatting sweep (1285bad7, #46809) reindented the line but the `esc_attr($extraAttrs.$dataAttrStr)` shape predates it.
Found while working on #38.
Contributor guide
No contributing guide indexed for this repository
Research direction
Start in includes/ZeroBSCRM.MetaBox.php around lines 826, 839, and 850, then read js/ZeroBSCRM.admin.metabox.manager.js around line 101 to understand the live data-tab path. Verify the generated HTML and, if possible, tab behavior in the edit screen. Done means each data value is escaped correctly, the missing separator is restored, and the pane and menu data-tab values match.
Written by the indexing model from the issue text.
Assessment
- Tech stack
- php, wordpress
- Domain
- backend, web-dev
- Issue type
- Bug
- Difficulty
- 2/5
- Estimated time
- 1-3 hours
- Activity status
- Active
- Clarity
- Clearly specified
- Newbie friendliness
- 76/100