{"record":{"id":"9cda78203d12f6b7","repo":"gchq/CyberChef","slug":"malformed-lznt1-stream-block-too-small-has-the-s","errorCode":null,"errorMessage":"Malformed LZNT1 stream: Block too small! Has the stream been truncated?","messagePattern":"Malformed LZNT1 stream: Block too small! Has the stream been truncated\\?","errorType":"exception","errorClass":"OperationError","httpStatus":null,"severity":"error","filePath":"src/core/lib/LZNT1.mjs","lineNumber":51,"sourceCode":" * @returns {byteArray}\n */\nexport function decompress(compressed) {\n    const decompressed = Array();\n    let coffset = 0;\n\n    while (coffset + 2 <= compressed.length) {\n        const doffset = decompressed.length;\n\n        const blockHeader = Utils.byteArrayToInt(compressed.slice(coffset, coffset + 2), \"little\");\n        coffset += 2;\n\n        const size = blockHeader & SIZE_MASK;\n        const blockEnd = coffset + size + 1;\n\n        if (size === 0) {\n            break;\n        } else if (compressed.length < coffset + size) {\n            throw new OperationError(\"Malformed LZNT1 stream: Block too small! Has the stream been truncated?\");\n        }\n\n        if ((blockHeader & COMPRESSED_MASK) !== 0) {\n            while (coffset < blockEnd) {\n                let header = compressed[coffset++];\n\n                for (let i = 0; i < 8 && coffset < blockEnd; i++) {\n                    if ((header & 1) === 0) {\n                        decompressed.push(compressed[coffset++]);\n                    } else {\n                        const pointer = Utils.byteArrayToInt(compressed.slice(coffset, coffset + 2), \"little\");\n                        coffset += 2;\n\n                        const displacement = getDisplacement(decompressed.length - doffset - 1);\n                        const symbolOffset = (pointer >> (12 - displacement)) + 1;\n                        const symbolLength = (pointer & (0xFFF >> displacement)) + 2;\n                        const shiftOffset = decompressed.length - symbolOffset;\n","sourceCodeStart":33,"sourceCodeEnd":69,"githubUrl":"https://github.com/gchq/CyberChef/blob/4290ea753912378913b1f3f54e0fc5720afeda5d/src/core/lib/LZNT1.mjs#L33-L69","documentation":"LZNT1 is the compression format used by NTFS for compressed files. decompress() reads 2-byte block headers, extracts a 12-bit size field, and requires the remaining buffer to hold that many bytes. If compressed.length < coffset + size the block cannot be complete, so the stream is reported as truncated.","triggerScenarios":"Calling LZNT1.decompress(byteArray) where the byte array ends before the declared size of the current block. Happens with NTFS-compressed data that was cut off, parsed from the wrong offset, or read past an actual end-of-stream marker.","commonSituations":"Carving compressed NTFS data from a disk image and slicing at the wrong length; networking or storage layer truncating the buffer; misinterpreting a non-LZNT1 stream as LZNT1 (wrong magic/offset); a previous decompression step stopped early.","solutions":["Re-extract the source buffer with extra trailing bytes to confirm whether it is genuinely truncated.","Verify the input was produced by NTFS LZNT1 compression and not by another algorithm (LZSS, XPRESS).","Check the offset where the LZNT1 stream starts in the parent structure (e.g. NTFS $DATA attribute) and re-slice.","If you control the producer, ensure the full compressed attribute is captured including the terminating zero-size block."],"exampleFix":"// before\nconst out = LZNT1.decompress(truncatedBytes);\n\n// after - verify length against the declared last block before calling\nfunction safeLZNT1(bytes) {\n  // walk headers to confirm the buffer covers every declared block\n  let off = 0;\n  while (off + 2 <= bytes.length) {\n    const hdr = bytes[off] | (bytes[off+1] << 8);\n    const size = hdr & 0x0FFF;\n    off += 2;\n    if (size === 0) break;\n    if (bytes.length < off + size) throw new Error('truncated upstream');\n    off += size + 1;\n  }\n  return LZNT1.decompress(bytes);\n}","handlingStrategy":"validation","validationCode":"function isCompleteLZNT1(bytes) {\n  let off = 0;\n  while (off + 2 <= bytes.length) {\n    const hdr = bytes[off] | (bytes[off + 1] << 8);\n    off += 2;\n    const size = hdr & 0x0FFF;\n    if (size === 0) return true; // legitimate end-of-stream\n    if (bytes.length < off + size) return false;\n    off += size + 1;\n  }\n  return true; // ran out cleanly\n}\n\nif (!isCompleteLZNT1(bytes)) throw new Error('truncated upstream');\nLZNT1.decompress(bytes);","typeGuard":null,"tryCatchPattern":"try {\n  return LZNT1.decompress(bytes);\n} catch (e) {\n  if (e instanceof OperationError && /truncated/i.test(e.message)) {\n    return { error: 'truncated', partial: null };\n  }\n  throw e;\n}","preventionTips":["Re-extract the compressed attribute from a trusted source.","Verify the starting offset within the parent NTFS structure.","Confirm the data is LZNT1 and not XPRESS/LZSS.","Walk the block headers yourself to confirm completeness before decompressing."],"tags":["compression","lznt1","ntfs","data-corruption"],"backgroundTag":null,"analyzedSha":"4290ea753912378913b1f3f54e0fc5720afeda5d","analyzedAt":"2026-08-13T06:05:50.210Z","schemaVersion":2},"datasetVersion":"2026-08-13T09:17:06.757Z"}