fix(ai): reject non-ASCII base64 that Node decodes by its low byte

Node reads code units above 0xFF by their low byte, so U+012B decodes
as '+' and keeps the decoded length the input predicts. Require ASCII
(~1-2 ms for 43M chars) before the length check.
This commit is contained in:
Daniel Salazar committed 2026-10-18 21:53:03 -07:00
1 parent 9d9c0e85fd
commit e16cfb8302
2 files changed
+4

No files matched your search

+1
View File
@@ -35,6 +35,7 @@ describe('decodeStrictBase64', () => {
['the URL-safe alphabet', 'a-b_'],
['whitespace', 'YW Jj'],
['a character outside the alphabet', 'YW*j'],
['a non-ASCII character whose low byte is in the alphabet', 'YWJī'],
['padding mid-string', 'YQ==YWJj'],
['three pad characters', 'Y==='],
])('rejects %s', (_label, value) => {
+3
View File
@@ -59,6 +59,9 @@ const MP4_BRANDS = new Set([
*/
export function decodeStrictBase64(value: string): Buffer | null {
if (value.length === 0 || value.length % 4 !== 0) return null;
// Node decodes a code unit above 0xFF by its low byte (`ī` as `+`), so
// only ASCII is decoded.
if (Buffer.byteLength(value, 'utf8') !== value.length) return null;
// Node also decodes the URL-safe alphabet.
if (value.includes('-') || value.includes('_')) return null;
const padding = value.endsWith('==') ? 2 : value.endsWith('=') ? 1 : 0;