diff --git a/packages/grafana-data/src/text/index.ts b/packages/grafana-data/src/text/index.ts index bece68920ac..be85edccaf9 100644 --- a/packages/grafana-data/src/text/index.ts +++ b/packages/grafana-data/src/text/index.ts @@ -1,11 +1,12 @@ export * from './string'; export * from './markdown'; export * from './text'; -import { escapeHtml, hasAnsiCodes, sanitize, sanitizeUrl } from './sanitize'; +import { escapeHtml, hasAnsiCodes, sanitize, sanitizeUrl, sanitizeAngularInterpolation } from './sanitize'; export const textUtil = { escapeHtml, hasAnsiCodes, sanitize, sanitizeUrl, + sanitizeAngularInterpolation, }; diff --git a/packages/grafana-data/src/text/sanitize.ts b/packages/grafana-data/src/text/sanitize.ts index d32c4a96178..ae14afed422 100644 --- a/packages/grafana-data/src/text/sanitize.ts +++ b/packages/grafana-data/src/text/sanitize.ts @@ -38,3 +38,7 @@ export function hasAnsiCodes(input: string): boolean { export function escapeHtml(str: string): string { return String(str).replace(/&/g, '&').replace(//g, '>').replace(/"/g, '"'); } + +export function sanitizeAngularInterpolation(url: string): string { + return url.replace(/\{\{/g, '%7B%7B').replace(/\}\}/g, '%7D%7D'); +} diff --git a/public/app/core/components/NavBar/DropdownChild.tsx b/public/app/core/components/NavBar/DropdownChild.tsx index 6badadb67d7..22c2516a9b1 100644 --- a/public/app/core/components/NavBar/DropdownChild.tsx +++ b/public/app/core/components/NavBar/DropdownChild.tsx @@ -1,6 +1,6 @@ import React from 'react'; import { css } from '@emotion/css'; -import { GrafanaTheme2 } from '@grafana/data'; +import { GrafanaTheme2, textUtil } from '@grafana/data'; import { Icon, IconName, Link, useTheme2 } from '@grafana/ui'; export interface Props { @@ -29,13 +29,15 @@ const DropdownChild = ({ isDivider = false, icon, onClick, target, text, url }: ); if (url) { + const sanitizedUrl = textUtil.sanitizeAngularInterpolation(url); + element = !target && url.startsWith('/') ? ( - + {linkContent} ) : ( - + {linkContent} ); diff --git a/public/app/core/components/NavBar/NavBarItem.tsx b/public/app/core/components/NavBar/NavBarItem.tsx index 134d67b2b61..0d325a7c7a4 100644 --- a/public/app/core/components/NavBar/NavBarItem.tsx +++ b/public/app/core/components/NavBar/NavBarItem.tsx @@ -1,6 +1,6 @@ import React, { ReactNode } from 'react'; import { css, cx } from '@emotion/css'; -import { GrafanaTheme2, NavModelItem } from '@grafana/data'; +import { GrafanaTheme2, NavModelItem, textUtil } from '@grafana/data'; import { Link, useTheme2 } from '@grafana/ui'; import NavBarDropdown from './NavBarDropdown'; @@ -34,13 +34,14 @@ const NavBarItem = ({ {children} ); + const sanitizedUrl = textUtil.sanitizeAngularInterpolation(url ?? ''); if (url) { element = - !target && url.startsWith('/') ? ( + !target && sanitizedUrl.startsWith('/') ? ( {children} ) : ( - + {children} ); @@ -61,7 +62,7 @@ const NavBarItem = ({