Skip to content

Heading anchors are double URL-encoded for non-Latin scripts (Japanese, Chinese, Korean, Cyrillic, etc.) #89

Description

@kaitohm

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

  1. On a site with SimpleTOC active, create a post with two headings:
    • Test Heading ABC
    • テスト見出し
  2. Save and inspect the rendered heading id attributes.

Actual

<h2 id="test-heading-abc">Test Heading ABC</h2>
<h2 id="%25e3%2583%2586%25e3%2582%25b9%25e3%2583%2588%25e8%25a6%258b%25e5%2587%25ba%25e3%2581%2597">テスト見出し</h2>

Expected

<h2 id="%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.

return sanitize_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

Activity

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions