aboutsummaryrefslogtreecommitdiff
path: root/modules/ext/cryptogzip.js
diff options
context:
space:
mode:
Diffstat (limited to 'modules/ext/cryptogzip.js')
-rw-r--r--modules/ext/cryptogzip.js42
1 files changed, 28 insertions, 14 deletions
diff --git a/modules/ext/cryptogzip.js b/modules/ext/cryptogzip.js
index cc275df..25b6ca4 100644
--- a/modules/ext/cryptogzip.js
+++ b/modules/ext/cryptogzip.js
@@ -190,8 +190,31 @@ class CryptoGzipModule {
for (let i = 0; i < bytes.length; i++) s += String.fromCharCode(bytes[i]);
return btoa(s);
}
+ // Validate before atob(): atob() throws a raw DOMException (InvalidCharacterError / "characters
+ // outside of the Latin1 range") for arbitrary input, which escaped as an unhandledrejection when
+ // called from the node/JS console (RPT-20261102-203616-e2641bff). Everything atob() would reject
+ // is rejected here first, as a plain Error the command wrappers can print.
+ _base64InvalidReason(s) {
+ if (typeof s !== 'string') return 'expected a string, got ' + (s === null ? 'null' : typeof s);
+ const t = s.replace(/[\t\n\f\r ]/g, ''); // atob() ignores exactly these ASCII whitespace chars
+ if (t.length === 0) return null; // atob('') === '' — keep the empty case working
+ if (!/^[A-Za-z0-9+/]*={0,2}$/.test(t)) {
+ const bad = t.match(/[^A-Za-z0-9+/=]/);
+ if (bad) return 'illegal character ' + JSON.stringify(bad[0]) + ' — expected A-Z a-z 0-9 + / =';
+ return 'misplaced or excessive "=" padding';
+ }
+ const rem = t.length % 4;
+ if (rem === 1) return 'length ' + t.length + ' is not a valid base64 length';
+ // Padding may be omitted (atob accepts that too), but when present the length must be a
+ // multiple of 4 — otherwise atob() throws "not correctly encoded".
+ if (t.indexOf('=') !== -1 && rem !== 0) return 'length ' + t.length + ' has incomplete "=" padding';
+ return null;
+ }
+ _isBase64(s) { return this._base64InvalidReason(s) === null; }
_base64ToBytes(b64) {
- const s = atob(b64);
+ const reason = this._base64InvalidReason(b64);
+ if (reason) throw new Error('not valid base64 input (' + reason + ')');
+ const s = atob(b64.replace(/[\t\n\f\r ]/g, ''));
const u8 = new Uint8Array(s.length);
for (let i = 0; i < s.length; i++) u8[i] = s.charCodeAt(i);
return u8;
@@ -582,19 +605,10 @@ class CryptoGzipModule {
}
// ============================================================
- // Base64 Helpers
- // ============================================================
-
- _bytesToBase64(bytes) {
- const bin = Array.from(bytes, (b) => String.fromCharCode(b)).join('');
- return btoa(bin);
- }
-
- _base64ToBytes(b64) {
- const bin = atob(b64);
- return Uint8Array.from(bin, (c) => c.charCodeAt(0));
- }
-
+ // Base64 Helpers — defined once, above, next to the gzip code.
+ // (A second, later copy of _bytesToBase64/_base64ToBytes used to
+ // live here and silently shadow the first; it carried the unguarded
+ // atob() call. Do not re-add a duplicate.)
// ============================================================
registerCommands() {