Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 3 additions & 0 deletions ChangeLog
Original file line number Diff line number Diff line change
@@ -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ć <sasa@simplify.ba>
* Version 4.5.4

Expand Down
16 changes: 15 additions & 1 deletion src/ibantools.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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");
Expand All @@ -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());
}

/**
Expand Down
33 changes: 33 additions & 0 deletions test/ibantools_test.js
Original file line number Diff line number Diff line change
Expand Up @@ -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() {
Expand Down