btoa encodes input as UTF-8 instead of Latin-1 (wrong output for 128-255, no InvalidCharacterError above 255)
Summary
The btoa polyfill encodes its input as UTF-8 instead of Latin-1, so any code point in 128–255 produces the wrong base64, and code points above 255 are silently multi-byte-encoded instead of throwing InvalidCharacterError. Per the HTML spec, btoa maps each code unit to one byte and throws for any code point > 0xFF.
obscura runs on deno_core (no deno_runtime), so V8 does not provide btoa/atob — this polyfill is the active implementation (the atob_decodes_large_payload test exercises the pair).
Details
crates/obscura-js/js/bootstrap.js:
globalThis.btoa = globalThis.btoa || ((s) => { const b = new TextEncoder().encode(s); ... });btoa("é")(U+00E9) returns"w6k="(base64 of the UTF-8 bytes0xC3 0xA9) instead of the correct"6Q=="(Latin-1 byte0xE9).btoa("\\\u{1F600}")(an emoji) encodes 4 UTF-8 bytes and returns a string, when it should throwInvalidCharacterError.
Fix
Build the byte array with a charCodeAt loop: each code unit 0–255 maps to one byte; throw DOMException("...", "InvalidCharacterError") for any code unit > 255. Keep the existing base64 encoding loop.
Affected
crates/obscura-js/js/bootstrap.js—globalThis.btoa
Source: h4ckf0r0day/obscura