'
+ ].join('')
+
+ const navEl = fixtureEl.querySelector('[data-bs-toggle="nav-overflow"]')
+ const navOverflow = new NavOverflow(navEl)
+
+ const iconContainer = navEl.querySelector('.nav-overflow-icon')
+ expect(iconContainer.querySelector('img')).toBeNull()
+ expect(iconContainer.innerHTML).not.toMatch(/onerror/i)
+ expect(iconContainer.querySelector('i.bi-ok')).not.toBeNull()
+ expect(window.__navCustomIconXss).toBeUndefined()
+
+ navOverflow.dispose()
+ })
})
describe('collapseBelow', () => {
diff --git a/js/tests/unit/util/sanitizer.spec.js b/js/tests/unit/util/sanitizer.spec.js
index ccf5c5cf6dd4..41bbb1cd7aba 100644
--- a/js/tests/unit/util/sanitizer.spec.js
+++ b/js/tests/unit/util/sanitizer.spec.js
@@ -1,4 +1,4 @@
-import { DefaultAllowlist, sanitizeHtml } from '../../../src/util/sanitizer.js'
+import { DefaultAllowlist, DefaultIconAllowlist, sanitizeHtml } from '../../../src/util/sanitizer.js'
describe('Sanitizer', () => {
describe('sanitizeHtml', () => {
@@ -165,5 +165,28 @@ describe('Sanitizer', () => {
expect(firstResult).toContain('src')
expect(secondResult).toContain('src')
})
+
+ it('should keep safe SVG icon markup with DefaultIconAllowlist', () => {
+ const template = ''
+
+ const result = sanitizeHtml(template, DefaultIconAllowlist, null)
+
+ expect(result).toContain(''` | SVG or HTML icon for the overflow toggle button. Overridden by a child element with `data-bs-overflow-icon` if present. |
+| `moreText` | string | `'More'` | Text label for the overflow toggle button. Inserted as plain text. |
+| `moreIcon` | string | `'...'` | SVG or HTML icon for the overflow toggle button. Passed through the [icon content sanitizer]([[docsref:/getting-started/javascript#sanitizer]]) before insertion. Overridden by a child element with `data-bs-overflow-icon` if present (also sanitized). |
| `threshold` | number | `0` | Minimum number of items to keep visible before showing overflow. |
diff --git a/site/src/content/docs/forms/chips.mdx b/site/src/content/docs/forms/chips.mdx
index e9648d93d55b..e6e56972985f 100644
--- a/site/src/content/docs/forms/chips.mdx
+++ b/site/src/content/docs/forms/chips.mdx
@@ -198,7 +198,7 @@ Options can be passed via data attributes or JavaScript:
| `maxChips` | number \| null | `null` | Maximum number of chips allowed. `null` for unlimited. |
| `placeholder` | string | `''` | Placeholder text for dynamically created inputs. |
| `dismissible` | boolean | `true` | Add dismiss buttons to created chips. |
-| `dismissIcon` | string | `'...'` | HTML string for the dismiss button icon. |
+| `dismissIcon` | string | `'...'` | HTML string for the dismiss button icon. Passed through the [icon content sanitizer]([[docsref:/getting-started/javascript#sanitizer]]) before insertion. |
| `createOnBlur` | boolean | `true` | Create chip from input value when the input loses focus. |
diff --git a/site/src/content/docs/getting-started/javascript.mdx b/site/src/content/docs/getting-started/javascript.mdx
index d39b92f7011f..b055f5f59b1e 100644
--- a/site/src/content/docs/getting-started/javascript.mdx
+++ b/site/src/content/docs/getting-started/javascript.mdx
@@ -307,12 +307,17 @@ Every Bootstrap plugin exposes the following methods and static properties.
## Sanitizer
Our tooltip and popover components are able to render arbitrary HTML to the page if configured to do so.
+Chips and NavOverflow also accept HTML for their icon options (`dismissIcon`, `moreIcon`, and markup from `[data-bs-overflow-icon]`).
To prevent cross-site scripting (XSS) attacks, these components use our built-in content sanitizer to sanitize any options which accept HTML before they are rendered to the page. Content sanitization is enabled by default.
-The tags and attributes allowed by default are as follows. Any tags or attributes not explicitly allowed will be removed during sanitization:
+The tags and attributes allowed by default for tooltip and popover content are as follows. Any tags or attributes not explicitly allowed will be removed during sanitization:
+Icon options use a separate, tighter allowlist that covers the default SVG icons and common inline-icon markup:
+
+
+
**Exercise caution when using these advanced options.** Refer to [OWASP’s Cross Site Scripting Prevention Cheat Sheet](https://cheatsheetseries.owasp.org/cheatsheets/Cross_Site_Scripting_Prevention_Cheat_Sheet.html) for more information. Vulnerabilities caused solely by disabling or modifying content sanitization are not considered within scope for Bootstrap’s security model.
From 43dd7cf82fe44d4a148f9a3f5d83c5bbcd2010dd Mon Sep 17 00:00:00 2001
From: Aljo Joby <141745680+aljojoby9@users.noreply.github.com>
Date: Fri, 7 Aug 2026 00:48:07 +0530
Subject: [PATCH 2/5] Raise the minified JS budgets by a quarter kilobyte
The icon allowlist and sanitizer calls in Chips and NavOverflow push the
gzipped min builds a few dozen bytes over the previous caps.
---
.bundlewatch.config.json | 4 ++--
1 file changed, 2 insertions(+), 2 deletions(-)
diff --git a/.bundlewatch.config.json b/.bundlewatch.config.json
index 82ebf2677d03..4befec87e020 100644
--- a/.bundlewatch.config.json
+++ b/.bundlewatch.config.json
@@ -38,7 +38,7 @@
},
{
"path": "./dist/js/bootstrap.bundle.min.js",
- "maxSize": "55.0 kB"
+ "maxSize": "55.25 kB"
},
{
"path": "./dist/js/bootstrap.js",
@@ -46,7 +46,7 @@
},
{
"path": "./dist/js/bootstrap.min.js",
- "maxSize": "33.0 kB"
+ "maxSize": "33.25 kB"
}
],
"ci": {
From d8861db695724eedc86655c82f782fbdee2b90f2 Mon Sep 17 00:00:00 2001
From: Mark Otto
Date: Mon, 10 Aug 2026 11:04:09 -0700
Subject: [PATCH 3/5] Update sanitizer section for clarity on HTML rendering
Clarified the explanation of components that can render or accept HTML, emphasizing the use of the built-in content sanitizer to prevent XSS attacks.
---
site/src/content/docs/getting-started/javascript.mdx | 7 ++++---
1 file changed, 4 insertions(+), 3 deletions(-)
diff --git a/site/src/content/docs/getting-started/javascript.mdx b/site/src/content/docs/getting-started/javascript.mdx
index b055f5f59b1e..ed1bbdc7bc4a 100644
--- a/site/src/content/docs/getting-started/javascript.mdx
+++ b/site/src/content/docs/getting-started/javascript.mdx
@@ -306,9 +306,10 @@ Every Bootstrap plugin exposes the following methods and static properties.
## Sanitizer
-Our tooltip and popover components are able to render arbitrary HTML to the page if configured to do so.
-Chips and NavOverflow also accept HTML for their icon options (`dismissIcon`, `moreIcon`, and markup from `[data-bs-overflow-icon]`).
-To prevent cross-site scripting (XSS) attacks, these components use our built-in content sanitizer to sanitize any options which accept HTML before they are rendered to the page. Content sanitization is enabled by default.
+Some JavaScript components can render or accept arbitrary HTML in their configuration. To prevent cross-site scripting (XSS) attacks, these components use our built-in content sanitizer to sanitize any options which accept HTML before they are rendered to the page. Content sanitization is enabled by default. In particular:
+
+- Popovers and Tooltips can render arbitrary HTML to the page if configured to do so.
+- Chips and Nav overflow can accept HTML for their icon options (`dismissIcon`, `moreIcon`, and `[data-bs-overflow-icon]`).
The tags and attributes allowed by default for tooltip and popover content are as follows. Any tags or attributes not explicitly allowed will be removed during sanitization:
From 3d11838803f1aa3e68dae6bd3c3f7848b8fc3bbd Mon Sep 17 00:00:00 2001
From: Aljo Joby <141745680+aljojoby9@users.noreply.github.com>
Date: Mon, 10 Aug 2026 23:46:21 +0530
Subject: [PATCH 4/5] Add viewbox to the cspell dictionary
The icon allowlist uses the lowercased SVG attribute name that the
sanitizer matches on, so cspell flagged it as unknown.
---
.cspell.json | 1 +
1 file changed, 1 insertion(+)
diff --git a/.cspell.json b/.cspell.json
index 19b89343cc8f..5fc52b2be0e7 100644
--- a/.cspell.json
+++ b/.cspell.json
@@ -194,6 +194,7 @@
"urlize",
"urlquery",
"vbtn",
+ "viewbox",
"viewports",
"Vite",
"vstack",
From 6781afc6bf36d15a4d954c591072cb399567c70d Mon Sep 17 00:00:00 2001
From: Aljo Joby <141745680+aljojoby9@users.noreply.github.com>
Date: Tue, 11 Aug 2026 06:26:37 +0530
Subject: [PATCH 5/5] Tighten DefaultIconAllowlist after review feedback
Document that viewbox is lowercased on purpose for attribute matching,
and drop