Skip to content

fix(client): prevent URL corruption when replaceUrlParam contains $ replacement tokens - #5227

Merged
yusukebe merged 2 commits into
honojs:mainfrom
adityajha77:fix/replace-url-param-dollar-tokens
Aug 16, 2026
Merged

yusukebe merged 2 commits into
honojs:mainfrom
adityajha77:fix/replace-url-param-dollar-tokens

Conversation

@adityajha77

Copy link
Copy Markdown
Contributor

Fix: Prevent replaceUrlParam URL corruption on $ replacement tokens in RPC & SSG

Summary

Fixes a bug in replaceUrlParam (src/client/utils.ts) where route parameter values containing $ characters (e.g. $100, item$&, test$\``, test$') trigger JavaScript's string replacement token parser. This corrupts generated URLs in both the Hono RPC Client (hc) and the Static Site Generation (ssg) helper, leading to unexpected 404 Not Found` errors.


Root Cause Analysis

In replaceUrlParam, parameter values were passed directly as a replacement string to String.prototype.replace():

// BEFORE (Buggy)
urlString = urlString.replace(reg, v ? `/${v}` : '')

When the second argument is a raw string, JavaScript evaluates special replacement patterns inside v:

  • $&: Inserts the matched parameter token (e.g. :id), expanding /items/:id into /items/item/:id.
  • $1 / $2: Inserts capture groups, turning $100 into $00 or 00.
  • `$``: Inserts the string segment preceding the match, duplicating leading path segments.
  • $': Inserts the string segment following the match, duplicating trailing path segments.

🛠️ Solution

Pass a replacer function () => ... as the second argument to String.prototype.replace(). According to the ECMAScript specification, replacer functions bypass string replacement token parsing and treat the returned value as a literal string.

// AFTER (Fixed)
export const replaceUrlParam = (urlString: string, params: Record<string, string | undefined>) => {
  for (const [k, v] of Object.entries(params)) {
    const reg = new RegExp('/:' + k + '(?:{[^/]+})?\\??(?=/|$)')
    urlString = urlString.replace(reg, () => (v ? `/${v}` : ''))
  }
  return urlString
}

Reproduction / Test Coverage

Added unit tests covering $, $&, $1, $\``, and $'insrc/client/utils.test.ts`:

it('Should replace correctly when parameter value contains $ characters', () => {
  expect(
    replaceUrlParam('http://localhost/items/:id', { id: 'item$&' })
  ).toBe('http://localhost/items/item$&')

  expect(
    replaceUrlParam('http://localhost/items/:id', { id: '$100' })
  ).toBe('http://localhost/items/$100')

  expect(
    replaceUrlParam('http://localhost/users/:id', { id: 'test$`' })
  ).toBe('http://localhost/users/test$`')

  expect(
    replaceUrlParam('http://localhost/users/:id/posts', { id: "test$'" })
  ).toBe("http://localhost/users/test$'/posts")
})

Checklist

  • Added unit tests for special $ character replacement handling in src/client/utils.test.ts.
  • Verified existing replaceUrlParam unit tests pass, including regex parameters, optional parameters, and prefix matches.
  • Ran all test suites cleanly with npm test.

@codecov

codecov Bot commented Aug 13, 2026

Copy link
Copy Markdown

Codecov Report

✅ All modified and coverable lines are covered by tests.
✅ Project coverage is 79.77%. Comparing base (41bdc42) to head (eda4fb4).

Additional details and impacted files
@@           Coverage Diff           @@
##             main    #5227   +/-   ##
=======================================
  Coverage   79.77%   79.77%           
=======================================
  Files         155      155           
  Lines       10934    10934           
  Branches     2292     2292           
=======================================
  Hits         8723     8723           
  Misses       2211     2211           

☔ View full report in Codecov by Harness.
📢 Have feedback on the report? Share it here.

🚀 New features to boost your workflow:
  • ❄️ Test Analytics: Detect flaky tests, report on failures, and find test suite problems.
  • 📦 JS Bundle Analysis: Save yourself from yourself by tracking and limiting bundle sizes in JS merges.

@adityajha77

Copy link
Copy Markdown
Contributor Author

@yusukebe sir , please look into this PR.

@yusukebe

Copy link
Copy Markdown
Member

@adityajha77

Don't hurry me. I'll review this later.

@yusukebe yusukebe left a comment

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

LGTM!

@yusukebe

Copy link
Copy Markdown
Member

@adityajha77 Thanks!

@yusukebe
yusukebe merged commit 4eb022d into honojs:main Aug 16, 2026
20 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants