Skip to content

Icons: Allow rect and circle elements in the icon registry sanitizer - #13539

Closed
t-hamano wants to merge 1 commit into
WordPress:trunkfrom
t-hamano:66112-icons-rect-circle-sanitizer
Closed

t-hamano wants to merge 1 commit into
WordPress:trunkfrom
t-hamano:66112-icons-rect-circle-sanitizer

Conversation

@t-hamano

@t-hamano t-hamano commented Sep 15, 2026 •

Copy link
Copy Markdown
Contributor

Backports Gutenberg PR #82846 to the Core icon registry.

A number of icons in the icon library are redrawn with the rect and circle elements, but WP_Icons_Registry::sanitize_icon_content() allows the svg, path and polygon tags only, so wp_kses() removes both elements before the icon is stored or returned. Icons registered by plugins and themes through wp_register_icon() are affected today, and the redrawn library icons will lose part of their shape once they are synced to Core.

Both new entries carry the presentation attributes the existing shapes already have, plus the geometry attributes each element needs.

Notes:

  • The stroke attributes are intentionally not part of this PR. Once #13512 lands for #66101, rect and circle need to carry them as well, since the two elements are drawn with strokes in the library today.
  • focusable is not included. It is meaningful on the svg root element only, and the entry on polygon is a leftover from the Twenty Twenty allowlist this list was copied from.

Trac ticket: https://core.trac.wordpress.org/ticket/66112

Use of AI Tools

AI assistance: Yes
Tool(s): Claude Code
Model(s): Claude Opus 5
Used for: Porting the Gutenberg change to its Core equivalent and drafting the unit tests. The result was reviewed and edited by me.


This Pull Request is for code review only. Please keep all other discussion in the Trac ticket. Do not merge this Pull Request. See GitHub Pull Requests for Code Review in the Core Handbook for more details.

@github-actions

Copy link
Copy Markdown

Test using WordPress Playground

The changes in this pull request can previewed and tested using a WordPress Playground instance.

WordPress Playground is an experimental project that creates a full WordPress instance entirely within the browser.

Some things to be aware of

  • All changes will be lost when closing a tab with a Playground instance.
  • All changes will be lost when refreshing the page.
  • A fresh instance is created each time the link below is clicked.
  • Every time this pull request is updated, a new ZIP file containing all changes is created. If changes are not reflected in the Playground instance,
    it's possible that the most recent build failed, or has not completed. Check the list of workflow runs to be sure.

For more details about these limitations and more, check out the Limitations page in the WordPress Playground documentation.

Test this pull request with WordPress Playground.

@t-hamano
t-hamano force-pushed the 66112-icons-rect-circle-sanitizer branch 2 times, most recently from 44ca689 to 14edf58 Compare September 15, 2026 14:05
A number of icons in the icon library are redrawn with the `rect` and
`circle` elements. `WP_Icons_Registry::sanitize_icon_content()` allows the
`svg`, `path` and `polygon` tags only, so `wp_kses()` removes both elements
before the icon is stored or returned.

This adds `rect` and `circle` to the allowlist, carrying the same
presentation attributes the existing shapes have along with the geometry
attributes each element needs.

Props wildworks, retrofox.
See #66101.
Fixes #66112.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
@t-hamano
t-hamano force-pushed the 66112-icons-rect-circle-sanitizer branch from 14edf58 to e3e6d1e Compare September 15, 2026 14:10
@t-hamano t-hamano closed this Sep 15, 2026
fabiankaegy added a commit to 10up/wp-scaffold that referenced this pull request Oct 14, 2026
## Why

WordPress 7.0/7.1 ship a core Icons API (`wp_register_icon()`,
`WP_Icons_Registry`). Its sanitizer,
`WP_Icons_Registry::sanitize_icon_content()`, runs `wp_kses` with a
fixed allowlist and no filter:

- `svg`: `class`, `xmlns`, `width`, `height`, `viewbox`, `aria-hidden`,
`role`, `focusable`
- `path`: `fill`, `fill-rule`, `d`, `transform`
- `polygon`: `fill`, `fill-rule`, `points`, `transform`, `focusable`

Everything else (`g`, `polyline`, `circle`, `rect`, `stroke*`,
`clip-rule`, `id`, `style`, …) is removed without a warning. The icon
still registers, but it renders empty or wrong. A Feather `check.svg`
comes back as an empty `<svg>`.

Upstream is widening the list in WordPress/wordpress-develop#12197 and
WordPress/wordpress-develop#13539, but neither has merged, so this check
matches what ships today.

## Scope: a lint, not a sanitizer

This checks **rendering fidelity** of icons committed to a theme:
trusted code, reviewed like any PHP file. It answers "will core's
sanitizer leave this icon intact?", not "is this SVG safe?". The
tokenizer is deliberately simple because it never decides what reaches a
page. Core's `wp_kses` pass on registration still does that. Nothing
here accepts SVG from users. A feature that does (an admin icon upload,
say) needs its own sanitizer that rebuilds the markup from an allowlist
rather than filtering it.

## What

- `bin/lint-svg.mjs`: a zero-dependency Node script. It reports
disallowed elements (with counts) and disallowed attributes per element,
comparing names case-insensitively as kses does, and adds fix hints for
strokes, shapes and `<g>` wrappers. XML declarations, doctypes and
comments are ignored. It exits 1 on any problem. The allowlist sits in
one commented constant to update as core widens it.
- `npm run lint-svg`: with no arguments it scans `themes/*/assets/svg`
and `mu-plugins/*/assets/svg`. It also takes files, folders or globs.
- lint-staged runs it on staged `**/assets/svg/**/*.svg` files.
- A `lint-svg` job in `.github/workflows/node.yml`. It needs Node only,
no install.
- Docs: a new "SVG Icons" section in
`docs/frontend-development/frontend-build-process.md`, and the
pre-commit list in `docs/testing-quality/code-quality.md`.

## How to test

```bash
npm run lint-svg                        # passes: the svg folders are empty
npm run lint-svg -- path/to/icons       # point it at a folder of Feather icons to see failures
```

Example output for a Feather icon:

```
feather-check.svg
  ✖ element <polyline> is not allowed (1×)
  ✖ <svg> attributes not allowed: fill, stroke, stroke-width, stroke-linecap, stroke-linejoin
  → stroke attributes are stripped by core — outline strokes in your design tool (Figma: Outline stroke) before exporting
  → convert shapes to paths (e.g. SVGO convertShapeToPath)
```
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant