Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
5 changes: 5 additions & 0 deletions src/util/base64url.ts
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,11 @@ export function decode(input: Uint8Array | string): Uint8Array {
if (encoded instanceof Uint8Array) {
encoded = decoder.decode(encoded)
}
if (encoded.indexOf('+') !== -1 || encoded.indexOf('/') !== -1) {
// + and / are not in the Base64URL alphabet; reject them here so this
// path matches Uint8Array.fromBase64(..., { alphabet: 'base64url' }) above
throw new TypeError('The input to be decoded is not correctly encoded.')
}
encoded = encoded.replace(/-/g, '+').replace(/_/g, '/')
try {
return decodeBase64(encoded)
Expand Down
29 changes: 29 additions & 0 deletions test/unit/base64url.test.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,29 @@
import test from 'ava'

import { decode } from '../../src/util/base64url.js'

// `+` and `/` are valid Base64 but not Base64URL. Uint8Array.fromBase64 with
// the base64url alphabet rejects them, so the no-native-fromBase64 fallback
// (which translates `-_` to `+/` and decodes via atob) must reject them too
// instead of accepting them as standard Base64.

test('decode rejects + and / (native path)', (t) => {
t.throws(() => decode('+/+/'))
t.throws(() => decode('AB+CD'))
t.deepEqual(decode('-_-_'), new Uint8Array([0xfb, 0xff, 0xbf]))
})

test.serial('decode rejects + and / (fallback path)', (t) => {
// @ts-ignore
const native = Uint8Array.fromBase64
try {
// @ts-ignore
delete Uint8Array.fromBase64
t.throws(() => decode('+/+/'), { instanceOf: TypeError })
t.throws(() => decode('AB/CD'), { instanceOf: TypeError })
t.deepEqual(decode('-_-_'), new Uint8Array([0xfb, 0xff, 0xbf]))
} finally {
// @ts-ignore
Uint8Array.fromBase64 = native
}
})