diff --git a/README.md b/README.md
index a0ea7a65d..58fddad98 100644
--- a/README.md
+++ b/README.md
@@ -118,7 +118,7 @@ Validator | Description
**isHexadecimal(str)** | check if the string is a hexadecimal number.
**isHexColor(str)** | check if the string is a hexadecimal color.
**isHSL(str)** | check if the string is an HSL (hue, saturation, lightness, optional alpha) color based on [CSS Colors Level 4 specification][CSS Colors Level 4 Specification].
Comma-separated format supported. Space-separated format supported with the exception of a few edge cases (ex: `hsl(200grad+.1%62%/1)`).
-**isIBAN(str, [, options])** | check if the string is an IBAN (International Bank Account Number).
`options` is an object which accepts two attributes: `whitelist`: where you can restrict IBAN codes you want to receive data from and `blacklist`: where you can remove some of the countries from the current list. For both you can use an array with the following values `['AD','AE','AL','AT','AZ','BA','BE','BG','BH','BR','BY','CH','CR','CY','CZ','DE','DK','DO','EE','EG','ES','FI','FO','FR','GB','GE','GI','GL','GR','GT','HR','HU','IE','IL','IQ','IR','IS','IT','JO','KW','KZ','LB','LC','LI','LT','LU','LV','MC','MD','ME','MK','MR','MT','MU','MZ','NL','NO','PK','PL','PS','PT','QA','RO','RS','SA','SC','SE','SI','SK','SM','SV','TL','TN','TR','UA','VA','VG','XK']`.
+**isIBAN(str [, options])** | check if the string is an IBAN (International Bank Account Number).
`options` is an object which accepts:
- `strip` (default `true`): when `true`, whitespace and hyphens are removed before validation; when `false`, the input is validated as-is (it is still uppercased internally).
- `whitelist`: restrict valid IBANs to the listed ISO alpha-2 country codes.
- `blacklist`: exclude the listed ISO alpha-2 country codes.
For both `whitelist` and `blacklist`, you can use an array with the following values `['AD','AE','AL','AT','AZ','BA','BE','BG','BH','BR','BY','CH','CR','CY','CZ','DE','DK','DO','EE','EG','ES','FI','FO','FR','GB','GE','GI','GL','GR','GT','HR','HU','IE','IL','IQ','IR','IS','IT','JO','KW','KZ','LB','LC','LI','LT','LU','LV','MC','MD','ME','MK','MR','MT','MU','MZ','NL','NO','PK','PL','PS','PT','QA','RO','RS','SA','SC','SE','SI','SK','SM','SV','TL','TN','TR','UA','VA','VG','XK']`.
**isIdentityCard(str [, locale])** | check if the string is a valid identity card code.
`locale` is one of `['LK', 'PL', 'ES', 'FI', 'IN', 'IT', 'IR', 'MZ', 'NO', 'TH', 'zh-TW', 'he-IL', 'ar-LY', 'ar-TN', 'zh-CN', 'zh-HK', 'PK']` OR `'any'`. If 'any' is used, function will check if any of the locales match.
Defaults to 'any'.
**isIMEI(str [, options]))** | check if the string is a valid [IMEI number][IMEI]. IMEI should be of format `###############` or `##-######-######-#`.
`options` is an object which can contain the keys `allow_hyphens`. Defaults to first format. If `allow_hyphens` is set to true, the validator will validate the second format.
**isIn(str, values)** | check if the string is in an array of allowed values.
diff --git a/src/lib/isIBAN.js b/src/lib/isIBAN.js
index ed0abd6c0..1822d9fde 100644
--- a/src/lib/isIBAN.js
+++ b/src/lib/isIBAN.js
@@ -89,6 +89,22 @@ const ibanRegexThroughCountryCode = {
XK: /^(XK[0-9]{2})\d{16}$/,
};
+
+/**
+ * Normalize an IBAN string.
+ *
+ * If `strip` is true, removes characters matching `pattern` (by default, any
+ * non-alphanumeric). Converts the resulting string to uppercase.
+ *
+ * @param {string} str - IBAN string to normalize.
+ * @param {boolean} strip - Whether to remove characters matching `pattern`.
+ * @param {RegExp} pattern=/[^A-Z0-9]+/gi - Pattern used when stripping.
+ * @return {string} Normalized (uppercase) IBAN string.
+ */
+function normalizeIbanString(str, strip, pattern = /[^A-Z0-9]+/gi) {
+ return (strip ? str.replace(pattern, '') : str).toUpperCase();
+}
+
/**
* Check if the country codes passed are valid using the
* ibanRegexThroughCountryCode as a reference
@@ -117,12 +133,16 @@ function hasOnlyValidCountryCodes(countryCodeArray) {
* NOTE: Permitted IBAN characters are: digits [0-9] and the 26 latin alphabetic [A-Z]
*
* @param {string} str - string under validation
- * @param {object} options - object to pass the countries to be either whitelisted or blacklisted
+ * @param {boolean} options.strip - If truthy, whitespace and hyphens are removed before validation.
+ * @param {string[]} options.whitelist - Optional set of allowed ISO country codes
+ * (e.g., ["DE","GB","CH"]). If present, IBANs for other countries are rejected.
+ * @param {string[]} options.blacklist - Optional set of disallowed ISO country codes
+ * (e.g., ["DE","GB","CH"]). If present, IBANs for these countries are rejected.
* @return {boolean}
*/
function hasValidIbanFormat(str, options) {
// Strip white spaces and hyphens
- const strippedStr = str.replace(/[\s\-]+/gi, '').toUpperCase();
+ const strippedStr = normalizeIbanString(str, options.strip, /[\s\-]+/gi);
const isoCountryCode = strippedStr.slice(0, 2).toUpperCase();
const isoCountryCodeInIbanRegexCodeObject = isoCountryCode in ibanRegexThroughCountryCode;
@@ -162,10 +182,11 @@ function hasValidIbanFormat(str, options) {
* Reference: https://en.wikipedia.org/wiki/International_Bank_Account_Number
*
* @param {string} str
+ * @param {boolean} strip - Whether to strip the string of non-alphanumeric characters
* @return {boolean}
*/
-function hasValidIbanChecksum(str) {
- const strippedStr = str.replace(/[^A-Z0-9]+/gi, '').toUpperCase(); // Keep only digits and A-Z latin alphabetic
+function hasValidIbanChecksum(str, strip) {
+ const strippedStr = normalizeIbanString(str, strip, /[^A-Z0-9]+/gi);
const rearranged = strippedStr.slice(4) + strippedStr.slice(0, 4);
const alphaCapsReplacedWithDigits = rearranged.replace(/[A-Z]/g, char => char.charCodeAt(0) - 55);
@@ -176,9 +197,12 @@ function hasValidIbanChecksum(str) {
}
export default function isIBAN(str, options = {}) {
+ const optionsWithDefaults = { strip: true, ...options };
+
assertString(str);
- return hasValidIbanFormat(str, options) && hasValidIbanChecksum(str);
+ return hasValidIbanFormat(str, optionsWithDefaults)
+ && hasValidIbanChecksum(str, optionsWithDefaults.strip);
}
export const locales = Object.keys(ibanRegexThroughCountryCode);
diff --git a/test/validators.test.js b/test/validators.test.js
index 12c5fc2ab..e9a716b7b 100644
--- a/test/validators.test.js
+++ b/test/validators.test.js
@@ -6176,6 +6176,119 @@ describe('Validators', () => {
'IT60X0542811101000000123456',
],
});
+ test({
+ validator: 'isIBAN',
+ args: [{ strip: true }],
+ valid: [
+ 'SC52BAHL01031234567890123456USD',
+ 'LC14BOSL123456789012345678901234',
+ 'MT31MALT01100000000000000000123',
+ 'SV43ACAT00000000000000123123',
+ 'EG800002000156789012345180002',
+ 'BE71 0961 2345 6769',
+ 'FR76 3000 6000 0112 3456 7890 189',
+ 'DE91 1000 0000 0123 4567 89',
+ 'GR96 0810 0010 0000 0123 4567 890',
+ 'RO09 BCYP 0000 0012 3456 7890',
+ 'SA44 2000 0001 2345 6789 1234',
+ 'ES79 2100 0813 6101 2345 6789',
+ 'CH56 0483 5012 3456 7800 9',
+ 'GB98 MIDL 0700 9312 3456 78',
+ 'IL170108000000012612345',
+ 'IT60X0542811101000000123456',
+ 'JO71CBJO0000000000001234567890',
+ 'TR320010009999901234567890',
+ 'BR1500000000000010932840814P2',
+ 'LB92000700000000123123456123',
+ 'IR200170000000339545727003',
+ 'MZ97123412341234123412341',
+ 'MA64011519000001205000534921',
+ 'VG96VPVG0000012345678901',
+ 'DZ580002100001113000000570',
+ 'IE29AIBK93115212345678',
+ 'PS92PALS000000000400123456702',
+ 'PS92PALS00000000040012345670O',
+ ],
+ invalid: [
+ 'XX22YYY1234567890123',
+ 'FR14 2004 1010 0505 0001 3',
+ 'FR7630006000011234567890189@',
+ 'FR7630006000011234567890189๐
',
+ 'FR763000600001123456!!๐คจ7890189@',
+ 'VG46H07Y0223060094359858',
+ 'IE95TE8270900834048660',
+ 'PS072435171802145240705922007',
+ ],
+ });
+ test({
+ validator: 'isIBAN',
+ args: [{ strip: false }],
+ valid: [
+ 'gb29nwbk60161331926819',
+ 'IE29AIBK93115212345678',
+ 'IL170108000000012612345',
+ 'JO71CBJO0000000000001234567890',
+ 'TR320010009999901234567890',
+ 'BR1500000000000010932840814P2',
+ 'LB92000700000000123123456123',
+ 'IR200170000000339545727003',
+ ],
+ invalid: [
+ 'GB29 NWBK 6016 1331 9268 19',
+ 'GB29-NWBK-6016-1331-9268-19',
+ 'GB29\tNWBK60161331926819',
+ 'CH56 0483 5012 3456 7800 9',
+ 'FR7630006000011234567890189@',
+ 'FR7630006000011234567890189๐
',
+ 'FR763000600001123456!!๐คจ7890189@',
+ 'VG46H07Y0223060094359858',
+ 'IE95TE8270900834048660',
+ 'PS072435171802145240705922007',
+ ],
+ });
+ test({
+ validator: 'isIBAN',
+ args: [{ strip: false, blacklist: ['IT'] }],
+ valid: [
+ 'GB29NWBK60161331926819',
+ 'IE29AIBK93115212345678',
+ 'IL170108000000012612345',
+ 'JO71CBJO0000000000001234567890',
+ 'TR320010009999901234567890',
+ 'BR1500000000000010932840814P2',
+ 'LB92000700000000123123456123',
+ 'IR200170000000339545727003',
+ ],
+ invalid: [
+ 'IT60X0542811101000000123456',
+ 'IT60 X054 2811 1010 0000 0123 456',
+ 'GB98 MIDL 0700 9312 3456 78',
+ 'CH56 0483 5012 3456 7800 9',
+ 'FR7630006000011234567890189@',
+ 'FR7630006000011234567890189๐
',
+ 'FR763000600001123456!!๐คจ7890189@',
+ 'VG46H07Y0223060094359858',
+ 'IE95TE8270900834048660',
+ 'PS072435171802145240705922007',
+ ],
+ });
+ test({
+ validator: 'isIBAN',
+ args: [{ strip: false, whitelist: ['DK', 'GB'] }],
+ valid: [
+ 'DK5000400440116243',
+ 'GB29NWBK60161331926819',
+ 'gb29nwbk60161331926819',
+ ],
+ invalid: [
+ 'BE71096123456769',
+ 'FR7630006000011234567890189',
+ 'GB98 MIDL 0700 9312 3456 78',
+ 'GB29-NWBK-6016-1331-9268-19',
+ 'DK50 0040 0440 1162 43',
+ 'IT60X0542811101000000123456',
+ ],
+ });
});
it('should validate BIC codes', () => {