orca/src/shared/raster-image-preview-limits.ts

73 lines
2.5 KiB
TypeScript

import { readRasterImageDimensions, type RasterImageDimensions } from './raster-image-dimensions'
export const MAX_RASTER_IMAGE_PREVIEW_DIMENSION_PX = 32_768
export const MAX_RASTER_IMAGE_PREVIEW_PIXELS = 32 * 1024 * 1024
// Bounds the transient decode buffer, not the image. 1 MiB cut off valid photos whose SOF sits past
// a large ICC/MPF block; 8 MiB clears every real-world metadata layout while staying a fraction of
// the base64 string the caller already holds.
export const RASTER_IMAGE_PREVIEW_HEADER_MAX_BYTES = 8 * 1024 * 1024
export const INVALID_RASTER_IMAGE_PREVIEW_ERROR =
'Image preview has invalid or unsupported raster dimensions'
export const RASTER_IMAGE_PREVIEW_TOO_LARGE_ERROR =
'Image dimensions exceed the preview safety limit'
const RASTER_IMAGE_MIME_TYPES = new Set([
'image/apng',
'image/bmp',
'image/gif',
'image/ico',
'image/jpeg',
'image/jpg',
'image/pjpeg',
'image/png',
'image/vnd.microsoft.icon',
'image/webp',
'image/x-bmp',
'image/x-icon',
'image/x-ms-bmp'
])
function normalizeMimeType(mimeType: string | undefined): string | null {
const normalized = mimeType?.split(';', 1)[0]?.trim().toLowerCase()
return normalized || null
}
export function isKnownRasterImageMimeType(mimeType: string | undefined): boolean {
const normalized = normalizeMimeType(mimeType)
return normalized !== null && RASTER_IMAGE_MIME_TYPES.has(normalized)
}
export function isRasterImagePreviewDimensions(value: unknown): value is RasterImageDimensions {
if (!value || typeof value !== 'object') {
return false
}
const dimensions = value as Partial<RasterImageDimensions>
return (
Number.isSafeInteger(dimensions.width) &&
Number.isSafeInteger(dimensions.height) &&
dimensions.width! > 0 &&
dimensions.height! > 0 &&
dimensions.width! <= MAX_RASTER_IMAGE_PREVIEW_DIMENSION_PX &&
dimensions.height! <= MAX_RASTER_IMAGE_PREVIEW_DIMENSION_PX &&
dimensions.width! <= Math.floor(MAX_RASTER_IMAGE_PREVIEW_PIXELS / dimensions.height!)
)
}
/** Validates encoded raster dimensions without invoking a native image decoder. */
export function assertRasterImagePreviewWithinLimits(
bytes: Uint8Array,
mimeType: string | undefined
): RasterImageDimensions | undefined {
if (!isKnownRasterImageMimeType(mimeType)) {
return undefined
}
const dimensions = readRasterImageDimensions(bytes)
if (!dimensions) {
throw new Error(INVALID_RASTER_IMAGE_PREVIEW_ERROR)
}
if (!isRasterImagePreviewDimensions(dimensions)) {
throw new Error(RASTER_IMAGE_PREVIEW_TOO_LARGE_ERROR)
}
return dimensions
}