From c616ce49764f7e2af37e3edb7868b72fa89e271d Mon Sep 17 00:00:00 2001 From: kinu01 Date: Wed, 26 Aug 2026 23:19:17 +0100 Subject: [PATCH] Fix electronicFormatIBAN() mapping non-ASCII characters into A-Z --- ChangeLog | 3 +++ src/ibantools.ts | 16 +++++++++++++++- test/ibantools_test.js | 33 +++++++++++++++++++++++++++++++++ 3 files changed, 51 insertions(+), 1 deletion(-) diff --git a/ChangeLog b/ChangeLog index 65b4efe..d54c282 100755 --- a/ChangeLog +++ b/ChangeLog @@ -1,3 +1,6 @@ +2026-08-26 Emmanuel S. <@kinu01> + * Fixed electronicFormatIBAN() mapping non-ASCII characters into A-Z (#690) + 2026-04-10 Saša Jovanić * Version 4.5.4 diff --git a/src/ibantools.ts b/src/ibantools.ts index f565bdd..12801a0 100755 --- a/src/ibantools.ts +++ b/src/ibantools.ts @@ -328,6 +328,20 @@ function checkFormatBBAN(bban: string, bformat: string): boolean { * Get IBAN in electronic format (no spaces) * IBAN validation is not performed. * When non-string value for IBAN is provided, returns null. + * + * Uppercasing is ASCII only, on purpose. String.prototype.toUpperCase() + * applies full Unicode case mapping, and several non-ASCII characters map + * into A-Z, some of them turning one character into two: + * + * 'fi'.toUpperCase() // 'FI' + * 'ß'.toUpperCase() // 'SS' + * 'ı'.toUpperCase() // 'I' + * + * ISO 13616 permits only digits and the 26 Latin capitals, so a character + * outside that set has to survive to be rejected by the country's BBAN + * pattern. A Unicode aware uppercase would instead turn it into a string + * that passes, and the caller would keep the original input, which no + * payment rail can parse. * ``` * // returns "NL91ABNA0417164300" * ibantools.electronicFormatIBAN("NL91 ABNA 0417 1643 00"); @@ -337,7 +351,7 @@ export function electronicFormatIBAN(iban?: string): string | null { if (typeof iban !== 'string') { return null; } - return iban.replace(/[-\ ]/g, '').toUpperCase(); + return iban.replace(/[-\ ]/g, '').replace(/[a-z]/g, (char) => char.toUpperCase()); } /** diff --git a/test/ibantools_test.js b/test/ibantools_test.js index 037a995..9278690 100644 --- a/test/ibantools_test.js +++ b/test/ibantools_test.js @@ -916,6 +916,39 @@ describe('IBANTools', function() { 'BR9700360305000010009795493P1', ); }); + it('with lowercase Dutch IBAN should return NL91ABNA0417164300', function() { + return expect(iban.electronicFormatIBAN('nl91 abna 0417 1643 00')).to.equal('NL91ABNA0417164300'); + }); + }); + + describe('When calling electronicFormatIBAN() with non-ASCII characters', function() { + it('does not map U+FB01 LATIN SMALL LIGATURE FI into FI', function() { + return expect(iban.electronicFormatIBAN('fi2112345600000785')).to.equal('fi2112345600000785'); + }); + it('does not map U+00DF LATIN SMALL LETTER SHARP S into SS', function() { + return expect(iban.electronicFormatIBAN('GB77ßBK60161331926819')).to.equal('GB77ßBK60161331926819'); + }); + it('does not map U+0131 LATIN SMALL LETTER DOTLESS I into I', function() { + return expect(iban.electronicFormatIBAN('Fı2112345600000785')).to.equal('Fı2112345600000785'); + }); + }); + + describe('When calling isValidIBAN() on electronically formatted non-ASCII input', function() { + it('U+FB01 is not accepted as a Finnish IBAN', function() { + return expect(iban.isValidIBAN(iban.electronicFormatIBAN('fi2112345600000785'))).to.be.false; + }); + it('U+0131 is not accepted as a Finnish IBAN', function() { + return expect(iban.isValidIBAN(iban.electronicFormatIBAN('Fı2112345600000785'))).to.be.false; + }); + it('U+00DF is not accepted inside a British bank code', function() { + return expect(iban.isValidIBAN(iban.electronicFormatIBAN('GB77ßBK60161331926819'))).to.be.false; + }); + it('U+FB01 is not accepted inside a British bank code', function() { + return expect(iban.isValidIBAN(iban.electronicFormatIBAN('GB66fiBK60161331926819'))).to.be.false; + }); + it('U+0131 is not accepted inside a British bank code', function() { + return expect(iban.isValidIBAN(iban.electronicFormatIBAN('GB48NWıK60161331926819'))).to.be.false; + }); }); describe('When calling friendlyFormatIBAN()', function() {