diff --git a/packages/base64/src/decode.js b/packages/base64/src/decode.js index 63542f60f4..6c8fea3a8f 100644 --- a/packages/base64/src/decode.js +++ b/packages/base64/src/decode.js @@ -44,12 +44,14 @@ export const jsDecodeBase64 = (string, name = '') => { i += 1; } - while (i < string.length && quantum % 8 !== 0) { - if (string[i] !== padding) { + while (quantum > 0) { + if (i === string.length || string[i] !== padding) { throw Error(`Missing padding at offset ${i} of string ${name}`); } + // We MAY reject non-zero padding bits, but choose not to. + // https://datatracker.ietf.org/doc/html/rfc4648#section-3.5 i += 1; - quantum += 6; + quantum -= 2; } if (i < string.length) { diff --git a/packages/base64/test/test-main.js b/packages/base64/test/test-main.js index 7e68df8123..5051fda520 100644 --- a/packages/base64/test/test-main.js +++ b/packages/base64/test/test-main.js @@ -57,3 +57,40 @@ test('bytes conversions', t => { t.is(atob(btoa(str)), str, `${str} round trips with atob(btoa)`); } }); + +test('invalid encodings', t => { + const badInputs = [ + ['%', /Invalid base64 character %/], + ['=', undefined], // this input is bad in multiple ways + + ['Z%', /Invalid base64 character %/], + ['Z', /Missing padding at offset 1/], + ['Z=', /Missing padding at offset 2/], + ['Z=%', /Missing padding at offset 2/], + ['Z==%', /Missing padding at offset 3/], + ['Z==m', /Missing padding at offset 3/], + + ['Zg%', /Invalid base64 character %/], + ['Zg', /Missing padding at offset 2/], + ['Zg=', /Missing padding at offset 3/], + ['Zg=%', /Missing padding at offset 3/], + ['Zg==%', /trailing garbage %/], + ['Zg==m', /trailing garbage m/], + + ['Zm8%', /Invalid base64 character %/], + ['Zm8', /Missing padding at offset 3/], + // not invalid: 'Zm8=' + ['Zm8=%', /trailing garbage %/], + ['Zm8==%', /trailing garbage =%/], + ['Zm8==m', /trailing garbage =m/], + + // non-zero padding bits (MAY reject): ['Qf==', ...], + ]; + for (const [badInput, message] of badInputs) { + t.throws( + () => decodeBase64(badInput), + message && { message }, + `${badInput} is rejected`, + ); + } +});