[release-11.6.3] FlameGraph: Fix bug for function names that conflict with JavaScript object prototype properties (#106624)

FlameGraph: Fix bug for function names that conflict with JavaScript object prototype properties (#106338)

* grafana-flamegraph: Fix bug for function names that conflict with JavaScript object prototype properties

The bug occurs in the flamegraph data transformation logic where function names from profiling data are used as object
keys without proper prototype pollution protection.

Fixes #106232 #101551



* Fix top table calculations

---------



(cherry picked from commit 6156e9c2d8)

Co-authored-by: Christian Simon <simon@swine.de>
Co-authored-by: vinayteki95 <vinayteki95@users.noreply.github.com>
Co-authored-by: Piotr Jamróz <pm.jamroz@gmail.com>
release-11.6.3
grafana-delivery-bot[bot] 1 month ago committed by GitHub
parent d6697ddde4
commit d48d89e993
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
  1. 24
      packages/grafana-flamegraph/src/FlameGraph/dataTransform.test.ts
  2. 2
      packages/grafana-flamegraph/src/FlameGraph/dataTransform.ts
  3. 2
      packages/grafana-flamegraph/src/TopTable/FlameGraphTopTableContainer.tsx

@ -75,6 +75,30 @@ describe('nestedSetToLevels', () => {
expect(levels[0]).toEqual([n1]);
expect(levels[1]).toEqual([n2, n3, n4]);
});
it('handles strings that collide with inherited prototype method names', () => {
const frame = createDataFrame({
fields: [
{ name: 'level', values: [0, 1, 1, 1] },
{ name: 'value', values: [10, 5, 3, 1] },
{ name: 'label', values: ['toString', 'valueOf', 'hasOwnProperty', 'isPrototypeOf'], type: FieldType.string },
{ name: 'self', values: [10, 5, 3, 1] },
],
});
const [levels] = nestedSetToLevels(new FlameGraphDataContainer(frame, { collapsing: true }));
const n4: LevelItem = { itemIndexes: [3], start: 8, children: [], value: 1, level: 1 };
const n3: LevelItem = { itemIndexes: [2], start: 5, children: [], value: 3, level: 1 };
const n2: LevelItem = { itemIndexes: [1], start: 0, children: [], value: 5, level: 1 };
const n1: LevelItem = { itemIndexes: [0], start: 0, children: [n2, n3, n4], value: 10, level: 0 };
n2.parents = [n1];
n3.parents = [n1];
n4.parents = [n1];
expect(levels[0]).toEqual([n1]);
expect(levels[1]).toEqual([n2, n3, n4]);
});
});
describe('FlameGraphDataContainer', () => {

@ -46,7 +46,7 @@ export function nestedSetToLevels(
let offset = 0;
let parent: LevelItem | undefined = undefined;
const uniqueLabels: Record<string, LevelItem[]> = {};
const uniqueLabels: Record<string, LevelItem[]> = Object.create(null);
for (let i = 0; i < container.data.length; i++) {
const currentLevel = container.getLevel(i);

@ -56,7 +56,7 @@ const FlameGraphTopTableContainer = memo(
const table = useMemo(() => {
// Group the data by label, we show only one row per label and sum the values
// TODO: should be by filename + funcName + linenumber?
let filteredTable: { [key: string]: TableData } = {};
let filteredTable: { [key: string]: TableData } = Object.create(null);
for (let i = 0; i < data.data.length; i++) {
const value = data.getValue(i);
const valueRight = data.getValueRight(i);

Loading…
Cancel
Save