From 337668de9f3d09677991c89b65a6babf643ec344 Mon Sep 17 00:00:00 2001 From: deepshekhardas Date: Sat, 18 Jul 2026 12:36:57 +0530 Subject: [PATCH] fix: safe decode malformed URI route params --- .changeset/safe-route-id-decoding.md | 7 +++++++ .../src/definitions/helpers/handleUseParams/index.tsx | 10 +++++++++- packages/react-router/src/bindings.tsx | 5 +++-- packages/react-router/src/safe-decode-uri-component.ts | 7 +++++++ packages/remix-router/src/bindings.tsx | 7 ++++--- packages/remix-router/src/safe-decode-uri-component.ts | 7 +++++++ 6 files changed, 37 insertions(+), 6 deletions(-) create mode 100644 .changeset/safe-route-id-decoding.md create mode 100644 packages/react-router/src/safe-decode-uri-component.ts create mode 100644 packages/remix-router/src/safe-decode-uri-component.ts diff --git a/.changeset/safe-route-id-decoding.md b/.changeset/safe-route-id-decoding.md new file mode 100644 index 0000000000000..aadb4c9cbc108 --- /dev/null +++ b/.changeset/safe-route-id-decoding.md @@ -0,0 +1,7 @@ +--- +"@refinedev/core": patch +"@refinedev/react-router": patch +"@refinedev/remix-router": patch +--- + +Safely handle malformed URI-encoded route params when parsing route ids and query targets. diff --git a/packages/core/src/definitions/helpers/handleUseParams/index.tsx b/packages/core/src/definitions/helpers/handleUseParams/index.tsx index d71cd1f8e1486..4dc1aff4c55be 100644 --- a/packages/core/src/definitions/helpers/handleUseParams/index.tsx +++ b/packages/core/src/definitions/helpers/handleUseParams/index.tsx @@ -1,8 +1,16 @@ +const safeDecodeURIComponent = (value: string) => { + try { + return decodeURIComponent(value); + } catch { + return value; + } +}; + export const handleUseParams = (params: any = {}): any => { if (params?.id) { return { ...params, - id: decodeURIComponent(params.id), + id: safeDecodeURIComponent(params.id), }; } return params; diff --git a/packages/react-router/src/bindings.tsx b/packages/react-router/src/bindings.tsx index 5750eb9602ed8..1f01450215b3b 100644 --- a/packages/react-router/src/bindings.tsx +++ b/packages/react-router/src/bindings.tsx @@ -17,6 +17,7 @@ import { useParams, } from "react-router"; import { convertToNumberIfPossible } from "./convert-to-number-if-possible"; +import { safeDecodeURIComponent } from "./safe-decode-uri-component"; export const stringifyConfig = { addQueryPrefix: true, @@ -124,7 +125,7 @@ export const routerProvider: RouterProvider = { const response: ParseResponse = { ...(resource && { resource }), ...(action && { action }), - ...(params?.id && { id: decodeURIComponent(params.id) }), + ...(params?.id && { id: safeDecodeURIComponent(params.id) }), // ...(params?.action && { action: params.action }), // lets see if there is a need for this pathname, params: { @@ -136,7 +137,7 @@ export const routerProvider: RouterProvider = { combinedParams.pageSize as string, ) as number | undefined, to: combinedParams.to - ? decodeURIComponent(combinedParams.to as string) + ? safeDecodeURIComponent(combinedParams.to as string) : undefined, }, }; diff --git a/packages/react-router/src/safe-decode-uri-component.ts b/packages/react-router/src/safe-decode-uri-component.ts new file mode 100644 index 0000000000000..b13259acebb4b --- /dev/null +++ b/packages/react-router/src/safe-decode-uri-component.ts @@ -0,0 +1,7 @@ +export const safeDecodeURIComponent = (value: string) => { + try { + return decodeURIComponent(value); + } catch { + return value; + } +}; diff --git a/packages/remix-router/src/bindings.tsx b/packages/remix-router/src/bindings.tsx index d010b15212a39..5b477a43743b4 100644 --- a/packages/remix-router/src/bindings.tsx +++ b/packages/remix-router/src/bindings.tsx @@ -11,6 +11,7 @@ import qs from "qs"; import React, { type ComponentProps, useCallback, useContext } from "react"; import { paramsFromCurrentPath } from "./params-from-current-path"; import { convertToNumberIfPossible } from "./convert-to-number-if-possible"; +import { safeDecodeURIComponent } from "./safe-decode-uri-component"; export const stringifyConfig = { addQueryPrefix: true, @@ -116,8 +117,8 @@ export const routerProvider: RouterProvider = { const response: ParseResponse = { ...(resource && { resource }), ...(action && { action }), - ...(inferredId && { id: decodeURIComponent(inferredId) }), - ...(params?.id && { id: decodeURIComponent(params.id) }), + ...(inferredId && { id: safeDecodeURIComponent(inferredId) }), + ...(params?.id && { id: safeDecodeURIComponent(params.id) }), // ...(params?.action && { action: params.action }), // lets see if there is a need for this pathname, params: { @@ -129,7 +130,7 @@ export const routerProvider: RouterProvider = { combinedParams.pageSize as string, ) as number | undefined, to: combinedParams.to - ? decodeURIComponent(combinedParams.to as string) + ? safeDecodeURIComponent(combinedParams.to as string) : undefined, }, }; diff --git a/packages/remix-router/src/safe-decode-uri-component.ts b/packages/remix-router/src/safe-decode-uri-component.ts new file mode 100644 index 0000000000000..b13259acebb4b --- /dev/null +++ b/packages/remix-router/src/safe-decode-uri-component.ts @@ -0,0 +1,7 @@ +export const safeDecodeURIComponent = (value: string) => { + try { + return decodeURIComponent(value); + } catch { + return value; + } +};