Skip to content

Add html_attr_relaxed escaping strategy#4743

Merged
fabpot merged 1 commit intotwigphp:3.xfrom
mpdude:escape-attr-relaxed
Feb 8, 2026
Merged

Add html_attr_relaxed escaping strategy#4743
fabpot merged 1 commit intotwigphp:3.xfrom
mpdude:escape-attr-relaxed

Conversation

@mpdude
Copy link
Contributor

@mpdude mpdude commented Jan 20, 2026

This adds html_attr_relaxed, a relaxed variant of the html_attr escaping strategy. The difference is that html_attr_relaxed does not escape the :, @, [ and ] characters. These are used by some front-end frameworks in attribute names to wire special handling/value binding. See https://v2.vuejs.org/v2/guide/syntax.html#v-bind-Shorthand for an example.

The HTML 5 spec does not exclude all those characters from attribute names (html.spec.whatwg.org/multipage/syntax.html#attributes-2).

However, at least XML processors will treat the colon as the XML namespace separator.

HTML 5 allows XML only on SVG and MathML elements, and only for pre-defined namespace-prefixes (developer.mozilla.org/en-US/docs/Web/API/Attr/localName#:~:text=That means that the local,different from the qualified name). For other something: prefixes, these will simply be passed on as part of the local attribute name.

According to engine.sygnal.com/research/html5-attribute-names, all current browser implementations handle at least the colon fine, and the aforementioned Vue.js documentation suggests that this is also the case for @.

Note also that Symfony UX only conditionally escapes attribute names, and it has : and @ in its safe list:
https://github.com/symfony/ux/blob/c9a3e66b8ac53e870097e8a828913e57204398e7/src/TwigComponent/src/ComponentAttributes.php#L82

Closes #3614.

@mpdude mpdude force-pushed the escape-attr-relaxed branch from 97bbe49 to e641a34 Compare January 20, 2026 10:30
@mpdude
Copy link
Contributor Author

mpdude commented Jan 30, 2026

@fabpot WDYT, would you endorse such an additional strategy?

@mpdude mpdude requested a review from stof February 1, 2026 15:02
if (\in_array($literal, $immune)) {
$this->assertEquals($literal, (new EscaperRuntime())->escape($literal, 'html_attr_relaxed'));
} else {
$this->assertNotEquals(
Copy link
Contributor

Choose a reason for hiding this comment

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

Should be on one line.


public function testHtmlAttributeRelaxedEscapingConvertsSpecialChars()
{
foreach ($this->htmlAttrSpecialChars as $key => $value) {
Copy link
Contributor

Choose a reason for hiding this comment

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

Shouldn't we test :, @, [, or ] as well here (they are not part of $this->htmlAttrSpecialChars)?

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Short: I don't think we need to.

I don't fully understand why the tests are written and organized the way they are. It seems that has been inherited from old Zend Framework Escaper tests.

testHtmlAttributeRelaxedEscapingConvertsSpecialChars and testHtmlAttributeEscapingConvertsSpecialChars use $htmlAttrSpecialChars to test for a few samples that

  • alnums are not escaped
  • a few "immune" chars (common to both html_attr and html_attr_relaxed) are not escaped
  • two examples beyond ASCII 0xFF are escaped
  • other examples like <>&" are being escaped.

In addition to that, we have testHtmlAttributeEscapingEscapesOwaspRecommendedRanges and testHtmlAttributeRelaxedEscapingEscapesOwaspRecommendedRanges. Those tests will fully iterate the ASCII 0x01 to 0xFF range and test every single character from it. The test expects escaping to happen for all cases except the 0-9, a-z, A-Z ranges and explicitly given whitelists of characters.

So, I think we're safe as-is.

@mpdude mpdude force-pushed the escape-attr-relaxed branch from 675cd42 to d06e902 Compare February 8, 2026 15:49
@mpdude mpdude requested a review from fabpot February 8, 2026 16:11
@mpdude
Copy link
Contributor Author

mpdude commented Feb 8, 2026

Feedback addressed. Thank you!

When this gets merged, I'd like to use it as the escaping strategy for attribute names in #3930.

@fabpot fabpot force-pushed the escape-attr-relaxed branch from d06e902 to 04aa3df Compare February 8, 2026 17:59
@fabpot
Copy link
Contributor

fabpot commented Feb 8, 2026

Thank you @mpdude.

@fabpot fabpot merged commit afe8e19 into twigphp:3.x Feb 8, 2026
8 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Development

Successfully merging this pull request may close these issues.

3 participants