Alerting: Don't crash the page when trying to filter rules by regex (#89466)

This commit is contained in:
Tom Ratcliffe 2024-06-20 16:24:49 +01:00 committed by GitHub
parent 27e800768e
commit c88de7f4d0
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
2 changed files with 43 additions and 3 deletions

View File

@ -256,4 +256,26 @@ describe('filterRules', function () {
expect(filtered[0]?.groups[0]?.rules).toHaveLength(1);
expect(filtered[0]?.groups[0]?.rules[0]?.name).toBe('CPU too high');
});
it('does not crash when trying to filter with regex-like strings', () => {
const rules = [mockCombinedRule({ name: '[alongnameinthefirstgroup]' })];
const ns = mockCombinedRuleNamespace({
name: 'foo|bar',
groups: [
// Create group with regex-like name so we can test that searching for it doesn't crash,
// and so we can test further paths of the filtering
// (we need some a group to be matched so we can test filtering by rule name as well)
mockCombinedRuleGroup('some|group', rules),
],
});
const ruleQuery = '[alongnameinthefirstgroup][thishas spaces][somethingelse]';
const namespaceQuery = 'foo|bar';
const groupQuery = 'some|group';
const performFilter = () =>
filterRules([ns], getFilter({ groupName: groupQuery, ruleName: ruleQuery, namespace: namespaceQuery }));
expect(performFilter).not.toThrow();
});
});

View File

@ -28,6 +28,19 @@ import { useURLSearchParams } from './useURLSearchParams';
const MAX_NEEDLE_SIZE = 25;
const INFO_THRESHOLD = Infinity;
/**
* Escape query strings so that regex characters don't interfere
* with uFuzzy search methods.
*
* The fuzzy searching will take the query and generate a regex - but if the query
* contains a regex itself, then it can easily end up being split in a bad place
* and end up creating an invalid expression
*/
const escapeQueryRegex = (query: string) => {
// see https://stackoverflow.com/a/6969486
return query.replace(/[.*+?^${}()|[\]\\]/g, '\\$&');
};
export function useRulesFilter() {
const [queryParams, updateQueryParams] = useURLSearchParams();
const searchQuery = queryParams.get('search') ?? '';
@ -130,10 +143,12 @@ export const filterRules = (
if (namespaceFilter) {
const namespaceHaystack = filteredNamespaces.map((ns) => ns.name);
const escapedQuery = escapeQueryRegex(namespaceFilter);
const ufuzzy = getSearchInstance(namespaceFilter);
const [idxs, info, order] = ufuzzy.search(
namespaceHaystack,
namespaceFilter,
escapedQuery,
getOutOfOrderLimit(namespaceFilter),
INFO_THRESHOLD
);
@ -157,9 +172,11 @@ const reduceNamespaces = (filterState: RulesFilter) => {
const groupsHaystack = filteredGroups.map((g) => g.name);
const ufuzzy = getSearchInstance(groupNameFilter);
const escapedQuery = escapeQueryRegex(groupNameFilter);
const [idxs, info, order] = ufuzzy.search(
groupsHaystack,
groupNameFilter,
escapedQuery,
getOutOfOrderLimit(groupNameFilter),
INFO_THRESHOLD
);
@ -193,10 +210,11 @@ const reduceGroups = (filterState: RulesFilter) => {
if (ruleNameQuery) {
const rulesHaystack = filteredRules.map((r) => r.name);
const ufuzzy = getSearchInstance(ruleNameQuery);
const escapedQuery = escapeQueryRegex(ruleNameQuery);
const [idxs, info, order] = ufuzzy.search(
rulesHaystack,
ruleNameQuery,
escapedQuery,
getOutOfOrderLimit(ruleNameQuery),
INFO_THRESHOLD
);