mirror of
https://github.com/grafana/grafana.git
synced 2025-02-25 18:55:37 -06:00
Loki: Fix wrongly escaped label values when using LabelFilter (#59812)
* change backticks to quotes * add unescaping to `addFilterAsLabelFilter` * fix removal of wrong quotes * change unescape order
This commit is contained in:
parent
78ef55eb06
commit
932349b5ab
@ -1,4 +1,4 @@
|
||||
import { isBytesString } from './languageUtils';
|
||||
import { escapeLabelValueInExactSelector, isBytesString, unescapeLabelValue } from './languageUtils';
|
||||
|
||||
describe('isBytesString', () => {
|
||||
it('correctly matches bytes string with integers', () => {
|
||||
@ -18,3 +18,27 @@ describe('isBytesString', () => {
|
||||
expect(isBytesString('1.234')).toBe(false);
|
||||
});
|
||||
});
|
||||
|
||||
describe('escapeLabelValueInExactSelector', () => {
|
||||
it.each`
|
||||
value | escapedValue
|
||||
${'nothing to escape'} | ${'nothing to escape'}
|
||||
${'escape quote: "'} | ${'escape quote: \\"'}
|
||||
${'escape newline: \nend'} | ${'escape newline: \\nend'}
|
||||
${'escape slash: \\'} | ${'escape slash: \\\\'}
|
||||
`('when called with $value', ({ value, escapedValue }) => {
|
||||
expect(escapeLabelValueInExactSelector(value)).toEqual(escapedValue);
|
||||
});
|
||||
});
|
||||
|
||||
describe('unescapeLabelValueInExactSelector', () => {
|
||||
it.each`
|
||||
value | unescapedValue
|
||||
${'nothing to unescape'} | ${'nothing to unescape'}
|
||||
${'escape quote: \\"'} | ${'escape quote: "'}
|
||||
${'escape newline: \\nend'} | ${'escape newline: \nend'}
|
||||
${'escape slash: \\\\'} | ${'escape slash: \\'}
|
||||
`('when called with $value', ({ value, unescapedValue }) => {
|
||||
expect(unescapeLabelValue(value)).toEqual(unescapedValue);
|
||||
});
|
||||
});
|
||||
|
@ -35,6 +35,10 @@ export function escapeLabelValueInExactSelector(labelValue: string): string {
|
||||
return labelValue.replace(/\\/g, '\\\\').replace(/\n/g, '\\n').replace(/"/g, '\\"');
|
||||
}
|
||||
|
||||
export function unescapeLabelValue(labelValue: string): string {
|
||||
return labelValue.replace(/\\n/g, '\n').replace(/\\"/g, '"').replace(/\\\\/g, '\\');
|
||||
}
|
||||
|
||||
export function escapeLabelValueInRegexSelector(labelValue: string): string {
|
||||
return escapeLabelValueInExactSelector(escapeLokiRegexp(labelValue));
|
||||
}
|
||||
|
@ -44,6 +44,14 @@ describe('addLabelToQuery()', () => {
|
||||
${'{} | logfmt | x="y"'} | ${'query without stream selector and with parser and label filter'} | ${'bar'} | ${'='} | ${'baz'} | ${'{bar="baz"}| logfmt | x="y"'}
|
||||
${'sum(rate({x="y"} [5m])) + sum(rate({} | logfmt [5m]))'} | ${'metric query with 1 empty and 1 not empty stream selector with parser'} | ${'bar'} | ${'='} | ${'baz'} | ${'sum(rate({x="y", bar="baz"} [5m])) + sum(rate({bar="baz"}| logfmt [5m]))'}
|
||||
${'sum(rate({x="y"} | logfmt [5m])) + sum(rate({} [5m]))'} | ${'metric query with 1 non-empty and 1 not empty stream selector with parser'} | ${'bar'} | ${'='} | ${'baz'} | ${'sum(rate({x="y", bar="baz"} | logfmt [5m])) + sum(rate({bar="baz"}[5m]))'}
|
||||
${'{x="yy"}'} | ${'simple query with escaped value'} | ${'bar'} | ${'='} | ${'"baz"'} | ${'{x="yy", bar=""baz""}'}
|
||||
${'{x="yy"}'} | ${'simple query with escaped value'} | ${'bar'} | ${'='} | ${'\\"baz\\"'} | ${'{x="yy", bar="\\"baz\\""}'}
|
||||
${'{x="yy"}'} | ${'simple query with an other escaped value'} | ${'bar'} | ${'='} | ${'baz\\\\'} | ${'{x="yy", bar="baz\\\\"}'}
|
||||
${'{x="yy"}'} | ${'simple query with escaped value and regex operator'} | ${'bar'} | ${'~='} | ${'baz\\\\'} | ${'{x="yy", bar~="baz\\\\"}'}
|
||||
${'{foo="bar"} | logfmt'} | ${'query with parser with escaped value'} | ${'bar'} | ${'='} | ${'\\"baz\\"'} | ${'{foo="bar"} | logfmt | bar=`"baz"`'}
|
||||
${'{foo="bar"} | logfmt'} | ${'query with parser with an other escaped value'} | ${'bar'} | ${'='} | ${'baz\\\\'} | ${'{foo="bar"} | logfmt | bar=`baz\\`'}
|
||||
${'{foo="bar"} | logfmt'} | ${'query with parser with escaped value and regex operator'} | ${'bar'} | ${'~='} | ${'\\"baz\\"'} | ${'{foo="bar"} | logfmt | bar~=`"baz"`'}
|
||||
${'{foo="bar"} | logfmt'} | ${'query with parser with escaped value and regex operator'} | ${'bar'} | ${'~='} | ${'\\"baz\\"'} | ${'{foo="bar"} | logfmt | bar~=`"baz"`'}
|
||||
`(
|
||||
'should add label to query: $query, description: $description',
|
||||
({ query, description, label, operator, value, expectedResult }) => {
|
||||
|
@ -18,6 +18,7 @@ import {
|
||||
|
||||
import { QueryBuilderLabelFilter } from '../prometheus/querybuilder/shared/types';
|
||||
|
||||
import { unescapeLabelValue } from './languageUtils';
|
||||
import { LokiQueryModeller } from './querybuilder/LokiQueryModeller';
|
||||
import { buildVisualQueryFromString } from './querybuilder/parsing';
|
||||
|
||||
@ -329,7 +330,9 @@ function addFilterAsLabelFilter(
|
||||
const start = query.substring(prev, match.to);
|
||||
const end = isLast ? query.substring(match.to) : '';
|
||||
|
||||
const labelFilter = ` | ${filter.label}${filter.op}\`${filter.value}\``;
|
||||
// we now unescape all escaped values again, because we are using backticks which can handle those cases.
|
||||
// we also don't care about the operator here, because we need to unescape for both, regex and equal.
|
||||
const labelFilter = ` | ${filter.label}${filter.op}\`${unescapeLabelValue(filter.value)}\``;
|
||||
newQuery += start + labelFilter + end;
|
||||
prev = match.to;
|
||||
}
|
||||
|
@ -571,6 +571,21 @@ describe('buildVisualQueryFromString', () => {
|
||||
})
|
||||
);
|
||||
});
|
||||
|
||||
it('parses simple query with quotes in label value', () => {
|
||||
expect(buildVisualQueryFromString('{app="\\"frontend\\""}')).toEqual(
|
||||
noErrors({
|
||||
labels: [
|
||||
{
|
||||
op: '=',
|
||||
value: '\\"frontend\\"',
|
||||
label: 'app',
|
||||
},
|
||||
],
|
||||
operations: [],
|
||||
})
|
||||
);
|
||||
});
|
||||
});
|
||||
|
||||
function noErrors(query: LokiVisualQuery) {
|
||||
|
@ -212,7 +212,9 @@ function getLabel(expr: string, node: SyntaxNode): QueryBuilderLabelFilter {
|
||||
const labelNode = node.getChild(Identifier);
|
||||
const label = getString(expr, labelNode);
|
||||
const op = getString(expr, labelNode!.nextSibling);
|
||||
const value = getString(expr, node.getChild(String)).replace(/"/g, '');
|
||||
let value = getString(expr, node.getChild(String));
|
||||
// `value` is wrapped in double quotes, so we need to remove them. As a value can contain double quotes, we can't use RegEx here.
|
||||
value = value.substring(1, value.length - 1);
|
||||
|
||||
return {
|
||||
label,
|
||||
|
Loading…
Reference in New Issue
Block a user