Skip to content

Commit

Permalink
Merge branch 'master' into master
Browse files Browse the repository at this point in the history
  • Loading branch information
salmento authored Apr 17, 2021
2 parents 7647fff + 05ceb18 commit de4f723
Show file tree
Hide file tree
Showing 13 changed files with 329 additions and 113 deletions.
22 changes: 11 additions & 11 deletions README.md

Large diffs are not rendered by default.

11 changes: 10 additions & 1 deletion src/lib/isBIC.js
Original file line number Diff line number Diff line change
@@ -1,8 +1,17 @@
import assertString from './util/assertString';
import { CountryCodes } from './isISO31661Alpha2';

const isBICReg = /^[A-z]{4}[A-z]{2}\w{2}(\w{3})?$/;
// https://en.wikipedia.org/wiki/ISO_9362
const isBICReg = /^[A-Za-z]{6}[A-Za-z0-9]{2}([A-Za-z0-9]{3})?$/;

export default function isBIC(str) {
assertString(str);

// toUpperCase() should be removed when a new major version goes out that changes
// the regex to [A-Z] (per the spec).
if (CountryCodes.indexOf(str.slice(4, 6).toUpperCase()) < 0) {
return false;
}

return isBICReg.test(str);
}
91 changes: 21 additions & 70 deletions src/lib/isIP.js
Original file line number Diff line number Diff line change
Expand Up @@ -28,86 +28,37 @@ import assertString from './util/assertString';
where the interface "ne0" belongs to the 1st link, "pvc1.3" belongs
to the 5th link, and "interface10" belongs to the 10th organization.
* * */
const ipv4Maybe = /^(([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])$/;
const ipv6Block = /^[0-9A-F]{1,4}$/i;
const IPv4SegmentFormat = '(?:[0-9]|[1-9][0-9]|1[0-9][0-9]|2[0-4][0-9]|25[0-5])';
const IPv4AddressFormat = `(${IPv4SegmentFormat}[.]){3}${IPv4SegmentFormat}`;
const IPv4AddressRegExp = new RegExp(`^${IPv4AddressFormat}$`);

const IPv6SegmentFormat = '(?:[0-9a-fA-F]{1,4})';
const IPv6AddressRegExp = new RegExp('^(' +
`(?:${IPv6SegmentFormat}:){7}(?:${IPv6SegmentFormat}|:)|` +
`(?:${IPv6SegmentFormat}:){6}(?:${IPv4AddressFormat}|:${IPv6SegmentFormat}|:)|` +
`(?:${IPv6SegmentFormat}:){5}(?::${IPv4AddressFormat}|(:${IPv6SegmentFormat}){1,2}|:)|` +
`(?:${IPv6SegmentFormat}:){4}(?:(:${IPv6SegmentFormat}){0,1}:${IPv4AddressFormat}|(:${IPv6SegmentFormat}){1,3}|:)|` +
`(?:${IPv6SegmentFormat}:){3}(?:(:${IPv6SegmentFormat}){0,2}:${IPv4AddressFormat}|(:${IPv6SegmentFormat}){1,4}|:)|` +
`(?:${IPv6SegmentFormat}:){2}(?:(:${IPv6SegmentFormat}){0,3}:${IPv4AddressFormat}|(:${IPv6SegmentFormat}){1,5}|:)|` +
`(?:${IPv6SegmentFormat}:){1}(?:(:${IPv6SegmentFormat}){0,4}:${IPv4AddressFormat}|(:${IPv6SegmentFormat}){1,6}|:)|` +
`(?::((?::${IPv6SegmentFormat}){0,5}:${IPv4AddressFormat}|(?::${IPv6SegmentFormat}){1,7}|:))` +
')(%[0-9a-zA-Z-.:]{1,})?$');

export default function isIP(str, version = '') {
assertString(str);
version = String(version);
if (!version) {
return isIP(str, 4) || isIP(str, 6);
} else if (version === '4') {
if (!ipv4Maybe.test(str)) {
}
if (version === '4') {
if (!IPv4AddressRegExp.test(str)) {
return false;
}
const parts = str.split('.').sort((a, b) => a - b);
return parts[3] <= 255;
} else if (version === '6') {
let addressAndZone = [str];
// ipv6 addresses could have scoped architecture
// according to https://tools.ietf.org/html/rfc4007#section-11
if (str.includes('%')) {
addressAndZone = str.split('%');
if (addressAndZone.length !== 2) {
// it must be just two parts
return false;
}
if (!addressAndZone[0].includes(':')) {
// the first part must be the address
return false;
}

if (addressAndZone[1] === '') {
// the second part must not be empty
return false;
}
}

const blocks = addressAndZone[0].split(':');
let foundOmissionBlock = false; // marker to indicate ::

// At least some OS accept the last 32 bits of an IPv6 address
// (i.e. 2 of the blocks) in IPv4 notation, and RFC 3493 says
// that '::ffff:a.b.c.d' is valid for IPv4-mapped IPv6 addresses,
// and '::a.b.c.d' is deprecated, but also valid.
const foundIPv4TransitionBlock = isIP(blocks[blocks.length - 1], 4);
const expectedNumberOfBlocks = foundIPv4TransitionBlock ? 7 : 8;

if (blocks.length > expectedNumberOfBlocks) {
return false;
}
// initial or final ::
if (str === '::') {
return true;
} else if (str.substr(0, 2) === '::') {
blocks.shift();
blocks.shift();
foundOmissionBlock = true;
} else if (str.substr(str.length - 2) === '::') {
blocks.pop();
blocks.pop();
foundOmissionBlock = true;
}

for (let i = 0; i < blocks.length; ++i) {
// test for a :: which can not be at the string start/end
// since those cases have been handled above
if (blocks[i] === '' && i > 0 && i < blocks.length - 1) {
if (foundOmissionBlock) {
return false; // multiple :: in address
}
foundOmissionBlock = true;
} else if (foundIPv4TransitionBlock && i === blocks.length - 1) {
// it has been checked before that the last
// block is a valid IPv4 address
} else if (!ipv6Block.test(blocks[i])) {
return false;
}
}
if (foundOmissionBlock) {
return blocks.length >= 1;
}
return blocks.length === expectedNumberOfBlocks;
}
if (version === '6') {
return !!IPv6AddressRegExp.test(str);
}
return false;
}
28 changes: 25 additions & 3 deletions src/lib/isIPRange.js
Original file line number Diff line number Diff line change
@@ -1,9 +1,11 @@
import assertString from './util/assertString';
import isIP from './isIP';

const subnetMaybe = /^\d{1,2}$/;
const subnetMaybe = /^\d{1,3}$/;
const v4Subnet = 32;
const v6Subnet = 128;

export default function isIPRange(str) {
export default function isIPRange(str, version = '') {
assertString(str);
const parts = str.split('/');

Expand All @@ -21,5 +23,25 @@ export default function isIPRange(str) {
return false;
}

return isIP(parts[0], 4) && parts[1] <= 32 && parts[1] >= 0;
const isValidIP = isIP(parts[0], version);
if (!isValidIP) {
return false;
}

// Define valid subnet according to IP's version
let expectedSubnet = null;
switch (String(version)) {
case '4':
expectedSubnet = v4Subnet;
break;

case '6':
expectedSubnet = v6Subnet;
break;

default:
expectedSubnet = isIP(parts[0], '6') ? v6Subnet : v4Subnet;
}

return parts[1] <= expectedSubnet && parts[1] >= 0;
}
57 changes: 40 additions & 17 deletions src/lib/isISIN.js
Original file line number Diff line number Diff line change
Expand Up @@ -2,34 +2,57 @@ import assertString from './util/assertString';

const isin = /^[A-Z]{2}[0-9A-Z]{9}[0-9]$/;

// this link details how the check digit is calculated:
// https://www.isin.org/isin-format/. it is a little bit
// odd in that it works with digits, not numbers. in order
// to make only one pass through the ISIN characters, the
// each alpha character is handled as 2 characters within
// the loop.

export default function isISIN(str) {
assertString(str);
if (!isin.test(str)) {
return false;
}

const checksumStr = str.replace(/[A-Z]/g, character => (parseInt(character, 36)));

let double = true;
let sum = 0;
let digit;
let tmpNum;
let shouldDouble = true;
for (let i = checksumStr.length - 2; i >= 0; i--) {
digit = checksumStr.substring(i, (i + 1));
tmpNum = parseInt(digit, 10);
if (shouldDouble) {
tmpNum *= 2;
if (tmpNum >= 10) {
sum += tmpNum + 1;
} else {
sum += tmpNum;
// convert values
for (let i = str.length - 2; i >= 0; i--) {
if (str[i] >= 'A' && str[i] <= 'Z') {
const value = str[i].charCodeAt(0) - 55;
const lo = value % 10;
const hi = Math.trunc(value / 10);
// letters have two digits, so handle the low order
// and high order digits separately.
for (const digit of [lo, hi]) {
if (double) {
if (digit >= 5) {
sum += 1 + ((digit - 5) * 2);
} else {
sum += digit * 2;
}
} else {
sum += digit;
}
double = !double;
}
} else {
sum += tmpNum;
const digit = str[i].charCodeAt(0) - '0'.charCodeAt(0);
if (double) {
if (digit >= 5) {
sum += 1 + ((digit - 5) * 2);
} else {
sum += digit * 2;
}
} else {
sum += digit;
}
double = !double;
}
shouldDouble = !shouldDouble;
}

return parseInt(str.substr(str.length - 1), 10) === (10000 - sum) % 10;
const check = (Math.trunc(((sum + 9) / 10)) * 10) - sum;

return +str[str.length - 1] === check;
}
5 changes: 3 additions & 2 deletions src/lib/isISO31661Alpha2.js
Original file line number Diff line number Diff line change
@@ -1,5 +1,4 @@
import assertString from './util/assertString';
import includes from './util/includes';

// from https://en.wikipedia.org/wiki/ISO_3166-1_alpha-2
const validISO31661Alpha2CountriesCodes = [
Expand Down Expand Up @@ -32,5 +31,7 @@ const validISO31661Alpha2CountriesCodes = [

export default function isISO31661Alpha2(str) {
assertString(str);
return includes(validISO31661Alpha2CountriesCodes, str.toUpperCase());
return validISO31661Alpha2CountriesCodes.indexOf(str.toUpperCase()) >= 0;
}

export const CountryCodes = validISO31661Alpha2CountriesCodes;
19 changes: 19 additions & 0 deletions src/lib/isIdentityCard.js
Original file line number Diff line number Diff line change
Expand Up @@ -75,6 +75,25 @@ const validators = {

return c === 0;
},
IR: (str) => {
if (!str.match(/^\d{10}$/)) return false;
str = (`0000${str}`).substr(str.length - 6);

if (parseInt(str.substr(3, 6), 10) === 0) return false;

const lastNumber = parseInt(str.substr(9, 1), 10);
let sum = 0;

for (let i = 0; i < 9; i++) {
sum += parseInt(str.substr(i, 1), 10) * (10 - i);
}

sum %= 11;

return (
(sum < 2 && lastNumber === sum) || (sum >= 2 && lastNumber === 11 - sum)
);
},
IT: function IT(str) {
if (str.length !== 9) return false;
if (str === 'CA00000AA') return false; // https://it.wikipedia.org/wiki/Carta_d%27identit%C3%A0_elettronica_italiana
Expand Down
2 changes: 1 addition & 1 deletion src/lib/isLocale.js
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
import assertString from './util/assertString';

const localeReg = /^[A-z]{2,4}([_-]([A-z]{4}|[\d]{3}))?([_-]([A-z]{2}|[\d]{3}))?$/;
const localeReg = /^[A-Za-z]{2,4}([_-]([A-Za-z]{4}|[\d]{3}))?([_-]([A-Za-z]{2}|[\d]{3}))?$/;

export default function isLocale(str) {
assertString(str);
Expand Down
7 changes: 4 additions & 3 deletions src/lib/isMobilePhone.js
Original file line number Diff line number Diff line change
Expand Up @@ -33,7 +33,7 @@ const phones = {
'en-AU': /^(\+?61|0)4\d{8}$/,
'en-GB': /^(\+?44|0)7\d{9}$/,
'en-GG': /^(\+?44|0)1481\d{6}$/,
'en-GH': /^(\+233|0)(20|50|24|54|27|57|26|56|23|28)\d{7}$/,
'en-GH': /^(\+233|0)(20|50|24|54|27|57|26|56|23|28|55|59)\d{7}$/,
'en-HK': /^(\+?852[-\s]?)?[456789]\d{3}[-\s]?\d{4}$/,
'en-MO': /^(\+?853[-\s]?)?[6]\d{3}[-\s]?\d{4}$/,
'en-IE': /^(\+?353|0)8[356789]\d{7}$/,
Expand All @@ -46,7 +46,7 @@ const phones = {
'en-PK': /^((\+92)|(0092))-{0,1}\d{3}-{0,1}\d{7}$|^\d{11}$|^\d{4}-\d{7}$/,
'en-PH': /^(09|\+639)\d{9}$/,
'en-RW': /^(\+?250|0)?[7]\d{8}$/,
'en-SG': /^(\+65)?[689]\d{7}$/,
'en-SG': /^(\+65)?[3689]\d{7}$/,
'en-SL': /^(?:0|94|\+94)?(7(0|1|2|5|6|7|8)( |-)?\d)\d{6}$/,
'en-TZ': /^(\+?255|0)?[67]\d{8}$/,
'en-UG': /^(\+?256|0)?[7]\d{8}$/,
Expand Down Expand Up @@ -89,6 +89,7 @@ const phones = {
'kl-GL': /^(\+?299)?\s?\d{2}\s?\d{2}\s?\d{2}$/,
'ko-KR': /^((\+?82)[ \-]?)?0?1([0|1|6|7|8|9]{1})[ \-]?\d{3,4}[ \-]?\d{4}$/,
'lt-LT': /^(\+370|8)\d{8}$/,
'lv-LV': /^(\+?371)2\d{7}$/,
'ms-MY': /^(\+?6?01){1}(([0145]{1}(\-|\s)?\d{7,8})|([236789]{1}(\s|\-)?\d{7}))$/,
'mz-MZ': /^(\+?258)?8[234567]\d{7}$/,
'nb-NO': /^(\+?47)?[49]\d{7}$/,
Expand All @@ -112,7 +113,7 @@ const phones = {
'uk-UA': /^(\+?38|8)?0\d{9}$/,
'uz-UZ': /^(\+?998)?(6[125-79]|7[1-69]|88|9\d)\d{7}$/,
'vi-VN': /^(\+?84|0)((3([2-9]))|(5([2689]))|(7([0|6-9]))|(8([1-9]))|(9([0-9])))([0-9]{7})$/,
'zh-CN': /^((\+|00)86)?1([3568][0-9]|4[579]|6[67]|7[01235678]|9[012356789])[0-9]{8}$/,
'zh-CN': /^((\+|00)86)?1([3456789][0-9]|4[579]|6[67]|7[01235678]|9[012356789])[0-9]{8}$/,
'zh-TW': /^(\+?886\-?|0)?9\d{8}$/,
};
/* eslint-enable max-len */
Expand Down
1 change: 1 addition & 0 deletions src/lib/isPassportNumber.js
Original file line number Diff line number Diff line change
Expand Up @@ -33,6 +33,7 @@ const passportRegexByCountryCode = {
HU: /^[A-Z]{2}(\d{6}|\d{7})$/, // HUNGARY
IE: /^[A-Z0-9]{2}\d{7}$/, // IRELAND
IN: /^[A-Z]{1}-?\d{7}$/, // INDIA
IR: /^[A-Z]\d{8}$/, // IRAN
IS: /^(A)\d{7}$/, // ICELAND
IT: /^[A-Z0-9]{2}\d{7}$/, // ITALY
JP: /^[A-Z]{2}\d{7}$/, // JAPAN
Expand Down
3 changes: 2 additions & 1 deletion src/lib/isPostalCode.js
Original file line number Diff line number Diff line change
Expand Up @@ -33,14 +33,15 @@ const patterns = {
HT: /^HT\d{4}$/,
HU: fourDigit,
ID: fiveDigit,
IE: /^(?!.*(?:o))[A-z]\d[\dw]\s\w{4}$/i,
IE: /^(?!.*(?:o))[A-Za-z]\d[\dw]\s\w{4}$/i,
IL: /^(\d{5}|\d{7})$/,
IN: /^((?!10|29|35|54|55|65|66|86|87|88|89)[1-9][0-9]{5})$/,
IR: /\b(?!(\d)\1{3})[13-9]{4}[1346-9][013-9]{5}\b/,
IS: threeDigit,
IT: fiveDigit,
JP: /^\d{3}\-\d{4}$/,
KE: fiveDigit,
KR: /^(\d{5}|\d{6})$/,
LI: /^(948[5-9]|949[0-7])$/,
LT: /^LT\-\d{5}$/,
LU: fourDigit,
Expand Down
5 changes: 4 additions & 1 deletion src/lib/isURL.js
Original file line number Diff line number Diff line change
Expand Up @@ -101,8 +101,11 @@ export default function isURL(url, options) {
if (options.disallow_auth) {
return false;
}
if (split[0] === '' || split[0].substr(0, 1) === ':') {
return false;
}
auth = split.shift();
if (auth.indexOf(':') === -1 || (auth.indexOf(':') >= 0 && auth.split(':').length > 2)) {
if (auth.indexOf(':') >= 0 && auth.split(':').length > 2) {
return false;
}
}
Expand Down
Loading

0 comments on commit de4f723

Please sign in to comment.