diff --git a/packages/components/package-lock.json b/packages/components/package-lock.json index 8ef7bc3fe5..39bef43037 100644 --- a/packages/components/package-lock.json +++ b/packages/components/package-lock.json @@ -1,12 +1,12 @@ { "name": "@labkey/components", - "version": "7.42.2", + "version": "7.42.3-fb-redirectGH1023.2", "lockfileVersion": 3, "requires": true, "packages": { "": { "name": "@labkey/components", - "version": "7.42.2", + "version": "7.42.3-fb-redirectGH1023.2", "license": "SEE LICENSE IN LICENSE.txt", "dependencies": { "@hello-pangea/dnd": "18.0.1", diff --git a/packages/components/package.json b/packages/components/package.json index 6d1c0f35a0..450153ab73 100644 --- a/packages/components/package.json +++ b/packages/components/package.json @@ -1,6 +1,6 @@ { "name": "@labkey/components", - "version": "7.42.2", + "version": "7.42.3-fb-redirectGH1023.2", "description": "Components, models, actions, and utility functions for LabKey applications and pages", "sideEffects": false, "files": [ diff --git a/packages/components/releaseNotes/components.md b/packages/components/releaseNotes/components.md index e8562f1d56..3454e7d7fb 100644 --- a/packages/components/releaseNotes/components.md +++ b/packages/components/releaseNotes/components.md @@ -1,6 +1,10 @@ # @labkey/components Components, models, actions, and utility functions for LabKey applications and pages +### version TBD +*Released*: TBD +- GitHub Issue #1023: Add redirect() helper that uses core-safeRedirect + ### version 7.42.2 *Released*: 22 June 2026 - Styling update for user comment on large storage modal diff --git a/packages/components/src/index.ts b/packages/components/src/index.ts index 40ce92de9d..c4065657fd 100644 --- a/packages/components/src/index.ts +++ b/packages/components/src/index.ts @@ -7,7 +7,7 @@ import { enableMapSet, enablePatches } from 'immer'; import { applyURL, AppURL, buildURL, spliceURL } from './internal/url/AppURL'; import { AppLink } from './internal/url/AppLink'; import { useAppNavigate } from './internal/url/useAppNavigate'; -import { hasParameter, imageURL, toggleParameter } from './internal/url/ActionURL'; +import { hasParameter, imageURL, redirect, toggleParameter } from './internal/url/ActionURL'; import { getIntegerSearchParam } from './internal/url/utils'; import { Container } from './internal/components/base/models/Container'; import { hasAllPermissions, hasAnyPermissions, hasPermissions, User } from './internal/components/base/models/User'; @@ -1613,6 +1613,7 @@ export { QuerySort, quoteValueWithDelimiters, RANGE_URIS, + redirect, registerDefaultURLMappers, registerFilterType, registerInputRenderer, diff --git a/packages/components/src/internal/url/ActionURL.ts b/packages/components/src/internal/url/ActionURL.ts index 230b6aacc1..728b5b78f1 100644 --- a/packages/components/src/internal/url/ActionURL.ts +++ b/packages/components/src/internal/url/ActionURL.ts @@ -83,3 +83,8 @@ export function imageURL(iconDir: string, src: string): string { export function toggleParameter(parameterName: string, value: any): void { setParameter(parameterName, hasParameter(parameterName) ? undefined : value); } + +// GitHub Issue #1023: Use the safeRedirect action to verify returnURL goes to local URLs only +export function redirect(url: string): void { + window.location.href = ActionURL.buildURL('core', 'safeRedirect', undefined, { returnUrl: url }); +} diff --git a/packages/components/src/internal/url/useAppNavigate.ts b/packages/components/src/internal/url/useAppNavigate.ts index 24cc0f5e57..2b96d4a889 100644 --- a/packages/components/src/internal/url/useAppNavigate.ts +++ b/packages/components/src/internal/url/useAppNavigate.ts @@ -5,6 +5,7 @@ import { useNavigate } from 'react-router-dom'; import { useCallback, useMemo } from 'react'; +import { redirect } from './ActionURL'; import { AppURL } from './AppURL'; import { parseAppPath } from './AppLink'; @@ -16,9 +17,9 @@ export type NavigateFn = (url: string | AppURL, replace?: boolean) => void; * - If url is an AppURL it navigates via React Router's navigate method * - If url is a string prefixed with # it will assume the URL is an app path, and use RR's navigate method * - If url is a string pointing to an app path for the current app it will navigate via RR's navigate method - * - In all other cases it will use window.location.href to navigate to the given URL - * string it navigates via window.location.href = url. If you have an AppURL you should always pass it, instead of - * AppURL.toString() or AppURL.toHref(). + * - In all other cases it navigates to the given URL via the redirect() helper, which routes through the + * core/safeRedirect action to guard against open-redirect vulnerabilities. If you have an AppURL you should always + * pass it, instead of AppURL.toString() or AppURL.toHref(). */ interface AppNavigateState { goBack: () => void; @@ -42,7 +43,7 @@ export function useAppNavigate(): AppNavigateState { return; } - window.location.href = url.toString(); + redirect(url.toString()); }, [rrNavigate] );