From 710e4da4c088db9b4f2d5fe01ca4adb59492906f Mon Sep 17 00:00:00 2001 From: Felix-Ayush <67006255+Ayush7614@users.noreply.github.com> Date: Thu, 30 Jul 2026 00:51:21 +0530 Subject: [PATCH] fix(puter-js): size img2txt data URIs by decoded bytes (#3457) Match speech2txt/speech2speech and reject on decoded payload size, not data-URI string length. Base64 expands ~4/3, so string-length checks falsely rejected valid images under the 10MB limit. --- src/puter-js/src/modules/ai/ai.test.js | 20 ++++++++++++++++++++ src/puter-js/src/modules/ai/ocr.js | 4 ++-- 2 files changed, 22 insertions(+), 2 deletions(-) diff --git a/src/puter-js/src/modules/ai/ai.test.js b/src/puter-js/src/modules/ai/ai.test.js index c9ef15cb1..5e772a912 100644 --- a/src/puter-js/src/modules/ai/ai.test.js +++ b/src/puter-js/src/modules/ai/ai.test.js @@ -289,6 +289,26 @@ describe('ai.img2txt driver payloads', () => { it('img2txt rejects without a source', async () => { await expect(ai.img2txt({})).rejects.toMatchObject({ code: 'source_required' }); }); + + it('img2txt sizes data URIs by decoded bytes, not string length', async () => { + FakeXHR.respondWith = () => ({ success: true, result: { text: 'ok' } }); + const prefix = 'data:image/png;base64,'; + // String length just over 10MB; base64 still decodes under the 10MB limit (~7.5MB). + const targetLen = 10 * 1024 * 1024 + 4; + let base64Len = targetLen - prefix.length; + base64Len += (4 - (base64Len % 4)) % 4; + const uri = prefix + 'A'.repeat(base64Len); + expect(uri.length).toBeGreaterThan(10 * 1024 * 1024); + await expect(ai.img2txt(uri)).resolves.toBe('ok'); + }); + + it('img2txt rejects when decoded data-URI bytes exceed 10MB', async () => { + const prefix = 'data:image/png;base64,'; + let base64Len = Math.ceil(((10 * 1024 * 1024) + 1) * 4 / 3); + base64Len += (4 - (base64Len % 4)) % 4; + const uri = prefix + 'A'.repeat(base64Len); + await expect(ai.img2txt(uri)).rejects.toMatchObject({ code: 'input_too_large' }); + }); }); describe('ai.txt2speech driver payloads', () => { diff --git a/src/puter-js/src/modules/ai/ocr.js b/src/puter-js/src/modules/ai/ocr.js index 457e3bffd..3d4831ac6 100644 --- a/src/puter-js/src/modules/ai/ocr.js +++ b/src/puter-js/src/modules/ai/ocr.js @@ -1,5 +1,5 @@ import * as utils from '../../lib/utils.js'; -import { isBlobLike, isPlainObject } from './lib/args.js'; +import { dataUriByteLength, isBlobLike, isPlainObject } from './lib/args.js'; /** @typedef {import('../../../types/modules/ai').Img2TxtOptions} Img2TxtOptions */ @@ -124,7 +124,7 @@ export async function img2txt (sourceOrOptions, optionsOrTestMode, testModeOrOpt if ( typeof options.source === 'string' && options.source.startsWith('data:') && - options.source.length > MAX_INPUT_SIZE ) { + dataUriByteLength(options.source) > MAX_INPUT_SIZE ) { throw { message: `Input size cannot be larger than ${ MAX_INPUT_SIZE}`, code: 'input_too_large' }; }