From 5fe23b4964c9b1e1fff924699a1e073e7719ba65 Mon Sep 17 00:00:00 2001 From: Sven Grossmann Date: Fri, 13 Jan 2023 17:16:47 +0100 Subject: [PATCH] Loki: Track obfuscated query (#61325) * obfuscate query * also conver numbers and comments * query obfuscation: simplify obfuscation and keep some identifiers * Query obfuscation: replace placeholder string with node name Co-authored-by: Matias Chomicki --- .../datasource/loki/queryUtils.test.ts | 31 ++++++++++++++++++- .../app/plugins/datasource/loki/queryUtils.ts | 16 ++++++++++ .../app/plugins/datasource/loki/tracking.ts | 3 +- 3 files changed, 48 insertions(+), 2 deletions(-) diff --git a/public/app/plugins/datasource/loki/queryUtils.test.ts b/public/app/plugins/datasource/loki/queryUtils.test.ts index 4b36192b8af..c2ac42d41ae 100644 --- a/public/app/plugins/datasource/loki/queryUtils.test.ts +++ b/public/app/plugins/datasource/loki/queryUtils.test.ts @@ -7,6 +7,7 @@ import { isValidQuery, parseToNodeNamesArray, getParserFromQuery, + obfuscate, } from './queryUtils'; import { LokiQuery, LokiQueryType } from './types'; @@ -168,7 +169,7 @@ describe('isValidQuery', () => { }); }); -describe('parseToArray', () => { +describe('parseToNodeNamesArray', () => { it('returns on empty query', () => { expect(parseToNodeNamesArray('{}')).toEqual(['LogQL', 'Expr', 'LogExpr', 'Selector', '⚠']); }); @@ -201,6 +202,34 @@ describe('parseToArray', () => { }); }); +describe('obfuscate', () => { + it('obfuscates on invalid query', () => { + expect(obfuscate('{job="grafana"')).toEqual('{Identifier=String'); + }); + it('obfuscates on valid query', () => { + expect( + obfuscate('sum(sum_over_time({test="test"} |= `` | logfmt | __error__=`` | unwrap test | __error__=`` [10m]))') + ).toEqual( + 'sum(sum_over_time({Identifier=String} |= String | logfmt | __error__=String | unwrap Identifier | __error__=String [10m]))' + ); + }); + it('obfuscates on arithmetic operation', () => { + expect(obfuscate('2 + 3')).toEqual('Number + Number'); + }); + it('obfuscates a comment', () => { + expect(obfuscate('{job="grafana"} # test comment')).toEqual('{Identifier=String} LineComment'); + }); + it('does not obfuscate interval variables', () => { + expect( + obfuscate( + 'sum(quantile_over_time(0.5, {label="$var"} | logfmt | __error__=`` | unwrap latency | __error__=`` [$__interval]))' + ) + ).toEqual( + 'sum(quantile_over_time(Number, {Identifier=String} | logfmt | __error__=String | unwrap Identifier | __error__=String [$__interval]))' + ); + }); +}); + describe('isLogsQuery', () => { it('returns false if metrics query', () => { expect(isLogsQuery('rate({job="grafana"}[5m])')).toBe(false); diff --git a/public/app/plugins/datasource/loki/queryUtils.ts b/public/app/plugins/datasource/loki/queryUtils.ts index 7922998f2a4..a5a1444b86e 100644 --- a/public/app/plugins/datasource/loki/queryUtils.ts +++ b/public/app/plugins/datasource/loki/queryUtils.ts @@ -108,6 +108,22 @@ export function getNormalizedLokiQuery(query: LokiQuery): LokiQuery { return { ...rest, queryType: LokiQueryType.Range }; } +const tagsToObscure = ['String', 'Identifier', 'LineComment', 'Number']; +const partsToKeep = ['__error__', '__interval', '__interval_ms']; +export function obfuscate(query: string): string { + let obfuscatedQuery: string = query; + const tree = parser.parse(query); + tree.iterate({ + enter: ({ name, from, to }): false | void => { + const queryPart = query.substring(from, to); + if (tagsToObscure.includes(name) && !partsToKeep.includes(queryPart)) { + obfuscatedQuery = obfuscatedQuery.replace(queryPart, name); + } + }, + }); + return obfuscatedQuery; +} + export function parseToNodeNamesArray(query: string): string[] { const queryParts: string[] = []; const tree = parser.parse(query); diff --git a/public/app/plugins/datasource/loki/tracking.ts b/public/app/plugins/datasource/loki/tracking.ts index ae273601bd6..a7abeff1aec 100644 --- a/public/app/plugins/datasource/loki/tracking.ts +++ b/public/app/plugins/datasource/loki/tracking.ts @@ -11,7 +11,7 @@ import { REF_ID_STARTER_LOG_VOLUME, } from './datasource'; import pluginJson from './plugin.json'; -import { getNormalizedLokiQuery, isLogsQuery, parseToNodeNamesArray } from './queryUtils'; +import { getNormalizedLokiQuery, isLogsQuery, obfuscate, parseToNodeNamesArray } from './queryUtils'; import { LokiQuery, LokiQueryType } from './types'; type LokiOnDashboardLoadedTrackingEvent = { @@ -167,6 +167,7 @@ export function trackQuery( legend: query.legendFormat, line_limit: query.maxLines, parsed_query: parseToNodeNamesArray(query.expr).join(','), + obfuscated_query: obfuscate(query.expr), query_type: isLogsQuery(query.expr) ? 'logs' : 'metric', query_vector_type: query.queryType, resolution: query.resolution,