Quick heads-up — Sources/LeafKit/String+HTMLEscape.swift on the v5 branch (sha 1a426bf2) still uses the old grapheme-cluster-based String.replacing(_:with:) / replacingOccurrences(of:with:) implementation:
self
.replacing("&", with: "&")
.replacing("\"", with: """)
.replacing("'", with: "'")
.replacing("<", with: "<")
.replacing(">", with: ">")
The CVE-2026-27120 fix 8919e394 switched this to a per-unicodeScalar reduce so that, for example, "\u{0022}\u{0301}" (a quote with a combining accent — one extended grapheme cluster, but two scalars) escapes to "\u{0301} instead of slipping through unchanged. The accompanying testExtendedGraphemeClusterBypass regression test is also absent in Tests/LeafKitTests/HTMLEscapeTests.swift on v5 (sha b21b7003).
So a payload like "\u{0301}=1 autofocus tabindex=0 onfocus=alert(1) survives htmlEscaped() on this branch when it doesn't on main. If v5 is still maintained for security I can put the cherry-pick together.
Cheers,
vulgraph
Quick heads-up —
Sources/LeafKit/String+HTMLEscape.swifton thev5branch (sha1a426bf2) still uses the old grapheme-cluster-basedString.replacing(_:with:)/replacingOccurrences(of:with:)implementation:The CVE-2026-27120 fix
8919e394switched this to a per-unicodeScalarreduce so that, for example,"\u{0022}\u{0301}"(a quote with a combining accent — one extended grapheme cluster, but two scalars) escapes to"\u{0301}instead of slipping through unchanged. The accompanyingtestExtendedGraphemeClusterBypassregression test is also absent inTests/LeafKitTests/HTMLEscapeTests.swiftonv5(shab21b7003).So a payload like
"\u{0301}=1 autofocus tabindex=0 onfocus=alert(1)surviveshtmlEscaped()on this branch when it doesn't onmain. Ifv5is still maintained for security I can put the cherry-pick together.Cheers,
vulgraph