Skip to content

Commit be44299

Browse files
committed
fix: allow properly quoted LoqQL querys in the selector field.
fixes https://redhat.atlassian.net/browse/RHDHSUPP-381 assisted by cursor
1 parent 22f75ad commit be44299

4 files changed

Lines changed: 99 additions & 9 deletions

File tree

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
---
2+
'@red-hat-developer-hub/backstage-plugin-orchestrator-backend-module-loki': patch
3+
---
4+
5+
Fix to allow properly quoted LoqQL querys in the selector field

workspaces/orchestrator/plugins/orchestrator-backend-module-loki/src/workflowLogsProviders/LokiProvider.test.ts

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -159,7 +159,7 @@ describe('LokiProvider', () => {
159159
},
160160
}),
161161
),
162-
).toThrow(/must not contain "\}"/);
162+
).toThrow(/must not contain unquoted "\{" or "\}"/);
163163
});
164164

165165
it('rejects logPipelineFilters containing opening brace', () => {
@@ -171,13 +171,13 @@ describe('LokiProvider', () => {
171171
loki: {
172172
baseUrl: 'http://localhost:3100',
173173
token: 't',
174-
logPipelineFilters: ['| pattern `{stream}`'],
174+
logPipelineFilters: ['| foo {bar="baz"}'],
175175
},
176176
},
177177
},
178178
}),
179179
),
180-
).toThrow(/must not contain "\{"/);
180+
).toThrow(/must not contain unquoted "\{" or "\}"/);
181181
});
182182

183183
it('rejects logPipelineFilters entries that trim to empty (whitespace-only)', () => {

workspaces/orchestrator/plugins/orchestrator-backend-module-loki/src/workflowLogsProviders/helpers.test.ts

Lines changed: 41 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -79,6 +79,47 @@ describe('parseAndValidateLogPipelineFilters', () => {
7979
/orchestrator\.workflowLogProvider\.loki\.logPipelineFilters\[0\]: entry must not contain line breaks/,
8080
);
8181
});
82+
83+
it('accepts line_format with Go template braces inside double quotes', () => {
84+
const mockConfig = {
85+
getOptionalStringArray: () => ['| line_format "{{.message}}"'],
86+
} as unknown as Config;
87+
expect(
88+
parseAndValidateLogPipelineFilters(
89+
mockConfig,
90+
'orchestrator.workflowLogProvider.loki.logPipelineFilters',
91+
),
92+
).toEqual(['| line_format "{{.message}}"']);
93+
});
94+
95+
it('accepts braces inside backtick-quoted pattern literals', () => {
96+
const mockConfig = {
97+
getOptionalStringArray: () => ['| pattern `{stream}`'],
98+
} as unknown as Config;
99+
expect(
100+
parseAndValidateLogPipelineFilters(
101+
mockConfig,
102+
'orchestrator.workflowLogProvider.loki.logPipelineFilters',
103+
),
104+
).toEqual(['| pattern `{stream}`']);
105+
});
106+
107+
it.each([['| json }'], ['| foo {bar="baz"}']])(
108+
'rejects unquoted braces: %s',
109+
raw => {
110+
const mockConfig = {
111+
getOptionalStringArray: () => [raw],
112+
} as unknown as Config;
113+
expect(() =>
114+
parseAndValidateLogPipelineFilters(
115+
mockConfig,
116+
'orchestrator.workflowLogProvider.loki.logPipelineFilters',
117+
),
118+
).toThrow(
119+
/orchestrator\.workflowLogProvider\.loki\.logPipelineFilters\[0\]: entry must not contain unquoted "\{" or "\}"/,
120+
);
121+
},
122+
);
82123
});
83124

84125
describe('hostnameMatchesAllowedHosts', () => {

workspaces/orchestrator/plugins/orchestrator-backend-module-loki/src/workflowLogsProviders/helpers.ts

Lines changed: 50 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -178,6 +178,55 @@ export function parseAndValidateLogStreamSelectors(
178178
});
179179
}
180180

181+
/**
182+
* Rejects `{` / `}` outside quoted LogQL literals so pipeline config cannot
183+
* close the stream selector (`{...}`) and inject a new selector. Braces inside
184+
* `"..."` or `` `...` `` (e.g. `line_format "{{.message}}"`) are allowed.
185+
*/
186+
function assertNoUnquotedLogQlBraces(element: string, context: string): void {
187+
let i = 0;
188+
while (i < element.length) {
189+
const ch = element[i];
190+
if (ch === '"') {
191+
i = skipQuotedString(element, i + 1, '"', context);
192+
continue;
193+
}
194+
if (ch === '`') {
195+
i = skipQuotedString(element, i + 1, '`', context);
196+
continue;
197+
}
198+
if (ch === '{' || ch === '}') {
199+
throw new InputError(
200+
`${context}: entry must not contain unquoted "{" or "}"`,
201+
);
202+
}
203+
i++;
204+
}
205+
}
206+
207+
function skipQuotedString(
208+
element: string,
209+
start: number,
210+
quote: '"' | '`',
211+
context: string,
212+
): number {
213+
let i = start;
214+
while (i < element.length) {
215+
const ch = element[i];
216+
if (quote === '"' && ch === '\\') {
217+
i += 2;
218+
continue;
219+
}
220+
if (ch === quote) {
221+
return i + 1;
222+
}
223+
i++;
224+
}
225+
throw new InputError(
226+
`${context}: entry contains an unclosed ${quote === '"' ? 'double-quoted' : 'backtick-quoted'} string`,
227+
);
228+
}
229+
181230
/**
182231
* Reads and validates `logPipelineFilters` at startup.
183232
*/
@@ -200,12 +249,7 @@ export function parseAndValidateLogPipelineFilters(
200249
if (/[\r\n\u2028\u2029]/.test(element)) {
201250
throw new InputError(`${ctx}: entry must not contain line breaks`);
202251
}
203-
if (element.includes('{')) {
204-
throw new InputError(`${ctx}: entry must not contain "{"`);
205-
}
206-
if (element.includes('}')) {
207-
throw new InputError(`${ctx}: entry must not contain "}"`);
208-
}
252+
assertNoUnquotedLogQlBraces(element, ctx);
209253
return element;
210254
});
211255
}

0 commit comments

Comments
 (0)