diff --git a/packages/grafana-prometheus/src/datasource.test.ts b/packages/grafana-prometheus/src/datasource.test.ts index 664206c9f1f..3e34926e944 100644 --- a/packages/grafana-prometheus/src/datasource.test.ts +++ b/packages/grafana-prometheus/src/datasource.test.ts @@ -289,6 +289,16 @@ describe('PrometheusDatasource', () => { operator: '=~', value: `v'.*`, }, + { + key: 'k3', + operator: '=~', + value: `v".*`, + }, + { + key: 'k4', + operator: '=~', + value: `\\v.*`, + }, ]; ds.query({ interval: '15s', @@ -298,7 +308,7 @@ describe('PrometheusDatasource', () => { } as DataQueryRequest); const [result] = fetchMockCalledWith(fetchMock); expect(result).toMatchObject({ - expr: `metric{job="foo", k1=~"v.*", k2=~"v\\\\'.*"} - metric{k1=~"v.*", k2=~"v\\\\'.*"}`, + expr: `metric{job="foo", k1=~"v.*", k2=~"v'.*", k3=~"v\\".*", k4=~"\\\\v.*"} - metric{k1=~"v.*", k2=~"v'.*", k3=~"v\\".*", k4=~"\\\\v.*"}`, }); }); }); @@ -478,8 +488,12 @@ describe('PrometheusDatasource', () => { expect(prometheusRegularEscape('cryptodepression')).toEqual('cryptodepression'); }); - it("should escape '", () => { - expect(prometheusRegularEscape("looking'glass")).toEqual("looking\\\\'glass"); + it("should not escape '", () => { + expect(prometheusRegularEscape("looking'glass")).toEqual("looking'glass"); + }); + + it('should escape "', () => { + expect(prometheusRegularEscape('looking"glass')).toEqual('looking\\"glass'); }); it('should escape \\', () => { @@ -487,11 +501,11 @@ describe('PrometheusDatasource', () => { }); it('should escape multiple characters', () => { - expect(prometheusRegularEscape("'looking'glass'")).toEqual("\\\\'looking\\\\'glass\\\\'"); + expect(prometheusRegularEscape('"looking"glass"')).toEqual('\\"looking\\"glass\\"'); }); it('should escape multiple different characters', () => { - expect(prometheusRegularEscape("'loo\\king'glass'")).toEqual("\\\\'loo\\\\king\\\\'glass\\\\'"); + expect(prometheusRegularEscape('"loo\\king"glass"')).toEqual('\\"loo\\\\king\\"glass\\"'); }); }); @@ -500,8 +514,9 @@ describe('PrometheusDatasource', () => { 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'); @@ -549,8 +564,8 @@ describe('PrometheusDatasource', () => { }); describe('and value is a string', () => { - it('should only escape single quotes', () => { - expect(ds.interpolateQueryExpr("abc'$^*{}[]+?.()|", customVariable)).toEqual("abc\\\\'$^*{}[]+?.()|"); + it('should only escape double quotes and backslashes', () => { + expect(ds.interpolateQueryExpr('abc\'"$^*{}[]+?.()|\\', customVariable)).toEqual('abc\'\\"$^*{}[]+?.()|\\\\'); }); }); diff --git a/packages/grafana-prometheus/src/datasource.ts b/packages/grafana-prometheus/src/datasource.ts index 214ca305e91..8009f33a952 100644 --- a/packages/grafana-prometheus/src/datasource.ts +++ b/packages/grafana-prometheus/src/datasource.ts @@ -640,7 +640,7 @@ export class PrometheusDatasource const labelFilters: QueryBuilderLabelFilter[] = options.filters.map((f) => ({ label: f.key, - value: f.value, + value: prometheusRegularEscape(f.value), op: f.operator, })); const expr = promQueryModeller.renderLabels(labelFilters); @@ -672,7 +672,7 @@ export class PrometheusDatasource const labelFilters: QueryBuilderLabelFilter[] = options.filters.map((f) => ({ label: f.key, - value: f.value, + value: prometheusRegularEscape(f.value), op: f.operator, })); @@ -1045,9 +1045,14 @@ export function extractRuleMappingFromGroups(groups: RawRecordingRules[]): RuleQ // in language_utils.ts, but they are not exactly the same algorithm, and we found // no way to reuse one in the another or vice versa. export function prometheusRegularEscape(value: T) { - return typeof value === 'string' ? value.replace(/\\/g, '\\\\').replace(/'/g, "\\\\'") : value; + return typeof value === 'string' ? value.replace(/\\/g, '\\\\').replace(/"/g, '\\"') : value; } export function prometheusSpecialRegexEscape(value: T) { - return typeof value === 'string' ? value.replace(/\\/g, '\\\\\\\\').replace(/[$^*{}\[\]\'+?.()|]/g, '\\\\$&') : value; + return typeof value === 'string' + ? value + .replace(/\\/g, '\\\\\\\\') + .replace(/"/g, '\\\\\\"') + .replace(/[$^*{}\[\]\'+?.()|]/g, '\\\\$&') + : value; }