Skip to content

Commit 1c109ce

Browse files
buffer: simplify atob approach of disregarding whitespace
* Use approach of counting non-ASCII whitespace characters instead of removing the characters via string replacement * Additional atob validation tests have been added
1 parent 2ad1a00 commit 1c109ce

File tree

2 files changed

+29
-25
lines changed

2 files changed

+29
-25
lines changed

lib/buffer.js

+16-17
Original file line numberDiff line numberDiff line change
@@ -1232,12 +1232,14 @@ function btoa(input) {
12321232
return buf.toString('base64');
12331233
}
12341234

1235-
// Refs: https://infra.spec.whatwg.org/#forgiving-base64-decode
1236-
const kForgivingBase64AllowedChars = [
1235+
const asciiWhitespaceCharacters = [
12371236
// ASCII whitespace
12381237
// Refs: https://infra.spec.whatwg.org/#ascii-whitespace
12391238
0x09, 0x0A, 0x0C, 0x0D, 0x20,
1239+
];
12401240

1241+
// Refs: https://infra.spec.whatwg.org/#forgiving-base64-decode
1242+
const kForgivingBase64AllowedChars = [
12411243
// Uppercase letters
12421244
...ArrayFrom({ length: 26 }, (_, i) => StringPrototypeCharCodeAt('A') + i),
12431245

@@ -1260,32 +1262,29 @@ function atob(input) {
12601262
throw new ERR_MISSING_ARGS('input');
12611263
}
12621264

1263-
if (input === undefined || input === false || typeof input === 'number') {
1264-
throw lazyDOMException(
1265-
'The string to be decoded is not correctly encoded.',
1266-
'ValidationError');
1267-
}
1265+
input = `${input}`;
1266+
let nonAsciiWhitespaceCharCount = 0;
12681267

1269-
// Remove all ASCII whitespace from data.
1270-
//
1271-
// See #1 - https://infra.spec.whatwg.org/#forgiving-base64
1272-
input = `${input}`.replace(/\s/g, '');
1268+
for (let n = 0; n < input.length; n++) {
1269+
const char = StringPrototypeCharCodeAt(input, n);
1270+
1271+
if (ArrayPrototypeIncludes(kForgivingBase64AllowedChars, char)) {
1272+
nonAsciiWhitespaceCharCount++;
1273+
} else if (!ArrayPrototypeIncludes(asciiWhitespaceCharacters, char)) {
1274+
throw lazyDOMException('Invalid character', 'InvalidCharacterError');
1275+
}
1276+
}
12731277

12741278
// If data's code point length divides by 4 leaving a remainder of 1, then
12751279
// return failure.
12761280
//
12771281
// See #3 - https://infra.spec.whatwg.org/#forgiving-base64
1278-
if (input.length % 4 === 1) {
1282+
if (nonAsciiWhitespaceCharCount % 4 === 1) {
12791283
throw lazyDOMException(
12801284
'The string to be decoded is not correctly encoded.',
12811285
'ValidationError');
12821286
}
12831287

1284-
for (let n = 0; n < input.length; n++) {
1285-
if (!ArrayPrototypeIncludes(kForgivingBase64AllowedChars,
1286-
StringPrototypeCharCodeAt(input, n)))
1287-
throw lazyDOMException('Invalid character', 'InvalidCharacterError');
1288-
}
12891288
return Buffer.from(input, 'base64').toString('latin1');
12901289
}
12911290

test/parallel/test-btoa-atob.js

+13-8
Original file line numberDiff line numberDiff line change
@@ -16,11 +16,16 @@ throws(() => buffer.btoa(), /TypeError/);
1616
strictEqual(atob(' '), '');
1717
strictEqual(atob(' YW\tJ\njZA=\r= '), 'abcd');
1818

19-
throws(() => buffer.atob(undefined), /ValidationError/);
20-
throws(() => buffer.atob(false), /ValidationError/);
21-
throws(() => buffer.atob(1), /ValidationError/);
22-
throws(() => buffer.atob(0), /ValidationError/);
23-
throws(() => buffer.atob('a'), /ValidationError/);
24-
throws(() => buffer.atob('a '), /ValidationError/);
25-
throws(() => buffer.atob(' a'), /ValidationError/);
26-
throws(() => buffer.atob('aaaaa'), /ValidationError/);
19+
strictEqual(atob(null), '\x9Eée');
20+
strictEqual(atob(NaN), '5£');
21+
strictEqual(atob(Infinity), '"wâ\x9E+r');
22+
strictEqual(atob(true), '¶»\x9E');
23+
strictEqual(atob(1234), '×mø');
24+
strictEqual(atob([]), '');
25+
strictEqual(atob({ toString: () => '' }), '');
26+
strictEqual(atob({ [Symbol.toPrimitive]: () => '' }), '');
27+
28+
throws(() => atob(), /ERR_MISSING_ARGS/);
29+
throws(() => atob(Symbol()), /TypeError/);
30+
[undefined, false, () => {}, 0, 1, 0n, 1n, -Infinity, [1], {}].forEach((value) =>
31+
throws(() => atob(value), { constructor: DOMException }));

0 commit comments

Comments
 (0)