plugin-app: fix param substitution to use word boundary

Use a word-boundary regex (:name\b) instead of a plain string replace
so that a shorter param like :a doesn't corrupt a longer param :ab
when both are present in the route.

Signed-off-by: Patrik Oldsberg <rugvip@backstage.io>
Signed-off-by: Fredrik Adelöw <freben@spotify.com>
Co-authored-by: Cursor <cursoragent@cursor.com>
This commit is contained in:
Fredrik Adelöw
2026-05-07 14:03:07 +02:00
parent a3458208a5
commit 5ebd1a14a0
2 changed files with 53 additions and 1 deletions
@@ -539,6 +539,53 @@ describe('AppRoutes', () => {
});
});
it('should not corrupt a longer param when a shorter param is a prefix of it', async () => {
const LocationDisplay = () => {
const location = useLocation();
return <div data-testid="location">{location.pathname}</div>;
};
const targetPage = PageBlueprint.make({
name: 'target',
params: {
path: '/target/:ab/:a',
loader: async () => (
<div>
Target Page
<LocationDisplay />
</div>
),
},
});
renderTestApp({
extensions: [targetPage],
initialRouteEntries: ['/source/bar/foo'],
config: {
...DEFAULT_CONFIG,
app: {
...DEFAULT_CONFIG.app,
extensions: [
{
'app/routes': {
config: {
redirects: [{ from: '/source/:ab/:a', to: '/target/:ab/:a' }],
},
},
},
],
},
},
});
await waitFor(() => {
expect(screen.getByText('Target Page')).toBeInTheDocument();
expect(screen.getByTestId('location')).toHaveTextContent(
'/target/bar/foo',
);
});
});
it('should not interfere with normal routes when redirects are configured', async () => {
const homePage = PageBlueprint.make({
name: 'home',
+6 -1
View File
@@ -27,7 +27,12 @@ function RedirectWithParams({ to }: { to: string }) {
const params = useParams() as Record<string, string>;
let target = to;
for (const [name, value] of Object.entries(params)) {
target = target.replaceAll(name === '*' ? '*' : `:${name}`, value ?? '');
// Use \b (word boundary) for named params so that `:a` doesn't
// accidentally match inside `:ab` when both are present.
target = target.replace(
name === '*' ? /\*/g : new RegExp(`:${name}\\b`, 'g'),
value ?? '',
);
}
return <Navigate to={target} replace />;
}