Skip to content

Commit 989942a

Browse files
akuitybotMarvin9
andauthored
chore(backport release-1.8): fix(ui): repoUrl validation (#5192)
Signed-off-by: Mayursinh Sarvaiya <[email protected]> Co-authored-by: Mayursinh Sarvaiya <[email protected]>
1 parent 1f50749 commit 989942a

File tree

2 files changed

+108
-36
lines changed

2 files changed

+108
-36
lines changed

ui/src/features/project/settings/views/credentials/create-credentials-modal.tsx

Lines changed: 17 additions & 36 deletions
Original file line numberDiff line numberDiff line change
@@ -4,57 +4,23 @@ import { FontAwesomeIcon } from '@fortawesome/react-fontawesome';
44
import { zodResolver } from '@hookform/resolvers/zod';
55
import { Input, Modal, Segmented } from 'antd';
66
import { Controller, useForm } from 'react-hook-form';
7-
import { z } from 'zod';
87

98
import { FieldContainer } from '@ui/features/common/form/field-container';
109
import { ModalComponentProps } from '@ui/features/common/modal/modal-context';
1110
import { SegmentLabel } from '@ui/features/common/segment-label';
12-
import { dnsRegex } from '@ui/features/common/utils';
1311
import {
1412
createCredentials,
1513
createProjectSecret,
1614
updateCredentials,
1715
updateProjectSecret
1816
} from '@ui/gen/api/service/v1alpha1/service-KargoService_connectquery';
1917
import { Secret } from '@ui/gen/k8s.io/api/core/v1/generated_pb';
20-
import { zodValidators } from '@ui/utils/validators';
2118

19+
import { createFormSchema } from './schema-validator';
2220
import { SecretEditor } from './secret-editor';
2321
import { CredentialsType } from './types';
2422
import { constructDefaults, labelForKey, typeLabel } from './utils';
2523

26-
const createFormSchema = (genericCreds: boolean, editing?: boolean) => {
27-
let schema = z.object({
28-
name: zodValidators.requiredString.regex(
29-
dnsRegex,
30-
'Credentials name must be a valid DNS subdomain.'
31-
),
32-
description: z.string().optional(),
33-
type: zodValidators.requiredString,
34-
repoUrl: zodValidators.requiredString,
35-
repoUrlIsRegex: z.boolean().optional(),
36-
username: zodValidators.requiredString,
37-
password: editing ? z.string().optional() : zodValidators.requiredString
38-
});
39-
40-
if (genericCreds) {
41-
// @ts-expect-error err
42-
schema = z.object({
43-
name: zodValidators.requiredString.regex(
44-
dnsRegex,
45-
'Credentials name must be a valid DNS subdomain.'
46-
),
47-
description: z.string().optional(),
48-
type: zodValidators.requiredString,
49-
data: z.array(z.array(z.string()))
50-
});
51-
}
52-
53-
return schema.refine((data) => ['git', 'helm', 'image', 'generic'].includes(data.type), {
54-
error: "Type must be one of 'git', 'helm', 'image' or 'generic'."
55-
});
56-
};
57-
5824
const placeholders = {
5925
name: 'My Credentials',
6026
description: 'An optional description',
@@ -63,6 +29,19 @@ const placeholders = {
6329
password: '********'
6430
};
6531

32+
const repoUrlPlaceholder = (credType: CredentialsType) => {
33+
switch (credType) {
34+
case 'git':
35+
return placeholders.repoUrl;
36+
case 'helm':
37+
return 'ghcr.io/nginxinc/charts/nginx-ingress';
38+
case 'image':
39+
return 'public.ecr.aws/nginx/nginx';
40+
}
41+
42+
return '';
43+
};
44+
6645
const genericCredentialPlaceholders = {
6746
name: 'My Secret',
6847
description: placeholders.description
@@ -234,7 +213,9 @@ export const CreateCredentialsModal = ({ project, onSuccess, editing, init, ...p
234213
placeholder={
235214
key === 'repoUrl' && repoUrlIsRegex
236215
? repoUrlPatternPlaceholder
237-
: placeholders[key as keyof typeof placeholders]
216+
: key === 'repoUrl'
217+
? repoUrlPlaceholder(credentialType)
218+
: placeholders[key as keyof typeof placeholders]
238219
}
239220
disabled={editing && key === 'name'}
240221
/>
Lines changed: 91 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,91 @@
1+
import { z } from 'zod';
2+
3+
import { dnsRegex } from '@ui/features/common/utils';
4+
import { zodValidators } from '@ui/utils/validators';
5+
6+
const imageNameRegex =
7+
/^(?![a-zA-Z][a-zA-Z0-9+.-]*:\/\/)(\w+([.-]\w+)*(:\d+)?\/)?(\w+([.-]\w+)*)(\/\w+([.-]\w+)*)*$/;
8+
9+
export const createFormSchema = (genericCreds: boolean, editing?: boolean) => {
10+
let schema = z
11+
.object({
12+
name: zodValidators.requiredString.regex(
13+
dnsRegex,
14+
'Credentials name must be a valid DNS subdomain.'
15+
),
16+
description: z.string().optional(),
17+
type: zodValidators.requiredString,
18+
repoUrl: zodValidators.requiredString,
19+
repoUrlIsRegex: z.boolean().optional(),
20+
username: zodValidators.requiredString,
21+
password: editing ? z.string().optional() : zodValidators.requiredString
22+
})
23+
.check(
24+
z.refine(
25+
(data) => {
26+
if (data.type === 'git' && !data.repoUrlIsRegex) {
27+
try {
28+
new URL(data.repoUrl);
29+
} catch {
30+
return false;
31+
}
32+
}
33+
34+
return true;
35+
},
36+
{ error: 'Repo URL must be a valid git URL.', path: ['repoUrl'] }
37+
),
38+
z.refine(
39+
(data) => {
40+
if (data.type === 'helm' && !data.repoUrlIsRegex) {
41+
try {
42+
const url = new URL(data.repoUrl);
43+
if (
44+
url.protocol !== 'http:' &&
45+
url.protocol !== 'https:' &&
46+
url.protocol !== 'oci:'
47+
) {
48+
return false;
49+
}
50+
} catch {
51+
return false;
52+
}
53+
}
54+
return true;
55+
},
56+
{
57+
error: 'Repo URL must be a valid Helm chart repository.',
58+
path: ['repoUrl']
59+
}
60+
),
61+
z.refine(
62+
(data) => {
63+
if (data.type === 'image' && !data.repoUrlIsRegex) {
64+
return imageNameRegex.test(data.repoUrl);
65+
}
66+
return true;
67+
},
68+
{
69+
error: 'Repo URL must be a valid container registry.',
70+
path: ['repoUrl']
71+
}
72+
)
73+
);
74+
75+
if (genericCreds) {
76+
// @ts-expect-error err
77+
schema = z.object({
78+
name: zodValidators.requiredString.regex(
79+
dnsRegex,
80+
'Credentials name must be a valid DNS subdomain.'
81+
),
82+
description: z.string().optional(),
83+
type: zodValidators.requiredString,
84+
data: z.array(z.array(z.string()))
85+
});
86+
}
87+
88+
return schema.refine((data) => ['git', 'helm', 'image', 'generic'].includes(data.type), {
89+
error: "Type must be one of 'git', 'helm', 'image' or 'generic'."
90+
});
91+
};

0 commit comments

Comments
 (0)