Checkpoint web parity through Chromium input tasks
This commit is contained in:
@@ -13,6 +13,8 @@ export const ASSET_LIBRARY_BUDGET = {
|
||||
maxEntryBytes: 2 * 1024 * 1024 * 1024,
|
||||
maxArchiveBytes: 4 * 1024 * 1024 * 1024,
|
||||
maxCompressionRatio: 100,
|
||||
maxArchivePathDepth: 64,
|
||||
maxArchiveFileNameBytes: 255,
|
||||
maxExternalUris: 10_000,
|
||||
} as const;
|
||||
|
||||
@@ -51,6 +53,11 @@ const FORMATS = new Set<IOFormat>(["GLB", "GLTF", "OBJ", "PLY", "STL", "USD", "A
|
||||
function record(value: unknown): value is Record<string, unknown> { return typeof value === "object" && value !== null && !Array.isArray(value); }
|
||||
function text(value: unknown, name: string, maximum = 256): string { if (typeof value !== "string" || value.length === 0 || value.length > maximum) throw new AssetLibraryValidationError("ASSET_MANIFEST_INVALID", `${name} is invalid`); return value; }
|
||||
function integer(value: unknown, name: string, minimum: number, maximum: number): number { if (typeof value !== "number" || !Number.isSafeInteger(value) || value < minimum || value > maximum) throw new AssetLibraryValidationError("ASSET_MANIFEST_INVALID", `${name} is outside the bounded range`); return value; }
|
||||
function archiveInteger(value: unknown, name: string, minimum: number, maximum: number): number {
|
||||
if (typeof value !== "number" || !Number.isSafeInteger(value) || value < minimum) throw new AssetLibraryValidationError("ASSET_MANIFEST_INVALID", `${name} is outside the bounded range`);
|
||||
if (value > maximum) throw new AssetLibraryValidationError("ASSET_BUDGET_EXCEEDED", `${name} exceeds the archive entry budget`);
|
||||
return value;
|
||||
}
|
||||
function digest(value: unknown, name: string): string { if (typeof value !== "string" || !SHA256.test(value)) throw new AssetLibraryValidationError("ASSET_MANIFEST_INVALID", `${name} must be a lowercase SHA-256 digest`); return value; }
|
||||
function projectPath(value: unknown, name: string, code: ErrorCode = "ASSET_MANIFEST_INVALID"): string { try { return normalizeProjectAssetPath(text(value, name, 2048)); } catch { throw new AssetLibraryValidationError(code, `${name} is outside the project`); } }
|
||||
|
||||
@@ -131,7 +138,10 @@ export function parseIORequest(value: unknown): IORequestIR {
|
||||
let totalCompressed = 0; let totalUncompressed = 0; const archivePaths = new Set<string>();
|
||||
request.archiveEntries = value.archiveEntries.map((entry, index): IOArchiveEntryIR => {
|
||||
if (!record(entry)) throw new AssetLibraryValidationError("IO_ARCHIVE_UNSAFE", `archiveEntries[${index}] is invalid`);
|
||||
const path = projectPath(entry.path, `archiveEntries[${index}].path`, "IO_ARCHIVE_UNSAFE"); const compressedBytes = integer(entry.compressedBytes, `archiveEntries[${index}].compressedBytes`, 0, ASSET_LIBRARY_BUDGET.maxEntryBytes); const uncompressedBytes = integer(entry.uncompressedBytes, `archiveEntries[${index}].uncompressedBytes`, 0, ASSET_LIBRARY_BUDGET.maxEntryBytes);
|
||||
const path = projectPath(entry.path, `archiveEntries[${index}].path`, "IO_ARCHIVE_UNSAFE"); const compressedBytes = archiveInteger(entry.compressedBytes, `archiveEntries[${index}].compressedBytes`, 0, ASSET_LIBRARY_BUDGET.maxEntryBytes); const uncompressedBytes = archiveInteger(entry.uncompressedBytes, `archiveEntries[${index}].uncompressedBytes`, 0, ASSET_LIBRARY_BUDGET.maxEntryBytes);
|
||||
const pathSegments = path.split("/");
|
||||
const fileNameBytes = new TextEncoder().encode(pathSegments[pathSegments.length - 1]).byteLength;
|
||||
if (pathSegments.length - 1 > ASSET_LIBRARY_BUDGET.maxArchivePathDepth || fileNameBytes > ASSET_LIBRARY_BUDGET.maxArchiveFileNameBytes) throw new AssetLibraryValidationError("IO_ARCHIVE_UNSAFE", `Archive path ${path} exceeds its depth or filename budget`);
|
||||
if (archivePaths.has(path) || [...archivePaths].some((existing) => existing.startsWith(`${path}/`) || path.startsWith(`${existing}/`))) throw new AssetLibraryValidationError("IO_ARCHIVE_UNSAFE", `Archive path ${path} is duplicated or conflicts with a file prefix`);
|
||||
archivePaths.add(path);
|
||||
totalCompressed += compressedBytes; totalUncompressed += uncompressedBytes;
|
||||
|
||||
Reference in New Issue
Block a user