{"record":{"id":"ecc89fe56c9fab02","repo":"affaan-m/ECC","slug":"unsafe-nasiko-archive-invalid-tar-size-field","errorCode":null,"errorMessage":"Unsafe Nasiko archive: invalid tar size field.","messagePattern":"Unsafe Nasiko archive: invalid tar size field\\.","errorType":"exception","errorClass":"Error","httpStatus":null,"severity":"error","filePath":"scripts/lib/nasiko-release.js","lineNumber":83,"sourceCode":"  }\n  const layer = manifest.layers[0];\n  if (layer.mediaType !== 'application/gzip' || !SHA256_PATTERN.test(layer.digest)) {\n    throw new Error('Nasiko manifest layer is not a qualified gzip artifact.');\n  }\n  if (!Number.isSafeInteger(layer.size) || layer.size <= 0 || layer.size > MAX_ARCHIVE_BYTES) {\n    throw new Error('Nasiko manifest layer size is outside the allowed range.');\n  }\n  return { digest: layer.digest, size: layer.size };\n}\n\nfunction readTarString(block, offset, length) {\n  return block.subarray(offset, offset + length).toString('utf8').replace(/\\0.*$/, '');\n}\n\nfunction readTarOctal(block, offset, length) {\n  const field = block.subarray(offset, offset + length).toString('ascii');\n  const match = /^ *([0-7]+)[ \\0]*$/.exec(field);\n  if (!match) throw new Error('Unsafe Nasiko archive: invalid tar size field.');\n  const size = Number.parseInt(match[1], 8);\n  if (!Number.isSafeInteger(size) || size < 0) {\n    throw new Error('Unsafe Nasiko archive: invalid tar size field.');\n  }\n  return size;\n}\n\nfunction extractQualifiedTarGzip(archiveBytes, expectedName) {\n  let tar;\n  try { tar = zlib.gunzipSync(archiveBytes, { maxOutputLength: MAX_BINARY_BYTES + 2048 }); }\n  catch (_error) { throw new Error('Nasiko archive is invalid or exceeds the decompressed size limit.'); }\n  let offset = 0;\n  let binary = null;\n  let terminated = false;\n  while (offset < tar.length) {\n    if (offset + 512 > tar.length) throw new Error('Unsafe Nasiko archive: truncated tar header.');\n    const header = tar.subarray(offset, offset + 512);\n    if (header.every(byte => byte === 0)) {","sourceCodeStart":65,"sourceCodeEnd":101,"githubUrl":"https://github.com/affaan-m/ECC/blob/8321021c54d670126ce3b2969d5deb880b4b0c2a/scripts/lib/nasiko-release.js#L65-L101","documentation":"readTarOctal parses a tar header size field, which must be space-padded octal digits. This error (thrown at the regex check) means the field does not match the expected tar numeric format, so the archive header is corrupt or not a real tar header. It is part of the library's defensive extraction of the gzip artifact.","triggerScenarios":"extractQualifiedTarGzip walks 512-byte tar blocks and calls readTarOctal on a header whose size field contains non-octal bytes — e.g. the gunzipped data is not a tar at all, the header offset drifted, or the file uses a non-standard numeric format (base-256 GNU extension the regex rejects).","commonSituations":"Extracting an artifact that is gzip but not tar (bare binary gzipped), a corrupted download, or a GNU/sparse tar feature that encodes size in base-256 binary instead of ASCII octal.","solutions":["Gunzip the archive manually and hexdump the first 512 bytes to confirm it is a real tar header with an octal size at offset 124.","Re-download the artifact — corruption mid-transfer is the most common cause.","If the producer uses GNU base-256 size encoding, repack with `tar --format=ustar` so sizes stay ASCII octal.","Verify you are not accidentally passing the gzip bytes where tar bytes are expected (double-gunzip)."],"exampleFix":"// before\n// extractQualifiedTarGzip(artifact) -> invalid tar size field\n// after\nconst tar = zlib.gunzipSync(fs.readFileSync('artifact.gz'));\nconsole.log(tar.subarray(0, 512).toString('hex').slice(0, 200)); // inspect header\n// repack if needed:\n// tar --format=ustar -cf artifact.tar bin/ && gzip artifact.tar","handlingStrategy":"validation","validationCode":"const tar = zlib.gunzipSync(archiveBytes);\nif (tar.length % 512 !== 0) throw new Error('tar length not block-aligned');\nconst sizeField = tar.subarray(124, 136).toString('ascii');\nif (!/^ *([0-7]+)[ \\0]*$/.test(sizeField)) throw new Error('first header not a ustar tar header');","typeGuard":"const isUstarHeader = (tar) => /^ *([0-7]+)[ \\0]*$/.test(tar.subarray(124, 136).toString('ascii'));","tryCatchPattern":"try { installNasiko(opts); } catch (e) { if (e.message.includes('invalid tar size field')) console.error('archive is not a plain ustar tar; repack with --format=ustar'); throw e; }","preventionTips":["Publish artifacts packed with --format=ustar to keep numeric fields ASCII octal.","Verify artifact digest before extraction to rule out corruption.","Spot-check the first 512 bytes of new artifacts in CI."],"tags":["tar","parsing","archive"],"backgroundTag":"invalid-argument-format","analyzedSha":"8321021c54d670126ce3b2969d5deb880b4b0c2a","analyzedAt":"2026-09-16T10:08:13.343Z","contentChangedAt":"2026-09-16T10:08:13.343Z","schemaVersion":2},"datasetVersion":"2026-09-23T08:17:48.524Z"}