mirror of https://github.com/rancher/dashboard.git
Merge pull request #14319 from torchiaf/14317-url-validation
Fleet improve Repository URL and OCI registry validation
This commit is contained in:
commit
68041cc26c
|
|
@ -6761,10 +6761,10 @@ validation:
|
||||||
flowOutput:
|
flowOutput:
|
||||||
both: Requires "Output" or "Cluster Output" to be selected.
|
both: Requires "Output" or "Cluster Output" to be selected.
|
||||||
global: Requires "Cluster Output" to be selected.
|
global: Requires "Cluster Output" to be selected.
|
||||||
git:
|
repository:
|
||||||
url: URL must be a HTTP(s) or SSH url with no trailing spaces
|
url: It must be a valid HTTP(s) or SSH URL with no trailing spaces
|
||||||
oci:
|
oci:
|
||||||
url: URL must be an OCI url with no trailing spaces
|
url: It must be a valid OCI URL with no trailing spaces
|
||||||
output:
|
output:
|
||||||
logdna:
|
logdna:
|
||||||
apiKey: Required an "Api Key" to be set.
|
apiKey: Required an "Api Key" to be set.
|
||||||
|
|
|
||||||
|
|
@ -107,10 +107,7 @@ export default {
|
||||||
targetsCreated: '',
|
targetsCreated: '',
|
||||||
fvFormRuleSets: [{
|
fvFormRuleSets: [{
|
||||||
path: 'spec.repo',
|
path: 'spec.repo',
|
||||||
rules: [
|
rules: ['urlRepository'],
|
||||||
'required',
|
|
||||||
'urlRepository'
|
|
||||||
],
|
|
||||||
}],
|
}],
|
||||||
touched: null,
|
touched: null,
|
||||||
};
|
};
|
||||||
|
|
|
||||||
|
|
@ -404,7 +404,7 @@ export default {
|
||||||
case SOURCE_TYPE.REPO:
|
case SOURCE_TYPE.REPO:
|
||||||
this.fvFormRuleSets = [{
|
this.fvFormRuleSets = [{
|
||||||
path: 'spec.helm.repo',
|
path: 'spec.helm.repo',
|
||||||
rules: ['required', 'urlRepository'],
|
rules: ['urlRepository'],
|
||||||
}, {
|
}, {
|
||||||
path: 'spec.helm.chart',
|
path: 'spec.helm.chart',
|
||||||
rules: ['required'],
|
rules: ['required'],
|
||||||
|
|
@ -416,7 +416,7 @@ export default {
|
||||||
case SOURCE_TYPE.OCI:
|
case SOURCE_TYPE.OCI:
|
||||||
this.fvFormRuleSets = [{
|
this.fvFormRuleSets = [{
|
||||||
path: 'spec.helm.repo',
|
path: 'spec.helm.repo',
|
||||||
rules: ['required', 'ociRegistry'],
|
rules: ['ociRegistry'],
|
||||||
}, {
|
}, {
|
||||||
path: 'spec.helm.version',
|
path: 'spec.helm.version',
|
||||||
rules: ['semanticVersion'],
|
rules: ['semanticVersion'],
|
||||||
|
|
@ -425,7 +425,7 @@ export default {
|
||||||
case SOURCE_TYPE.TARBALL:
|
case SOURCE_TYPE.TARBALL:
|
||||||
this.fvFormRuleSets = [{
|
this.fvFormRuleSets = [{
|
||||||
path: 'spec.helm.chart',
|
path: 'spec.helm.chart',
|
||||||
rules: ['required', 'urlRepository'],
|
rules: ['urlRepository'],
|
||||||
}];
|
}];
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -97,32 +97,54 @@ describe('formRules', () => {
|
||||||
});
|
});
|
||||||
|
|
||||||
describe('urlRepository', () => {
|
describe('urlRepository', () => {
|
||||||
const message = JSON.stringify({ message: 'validation.git.url' });
|
const message = JSON.stringify({ message: 'validation.repository.url' });
|
||||||
const testCases = [
|
const testCases = [
|
||||||
// Valid HTTP(s)
|
// Valid HTTP(s)
|
||||||
['https://github.com/rancher/dashboard.git', undefined],
|
['https://github.com/rancher/dashboard.git', undefined],
|
||||||
['http://github.com/rancher/dashboard.git', undefined],
|
['http://github.com/rancher/dashboard.git', undefined],
|
||||||
['https://github.com/rancher/dashboard', undefined],
|
['https://github.com/rancher/dashboard', undefined],
|
||||||
['https://github.com/rancher/dashboard/', undefined],
|
['https://github.com/rancher/dashboard/', undefined],
|
||||||
|
['https://github.com/rancher/%20dashboard/', undefined],
|
||||||
|
['https://github.com/rancher/dashboard/%20', undefined],
|
||||||
|
['https://localhost:8005', undefined],
|
||||||
|
|
||||||
// Valid SSH
|
// Valid SSH
|
||||||
['git@github.com:rancher/dashboard.git', undefined],
|
['git@github.com:rancher/dashboard.git', undefined],
|
||||||
['git@github.com:rancher/dashboard', undefined],
|
['git@github.com:rancher/dashboard', undefined],
|
||||||
['git@github.com:rancher/dashboard/', undefined],
|
['git@github.com:rancher/dashboard/', undefined],
|
||||||
|
['git@github.com:rancher/%20dashboard/', undefined],
|
||||||
|
['git@github.com:rancher/dashboard/%20', undefined],
|
||||||
|
|
||||||
// Not valid HTTP(s)
|
// Not valid HTTP(s)
|
||||||
['https://github.com/rancher/ dashboard.git', message],
|
['https://github.com/rancher/ dashboard.git', message],
|
||||||
['http://github.com/rancher/ dashboard.git', message],
|
['http://github.com/rancher/ dashboard.git', message],
|
||||||
|
['http://github.com/ rancher/dashboard.git', message],
|
||||||
|
['http://github.com /rancher/dashboard.git', message],
|
||||||
['https://github.com/rancher/dashboard ', message],
|
['https://github.com/rancher/dashboard ', message],
|
||||||
|
['https%20://github.com/rancher/dashboard ', message],
|
||||||
|
['ht%20tps://github.com/rancher/dashboard ', message],
|
||||||
|
['https://git%20hub.com/rancher/dashboard/%20', message],
|
||||||
|
['https://https://', message],
|
||||||
|
['http:/ww.abc.com', message],
|
||||||
|
['http:ww.abc.com', message],
|
||||||
['foo://github.com/rancher/dashboard/', message],
|
['foo://github.com/rancher/dashboard/', message],
|
||||||
['github.com/rancher/dashboard/', message],
|
['github.com/rancher/dashboard/', message],
|
||||||
|
|
||||||
// Not valid SSH
|
// Not valid SSH
|
||||||
['git@github.com:rancher/ dashboard.git', message],
|
['git@github.com:rancher/ dashboard.git', message],
|
||||||
['git@github.com:rancher/dashboard ', message],
|
['git@github.com:rancher/dashboard ', message],
|
||||||
|
['git@github.com:rancher/ dashboard', message],
|
||||||
|
['git @github.com:rancher/dashboard', message],
|
||||||
|
['git@github.com: rancher/dashboard', message],
|
||||||
['git@github.comrancher/dashboard', message],
|
['git@github.comrancher/dashboard', message],
|
||||||
|
['git@githubcomrancher/dashboard', message],
|
||||||
|
['%20git@github.comrancher/dashboard', message],
|
||||||
|
['git@git%20hub.comrancher/dashboard', message],
|
||||||
|
['git@.git', message],
|
||||||
|
['git@', message],
|
||||||
|
|
||||||
[undefined, undefined]
|
[undefined, message],
|
||||||
|
['', message]
|
||||||
];
|
];
|
||||||
|
|
||||||
it.each(testCases)(
|
it.each(testCases)(
|
||||||
|
|
@ -139,20 +161,26 @@ describe('formRules', () => {
|
||||||
const message = JSON.stringify({ message: 'validation.oci.url' });
|
const message = JSON.stringify({ message: 'validation.oci.url' });
|
||||||
const testCases = [
|
const testCases = [
|
||||||
// Valid
|
// Valid
|
||||||
['oci://bucket/object', undefined],
|
['oci://registry.example.com', undefined],
|
||||||
|
['oci://myregistry.dev:5000', undefined],
|
||||||
|
['oci://192.168.1.100', undefined],
|
||||||
|
['oci://my.domain.com/my/image:tag', undefined],
|
||||||
|
['oci://localhost:5000', undefined],
|
||||||
['oci://region.objectstorage.example.com/n', undefined],
|
['oci://region.objectstorage.example.com/n', undefined],
|
||||||
['oci://a', undefined],
|
|
||||||
['oci://UPPERCASE/path', undefined],
|
|
||||||
|
|
||||||
// Invalid
|
// Invalid
|
||||||
['http://example.com/oci', message],
|
['http://example.com/oci', message],
|
||||||
['https://oci.cloud.com', message],
|
['https://oci.cloud.com', message],
|
||||||
['ftp://oci.server.net', message],
|
['ftp://oci.server.net', message],
|
||||||
['/path/to/oci', message],
|
['path/to/oci', message],
|
||||||
|
['oci://a', message],
|
||||||
['oci:/missing/slash', message],
|
['oci:/missing/slash', message],
|
||||||
['oci:', message],
|
['oci:', message],
|
||||||
['oci://', message],
|
['oci://', message],
|
||||||
['oci://space between', message],
|
['oci://oci://duplicate/protocol', message],
|
||||||
|
['oci ://registry.example.com/foo/bar', message],
|
||||||
|
['oci://registry.example. com/foo/bar', message],
|
||||||
|
['oci://registry.example.com/ foo/bar', message],
|
||||||
['oci://resource multiple spaces', message],
|
['oci://resource multiple spaces', message],
|
||||||
['', message],
|
['', message],
|
||||||
[undefined, message],
|
[undefined, message],
|
||||||
|
|
|
||||||
|
|
@ -1,4 +1,5 @@
|
||||||
import semver from 'semver';
|
import semver from 'semver';
|
||||||
|
import { parse } from '@shell/utils/url';
|
||||||
import { RBAC } from '@shell/config/types';
|
import { RBAC } from '@shell/config/types';
|
||||||
import { HCI } from '@shell/config/labels-annotations';
|
import { HCI } from '@shell/config/labels-annotations';
|
||||||
import isEmpty from 'lodash/isEmpty';
|
import isEmpty from 'lodash/isEmpty';
|
||||||
|
|
@ -174,22 +175,82 @@ export default function(
|
||||||
const genericUrl: Validator = (val: string) => val && !isUrl(val) ? t('validation.genericUrl') : undefined;
|
const genericUrl: Validator = (val: string) => val && !isUrl(val) ? t('validation.genericUrl') : undefined;
|
||||||
|
|
||||||
const urlRepository: Validator = (url: string) => {
|
const urlRepository: Validator = (url: string) => {
|
||||||
const regexPart1 = /^((http|git|ssh|http(s)|file|\/?)|(git@[\w\.]+))(:(\/\/)?)/gm;
|
const message = t('validation.repository.url');
|
||||||
const regexPart2 = /^([\w\.@\:\/\-]+)([\d\/\w.-]+?)(.git){0,1}(\/)?$/gm;
|
|
||||||
|
|
||||||
if (url) {
|
if (!url) {
|
||||||
const urlPart2 = url.replaceAll(regexPart1, '');
|
return message;
|
||||||
|
}
|
||||||
|
|
||||||
return !urlPart2 || url === urlPart2 || !regexPart2.test(urlPart2.replaceAll('%20', '')) ? t('validation.git.url') : undefined;
|
if (url.includes(' ')) {
|
||||||
|
return message;
|
||||||
|
}
|
||||||
|
|
||||||
|
const {
|
||||||
|
protocol,
|
||||||
|
authority,
|
||||||
|
host,
|
||||||
|
path
|
||||||
|
} = parse(url);
|
||||||
|
|
||||||
|
// Test duplicate protocol
|
||||||
|
if (!host || protocol === host) {
|
||||||
|
return message;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Test http(s) protocol
|
||||||
|
if (protocol && (!/^(http|http(s))/gm.test(protocol) || (!url.startsWith('https://') && !url.startsWith('http://')))) {
|
||||||
|
return message;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Test ssh, authority must be valid (SSH user + host)
|
||||||
|
if (!protocol && !authority.endsWith(':')) {
|
||||||
|
return message;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Encoded space characters (%20) are allowed only in the path
|
||||||
|
const hostAndPath = `${ host }${ path.replaceAll('%20', '') }`;
|
||||||
|
|
||||||
|
// Test host/path
|
||||||
|
if (!/^([\w\.@\:\/\-]+)([\d\/\w.-]+?)(.git){0,1}(\/)?$/gm.test(hostAndPath)) {
|
||||||
|
return message;
|
||||||
}
|
}
|
||||||
|
|
||||||
return undefined;
|
return undefined;
|
||||||
};
|
};
|
||||||
|
|
||||||
const ociRegistry: Validator = (url: string) => {
|
const ociRegistry: Validator = (url: string) => {
|
||||||
const regex = /^oci:\/\/\S+$/gm;
|
const message = t('validation.oci.url');
|
||||||
|
|
||||||
return !regex.test(url) ? t('validation.oci.url') : undefined;
|
if (!url) {
|
||||||
|
return message;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (url.includes(' ')) {
|
||||||
|
return message;
|
||||||
|
}
|
||||||
|
|
||||||
|
const {
|
||||||
|
protocol,
|
||||||
|
host,
|
||||||
|
path
|
||||||
|
} = parse(url);
|
||||||
|
|
||||||
|
// Test duplicate protocol
|
||||||
|
if (!host || protocol === host) {
|
||||||
|
return message;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Test oci protocol
|
||||||
|
if (!url.startsWith('oci://')) {
|
||||||
|
return message;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Test host/path
|
||||||
|
if (!/^([\w\.@\:\/\-]+)([\d\/\w.-]+?)(\/)?$/gm.test(`${ host }${ path }`)) {
|
||||||
|
return message;
|
||||||
|
}
|
||||||
|
|
||||||
|
return undefined;
|
||||||
};
|
};
|
||||||
|
|
||||||
const version: Validator = (value: string) => {
|
const version: Validator = (value: string) => {
|
||||||
|
|
|
||||||
Loading…
Reference in New Issue