Skip to content

Commit db711d6

Browse files
authored
Alerting: Allow groups and namespaces with slashes (grafana#92183)
* Allow rule groups and namespaces with slashes * Fix lint
1 parent 130a86d commit db711d6

File tree

7 files changed

+18
-58
lines changed

7 files changed

+18
-58
lines changed

public/app/features/alerting/unified/api/ruler.ts

Lines changed: 7 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,7 @@ import { FetchResponse, getBackendSrv } from '@grafana/runtime';
55
import { RulerDataSourceConfig } from 'app/types/unified-alerting';
66
import { PostableRulerRuleGroupDTO, RulerRuleGroupDTO, RulerRulesConfigDTO } from 'app/types/unified-alerting-dto';
77

8-
import { checkForPathSeparator } from '../components/rule-editor/util';
8+
import { containsPathSeparator } from '../components/rule-editor/util';
99
import { RULER_NOT_SUPPORTED_MSG } from '../utils/constants';
1010
import { getDatasourceAPIUid, GRAFANA_RULES_SOURCE_NAME } from '../utils/datasource';
1111

@@ -73,11 +73,15 @@ interface RulerQueryDetailsProvider {
7373
group: (group: string) => GroupUrlParams;
7474
}
7575

76+
// some gateways (like Istio) will decode "/" and "\" characters – this will cause 404 errors for any API call
77+
// that includes these values in the URL (ie. /my/path%2fto/resource -> /my/path/to/resource)
78+
//
79+
// see https://istio.io/latest/docs/ops/best-practices/security/#customize-your-system-on-path-normalization
7680
function getQueryDetailsProvider(rulerConfig: RulerDataSourceConfig): RulerQueryDetailsProvider {
7781
const isGrafanaDatasource = rulerConfig.dataSourceName === GRAFANA_RULES_SOURCE_NAME;
7882

7983
const groupParamRewrite = (group: string): GroupUrlParams => {
80-
if (checkForPathSeparator(group) !== true) {
84+
if (containsPathSeparator(group) === true) {
8185
return { group: QUERY_GROUP_TAG, searchParams: { group } };
8286
}
8387
return { group, searchParams: {} };
@@ -93,7 +97,7 @@ function getQueryDetailsProvider(rulerConfig: RulerDataSourceConfig): RulerQuery
9397

9498
return {
9599
namespace: (namespace: string): NamespaceUrlParams => {
96-
if (checkForPathSeparator(namespace) !== true) {
100+
if (containsPathSeparator(namespace) === true) {
97101
return { namespace: QUERY_NAMESPACE_TAG, searchParams: { namespace } };
98102
}
99103
return { namespace, searchParams: {} };

public/app/features/alerting/unified/components/rule-editor/FolderAndGroup.tsx

Lines changed: 0 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -23,7 +23,6 @@ import { evaluateEveryValidationOptions } from '../rules/EditRuleGroupModal';
2323

2424
import { EvaluationGroupQuickPick } from './EvaluationGroupQuickPick';
2525
import { containsSlashes, Folder, RuleFolderPicker } from './RuleFolderPicker';
26-
import { checkForPathSeparator } from './util';
2726

2827
export const MAX_GROUP_RESULTS = 1000;
2928

@@ -175,9 +174,6 @@ export function FolderAndGroup({
175174
name="folder"
176175
rules={{
177176
required: { value: true, message: 'Select a folder' },
178-
validate: {
179-
pathSeparator: (folder: Folder) => checkForPathSeparator(folder.uid),
180-
},
181177
}}
182178
/>
183179
<Text color="secondary">or</Text>
@@ -251,9 +247,6 @@ export function FolderAndGroup({
251247
control={control}
252248
rules={{
253249
required: { value: true, message: 'Must enter a group name' },
254-
validate: {
255-
pathSeparator: (group_: string) => checkForPathSeparator(group_),
256-
},
257250
}}
258251
/>
259252
</Field>

public/app/features/alerting/unified/components/rule-editor/GroupAndNamespaceFields.tsx

Lines changed: 0 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -10,8 +10,6 @@ import { useUnifiedAlertingSelector } from '../../hooks/useUnifiedAlertingSelect
1010
import { fetchRulerRulesAction } from '../../state/actions';
1111
import { RuleFormValues } from '../../types/rule-form';
1212

13-
import { checkForPathSeparator } from './util';
14-
1513
interface Props {
1614
rulesSourceName: string;
1715
}
@@ -74,9 +72,6 @@ export const GroupAndNamespaceFields = ({ rulesSourceName }: Props) => {
7472
control={control}
7573
rules={{
7674
required: { value: true, message: 'Required.' },
77-
validate: {
78-
pathSeparator: checkForPathSeparator,
79-
},
8075
}}
8176
/>
8277
</Field>
@@ -98,9 +93,6 @@ export const GroupAndNamespaceFields = ({ rulesSourceName }: Props) => {
9893
control={control}
9994
rules={{
10095
required: { value: true, message: 'Required.' },
101-
validate: {
102-
pathSeparator: checkForPathSeparator,
103-
},
10496
}}
10597
/>
10698
</Field>

public/app/features/alerting/unified/components/rule-editor/util.test.ts

Lines changed: 8 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,7 @@ import { ClassicCondition, ExpressionQuery } from 'app/features/expressions/type
33
import { AlertQuery } from 'app/types/unified-alerting-dto';
44

55
import {
6-
checkForPathSeparator,
6+
containsPathSeparator,
77
findRenamedDataQueryReferences,
88
getThresholdsForQueries,
99
queriesWithUpdatedReferences,
@@ -229,17 +229,15 @@ describe('rule-editor', () => {
229229
});
230230
});
231231

232-
describe('checkForPathSeparator', () => {
233-
it('should not allow strings with /', () => {
234-
expect(checkForPathSeparator('foo / bar')).not.toBe(true);
235-
expect(typeof checkForPathSeparator('foo / bar')).toBe('string');
232+
describe('containsPathSeparator', () => {
233+
it('should return true for strings with /', () => {
234+
expect(containsPathSeparator('foo / bar')).toBe(true);
236235
});
237-
it('should not allow strings with \\', () => {
238-
expect(checkForPathSeparator('foo \\ bar')).not.toBe(true);
239-
expect(typeof checkForPathSeparator('foo \\ bar')).toBe('string');
236+
it('should return true for strings with \\', () => {
237+
expect(containsPathSeparator('foo \\ bar')).toBe(true);
240238
});
241-
it('should allow anything without / or \\', () => {
242-
expect(checkForPathSeparator('foo bar')).toBe(true);
239+
it('should return false for strings without / or \\', () => {
240+
expect(containsPathSeparator('foo !@#$%^&*() <> [] {} bar')).toBe(false);
243241
});
244242
});
245243

public/app/features/alerting/unified/components/rule-editor/util.ts

Lines changed: 2 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,4 @@
11
import { xor } from 'lodash';
2-
import { ValidateResult } from 'react-hook-form';
32

43
import {
54
DataFrame,
@@ -89,17 +88,8 @@ export function refIdExists(queries: AlertQuery[], refId: string | null): boolea
8988
return queries.find((query) => query.refId === refId) !== undefined;
9089
}
9190

92-
// some gateways (like Istio) will decode "/" and "\" characters – this will cause 404 errors for any API call
93-
// that includes these values in the URL (ie. /my/path%2fto/resource -> /my/path/to/resource)
94-
//
95-
// see https://istio.io/latest/docs/ops/best-practices/security/#customize-your-system-on-path-normalization
96-
export function checkForPathSeparator(value: string): ValidateResult {
97-
const containsPathSeparator = value.includes('/') || value.includes('\\');
98-
if (containsPathSeparator) {
99-
return 'Cannot contain "/" or "\\" characters';
100-
}
101-
102-
return true;
91+
export function containsPathSeparator(value: string): boolean {
92+
return value.includes('/') || value.includes('\\');
10393
}
10494

10595
// this function assumes we've already checked if the data passed in to the function is of the alert condition

public/app/features/alerting/unified/components/rules/EditRuleGroupModal.test.tsx

Lines changed: 1 addition & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
import { render, screen, userEvent } from 'test/test-utils';
1+
import { render } from 'test/test-utils';
22
import { byLabelText, byTestId, byText, byTitle } from 'testing-library-selector';
33

44
import { CombinedRuleNamespace } from 'app/types/unified-alerting';
@@ -158,12 +158,4 @@ describe('EditGroupModal component on grafana-managed alert rules', () => {
158158
expect(await ui.input.namespace.find()).toHaveValue('namespace1');
159159
expect(ui.folderLink.query()).not.toBeInTheDocument();
160160
});
161-
162-
it('does not allow slashes in the group name', async () => {
163-
const user = userEvent.setup();
164-
renderWithGrafanaGroup();
165-
await user.type(await ui.input.group.find(), 'group/with/slashes');
166-
await user.click(ui.input.interval.get());
167-
expect(await screen.findByText(/cannot contain \"\/\"/i)).toBeInTheDocument();
168-
});
169161
});

public/app/features/alerting/unified/components/rules/EditRuleGroupModal.tsx

Lines changed: 0 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -28,7 +28,6 @@ import { EvaluationIntervalLimitExceeded } from '../InvalidIntervalWarning';
2828
import { decodeGrafanaNamespace, encodeGrafanaNamespace } from '../expressions/util';
2929
import { EvaluationGroupQuickPick } from '../rule-editor/EvaluationGroupQuickPick';
3030
import { MIN_TIME_RANGE_STEP_S } from '../rule-editor/GrafanaEvaluationBehavior';
31-
import { checkForPathSeparator } from '../rule-editor/util';
3231

3332
const ITEMS_PER_PAGE = 10;
3433

@@ -300,11 +299,6 @@ export function EditCloudGroupModal(props: ModalProps): React.ReactElement {
300299
readOnly={intervalEditOnly || isGrafanaManagedGroup}
301300
{...register('namespaceName', {
302301
required: 'Namespace name is required.',
303-
validate: {
304-
// for Grafana-managed we do not validate the name of the folder because we use the UID anyway
305-
pathSeparator: (namespaceName) =>
306-
isGrafanaManagedGroup ? true : checkForPathSeparator(namespaceName),
307-
},
308302
})}
309303
/>
310304
</Field>
@@ -337,9 +331,6 @@ export function EditCloudGroupModal(props: ModalProps): React.ReactElement {
337331
readOnly={intervalEditOnly}
338332
{...register('groupName', {
339333
required: 'Evaluation group name is required.',
340-
validate: {
341-
pathSeparator: (namespace) => checkForPathSeparator(namespace),
342-
},
343334
})}
344335
/>
345336
</Field>

0 commit comments

Comments
 (0)