From db0af6aa13946d308640bebbd0d1b96f79cdc2da Mon Sep 17 00:00:00 2001 From: Alex Anderson <191496+alxndrsn@users.noreply.github.com> Date: Sat, 9 Nov 2024 09:01:25 +0300 Subject: [PATCH] base64ToUtf8(): reject invalid characters (#1277) #1214 changed the implemention of `base64ToUtf8()` from `atob(...)` to `Buffer.from(...)`. This also introduced a change in behaviour, which did not trigger any test failures. This commit reverts to behaviour similar to the previous version while continuing to avoid the deprecated `atob()` function. RFC4648 states: > Implementations MUST reject the encoded data if it contains characters outside the base alphabet when interpreting base-encoded data, unless the specification referring to this document explicitly states otherwise. See: https://datatracker.ietf.org/doc/html/rfc4648#section-3.2 `base64ToUtf8()` diverges from `atob()` by rejecting `null` instead of treating it as the string `"null"` --- lib/util/util.js | 2 ++ test/unit/util/util.js | 33 +++++++++++++++++++++++++++++++++ 2 files changed, 35 insertions(+) diff --git a/lib/util/util.js b/lib/util/util.js index f42ce48b3..b4c86d6b7 100644 --- a/lib/util/util.js +++ b/lib/util/util.js @@ -60,7 +60,9 @@ const pickAll = (keys, obj) => { return result; }; +const validBase64 = /^(?:[A-Za-z0-9+/]{4})*(?:[A-Za-z0-9+/][AQgw]={0,2}|[A-Za-z0-9+/]{2}[AEIMQUYcgkosw048]=?)?$/; function base64ToUtf8(base64) { + if (!validBase64.test(base64?.trim())) throw new Error('Invalid base64 string.'); return Buffer.from(base64, 'base64').toString('utf8'); } diff --git a/test/unit/util/util.js b/test/unit/util/util.js index bd7e44749..cfa7837e0 100644 --- a/test/unit/util/util.js +++ b/test/unit/util/util.js @@ -70,6 +70,39 @@ describe('util/util', () => { const base64 = utf8ToBase64(input); base64ToUtf8(base64).should.be.eql(input); }); + + describe('base64ToUtf8()', () => { + it(`should throw if no arg supplied`, () => { + (() => base64ToUtf8()).should.throw('Invalid base64 string.'); + }); + + [ + undefined, + null, + '!', + ].forEach(malformed64 => { + it(`should reject malformed input '${malformed64}'`, () => { + (() => base64ToUtf8(malformed64)).should.throw('Invalid base64 string.'); + }); + }); + + [ + [ '', '' ], + [ ' ', '' ], + [ 'c29tZSB0ZXh0', 'some text' ], // eslint-disable-line no-multi-spaces + [ 'c29tZSB0ZXh0 ', 'some text' ], // eslint-disable-line no-multi-spaces + [ ' c29tZSB0ZXh0 ', 'some text' ], + [ 'c29tZSB0ZXh0IA', 'some text ' ], // eslint-disable-line no-multi-spaces + [ 'c29tZSB0ZXh0IA=', 'some text ' ], // eslint-disable-line no-multi-spaces + [ 'c29tZSB0ZXh0IA==', 'some text ' ], + [ 'c29tZSB0ZXh0IDE=', 'some text 1' ], + [ 'c29tZSB0ZXh0IDEx', 'some text 11' ], + ].forEach(([ good64, expected ]) => { + it(`should decode '${good64}' to '${expected}'`, () => { + base64ToUtf8(good64).should.equal(expected); + }); + }); + }); }); });