{"record":{"id":"e323bfe7e8d35508","repo":"can1357/oh-my-pi","slug":"invalid-old-gnu-field","errorCode":null,"errorMessage":"Invalid old-GNU ${field}","messagePattern":"Invalid old-GNU (.+?)","errorType":"exception","errorClass":"ArchiveError","httpStatus":null,"severity":"error","filePath":"packages/utils/src/ar/tar.ts","lineNumber":254,"sourceCode":"}\n\nfunction paxDeclaresSparse(\n\tglobalPax: ReadonlyMap<string, string>,\n\tlocalPax: ReadonlyMap<string, string> | undefined,\n): boolean {\n\treturn paxAttribute(globalPax, localPax, PAX_SPARSE_MARKER) === \"1\";\n}\n\nfunction indexOfAscii(bytes: Uint8Array, value: string, start: number): number {\n\tfor (let offset = start; offset <= bytes.byteLength - value.length; offset++) {\n\t\tif (bytesMatchAscii(bytes, offset, value)) return offset;\n\t}\n\treturn -1;\n}\n\nfunction normalizeOldGnuName(value: string, field: string, limits: ArchiveLimits): string {\n\tconst portable = value.replace(/\\\\/g, \"/\");\n\tif (path.posix.isAbsolute(portable)) throw new ArchiveError(`Invalid old-GNU ${field}`);\n\tconst normalized = normalizeArchiveEntryPath(portable);\n\tif (!normalized) throw new ArchiveError(`Invalid old-GNU ${field}`);\n\tassertArchivePathString(normalized, field, limits.maxPathBytes);\n\treturn normalized;\n}\n\nfunction renameOldGnuEntries(\n\tentries: Map<string, ArchiveIndexEntry>,\n\tpendingLinks: Map<ArchiveIndexEntry, PendingTarLink>,\n\tfromPath: string,\n\ttoPath: string,\n\tlimits: ArchiveLimits,\n): void {\n\tconst moved = [...entries.entries()].filter(\n\t\t([entryPath]) => entryPath === fromPath || entryPath.startsWith(`${fromPath}/`),\n\t);\n\tif (moved.length === 0) return;\n\tfor (const [entryPath] of moved) entries.delete(entryPath);","sourceCodeStart":236,"sourceCodeEnd":272,"githubUrl":"https://github.com/can1357/oh-my-pi/blob/969062200754ea02cfac922e5ebb8c608c079e15/packages/utils/src/ar/tar.ts#L236-L272","documentation":"Thrown by normalizeOldGnuName when an old-GNU rename record's path is absolute (starts with '/') after backslash-to-slash conversion. The library rejects absolute paths in old-GNU 'N' metadata to prevent archive members escaping the archive root.","triggerScenarios":"Reading a tar with an old-GNU name record (typeflag 'N') containing 'Rename <abs-path> to ...' where the source path is absolute; the source-path normalization (normalizeOldGnuName(source, \"source path\")) throws this first.","commonSituations":"Archives written by very old or non-standard GNU tar variants with odd rename metadata; hand-edited or malicious archives attempting path escape.","solutions":["Inspect the archive with `tar tvf` to find the offending old-GNU record and re-pack the archive with a modern tar implementation.","Strip or rewrite the typeflag-'N' records from the archive if the rename metadata is unnecessary.","Treat the archive as untrusted/corrupt and reject it if it comes from an unknown source."],"exampleFix":null,"handlingStrategy":"validation","validationCode":"// Pre-scan for old-GNU 'N' records with absolute paths before indexing\nfunction hasAbsoluteOldGnuRename(bytes: Uint8Array): boolean {\n  // crude scan: block typeflag at offset 156 === 'N' plus 'Rename /' pattern\n  for (let off = 0; off + 512 <= bytes.byteLength; ) {\n    if (bytes[off + 156] === 0x4e /* N */) return true; // inspect record contents\n    const size = /* parse octal size at 124 */ 0;\n    off += 512 + Math.ceil(size / 512) * 512;\n  }\n  return false;\n}","typeGuard":null,"tryCatchPattern":"try {\n  await readTar(bytes, opts);\n} catch (e) {\n  if (e instanceof ArchiveError && e.message.startsWith(\"Invalid old-GNU\")) {\n    throw new Error(\"Archive contains unsafe old-GNU rename paths; refusing to index\");\n  }\n  throw e;\n}","preventionTips":["Re-pack legacy archives with modern GNU tar to drop old-GNU 'N' records.","Reject archives whose rename metadata contains absolute paths.","Treat typeflag-'N' blocks in untrusted archives as a red flag."],"tags":["archive","tar","path-validation","gnu"],"backgroundTag":"invalid-archive-path","analyzedSha":"969062200754ea02cfac922e5ebb8c608c079e15","analyzedAt":"2026-08-31T10:29:35.737Z","schemaVersion":2},"datasetVersion":"2026-08-31T14:17:45.589Z"}