Summary
simpletoc_sanitize_string() applies rawurlencode() to a string that sanitize_title_with_dashes() has already percent-encoded. For non-Latin headings this produces double-encoded anchors. Latin-script headings are unaffected, which could be why this has gone unnoticed.
Note: This is a bug I came across on my site and I had AI find the cause for me. This issue was written with the assistance of AI.
Steps to reproduce
- On a site with SimpleTOC active, create a post with two headings:
- Save and inspect the rendered heading
id attributes.
Actual
<h2id="test-heading-abc">Test Heading ABC</h2><h2id="%25e3%2583%2586%25e3%2582%25b9%25e3%2583%2588%25e8%25a6%258b%25e5%2587%25ba%25e3%2581%2597">テスト見出し</h2>
Expected
<h2id="%e3%83%86%e3%82%b9%e3%83%88%e8%a6%8b%e5%87%ba%e3%81%97">テスト見出し</h2>
Cause
In plugin.php, simpletoc_sanitize_string():
$sanitized_string = sanitize_title_with_dashes( $string_without_accents );
// Encode for use in an url.$urlencoded = rawurlencode( $sanitized_string );
return$urlencoded;
sanitize_title_with_dashes() already calls utf8_uri_encode(), so its return value is percent-encoded for any non-ASCII input. rawurlencode() then escapes each % as %25.
For ASCII input the output contains no %, so rawurlencode() is a no-op — the defect cannot manifest in Latin script.
Suggested fix
Drop the rawurlencode() call; sanitize_title_with_dashes() already returns a URL-safe slug.
returnsanitize_title_with_dashes( $string_without_accents );
Impact
In-page TOC links still resolve, because get_included_toc_headings() and add_anchor_attribute() generate the anchor the same way. But any externally shared, hand-written, or search-indexed fragment link to a non-Latin heading will not match, and the URLs are unreadable when shared.
Manually set HTML anchors are unaffected — add_anchor_attribute() correctly skips headings that already have an id.
Environment
- SimpleTOC 7.1.1
- WordPress.com, site language
ja
Summary
simpletoc_sanitize_string()appliesrawurlencode()to a string thatsanitize_title_with_dashes()has already percent-encoded. For non-Latin headings this produces double-encoded anchors. Latin-script headings are unaffected, which could be why this has gone unnoticed.Note: This is a bug I came across on my site and I had AI find the cause for me. This issue was written with the assistance of AI.
Steps to reproduce
Test Heading ABCテスト見出しidattributes.Actual
Expected
Cause
In
plugin.php,simpletoc_sanitize_string():sanitize_title_with_dashes()already callsutf8_uri_encode(), so its return value is percent-encoded for any non-ASCII input.rawurlencode()then escapes each%as%25.For ASCII input the output contains no
%, sorawurlencode()is a no-op — the defect cannot manifest in Latin script.Suggested fix
Drop the
rawurlencode()call;sanitize_title_with_dashes()already returns a URL-safe slug.Impact
In-page TOC links still resolve, because
get_included_toc_headings()andadd_anchor_attribute()generate the anchor the same way. But any externally shared, hand-written, or search-indexed fragment link to a non-Latin heading will not match, and the URLs are unreadable when shared.Manually set HTML anchors are unaffected —
add_anchor_attribute()correctly skips headings that already have anid.Environment
ja