Tempo: Properly escape double quotes in TraceQL query (#106909)

* Properly escape double quotes

* Simplify regex
pull/103755/head
Joey 3 days ago committed by GitHub
parent 3503fc209e
commit 1f7cfda03c
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
  1. 15
      public/app/plugins/datasource/tempo/SearchTraceQLEditor/utils.ts
  2. 7
      public/app/plugins/datasource/tempo/datasource.test.ts
  3. 10
      public/app/plugins/datasource/tempo/datasource.ts
  4. 2
      public/app/plugins/datasource/tempo/language_provider.test.ts

@ -5,7 +5,7 @@ import { getTemplateSrv } from '@grafana/runtime';
import { VariableFormatID } from '@grafana/schema'; import { VariableFormatID } from '@grafana/schema';
import { TraceqlFilter, TraceqlSearchScope } from '../dataquery.gen'; import { TraceqlFilter, TraceqlSearchScope } from '../dataquery.gen';
import { getEscapedSpanNames } from '../datasource'; import { getEscapedRegexValues, getEscapedValues } from '../datasource';
import TempoLanguageProvider from '../language_provider'; import TempoLanguageProvider from '../language_provider';
import { intrinsics } from '../traceql/traceql'; import { intrinsics } from '../traceql/traceql';
import { Scope } from '../types'; import { Scope } from '../types';
@ -32,11 +32,16 @@ export const interpolateFilters = (filters: TraceqlFilter[], scopedVars?: Scoped
const isRegExpOperator = (operator: string) => operator === '=~' || operator === '!~'; const isRegExpOperator = (operator: string) => operator === '=~' || operator === '!~';
const escapeValues = (values: string[]) => getEscapedSpanNames(values);
export const valueHelper = (f: TraceqlFilter) => { export const valueHelper = (f: TraceqlFilter) => {
const value = let value = f.value;
Array.isArray(f.value) && isRegExpOperator(f.operator!) && !f.isCustomValue ? escapeValues(f.value) : f.value;
if (Array.isArray(value) && !f.isCustomValue) {
value = getEscapedValues(value);
if (isRegExpOperator(f.operator!)) {
value = getEscapedRegexValues(value);
}
}
if (Array.isArray(value) && value.length > 1) { if (Array.isArray(value) && value.length > 1) {
return `"${value.join('|')}"`; return `"${value.join('|')}"`;

@ -41,7 +41,8 @@ import {
makeServiceGraphViewRequest, makeServiceGraphViewRequest,
makeTempoLink, makeTempoLink,
getFieldConfig, getFieldConfig,
getEscapedSpanNames, getEscapedRegexValues,
getEscapedValues,
makeHistogramLink, makeHistogramLink,
makePromServiceMapRequest, makePromServiceMapRequest,
} from './datasource'; } from './datasource';
@ -778,12 +779,12 @@ describe('Tempo service graph view', () => {
'server.cluster.local:9090^/sample.test(.*)?', 'server.cluster.local:9090^/sample.test(.*)?',
'test\\path', 'test\\path',
]; ];
let escaped = getEscapedSpanNames(spanNames); let escaped = getEscapedRegexValues(getEscapedValues(spanNames));
expect(escaped).toEqual([ expect(escaped).toEqual([
'/actuator/health/\\\\*\\\\*', '/actuator/health/\\\\*\\\\*',
'\\\\$type \\\\+ \\\\[test\\\\]\\\\|HTTP POST - post', '\\\\$type \\\\+ \\\\[test\\\\]\\\\|HTTP POST - post',
'server\\\\.cluster\\\\.local:9090\\\\^/sample\\\\.test\\\\(\\\\.\\\\*\\\\)\\\\?', 'server\\\\.cluster\\\\.local:9090\\\\^/sample\\\\.test\\\\(\\\\.\\\\*\\\\)\\\\?',
'test\\\\\\\\path', 'test\\\\path',
]); ]);
}); });

@ -1081,7 +1081,7 @@ function errorAndDurationQuery(
} }
}); });
} }
const spanNames = getEscapedSpanNames(labels); const spanNames = getEscapedRegexValues(getEscapedValues(labels));
if (spanNames.length > 0) { if (spanNames.length > 0) {
errorRateBySpanName = buildExpr(errorRateMetric, 'span_name=~"' + spanNames.join('|') + '"', request); errorRateBySpanName = buildExpr(errorRateMetric, 'span_name=~"' + spanNames.join('|') + '"', request);
@ -1151,8 +1151,12 @@ function makePromLink(title: string, expr: string, datasourceUid: string, instan
// TODO: this is basically the same as prometheus/datasource.ts#prometheusSpecialRegexEscape which is used to escape // TODO: this is basically the same as prometheus/datasource.ts#prometheusSpecialRegexEscape which is used to escape
// template variable values. It would be best to move it to some common place. // template variable values. It would be best to move it to some common place.
export function getEscapedSpanNames(values: string[]) { export function getEscapedRegexValues(values: string[]) {
return values.map((value: string) => value.replace(/\\/g, '\\\\\\\\').replace(/[$^*{}\[\]\'+?.()|]/g, '\\\\$&')); return values.map((value: string) => value.replace(/[$^*{}\[\]\'+?.()|]/g, '\\\\$&'));
}
export function getEscapedValues(values: string[]) {
return values.map((value: string) => value.replace(/["\\]/g, '\\$&'));
} }
export function getFieldConfig( export function getFieldConfig(

@ -174,7 +174,7 @@ describe('Language_provider', () => {
}, },
], ],
}) })
).toBe(`{name${operator}"api/v2/variants/by-upc/\\\\(\\\\?P<upc>\\\\[\\\\\\\\s\\\\\\\\S\\\\]\\\\*\\\\)/\\\\$"}`); ).toBe(`{name${operator}"api/v2/variants/by-upc/\\\\(\\\\?P<upc>\\\\[\\\\s\\\\S\\\\]\\\\*\\\\)/\\\\$"}`);
}); });
it('two fields with everything filled in', () => { it('two fields with everything filled in', () => {
expect( expect(

Loading…
Cancel
Save