Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

fix: Node.js atob input validation #35444

Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
1 change: 1 addition & 0 deletions patches/node/.patches
Expand Up @@ -44,3 +44,4 @@ process_fix_hang_after_note_exit_3521.patch
feat_add_uv_loop_interrupt_on_io_change_option_to_uv_loop_configure.patch
fix_preserve_proper_method_names_as-is_in_error_stack.patch
macos_avoid_posix_spawnp_cwd_bug_3597.patch
buffer_fix_atob_input_validation.patch
89 changes: 89 additions & 0 deletions patches/node/buffer_fix_atob_input_validation.patch
@@ -0,0 +1,89 @@
From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001
From: Shelley Vohr <shelley.vohr@gmail.com>
Date: Tue, 23 Aug 2022 11:13:45 +0200
Subject: buffer: fix `atob` input validation

This patch combines:

* https://github.com/nodejs/node/pull/42539
* https://github.com/nodejs/node/pull/42662

To bring the Node.js implementation of atob into alignment with the
WHATWG spec.

diff --git a/lib/buffer.js b/lib/buffer.js
index 2d0057544395bc4aabff891fb30bfb9932a441f7..565753f76663611f0c069321b938c465b46f4ec8 100644
--- a/lib/buffer.js
+++ b/lib/buffer.js
@@ -23,8 +23,10 @@

const {
Array,
+ ArrayFrom,
ArrayIsArray,
ArrayPrototypeForEach,
+ ArrayPrototypeIndexOf,
Error,
MathFloor,
MathMin,
@@ -1227,8 +1229,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
@@ -1237,11 +1256,31 @@ function atob(input) {
if (arguments.length === 0) {
throw new ERR_MISSING_ARGS('input');
}
+
input = `${input}`;
+ let nonAsciiWhitespaceCharCount = 0;
+
for (let n = 0; n < input.length; n++) {
- if (!kBase64Digits.includes(input[n]))
+ const index = ArrayPrototypeIndexOf(
+ kForgivingBase64AllowedChars,
+ StringPrototypeCharCodeAt(input, n));
+
+ if (index > 4) {
+ // The first 5 elements of `kForgivingBase64AllowedChars` are
+ // ASCII whitespace char codes.
+ nonAsciiWhitespaceCharCount++;
+ } else if (index === -1) {
throw lazyDOMException('Invalid character', 'InvalidCharacterError');
+ }
}
+
+ // See #3 - https://infra.spec.whatwg.org/#forgiving-base64
+ if (nonAsciiWhitespaceCharCount % 4 === 1) {
+ throw lazyDOMException(
+ 'The string to be decoded is not correctly encoded.',
+ 'InvalidCharacterError');
+ }
+
return Buffer.from(input, 'base64').toString('latin1');
}