Skip to content

fix(get-selector): escape control characters in attribute selectors - #5273

Merged
straker merged 1 commit into
developfrom
chut/5204-escape-attr-control-chars
Aug 4, 2026
Merged

fix(get-selector): escape control characters in attribute selectors#5273
straker merged 1 commit into
developfrom
chut/5204-escape-attr-control-chars

Conversation

@chutchins25

Copy link
Copy Markdown
Contributor

Fixes #5204. When an element's attribute value contains a C0 control character (observed: form feed U+000C), generateSelector emitted it raw into an attribute selector. In CSS, form feed is a string-terminating newline, so img[alt=" \f¼"] is invalid, Element.matches throws inside getNthChildString, and the entire axe.run() fails — one bad attribute value on a page kills the whole audit.

Fix

escapeAttribute (in lib/core/utils/get-selector.js) previously escaped only \, ", and CR/LF newlines — missing form feed and every other control character. It now escapes all C0 control characters (U+0000–U+001F) and DEL (U+007F) as CSS numeric escapes (\<hex> ), which subsumes the existing newline handling (U+000A\a , U+000C\c ). This matches the issue's minimal proof that CSS.escape('\f') produces a valid selector.

Compatibility

No behavior change for existing cases: newline (U+000A) still escapes to \a . CR and CRLF now escape to \d /\d \a (previously both collapsed to \a ) — more correct round-tripping, and not covered by any existing test. All existing get-selector escape tests still pass.

Test

test/core/utils/get-selector.js — an attribute value containing form feed, vertical tab, and U+001F now produces a valid selector that both matches the expected escaped string and passes matchesSelector (which threw on the pre-fix code).

Confirmed by @straker on the issue.

Closes #5204

Copilot AI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Pull request overview

This PR hardens selector generation (axe.utils.getSelector) so attribute values containing C0 control characters (U+0000–U+001F) or DEL (U+007F) are escaped as CSS numeric escapes, preventing invalid selectors that can throw inside Element.matches and abort an axe.run().

Changes:

  • Update escapeAttribute to escape all C0 control characters + DEL using CSS numeric escapes (\<hex> ).
  • Coerce the attribute value via String(str) before escaping to ensure consistent string handling.
  • Add a regression test covering control characters (form feed, vertical tab, U+001F) in attribute selectors.

Reviewed changes

Copilot reviewed 2 out of 2 changed files in this pull request and generated 1 comment.

File Description
lib/core/utils/get-selector.js Expands attribute escaping to cover all control characters that would otherwise break CSS selector parsing.
test/core/utils/get-selector.js Adds a regression test to ensure generated selectors remain valid and usable with matchesSelector.

💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.

Comment thread test/core/utils/get-selector.js
@chutchins25
chutchins25 force-pushed the chut/5204-escape-attr-control-chars branch from db11901 to 4607470 Compare July 31, 2026 13:19
@chutchins25
chutchins25 marked this pull request as ready for review July 31, 2026 13:25
@chutchins25
chutchins25 requested a review from a team as a code owner July 31, 2026 13:25
@chutchins25
chutchins25 requested a review from straker July 31, 2026 13:25
Comment thread lib/core/utils/get-selector.js Outdated
Attribute values containing a C0 control character (e.g. form feed
U+000C, a CSS newline) produced an invalid selector, so Element.matches
threw and the entire axe.run failed. Escape all control characters as
CSS numeric escapes.

