updateSchema: Be resilient to invalid images

This commit is contained in:
Scott Nonnenberg 2022-07-05 17:28:00 -07:00 committed by GitHub
parent 5fcf97b43b
commit 064f3dd0e0
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
3 changed files with 44 additions and 23 deletions

View File

@ -28,6 +28,7 @@ import { scaleImageToLevel } from '../util/scaleImageToLevel';
import * as GoogleChrome from '../util/GoogleChrome'; import * as GoogleChrome from '../util/GoogleChrome';
import { parseIntOrThrow } from '../util/parseIntOrThrow'; import { parseIntOrThrow } from '../util/parseIntOrThrow';
import { getValue } from '../RemoteConfig'; import { getValue } from '../RemoteConfig';
import { isRecord } from '../util/isRecord';
const MAX_WIDTH = 300; const MAX_WIDTH = 300;
const MAX_HEIGHT = MAX_WIDTH * 1.5; const MAX_HEIGHT = MAX_WIDTH * 1.5;
@ -251,7 +252,7 @@ export function isValid(
// part of re-encoding the image: // part of re-encoding the image:
export async function autoOrientJPEG( export async function autoOrientJPEG(
attachment: AttachmentType, attachment: AttachmentType,
_: unknown, { logger }: { logger: LoggerType },
{ {
sendHQImages = false, sendHQImages = false,
isIncoming = false, isIncoming = false,
@ -280,26 +281,37 @@ export async function autoOrientJPEG(
const dataBlob = new Blob([attachment.data], { const dataBlob = new Blob([attachment.data], {
type: attachment.contentType, type: attachment.contentType,
}); });
const { blob: xcodedDataBlob } = await scaleImageToLevel( try {
dataBlob, const { blob: xcodedDataBlob } = await scaleImageToLevel(
attachment.contentType, dataBlob,
isIncoming attachment.contentType,
); isIncoming
const xcodedDataArrayBuffer = await blobToArrayBuffer(xcodedDataBlob); );
const xcodedDataArrayBuffer = await blobToArrayBuffer(xcodedDataBlob);
// IMPORTANT: We overwrite the existing `data` `Uint8Array` losing the original // IMPORTANT: We overwrite the existing `data` `Uint8Array` losing the original
// image data. Ideally, wed preserve the original image data for users who want to // image data. Ideally, wed preserve the original image data for users who want to
// retain it but due to reports of data loss, we dont want to overburden IndexedDB // retain it but due to reports of data loss, we dont want to overburden IndexedDB
// by potentially doubling stored image data. // by potentially doubling stored image data.
// See: https://github.com/signalapp/Signal-Desktop/issues/1589 // See: https://github.com/signalapp/Signal-Desktop/issues/1589
const xcodedAttachment = { const xcodedAttachment = {
// `digest` is no longer valid for auto-oriented image data, so we discard it: // `digest` is no longer valid for auto-oriented image data, so we discard it:
...omit(attachment, 'digest'), ...omit(attachment, 'digest'),
data: new Uint8Array(xcodedDataArrayBuffer), data: new Uint8Array(xcodedDataArrayBuffer),
size: xcodedDataArrayBuffer.byteLength, size: xcodedDataArrayBuffer.byteLength,
}; };
return xcodedAttachment; return xcodedAttachment;
} catch (error: unknown) {
const errorString =
isRecord(error) && 'stack' in error ? error.stack : error;
logger.error(
'autoOrientJPEG: Failed to rotate/scale attachment',
errorString
);
return attachment;
}
} }
const UNICODE_LEFT_TO_RIGHT_OVERRIDE = '\u202D'; const UNICODE_LEFT_TO_RIGHT_OVERRIDE = '\u202D';

View File

@ -580,9 +580,13 @@ export const processNewAttachment = async (
throw new TypeError('context.logger is required'); throw new TypeError('context.logger is required');
} }
const rotatedAttachment = await autoOrientJPEG(attachment, undefined, { const rotatedAttachment = await autoOrientJPEG(
isIncoming: true, attachment,
}); { logger },
{
isIncoming: true,
}
);
const onDiskAttachment = await migrateDataToFileSystem(rotatedAttachment, { const onDiskAttachment = await migrateDataToFileSystem(rotatedAttachment, {
writeNewAttachmentData, writeNewAttachmentData,
}); });

View File

@ -8,6 +8,7 @@ import { IMAGE_JPEG } from '../types/MIME';
import { canvasToBlob } from './canvasToBlob'; import { canvasToBlob } from './canvasToBlob';
import { getValue } from '../RemoteConfig'; import { getValue } from '../RemoteConfig';
import { parseNumber } from './libphonenumberUtil'; import { parseNumber } from './libphonenumberUtil';
import { isRecord } from './isRecord';
enum MediaQualityLevels { enum MediaQualityLevels {
One = 1, One = 1,
@ -126,7 +127,11 @@ export async function scaleImageToLevel(
} }
({ image } = data); ({ image } = data);
} catch (err) { } catch (err) {
const error = new Error('scaleImageToLevel: Failed to process image'); const errorString = isRecord(err) && 'stack' in err ? err.stack : err;
const error = new Error(
'scaleImageToLevel: Failed to process image',
errorString
);
error.originalError = err; error.originalError = err;
throw error; throw error;
} }