{"record":{"id":"39d68b1a66273b25","repo":"can1357/oh-my-pi","slug":"invalid-cab-archive-reserved-cfheader-fields-must","errorCode":null,"errorMessage":"Invalid CAB archive: reserved CFHEADER fields must be zero","messagePattern":"Invalid CAB archive: reserved CFHEADER fields must be zero","errorType":"exception","errorClass":"ArchiveError","httpStatus":null,"severity":"error","filePath":"packages/utils/src/ar/cab.ts","lineNumber":248,"sourceCode":"\tasync read(size: number, memberPath: string): Promise<Uint8Array> {\n\t\tif (size !== this.#declaredSize) {\n\t\t\tthrow new ArchiveError(`Invalid CAB archive: size changed while extracting '${memberPath}'`);\n\t\t}\n\t\tconst folder = await this.#folder.readAll();\n\t\tconst end = this.#offset + size;\n\t\tif (!Number.isSafeInteger(end) || this.#offset < 0 || end > folder.byteLength) {\n\t\t\tthrow new ArchiveError(`Invalid CAB archive: member '${memberPath}' is outside its folder data`);\n\t\t}\n\t\treturn folder.slice(this.#offset, end);\n\t}\n}\n\nasync function readCabArchive(source: ByteSource, options: Parameters<FormatReader>[1]): Promise<ArchiveIndexEntry[]> {\n\tif (source.size < FIXED_HEADER_SIZE) throw new ArchiveError(\"Invalid CAB archive: truncated CFHEADER\");\n\tconst fixed = await readExact(source, 0, FIXED_HEADER_SIZE);\n\tif (!hasSignature(fixed)) throw new ArchiveError(`Invalid CAB archive: expected ${CAB_SIGNATURE} signature`);\n\tif (readUInt32LE(fixed, 4) !== 0 || readUInt32LE(fixed, 12) !== 0 || readUInt32LE(fixed, 20) !== 0) {\n\t\tthrow new ArchiveError(\"Invalid CAB archive: reserved CFHEADER fields must be zero\");\n\t}\n\tconst cabinetSize = readUInt32LE(fixed, 8);\n\tif (cabinetSize < FIXED_HEADER_SIZE || cabinetSize > source.size) {\n\t\tthrow new ArchiveError(\"Invalid CAB archive: declared cabinet size is out of bounds\");\n\t}\n\tconst fileTableOffset = readUInt32LE(fixed, 16);\n\tif (fileTableOffset < FIXED_HEADER_SIZE || fileTableOffset > cabinetSize) {\n\t\tthrow new ArchiveError(\"Invalid CAB archive: CFFILE table offset is out of bounds\");\n\t}\n\tif (fixed[24] !== 3 || fixed[25] !== 1) {\n\t\tthrow new ArchiveError(`Unsupported CAB format version ${fixed[25]}.${fixed[24]} (expected 1.3)`);\n\t}\n\tconst folderCount = readUInt16LE(fixed, 26);\n\tconst fileCount = readUInt16LE(fixed, 28);\n\tconst flags = readUInt16LE(fixed, 30);\n\tif (flags & 0x0003) throw new ArchiveError(\"Unsupported multi-volume CAB archive (previous/next cabinet link)\");\n\tassertEntryCount(folderCount + fileCount, options.limits);\n\tif (folderCount === 0 && fileCount !== 0)","sourceCodeStart":230,"sourceCodeEnd":266,"githubUrl":"https://github.com/can1357/oh-my-pi/blob/969062200754ea02cfac922e5ebb8c608c079e15/packages/utils/src/ar/cab.ts#L230-L266","documentation":"The CFHEADER contains three reserved 32-bit fields (offsets 4, 12, and 20) that the CAB specification requires to be zero. The library rejects any cabinet where any of these is non-zero, since a valid writer never sets them and non-zero values indicate a corrupt or hand-crafted file.","triggerScenarios":"Calling readCab() on a file with a valid 'MSCF' signature but non-zero data at header offsets 4, 12, or 20 — typically a bit-flipped, partially overwritten, or maliciously malformed cabinet.","commonSituations":"Corrupted downloads or disk-level damage that only flipped a few header bytes, fuzzed or hand-edited test files, and cabinets assembled by broken third-party tooling that writes garbage into reserved fields.","solutions":["Re-obtain the archive from a trusted source; the header is corrupt and cannot be safely repaired by the caller.","Inspect header bytes 4-7, 12-15, and 20-23 with a hex dump to confirm which reserved field is non-zero.","If the file comes from an in-house generator, fix the writer to zero the reserved fields per the MS-CAB specification.","Test the cabinet with a reference tool (e.g. cabextract) to see whether it is generally considered corrupt."],"exampleFix":null,"handlingStrategy":"validation","validationCode":"import { readUInt32LE } from \"@oh-my-pi/pi-utils/ar/bytes\";\nconst fixed = new Uint8Array(buf.slice(0, 36));\nif (fixed[4] !== 0 || fixed[5] !== 0 || fixed[6] !== 0 || fixed[7] !== 0 ||\n\tfixed[12] !== 0 || fixed[13] !== 0 || fixed[14] !== 0 || fixed[15] !== 0 ||\n\tfixed[20] !== 0 || fixed[21] !== 0 || fixed[22] !== 0 || fixed[23] !== 0) {\n\tthrow new Error(\"CAB reserved header fields are non-zero; file is corrupt\");\n}","typeGuard":null,"tryCatchPattern":"try {\n\tconst entries = await readCab(source);\n} catch (err) {\n\tif (err instanceof ArchiveError && err.message.includes(\"reserved CFHEADER fields\")) {\n\t\tlogger.warn(\"CAB header corrupt; skipping archive\", { path });\n\t\treturn null;\n\t}\n\tthrow err;\n}","preventionTips":["Treat this as file corruption: re-download instead of trying to repair.","Validate checksums on transfer for archive files.","If generating CABs, zero all reserved header fields per the spec."],"tags":["archive","cab","corrupt-header","file-format"],"backgroundTag":"corrupt-archive-header","analyzedSha":"969062200754ea02cfac922e5ebb8c608c079e15","analyzedAt":"2026-08-31T10:29:35.737Z","schemaVersion":2},"datasetVersion":"2026-08-31T14:17:45.589Z"}