From eeaf6cf959b65f4306167a795b6a2ed1d15d2741 Mon Sep 17 00:00:00 2001 From: Danilo Lalic Date: Tue, 14 Oct 2025 14:30:40 +0200 Subject: [PATCH 1/3] Add new strip option with tests --- src/lib/isIBAN.js | 15 ++++-- test/validators.test.js | 113 ++++++++++++++++++++++++++++++++++++++++ 2 files changed, 124 insertions(+), 4 deletions(-) diff --git a/src/lib/isIBAN.js b/src/lib/isIBAN.js index ed0abd6c0..aee78315e 100644 --- a/src/lib/isIBAN.js +++ b/src/lib/isIBAN.js @@ -89,6 +89,10 @@ const ibanRegexThroughCountryCode = { XK: /^(XK[0-9]{2})\d{16}$/, }; +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 @@ -122,7 +126,7 @@ function hasOnlyValidCountryCodes(countryCodeArray) { */ 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; @@ -164,8 +168,8 @@ function hasValidIbanFormat(str, options) { * @param {string} str * @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 +180,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', () => { From 658c639b6cce7f275d024e7f202f95e604012b7c Mon Sep 17 00:00:00 2001 From: Danilo Lalic Date: Tue, 14 Oct 2025 14:57:14 +0200 Subject: [PATCH 2/3] Change docs --- src/lib/isIBAN.js | 19 ++++++++++++++++++- 1 file changed, 18 insertions(+), 1 deletion(-) diff --git a/src/lib/isIBAN.js b/src/lib/isIBAN.js index aee78315e..1822d9fde 100644 --- a/src/lib/isIBAN.js +++ b/src/lib/isIBAN.js @@ -89,6 +89,18 @@ 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(); } @@ -121,7 +133,11 @@ 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) { @@ -166,6 +182,7 @@ 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, strip) { From e38f6fd0fbe685726b4f2b246c710d79b55bb462 Mon Sep 17 00:00:00 2001 From: Danilo Lalic Date: Tue, 14 Oct 2025 15:22:25 +0200 Subject: [PATCH 3/3] Change README.md to include strip --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) 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.