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 #35443

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 @@ -47,3 +47,4 @@ support_v8_sandboxed_pointers.patch
build_ensure_v8_pointer_compression_sandbox_is_enabled_on_64bit.patch
build_ensure_native_module_compilation_fails_if_not_using_a_new.patch
fix_override_createjob_in_node_platform.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 57d6cddbaa2e6bdd846a667897588dea18daeb42..7602d4049e9bb1c09440bc3af09ad5ad9c768308 100644
--- a/lib/buffer.js
+++ b/lib/buffer.js
@@ -23,8 +23,10 @@

const {
Array,
+ ArrayFrom,
ArrayIsArray,
ArrayPrototypeForEach,
+ ArrayPrototypeIndexOf,
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
@@ -1241,11 +1260,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');
}