diff --git a/nym-wallet/package.json b/nym-wallet/package.json index a1a302bf9a..c930554e2b 100644 --- a/nym-wallet/package.json +++ b/nym-wallet/package.json @@ -29,9 +29,9 @@ "@mui/styles": "^5.2.2", "@mui/utils": "^5.7.0", "@nymproject/mui-theme": "^1.0.0", + "@nymproject/node-tester": "^1.0.0", "@nymproject/react": "^1.0.0", "@nymproject/types": "^1.0.0", - "@nymproject/node-tester": "^1.0.0", "@storybook/react": "^6.5.15", "@tauri-apps/api": "^1.2.0", "@tauri-apps/tauri-forage": "^1.0.0-beta.2", @@ -39,6 +39,7 @@ "bs58": "^4.0.1", "clsx": "^1.1.1", "date-fns": "^2.28.0", + "joi": "^17.11.0", "lodash": "^4.17.21", "notistack": "^2.0.3", "npm-run-all": "^4.1.5", @@ -123,4 +124,4 @@ "webpack-favicons": "^1.3.8", "webpack-merge": "^5.8.0" } -} \ No newline at end of file +} diff --git a/nym-wallet/src/components/Bonding/forms/gatewayValidationSchema.ts b/nym-wallet/src/components/Bonding/forms/gatewayValidationSchema.ts index 6935b6e623..9de1e6a1f7 100644 --- a/nym-wallet/src/components/Bonding/forms/gatewayValidationSchema.ts +++ b/nym-wallet/src/components/Bonding/forms/gatewayValidationSchema.ts @@ -11,7 +11,7 @@ import { export const gatewayValidationSchema = Yup.object().shape({ identityKey: Yup.string() - .required('An indentity key is required') + .required('An identity key is required') .test('valid-id-key', 'A valid identity key is required', (value) => validateKey(value || '', 32)), sphinxKey: Yup.string() @@ -20,10 +20,12 @@ export const gatewayValidationSchema = Yup.object().shape({ host: Yup.string() .required('A host is required') + .test('no-whitespace', 'Host cannot contain whitespace', (value) => !/\s/.test(value || '')) .test('valid-host', 'A valid host is required', (value) => (value ? isValidHostname(value) : false)), version: Yup.string() .required('A version is required') + .test('no-whitespace', 'A version cannot contain whitespace', (value) => !/\s/.test(value || '')) .test('valid-version', 'A valid version is required', (value) => (value ? validateVersion(value) : false)), location: Yup.string() @@ -70,6 +72,7 @@ export const amountSchema = Yup.object().shape({ export const updateGatewayValidationSchema = Yup.object().shape({ host: Yup.string() .required('A host is required') + .test('no-whitespace', 'Host cannot contain whitespace', (value) => !/\s/.test(value || '')) .test('valid-host', 'A valid host is required', (value) => (value ? isValidHostname(value) : false)), mixPort: Yup.number() @@ -82,7 +85,7 @@ export const updateGatewayValidationSchema = Yup.object().shape({ location: Yup.string().test('valid-location', 'A valid location is required', (value) => value ? validateLocation(value) : false, ), - version: Yup.string().test('valid-version', 'A valid version is required', (value) => - value ? validateVersion(value) : false, - ), + version: Yup.string() + .test('no-whitespace', 'A version cannot contain whitespace', (value) => !/\s/.test(value || '')) + .test('valid-version', 'A valid version is required', (value) => (value ? validateVersion(value) : false)), }); diff --git a/nym-wallet/src/components/Bonding/forms/mixnodeValidationSchema.ts b/nym-wallet/src/components/Bonding/forms/mixnodeValidationSchema.ts index 601731bd3e..3230fc0714 100644 --- a/nym-wallet/src/components/Bonding/forms/mixnodeValidationSchema.ts +++ b/nym-wallet/src/components/Bonding/forms/mixnodeValidationSchema.ts @@ -12,10 +12,12 @@ export const mixnodeValidationSchema = Yup.object().shape({ host: Yup.string() .required('A host is required') + .test('no-whitespace', 'Host cannot contain whitespace', (value) => !/\s/.test(value || '')) .test('valid-host', 'A valid host is required', (value) => (value ? isValidHostname(value) : false)), version: Yup.string() .required('A version is required') + .test('no-whitespace', 'A version cannot contain whitespace', (value) => !/\s/.test(value || '')) .test('valid-version', 'A valid version is required', (value) => (value ? validateVersion(value) : false)), mixPort: Yup.number() @@ -65,10 +67,12 @@ export const amountSchema = Yup.object().shape({ export const bondedInfoParametersValidationSchema = Yup.object().shape({ host: Yup.string() .required('A host is required') + .test('no-whitespace', 'Host cannot contain whitespace', (value) => !/\s/.test(value || '')) .test('valid-host', 'A valid host is required', (value) => (value ? isValidHostname(value) : false)), version: Yup.string() .required('A version is required') + .test('no-whitespace', 'A version cannot contain whitespace', (value) => !/\s/.test(value || '')) .test('valid-version', 'A valid version is required', (value) => (value ? validateVersion(value) : false)), mixPort: Yup.number() diff --git a/nym-wallet/src/utils/common.ts b/nym-wallet/src/utils/common.ts index 1f113ff37a..6d953db072 100644 --- a/nym-wallet/src/utils/common.ts +++ b/nym-wallet/src/utils/common.ts @@ -5,6 +5,7 @@ import { valid } from 'semver'; import { add, format, fromUnixTime } from 'date-fns'; import { DecCoin, isValidRawCoin, MixNodeCostParams } from '@nymproject/types'; import { TPoolOption } from 'src/components'; +import Joi from 'joi'; import { getCurrentInterval, getDefaultMixnodeCostParams, @@ -47,13 +48,15 @@ export const validateAmount = async ( }; export const isValidHostname = (value: string) => { - // regex for ipv4 and ipv6 and hostname- source http://jsfiddle.net/DanielD/8S4nq/ - const trimmedValue = value.replace(/\s+/g, ''); + const trimmedValue = value.trim(); - const hostnameRegex = - /((^((([0-9]|[1-9][0-9]|1[0-9]{2}|2[0-4][0-9]|25[0-5])\.){3}([0-9]|[1-9][0-9]|1[0-9]{2}|2[0-4][0-9]|25[0-5]))\s*$)|(^\s*((([0-9A-Fa-f]{1,4}:){7}([0-9A-Fa-f]{1,4}|:))|(([0-9A-Fa-f]{1,4}:){6}(:[0-9A-Fa-f]{1,4}|((25[0-5]|2[0-4]\d|1\d\d|[1-9]?\d)(\.(25[0-5]|2[0-4]\d|1\d\d|[1-9]?\d)){3})|:))|(([0-9A-Fa-f]{1,4}:){5}(((:[0-9A-Fa-f]{1,4}){1,2})|:((25[0-5]|2[0-4]\d|1\d\d|[1-9]?\d)(\.(25[0-5]|2[0-4]\d|1\d\d|[1-9]?\d)){3})|:))|(([0-9A-Fa-f]{1,4}:){4}(((:[0-9A-Fa-f]{1,4}){1,3})|((:[0-9A-Fa-f]{1,4})?:((25[0-5]|2[0-4]\d|1\d\d|[1-9]?\d)(\.(25[0-5]|2[0-4]\d|1\d\d|[1-9]?\d)){3}))|:))|(([0-9A-Fa-f]{1,4}:){3}(((:[0-9A-Fa-f]{1,4}){1,4})|((:[0-9A-Fa-f]{1,4}){0,2}:((25[0-5]|2[0-4]\d|1\d\d|[1-9]?\d)(\.(25[0-5]|2[0-4]\d|1\d\d|[1-9]?\d)){3}))|:))|(([0-9A-Fa-f]{1,4}:){2}(((:[0-9A-Fa-f]{1,4}){1,5})|((:[0-9A-Fa-f]{1,4}){0,3}:((25[0-5]|2[0-4]\d|1\d\d|[1-9]?\d)(\.(25[0-5]|2[0-4]\d|1\d\d|[1-9]?\d)){3}))|:))|(([0-9A-Fa-f]{1,4}:){1}(((:[0-9A-Fa-f]{1,4}){1,6})|((:[0-9A-Fa-f]{1,4}){0,4}:((25[0-5]|2[0-4]\d|1\d\d|[1-9]?\d)(\.(25[0-5]|2[0-4]\d|1\d\d|[1-9]?\d)){3}))|:))|(:(((:[0-9A-Fa-f]{1,4}){1,7})|((:[0-9A-Fa-f]{1,4}){0,5}:((25[0-5]|2[0-4]\d|1\d\d|[1-9]?\d)(\.(25[0-5]|2[0-4]\d|1\d\d|[1-9]?\d)){3}))|:)))(%.+)?\s*$))|(^\s*((?=.{1,255}$)(?=.*[A-Za-z].*)[0-9A-Za-z](?:(?:[0-9A-Za-z]|\b-){0,61}[0-9A-Za-z])?(?:\.[0-9A-Za-z](?:(?:[0-9A-Za-z]|\b-){0,61}[0-9A-Za-z])?)*)\s*$)/; + const hostnameSchema = Joi.alternatives().try( + Joi.string().hostname(), + Joi.string().ip({ version: ['ipv4', 'ipv6'] }), + ); - return hostnameRegex.test(trimmedValue); + const result = hostnameSchema.validate(trimmedValue); + return !result.error; }; export const validateVersion = (version: string): boolean => { diff --git a/yarn.lock b/yarn.lock index e5fc44427d..d982958fcf 100644 --- a/yarn.lock +++ b/yarn.lock @@ -1830,6 +1830,18 @@ resolved "https://registry.yarnpkg.com/@gar/promisify/-/promisify-1.1.3.tgz#555193ab2e3bb3b6adc3d551c9c030d9e860daf6" integrity sha512-k2Ty1JcVojjJFwrg/ThKi2ujJ7XNLYaFGNB/bWT9wGR+oSMJHMa5w+CUq6p/pVrKeNNgA7pCqEcjSnHVoqJQFw== +"@hapi/hoek@^9.0.0": + version "9.3.0" + resolved "https://registry.yarnpkg.com/@hapi/hoek/-/hoek-9.3.0.tgz#8368869dcb735be2e7f5cb7647de78e167a251fb" + integrity sha512-/c6rf4UJlmHlC9b5BaNvzAcFv7HZ2QHaV0D4/HNlBdvFnvQq8RI4kYdhyPCl7Xj+oWvTWQ8ujhqS53LIgAe6KQ== + +"@hapi/topo@^5.0.0": + version "5.1.0" + resolved "https://registry.yarnpkg.com/@hapi/topo/-/topo-5.1.0.tgz#dc448e332c6c6e37a4dc02fd84ba8d44b9afb012" + integrity sha512-foQZKJig7Ob0BMAYBfcJk8d77QtOe7Wo4ox7ff1lQYoNNAb6jwcY1ncdoy2e9wQZzvNy7ODZCYJkK8kzmcAnAg== + dependencies: + "@hapi/hoek" "^9.0.0" + "@hookform/resolvers@^2.8.0": version "2.9.11" resolved "https://registry.yarnpkg.com/@hookform/resolvers/-/resolvers-2.9.11.tgz#9ce96e7746625a89239f68ca57c4f654264c17ef" @@ -3035,6 +3047,23 @@ "@sentry/types" "7.73.0" tslib "^2.4.1 || ^1.9.3" +"@sideway/address@^4.1.3": + version "4.1.4" + resolved "https://registry.yarnpkg.com/@sideway/address/-/address-4.1.4.tgz#03dccebc6ea47fdc226f7d3d1ad512955d4783f0" + integrity sha512-7vwq+rOHVWjyXxVlR76Agnvhy8I9rpzjosTESvmhNeXOXdZZB15Fl+TI9x1SiHZH5Jv2wTGduSxFDIaq0m3DUw== + dependencies: + "@hapi/hoek" "^9.0.0" + +"@sideway/formula@^3.0.1": + version "3.0.1" + resolved "https://registry.yarnpkg.com/@sideway/formula/-/formula-3.0.1.tgz#80fcbcbaf7ce031e0ef2dd29b1bfc7c3f583611f" + integrity sha512-/poHZJJVjx3L+zVD6g9KgHfYnb443oi7wLu/XKojDviHy6HOEOA6z1Trk5aR1dGcmPenJEgb2sK2I80LeS3MIg== + +"@sideway/pinpoint@^2.0.0": + version "2.0.0" + resolved "https://registry.yarnpkg.com/@sideway/pinpoint/-/pinpoint-2.0.0.tgz#cff8ffadc372ad29fd3f78277aeb29e632cc70df" + integrity sha512-RNiOoTPkptFtSVzQevY/yWtZwf/RxyVnPy/OcA9HBM3MlGDnBEYL5B41H0MTn0Uec8Hi+2qUtTfG2WWZBmMejQ== + "@sigstore/bundle@^1.1.0": version "1.1.0" resolved "https://registry.yarnpkg.com/@sigstore/bundle/-/bundle-1.1.0.tgz#17f8d813b09348b16eeed66a8cf1c3d6bd3d04f1" @@ -12417,6 +12446,17 @@ jest@^27.1.0: import-local "^3.0.2" jest-cli "^27.5.1" +joi@^17.11.0: + version "17.11.0" + resolved "https://registry.yarnpkg.com/joi/-/joi-17.11.0.tgz#aa9da753578ec7720e6f0ca2c7046996ed04fc1a" + integrity sha512-NgB+lZLNoqISVy1rZocE9PZI36bL/77ie924Ri43yEvi9GUUMPeyVIr8KdFTMUlby1p0PBYMk9spIxEUQYqrJQ== + dependencies: + "@hapi/hoek" "^9.0.0" + "@hapi/topo" "^5.0.0" + "@sideway/address" "^4.1.3" + "@sideway/formula" "^3.0.1" + "@sideway/pinpoint" "^2.0.0" + js-sha3@^0.8.0: version "0.8.0" resolved "https://registry.yarnpkg.com/js-sha3/-/js-sha3-0.8.0.tgz#b9b7a5da73afad7dedd0f8c463954cbde6818840"