Tempo: Properly escape double quotes in TraceQL query (#106909)
* Properly escape double quotes * Simplify regex
This commit is contained in:
@@ -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(
|
||||||
|
|||||||
Reference in New Issue
Block a user