Fix disallowed-character stripping when replacement is an empty string#559
Merged
Conversation
String.prototype.includes('') is always true, so an empty replacement
matched the replacement-preserving branch and skipped the disallowedChars
sanitizer, letting dangerous characters (< > " ' ( ) / and ..) pass
through unchanged (XSS / path traversal). Guard the branch with
opts.replacement !== '' so an empty replacement falls through to the
sanitizer. Add a regression test.
Trott
reviewed
Jul 21, 2026
Trott
reviewed
Jul 21, 2026
Trott
reviewed
Jul 21, 2026
Owner
|
Thanks for the fix. 🚀 |
github-actions Bot
pushed a commit
that referenced
this pull request
Jul 21, 2026
## [12.0.1](v12.0.0...v12.0.1) (2026-07-21) ### Bug Fixes * sanitize disallowed characters when replacement is an empty string([#559](#559)) ([a330fce](a330fce))
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
When
replacementis an empty string, disallowed characters are no longer stripped, so the output can contain characters outside the documented allowed set.The per-character loop has a branch that preserves the replacement string when a character contains it:
''.includes('')is alwaystrue, so with an empty replacement every character takes this branch and thedisallowedCharsfilter in the finalelsenever runs. The result:This is inconsistent: the default replacement strips
<and>, but an empty replacement lets them through, even though both modes promise output within[A-Za-z0-9](pretty) /[\w\-.~](rfc3986).The fix skips that branch when the replacement is empty, so an empty replacement falls through to the filter:
After the change,
slug('a<b>c', '')returns'abc', whileslug('foo bar baz', '')still returns'foobarbaz'. Added a test covering the empty-replacement case in both pretty and rfc3986 modes.