{"record":{"id":"4d4cba45abe40961","repo":"can1357/oh-my-pi","slug":"invalid-cab-archive-cffolder-table-is-out-of-boun","errorCode":null,"errorMessage":"Invalid CAB archive: CFFOLDER table is out of bounds","messagePattern":"Invalid CAB archive: CFFOLDER table is out of bounds","errorType":"exception","errorClass":"ArchiveError","httpStatus":null,"severity":"error","filePath":"packages/utils/src/ar/cab.ts","lineNumber":285,"sourceCode":"\t\tthrow new ArchiveError(\"Invalid CAB archive: files exist without a folder\");\n\n\tlet headerReserveSize = 0;\n\tlet folderReserveSize = 0;\n\tlet dataReserveSize = 0;\n\tlet folderTableOffset = FIXED_HEADER_SIZE;\n\tif (flags & 0x0004) {\n\t\tconst reserveHeader = await readExact(source, FIXED_HEADER_SIZE, FIXED_HEADER_SIZE + 4, cabinetSize);\n\t\theaderReserveSize = readUInt16LE(reserveHeader, 0);\n\t\tfolderReserveSize = reserveHeader[2]!;\n\t\tdataReserveSize = reserveHeader[3]!;\n\t\tif (headerReserveSize > 60_000)\n\t\t\tthrow new ArchiveError(\"Invalid CAB archive: CFHEADER reserve area exceeds 60000 bytes\");\n\t\tfolderTableOffset += 4 + headerReserveSize;\n\t}\n\tconst folderRecordSize = 8 + folderReserveSize;\n\tconst folderTableEnd = folderTableOffset + folderCount * folderRecordSize;\n\tif (!Number.isSafeInteger(folderTableEnd) || folderTableEnd > cabinetSize || folderTableEnd > fileTableOffset) {\n\t\tthrow new ArchiveError(\"Invalid CAB archive: CFFOLDER table is out of bounds\");\n\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) {","sourceCodeStart":267,"sourceCodeEnd":303,"githubUrl":"https://github.com/can1357/oh-my-pi/blob/969062200754ea02cfac922e5ebb8c608c079e15/packages/utils/src/ar/cab.ts#L267-L303","documentation":"The library computes where the CFFOLDER table ends: folderTableOffset (fixed header + optional reserve) + folderCount * folderRecordSize (8 + folder reserve). It requires this end to be a safe integer, to fit within the declared cabinet size, and not to run past the CFFILE table offset. Otherwise the folder table would overlap other structures or read past the file, so it throws.","triggerScenarios":"Calling readCab() on a CAB whose folder count, folder reserve size, header reserve size, or offsets are inconsistent — e.g. folderCount * (8 + folderReserveSize) overflows to a non-safe integer, or the table extends beyond cbCabinet or coffFiles.","commonSituations":"Fuzzed archives with huge folder counts, corrupted files where the folder count or reserve fields were altered, and buggy writers that place the CFFILE table before the folder table ends.","solutions":["Validate the cabinet with cabextract/7-Zip to confirm the header layout is inconsistent, then re-obtain the file.","Check cFolders (offset 26), cbCFHeader reserve (offset 36 area) and coffFiles (offset 16) against each other with a hex dump.","If you write CAB files, compute the folder-table end before placing the CFFILE table and ensure it fits inside cbCabinet.","Reduce folder counts in generated archives; enormous folder counts usually indicate a writer bug or an attack input."],"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 u16 = (o: number) => buf[o]! | (buf[o + 1]! << 8);\nconst cbCabinet = u32(8), coffFiles = u32(16);\nconst folderCount = u16(26);\nconst flags = u16(30);\nlet folderTableOffset = 36;\nif (flags & 0x0004) folderTableOffset += 4 + u16(36);\nconst end = folderTableOffset + folderCount * 8; // assumes no folder reserve\nif (end > cbCabinet || end > coffFiles) throw new Error(\"CAB folder table would overflow the cabinet\");","typeGuard":null,"tryCatchPattern":"try {\n\tconst entries = await readCab(source);\n} catch (err) {\n\tif (err instanceof ArchiveError && err.message.includes(\"CFFOLDER table is out of bounds\")) {\n\t\tlogger.warn(\"Skipping CAB with inconsistent folder table\", { path });\n\t\treturn null;\n\t}\n\tthrow err;\n}","preventionTips":["Compute folder-table extent against cbCabinet and coffFiles before parsing untrusted files.","Reject archives with implausible folder counts early.","Validate with an external tool when the archive origin is unknown."],"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"}