Skip to content

fix(Helpers): escape attributes and unsupported messages in HTML media tags - #10765

Open
gr8man wants to merge 4 commits into
codeigniter4:developfrom
gr8man:fix/html-helper-media-escaping
Open

gr8man wants to merge 4 commits into
codeigniter4:developfrom
gr8man:fix/html-helper-media-escaping

Conversation

@gr8man

@gr8man gr8man commented Nov 11, 2026 •

Copy link
Copy Markdown
Contributor

Description

The video(), audio(), _media(), source(), track(), object(),
param() and embed() helpers concatenated their arguments directly into
double-quoted HTML attributes. Any value containing a double quote closed
the attribute it was placed in, so caller-controlled data - a src, type,
kind, label, srclang, name or value coming from a database column
or a request parameter - could inject arbitrary attributes such as onerror
into the generated tag.

Attribute values now go through the existing esc($value, 'html') helper,
which applies htmlspecialchars() with ENT_QUOTES | ENT_SUBSTITUTE. Both
quote styles, <, > and & are encoded while ordinary URLs stay
readable, and invalid UTF-8 is substituted instead of emptying the value.

Values are escaped exactly once: the array branch of video()/audio()
reaches source(), which escapes its own arguments before _media()
concatenates them.

The $unsupportedMessage, $attributes, $tracks and $params parameters
are intentionally left as raw HTML. $unsupportedMessage therefore keeps
supporting fallback markup such as download links, and callers are
responsible for not passing unescaped user input in these parameters. The
user guide now documents this behaviour with a MkDocs note.

Output for input without special characters is unchanged, which the
existing tests assert. Regression tests cover every affected helper,
including single escaping of &.

Checklist:

  • Securely signed commits
  • Component(s) with PHPDoc blocks, only if necessary or adds value (without duplication)
  • Unit testing, with >80% coverage
  • User guide updated
  • Conforms to style guide

@carson-codeigniter4 carson-codeigniter4 Bot added the bug Verified issues on the current code behavior or pull requests that will fix them label Nov 11, 2026
Comment thread system/Helpers/html_helper.php Resolved
Comment thread system/Helpers/html_helper.php Outdated
Comment on lines +560 to +568
if (! function_exists('_esc_attr')) {
/**
* Escapes a value for use inside a double-quoted HTML attribute.
*/
function _esc_attr(string $value): string
{
return htmlspecialchars($value, ENT_QUOTES | ENT_SUBSTITUTE, 'UTF-8');
}
}

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

What is the reason for creating this function and not using esc($value, 'html')?

…a tags

The `video()`, `audio()`, `_media()`, `source()`, `track()`, `object()`,
`param()` and `embed()` helpers concatenated their arguments straight into
double-quoted HTML attributes. A value containing a double quote - a `src`,
`type`, `kind`, `label` or `name` that comes from a database column or a
request parameter, for instance - could therefore close the attribute and
inject arbitrary ones, such as `onerror`, into the generated markup.

Attribute values now go through a new `_esc_attr()` helper that applies
`htmlspecialchars()` with `ENT_QUOTES | ENT_SUBSTITUTE`, so both quote
styles, `<`, `>` and `&` are encoded while ordinary URLs stay readable.
`ENT_SUBSTITUTE` keeps invalid UTF-8 from turning the whole value into an
empty string.

The `$unsupportedMessage` fallback text of `video()`, `audio()` and
`_media()` was emitted verbatim as well, so markup passed as the fallback
message was rendered as HTML. It is now escaped with
`esc($message, 'html')` and displayed as plain text.

Regression tests cover each affected helper, including the alternative
`ENT_SUBSTITUTE` behaviour.
@gr8man
gr8man force-pushed the fix/html-helper-media-escaping branch from 675995f to 3db4058 Compare November 13, 2026 20:45
Comment thread docs/helpers/html_helper.md Outdated
Comment on lines +403 to +404
> [!NOTE]
> Attribute values generated by media helper functions (`src`, `type`, `kind`, `srclang`, `label`, `name`, `value`, `data`) are automatically escaped. The `$unsupportedMessage` parameter in `video()` and `audio()` accepts raw HTML (such as fallback links).

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

This will not render correctly on MkDocs. Please use the note admonition syntax instead set by contributing/documentation.md.

GitHub-only alert `> [!NOTE]` does not render in MkDocs; use the
admonition syntax from contributing/documentation.md, and document that
`$unsupportedMessage`, `$attributes`, `$tracks` and `$params` are
inserted as raw HTML.
Cover `&` encoding through `video()` and through
`video()` -> `source()` -> `_media()`, so a missing or a duplicated
`esc()` fails the test.

This branch has not been deployed

No deployments
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

bug Verified issues on the current code behavior or pull requests that will fix them

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants