From 691e5d8bf3e916d670ec87e2dc036b16c101386a Mon Sep 17 00:00:00 2001 From: Antoine du Hamel Date: Sun, 3 Apr 2022 00:50:27 +0200 Subject: [PATCH] buffer: fix `atob` input validation MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Fixes: https://github.com/nodejs/node/issues/42530 PR-URL: https://github.com/nodejs/node/pull/42539 Reviewed-By: Michaƫl Zasso Reviewed-By: Rich Trott Reviewed-By: Mestery Reviewed-By: Akhil Marsonya --- lib/buffer.js | 26 +++++++++++++++++++++++--- test/parallel/test-btoa-atob.js | 3 +++ 2 files changed, 26 insertions(+), 3 deletions(-) diff --git a/lib/buffer.js b/lib/buffer.js index 57d6cddbaa2e6b..8e1361b2e4b6e1 100644 --- a/lib/buffer.js +++ b/lib/buffer.js @@ -23,8 +23,10 @@ const { Array, + ArrayFrom, ArrayIsArray, ArrayPrototypeForEach, + ArrayPrototypeIncludes, MathFloor, MathMin, MathTrunc, @@ -1231,8 +1233,25 @@ function btoa(input) { return buf.toString('base64'); } -const kBase64Digits = - 'ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/='; +// Refs: https://infra.spec.whatwg.org/#forgiving-base64-decode +const kForgivingBase64AllowedChars = [ + // ASCII whitespace + // Refs: https://infra.spec.whatwg.org/#ascii-whitespace + 0x09, 0x0A, 0x0C, 0x0D, 0x20, + + // Uppercase letters + ...ArrayFrom({ length: 26 }, (_, i) => StringPrototypeCharCodeAt('A') + i), + + // Lowercase letters + ...ArrayFrom({ length: 26 }, (_, i) => StringPrototypeCharCodeAt('a') + i), + + // Decimal digits + ...ArrayFrom({ length: 10 }, (_, i) => StringPrototypeCharCodeAt('0') + i), + + 0x2B, // + + 0x2F, // / + 0x3D, // = +]; function atob(input) { // The implementation here has not been performance optimized in any way and @@ -1243,7 +1262,8 @@ function atob(input) { } input = `${input}`; for (let n = 0; n < input.length; n++) { - if (!kBase64Digits.includes(input[n])) + if (!ArrayPrototypeIncludes(kForgivingBase64AllowedChars, + StringPrototypeCharCodeAt(input, n))) throw lazyDOMException('Invalid character', 'InvalidCharacterError'); } return Buffer.from(input, 'base64').toString('latin1'); diff --git a/test/parallel/test-btoa-atob.js b/test/parallel/test-btoa-atob.js index 162406dd9f6b50..64f53671030ba0 100644 --- a/test/parallel/test-btoa-atob.js +++ b/test/parallel/test-btoa-atob.js @@ -12,3 +12,6 @@ strictEqual(globalThis.btoa, buffer.btoa); // Throws type error on no argument passed throws(() => buffer.atob(), /TypeError/); throws(() => buffer.btoa(), /TypeError/); + +strictEqual(atob(' '), ''); +strictEqual(atob(' YW\tJ\njZA=\r= '), 'abcd');