From 434f3869820c7820b41dfd13351017a31409eff3 Mon Sep 17 00:00:00 2001 From: kay delaney <45561153+kaydelaney@users.noreply.github.com> Date: Tue, 9 Jul 2024 16:15:59 +0100 Subject: [PATCH] Frontend: Use safe stringifier in parseBody (#90047) * Frontend: Use safe stringifier in parseBody Closes #88064 --- packages/grafana-data/src/index.ts | 2 +- packages/grafana-data/src/utils/object.ts | 19 +++++++++++++++++++ public/app/core/utils/fetch.ts | 4 ++-- 3 files changed, 22 insertions(+), 3 deletions(-) diff --git a/packages/grafana-data/src/index.ts b/packages/grafana-data/src/index.ts index c45b86fe1ba..09c7d2af88d 100644 --- a/packages/grafana-data/src/index.ts +++ b/packages/grafana-data/src/index.ts @@ -246,7 +246,7 @@ export { } from './utils/csv'; export { parseLabels, findCommonLabels, findUniqueLabels, matchAllLabels, formatLabels } from './utils/labels'; export { roundDecimals, guessDecimals } from './utils/numbers'; -export { objRemoveUndefined, isEmptyObject } from './utils/object'; +export { objRemoveUndefined, isEmptyObject, safeStringifyValue } from './utils/object'; export { classicColors } from './utils/namedColorsPalette'; export { getSeriesTimeStep, hasMsResolution } from './utils/series'; export { BinaryOperationID, type BinaryOperation, binaryOperators } from './utils/binaryOperators'; diff --git a/packages/grafana-data/src/utils/object.ts b/packages/grafana-data/src/utils/object.ts index 0f40f8e636a..50d3ff25507 100644 --- a/packages/grafana-data/src/utils/object.ts +++ b/packages/grafana-data/src/utils/object.ts @@ -10,3 +10,22 @@ export const isEmptyObject = (value: unknown): value is Record => { return typeof value === 'object' && value !== null && Object.keys(value).length === 0; }; + +/** Stringifies an object that may contain circular references */ +export function safeStringifyValue(value: unknown) { + const getCircularReplacer = () => { + const seen = new WeakSet(); + return (_: string, value: object | null) => { + if (typeof value === 'object' && value !== null) { + if (seen.has(value)) { + return; + } + seen.add(value); + } + + return value; + }; + }; + + return JSON.stringify(value, getCircularReplacer()); +} diff --git a/public/app/core/utils/fetch.ts b/public/app/core/utils/fetch.ts index 49253f74a31..8b4031123b7 100644 --- a/public/app/core/utils/fetch.ts +++ b/public/app/core/utils/fetch.ts @@ -1,6 +1,6 @@ import { omitBy } from 'lodash'; -import { deprecationWarning } from '@grafana/data'; +import { deprecationWarning, safeStringifyValue } from '@grafana/data'; import { BackendSrvRequest } from '@grafana/runtime'; export const parseInitFromOptions = (options: BackendSrvRequest): RequestInit => { @@ -93,7 +93,7 @@ export const parseBody = (options: BackendSrvRequest, isAppJson: boolean) => { return options.data; } - return isAppJson ? JSON.stringify(options.data) : new URLSearchParams(options.data); + return isAppJson ? safeStringifyValue(options.data) : new URLSearchParams(options.data); }; export async function parseResponseBody(