{"record":{"id":"05853c711a483d3c","repo":"can1357/oh-my-pi","slug":"invalid-cab-archive-cffolder-data-offset-is-out-o","errorCode":null,"errorMessage":"Invalid CAB archive: CFFOLDER data offset is out of bounds","messagePattern":"Invalid CAB archive: CFFOLDER data offset is out of bounds","errorType":"exception","errorClass":"ArchiveError","httpStatus":null,"severity":"error","filePath":"packages/utils/src/ar/cab.ts","lineNumber":304,"sourceCode":"\t}\n\tassertIndexSize(folderTableEnd, options.limits, \"CAB header\");\n\tconst header = await readExact(source, 0, folderTableEnd, cabinetSize);\n\tconst descriptions: CabFolderDescription[] = [];\n\tfor (let index = 0; index < folderCount; index++) {\n\t\tconst offset = folderTableOffset + index * folderRecordSize;\n\t\tconst type = readUInt16LE(header, offset + 6);\n\t\tdescriptions.push({\n\t\t\tdataStart: readUInt32LE(header, offset),\n\t\t\tdataEnd: cabinetSize,\n\t\t\tblockCount: readUInt16LE(header, offset + 4),\n\t\t\tmethod: type & 0x000f,\n\t\t\tparameter: type >>> 8,\n\t\t\trequiredSize: 0,\n\t\t});\n\t}\n\tfor (const description of descriptions) {\n\t\tif (description.dataStart < folderTableEnd || description.dataStart > cabinetSize) {\n\t\t\tthrow new ArchiveError(\"Invalid CAB archive: CFFOLDER data offset is out of bounds\");\n\t\t}\n\t\tfor (const candidate of descriptions) {\n\t\t\tif (candidate.dataStart > description.dataStart && candidate.dataStart < description.dataEnd) {\n\t\t\t\tdescription.dataEnd = candidate.dataStart;\n\t\t\t}\n\t\t}\n\t}\n\tconst firstDataOffset = descriptions.reduce(\n\t\t(minimum, description) => Math.min(minimum, description.dataStart),\n\t\tcabinetSize,\n\t);\n\tif (fileTableOffset > firstDataOffset)\n\t\tthrow new ArchiveError(\"Invalid CAB archive: CFFILE table overlaps folder data\");\n\tassertIndexSize(folderTableEnd + (firstDataOffset - fileTableOffset), options.limits, \"CAB index\");\n\tconst fileTable = await readExact(source, fileTableOffset, firstDataOffset, cabinetSize);\n\tconst folders = descriptions.map(description => new CabFolder(source, description, dataReserveSize, options.limits));\n\tconst entries: ArchiveIndexEntry[] = [];\n\tlet position = 0;","sourceCodeStart":286,"sourceCodeEnd":322,"githubUrl":"https://github.com/can1357/oh-my-pi/blob/969062200754ea02cfac922e5ebb8c608c079e15/packages/utils/src/ar/cab.ts#L286-L322","documentation":"Each CFFOLDER record declares the offset of its first data block (coffDataStart, relative to the cabinet start). After parsing all folder records, the library requires each folder's data start to lie after the folder table and within the declared cabinet size; an offset outside that window means the folder metadata points at impossible data.","triggerScenarios":"Calling readCab() on a CAB where a CFFOLDER's coffDataStart (bytes 8-11 of the folder record) is smaller than the end of the folder table or greater than cbCabinet — corruption, a bad writer, or overlapping folder data pointers.","commonSituations":"Fuzzed archives, partially overwritten files where a folder record's data pointer was damaged, and custom CAB writers that computed data offsets incorrectly (e.g. forgetting the header reserve when laying out blocks).","solutions":["Validate with cabextract to confirm the folder records are inconsistent; replace the archive if so.","Hex-dump each CFFOLDER record and verify coffDataStart falls between the end of the folder table and cbCabinet.","If using a custom writer, compute data-block offsets from the actual bytes emitted (header + reserve + folder table) rather than assumed sizes.","Check for byte-level corruption by comparing checksums against a known-good copy of the cabinet."],"exampleFix":null,"handlingStrategy":"validation","validationCode":"const buf = new Uint8Array(await Bun.file(path).arrayBuffer());\nconst u32 = (o: number) => (buf[o]! | (buf[o + 1]! << 8) | (buf[o + 2]! << 16) | (buf[o + 3]! << 24)) >>> 0;\nconst cbCabinet = u32(8);\n// after locating the folder table at folderTableOffset with recordSize 8 + folderReserve:\n// for each folder i: const dataStart = u32(folderTableOffset + i * recordSize);\n// if (dataStart < folderTableEnd || dataStart > cbCabinet) throw new Error(\"CFFOLDER data offset out of range\");","typeGuard":null,"tryCatchPattern":"try {\n\tconst entries = await readCab(source);\n} catch (err) {\n\tif (err instanceof ArchiveError && err.message.includes(\"CFFOLDER data offset is out of bounds\")) {\n\t\tthrow new Error(\"CAB folder records point outside the cabinet; archive is corrupt\", { cause: err });\n\t}\n\tthrow err;\n}","preventionTips":["Verify each CFFOLDER's data offset lies within the cabinet before full parsing.","In CAB writers, derive data-block offsets from bytes actually emitted, including reserves.","Checksum-verify archives transferred over unreliable channels."],"tags":["archive","cab","out-of-bounds","corrupt-header"],"backgroundTag":"corrupt-archive-header","analyzedSha":"969062200754ea02cfac922e5ebb8c608c079e15","analyzedAt":"2026-08-31T10:29:35.737Z","schemaVersion":2},"datasetVersion":"2026-08-31T14:17:45.589Z"}