fix(twenty-shared): stop decoding URI components in URL normalization#18699
Closed
sonarly[bot] wants to merge 1 commit intomainfrom
Closed
fix(twenty-shared): stop decoding URI components in URL normalization#18699sonarly[bot] wants to merge 1 commit intomainfrom
sonarly[bot] wants to merge 1 commit intomainfrom
Conversation
https://sonarly.com/issue/15564?type=bug The `lowercaseUrlOriginAndRemoveTrailingSlash` function applies `decodeURIComponent` to URL pathname and search components, converting intentionally encoded characters like `%2F` to `/`, which breaks URLs that depend on encoding (e.g., Google Maps). Fix: Removed `safeDecodeURIComponent()` wrapping from `url.pathname` and `url.search` in `lowercaseUrlOriginAndRemoveTrailingSlash`. This function was added in commit `1119e3d77e` to handle accented characters, but `new URL()` already normalizes non-ASCII characters in pathnames (encoding `é` as `%C3%A9`). The `decodeURIComponent` call was incorrectly decoding structurally significant percent-encoded characters like `%2F` (encoded slash) back to `/`, breaking URLs that depend on encoding (e.g., Google Maps `data=` parameter). The fix restores the original `url.pathname + url.search + url.hash` concatenation, which correctly preserves all percent-encoding as provided by the URL constructor. The `safeDecodeURIComponent` utility itself is kept since it's still used by the IMAP message text extractor service. Updated test expectations to match correct URL normalization behavior: - Percent-encoded characters like `%2F`, `%20`, `%2520` are preserved as-is - Non-ASCII characters are encoded by the URL constructor (e.g., `é` → `%C3%A9`) - Added a Google Maps URL test case to prevent regression
Contributor
There was a problem hiding this comment.
Your free trial has ended. If you'd like to continue receiving code reviews, you can add a payment method here.
Collaborator
|
Fixes #18698 |
Member
|
Fixed by: #18792 Closing |
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.
Automated fix for bug 15564
Severity:
highSummary
The
lowercaseUrlOriginAndRemoveTrailingSlashfunction appliesdecodeURIComponentto URL pathname and search components, converting intentionally encoded characters like%2Fto/, which breaks URLs that depend on encoding (e.g., Google Maps).User Impact
Any user storing URLs with percent-encoded characters in link fields (via import, direct edit, or API) gets corrupted URLs. Google Maps links, encoded LinkedIn paths, and any URL with meaningful percent-encoding are broken and unclickable.
Root Cause
Proximate cause: In
packages/twenty-shared/src/utils/url/lowercaseUrlOriginAndRemoveTrailingSlash.ts(lines 14-15), the function wrapsurl.pathnameandurl.searchwithsafeDecodeURIComponent():safeDecodeURIComponentcallsdecodeURIComponent(), which converts%2F→/,%20→ space, etc. For URLs where encoding is semantically meaningful (like Google Maps'data=parameter containing%2Fg%2F), this decoding produces a broken URL that the target service cannot parse.This function is invoked in two critical paths:
transform-links-value.util.tscallslowercaseUrlOriginAndRemoveTrailingSlash()on bothprimaryLinkUrland allsecondaryLinksURLsbuildRecordFromImportedStructuredRow.tsuses it as the transform forprimaryLinkUrlThe original function (commit
d916ec0af9) usedurl.pathname + url.search + url.hashwithout decoding, which correctly preserved percent-encoding.Triggering cause: Commit
1119e3d77e("fix(twenty-shared): preserve special characters in URLs", merged 2025-12-15, PR #16312) introducedsafeDecodeURIComponentwrapping. The stated intent was to handle special characters like%C3%A9(accented characters) but the fix over-corrected by decoding ALL percent-encoded characters, including structurally significant ones like%2F(encoded forward slash). The test added in this commit explicitly asserts that%2Fis decoded to/(test case: "should handle mixed encoded and non-encoded in same URL"), confirming this was an intentional but incorrect design decision.Every URL with meaningful percent-encoding stored after 2025-12-15 is affected.
Introduced by: asasin235 on 2025-12-15 in commit
1119e3dSuggested Fix
Removed
safeDecodeURIComponent()wrapping fromurl.pathnameandurl.searchinlowercaseUrlOriginAndRemoveTrailingSlash. This function was added in commit1119e3d77eto handle accented characters, butnew URL()already normalizes non-ASCII characters in pathnames (encodingéas%C3%A9). ThedecodeURIComponentcall was incorrectly decoding structurally significant percent-encoded characters like%2F(encoded slash) back to/, breaking URLs that depend on encoding (e.g., Google Mapsdata=parameter).The fix restores the original
url.pathname + url.search + url.hashconcatenation, which correctly preserves all percent-encoding as provided by the URL constructor. ThesafeDecodeURIComponentutility itself is kept since it's still used by the IMAP message text extractor service.Updated test expectations to match correct URL normalization behavior:
%2F,%20,%2520are preserved as-isé→%C3%A9)Generated by Sonarly