Automattic / Automattic/jetpack-crm

Metabox data attributes are escaped as one string, so their values carry literal quotes

Open Beginner friendly
#40 0 comments 0 reactions 0 assignees View on GitHub
needs-triage
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

Get new issues in your inbox

A short digest of beginner-friendly GitHub issues.