@tanstack/react-router 1.159.14 → 1.160.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -1553,6 +1553,41 @@ The \`RouterOptions\` type accepts an object with the following properties and m
1553
1553
  - When \`true\`, disables the global catch boundary that normally wraps all route matches. This allows unhandled errors to bubble up to top-level error handlers in the browser.
1554
1554
  - Useful for testing tools, error reporting services, and debugging scenarios.
1555
1555
 
1556
+ ### \`protocolAllowlist\` property
1557
+
1558
+ - Type: \`Array<string>\`
1559
+ - Optional
1560
+ - Defaults to \`DEFAULT_PROTOCOL_ALLOWLIST\` which includes:
1561
+ - Web navigation: \`http:\`, \`https:\`
1562
+ - Common browser-safe actions: \`mailto:\`, \`tel:\`
1563
+ - An array of URL protocols that are allowed in links, redirects, and navigation. Absolute URLs with protocols not in this list are rejected to prevent security vulnerabilities like XSS attacks.
1564
+ - This check is applied across router navigation APIs, including:
1565
+ - \`<Link to="...">\`
1566
+ - \`navigate({ to: ... })\` and \`navigate({ href: ... })\`
1567
+ - \`redirect({ to: ... })\` and \`redirect({ href: ... })\`
1568
+ - Protocol entries must match \`URL.protocol\` format (lowercase with a trailing \`:\`), for example \`blob:\` or \`data:\`. If you configure \`protocolAllowlist: ['blob']\` (without \`:\`), links using \`blob:\` will still be blocked.
1569
+
1570
+ **Example**
1571
+
1572
+ \`\`\`tsx
1573
+ import {
1574
+ createRouter,
1575
+ DEFAULT_PROTOCOL_ALLOWLIST,
1576
+ } from '@tanstack/react-router'
1577
+
1578
+ // Use a custom allowlist (replaces the default)
1579
+ const router = createRouter({
1580
+ routeTree,
1581
+ protocolAllowlist: ['https:', 'mailto:'],
1582
+ })
1583
+
1584
+ // Or extend the default allowlist
1585
+ const router = createRouter({
1586
+ routeTree,
1587
+ protocolAllowlist: [...DEFAULT_PROTOCOL_ALLOWLIST, 'ftp:'],
1588
+ })
1589
+ \`\`\`
1590
+
1556
1591
  ### \`defaultViewTransition\` property
1557
1592
 
1558
1593
  - Type: \`boolean | ViewTransitionOptions\`
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@tanstack/react-router",
3
- "version": "1.159.14",
3
+ "version": "1.160.0",
4
4
  "description": "Modern and scalable routing for React applications",
5
5
  "author": "Tanner Linsley",
6
6
  "license": "MIT",
@@ -82,7 +82,7 @@
82
82
  "tiny-invariant": "^1.3.3",
83
83
  "tiny-warning": "^1.0.3",
84
84
  "@tanstack/history": "1.154.14",
85
- "@tanstack/router-core": "1.159.9"
85
+ "@tanstack/router-core": "1.160.0"
86
86
  },
87
87
  "devDependencies": {
88
88
  "@testing-library/jest-dom": "^6.6.3",
package/src/index.tsx CHANGED
@@ -243,7 +243,12 @@ export { useMatch } from './useMatch'
243
243
  export { useLoaderDeps } from './useLoaderDeps'
244
244
  export { useLoaderData } from './useLoaderData'
245
245
 
246
- export { redirect, isRedirect, createRouterConfig } from '@tanstack/router-core'
246
+ export {
247
+ redirect,
248
+ isRedirect,
249
+ createRouterConfig,
250
+ DEFAULT_PROTOCOL_ALLOWLIST,
251
+ } from '@tanstack/router-core'
247
252
 
248
253
  export {
249
254
  RouteApi,
package/src/link.tsx CHANGED
@@ -118,7 +118,7 @@ export function useLinkProps<
118
118
  ) {
119
119
  try {
120
120
  new URL(to)
121
- if (isDangerousProtocol(to)) {
121
+ if (isDangerousProtocol(to, router.protocolAllowlist)) {
122
122
  if (process.env.NODE_ENV !== 'production') {
123
123
  console.warn(`Blocked Link with dangerous protocol: ${to}`)
124
124
  }
@@ -170,7 +170,7 @@ export function useLinkProps<
170
170
 
171
171
  const externalLink = (() => {
172
172
  if (hrefOption?.external) {
173
- if (isDangerousProtocol(hrefOption.href)) {
173
+ if (isDangerousProtocol(hrefOption.href, router.protocolAllowlist)) {
174
174
  if (process.env.NODE_ENV !== 'production') {
175
175
  console.warn(
176
176
  `Blocked Link with dangerous protocol: ${hrefOption.href}`,
@@ -187,7 +187,7 @@ export function useLinkProps<
187
187
  if (typeof to === 'string' && to.indexOf(':') > -1) {
188
188
  try {
189
189
  new URL(to)
190
- if (isDangerousProtocol(to)) {
190
+ if (isDangerousProtocol(to, router.protocolAllowlist)) {
191
191
  if (process.env.NODE_ENV !== 'production') {
192
192
  console.warn(`Blocked Link with dangerous protocol: ${to}`)
193
193
  }
@@ -438,7 +438,7 @@ export function useLinkProps<
438
438
  const externalLink = React.useMemo(() => {
439
439
  if (hrefOption?.external) {
440
440
  // Block dangerous protocols for external links
441
- if (isDangerousProtocol(hrefOption.href)) {
441
+ if (isDangerousProtocol(hrefOption.href, router.protocolAllowlist)) {
442
442
  if (process.env.NODE_ENV !== 'production') {
443
443
  console.warn(
444
444
  `Blocked Link with dangerous protocol: ${hrefOption.href}`,
@@ -453,8 +453,8 @@ export function useLinkProps<
453
453
  if (typeof to !== 'string' || to.indexOf(':') === -1) return undefined
454
454
  try {
455
455
  new URL(to as any)
456
- // Block dangerous protocols like javascript:, data:, vbscript:
457
- if (isDangerousProtocol(to)) {
456
+ // Block dangerous protocols like javascript:, blob:, data:
457
+ if (isDangerousProtocol(to, router.protocolAllowlist)) {
458
458
  if (process.env.NODE_ENV !== 'production') {
459
459
  console.warn(`Blocked Link with dangerous protocol: ${to}`)
460
460
  }
@@ -463,7 +463,7 @@ export function useLinkProps<
463
463
  return to
464
464
  } catch {}
465
465
  return undefined
466
- }, [to, hrefOption])
466
+ }, [to, hrefOption, router.protocolAllowlist])
467
467
 
468
468
  // eslint-disable-next-line react-hooks/rules-of-hooks
469
469
  const isActive = useRouterState({