Closes #5204
@chutchins25
chutchins25 force-pushed the chut/5204-escape-attr-control-chars branch from 4607470 to 4b4b568 Compare August 3, 2026 21:03
@straker
straker merged commit 4b60ac5 into develop Aug 4, 2026
23 checks passed
@straker
straker deleted the chut/5204-escape-attr-control-chars branch August 4, 2026 15:00
WilcoFiers added a commit that referenced this pull request Aug 5, 2026
All notable changes to this project will be documented in this file. See
[commit-and-tag-version](https://github.com/absolute-version/commit-and-tag-version)
for commit guidelines.

##
[4.13.0](v4.12.1...v4.13.0)
(2026-08-05)

### Features

- **aria-actions:** add aria-actions to allowed ARIA attributes
([#5200](#5200))
([029655d](029655d)),
closes [#4584](#4584)
[#5199](#5199), references
[#5215](#5215)
[#5215](#5215)
- **aria-allowed-attr:** flag deprecated ARIA attributes as needs-review
([#5246](#5246))
([518f3cc](518f3cc)),
closes [#3341](#3341)
- **aria-prohibited-attr:** allow many elements to be named and disallow
label and body from being named
([#5259](#5259))
([d8b1ea5](d8b1ea5))
- **aria-roles:** add sectionheader and sectionfooter roles
([#5238](#5238))
([c36c109](c36c109)),
closes [#4734](#4734),
references [#4734](#4734)
- **aria/get-aria-value:** new function to get aria values of a node
([#5109](#5109))
([a7d8f3e](a7d8f3e)),
references [#5042](#5042)
- **aria/has-attr-value:** new function to check if node has aria value
([#5136](#5136))
([61f2624](61f2624)),
references [#5109](#5109)
- **aria:** support role=image as equivalent to role=img
([#5248](#5248))
([5aa8aaf](5aa8aaf)),
closes [#4656](#4656),
references [#5272](#5272)
- **checks/aria:** support ARIA element internals properties
([#5172](#5172))
([9b7f754](9b7f754))
- **checks/label:** support ARIA element internals properties
([#5170](#5170))
([21c5f8b](21c5f8b))
- **checks/navigation:** support ARIA element internals properties
([#5167](#5167))
([2c3a98f](2c3a98f))
- **commons/aria:** support ARIA element internals properties
([#5171](#5171))
([31f09e7](31f09e7))
- **commons/dom:** support ARIA element internals properties
([#5163](#5163))
([f0a12cf](f0a12cf))
- **commons/forms:** support ARIA element internals properties
([#5165](#5165))
([27a4686](27a4686))
- **commons/matches/fromPrimative:** deprecate in favor of correct
spelling ([#5270](#5270))
([31cfb2e](31cfb2e))
- **commons/text:** support ARIA element internals properties
([#5169](#5169))
([e841a33](e841a33))
- **commons/text:** support form-associated labels via element internals
([#5182](#5182))
([57cfe0a](57cfe0a)),
closes [#5045](#5045),
references [#5170](#5170)
[#5039](#5039)
[#5151](#5151)
[#5039](#5039)
- **dom/getResolvedRefs:** new function to get the resolved virtual
nodes of idrefs
([#5151](#5151))
([489cdea](489cdea)),
references [#5109](#5109)
- **element-internals:** enable ElementInternals by default
([#5284](#5284))
([2740d42](2740d42)),
closes [#5277](#5277)
- **i18n:** Add Swedish locale
([#5190](#5190))
([dcd13f2](dcd13f2)),
references [#5189](#5189)
- **matches:** add inSectioningContent, hasChild, and
isSummaryForDetails matches
([#5262](#5262))
([c47cdcd](c47cdcd))
- **rules:** support ARIA element internals properties
([#5168](#5168))
([065baf7](065baf7))
- **standards/ariaAttrs:** add caseInsensitive property for attributes
([#5224](#5224))
([bcd791c](bcd791c))

### Bug Fixes

- **aria-allowed-role:** allow roles on a non-details summary
([#5242](#5242))
([3bd9875](3bd9875)),
closes [#3911](#3911),
references [#3443](#3443)
[#3911](#3911)
- **aria-allowed-role:** restrict figure roles with child figcaption
([#5240](#5240))
([178a635](178a635)),
closes [#3443](#3443)
- **aria-prohibited-attr:** visible aria-labelledby requires review only
([#5285](#5285))
([fd6fa9f](fd6fa9f))
- **axe.d.ts:** make enabled property of RuleMetadata optional
([#5129](#5129))
([90fce18](90fce18))
- **color-contrast:** fix various stacking context bugs
([#5214](#5214))
([d5e5b04](d5e5b04)),
references [#8](#8)
[#5213](#5213)
- **gather-internals:** handle non-HTMLElement nodes
([#5161](#5161))
([06e84c3](06e84c3))
- **get-selector:** escape control characters in attribute selectors
([#5273](#5273))
([4b60ac5](4b60ac5)),
closes [#5204](#5204)
[#5204](#5204)
- **image-alt:** allow whitespace alt on presentational images
([#5218](#5218))
([c5dd0ef](c5dd0ef)),
closes [#5216](#5216)
- **landmark-unique:** exclude section/form with non-landmark roles from
landmark match
([#5085](#5085))
([c5fd013](c5fd013)),
closes [#4722](#4722)
[#5064](#5064)
- name the image role in role-img-alt and svg-img-alt metadata
([#5279](#5279))
([995a269](995a269)),
closes [#5272](#5272),
references [#5248](#5248)
[#5248](#5248)
- **standards:** update aria-errormessage and aria-details to be idrefs
([#5157](#5157))
([fb94f8a](fb94f8a))

This PR was opened by a robot 🤖 🎉
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

3 participants