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 = ({