Commit 8ef961bb by Charlie Briggs Committed by Torkel Ödegaard

Prometheus: Correctly escape '|' literals in interpolated PromQL variables (#16932)

This escape is currently not happening which breaks multi-value queries with
'|' characters in the variable value.

Added tests for 'interpolateQueryExpr' which should possibly be private, but is difficult to
test due to TemplateSrv calling it.

Closes #16840
parent d1ab29c3
...@@ -701,7 +701,7 @@ export function prometheusRegularEscape(value) { ...@@ -701,7 +701,7 @@ export function prometheusRegularEscape(value) {
export function prometheusSpecialRegexEscape(value) { export function prometheusSpecialRegexEscape(value) {
if (typeof value === 'string') { if (typeof value === 'string') {
return prometheusRegularEscape(value.replace(/\\/g, '\\\\\\\\').replace(/[$^*{}\[\]+?.()]/g, '\\\\$&')); return prometheusRegularEscape(value.replace(/\\/g, '\\\\\\\\').replace(/[$^*{}\[\]+?.()|]/g, '\\\\$&'));
} }
return value; return value;
} }
...@@ -12,6 +12,7 @@ import { DataSourceInstanceSettings } from '@grafana/ui'; ...@@ -12,6 +12,7 @@ import { DataSourceInstanceSettings } from '@grafana/ui';
import { PromOptions } from '../types'; import { PromOptions } from '../types';
import { TemplateSrv } from 'app/features/templating/template_srv'; import { TemplateSrv } from 'app/features/templating/template_srv';
import { TimeSrv } from 'app/features/dashboard/services/TimeSrv'; import { TimeSrv } from 'app/features/dashboard/services/TimeSrv';
import { CustomVariable } from 'app/features/templating/custom_variable';
jest.mock('../metric_find_query'); jest.mock('../metric_find_query');
...@@ -287,7 +288,7 @@ describe('PrometheusDatasource', () => { ...@@ -287,7 +288,7 @@ describe('PrometheusDatasource', () => {
it('should not escape simple string', () => { it('should not escape simple string', () => {
expect(prometheusSpecialRegexEscape('cryptodepression')).toEqual('cryptodepression'); expect(prometheusSpecialRegexEscape('cryptodepression')).toEqual('cryptodepression');
}); });
it('should escape $^*+?.()\\', () => { it('should escape $^*+?.()|\\', () => {
expect(prometheusSpecialRegexEscape("looking'glass")).toEqual("looking\\\\'glass"); expect(prometheusSpecialRegexEscape("looking'glass")).toEqual("looking\\\\'glass");
expect(prometheusSpecialRegexEscape('looking{glass')).toEqual('looking\\\\{glass'); expect(prometheusSpecialRegexEscape('looking{glass')).toEqual('looking\\\\{glass');
expect(prometheusSpecialRegexEscape('looking}glass')).toEqual('looking\\\\}glass'); expect(prometheusSpecialRegexEscape('looking}glass')).toEqual('looking\\\\}glass');
...@@ -302,12 +303,60 @@ describe('PrometheusDatasource', () => { ...@@ -302,12 +303,60 @@ describe('PrometheusDatasource', () => {
expect(prometheusSpecialRegexEscape('looking(glass')).toEqual('looking\\\\(glass'); expect(prometheusSpecialRegexEscape('looking(glass')).toEqual('looking\\\\(glass');
expect(prometheusSpecialRegexEscape('looking)glass')).toEqual('looking\\\\)glass'); expect(prometheusSpecialRegexEscape('looking)glass')).toEqual('looking\\\\)glass');
expect(prometheusSpecialRegexEscape('looking\\glass')).toEqual('looking\\\\\\\\glass'); expect(prometheusSpecialRegexEscape('looking\\glass')).toEqual('looking\\\\\\\\glass');
expect(prometheusSpecialRegexEscape('looking|glass')).toEqual('looking\\\\|glass');
}); });
it('should escape multiple special characters', () => { it('should escape multiple special characters', () => {
expect(prometheusSpecialRegexEscape('+looking$glass?')).toEqual('\\\\+looking\\\\$glass\\\\?'); expect(prometheusSpecialRegexEscape('+looking$glass?')).toEqual('\\\\+looking\\\\$glass\\\\?');
}); });
}); });
describe('When interpolating variables', () => {
beforeEach(() => {
ctx.ds = new PrometheusDatasource(instanceSettings, q, ctx.backendSrvMock, ctx.templateSrvMock, ctx.timeSrvMock);
ctx.variable = new CustomVariable({}, {});
});
describe('and value is a string', () => {
it('should only escape single quotes', () => {
expect(ctx.ds.interpolateQueryExpr("abc'$^*{}[]+?.()|", ctx.variable)).toEqual("abc\\\\'$^*{}[]+?.()|");
});
});
describe('and value is a number', () => {
it('should return a number', () => {
expect(ctx.ds.interpolateQueryExpr(1000, ctx.variable)).toEqual(1000);
});
});
describe('and variable allows multi-value', () => {
beforeEach(() => {
ctx.variable.multi = true;
});
it('should regex escape values if the value is a string', () => {
expect(ctx.ds.interpolateQueryExpr('looking*glass', ctx.variable)).toEqual('looking\\\\*glass');
});
it('should return pipe separated values if the value is an array of strings', () => {
expect(ctx.ds.interpolateQueryExpr(['a|bc', 'de|f'], ctx.variable)).toEqual('a\\\\|bc|de\\\\|f');
});
});
describe('and variable allows all', () => {
beforeEach(() => {
ctx.variable.includeAll = true;
});
it('should regex escape values if the array is a string', () => {
expect(ctx.ds.interpolateQueryExpr('looking*glass', ctx.variable)).toEqual('looking\\\\*glass');
});
it('should return pipe separated values if the value is an array of strings', () => {
expect(ctx.ds.interpolateQueryExpr(['a|bc', 'de|f'], ctx.variable)).toEqual('a\\\\|bc|de\\\\|f');
});
});
});
describe('metricFindQuery', () => { describe('metricFindQuery', () => {
beforeEach(() => { beforeEach(() => {
const query = 'query_result(topk(5,rate(http_request_duration_microseconds_count[$__interval])))'; const query = 'query_result(topk(5,rate(http_request_duration_microseconds_count[$__interval])))';
...@@ -418,7 +467,7 @@ describe('PrometheusDatasource', () => { ...@@ -418,7 +467,7 @@ describe('PrometheusDatasource', () => {
expect(results.data[0].target).toBe('test{job="testjob"}'); expect(results.data[0].target).toBe('test{job="testjob"}');
}); });
}); });
describe('When querying prometheus with one target which return multiple series', () => { describe('When querying prometheus with one target which returns multiple series', () => {
let results; let results;
const start = 60; const start = 60;
const end = 360; const end = 360;
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment