{"record":{"id":"218dc88a85a63bcd","repo":"can1357/oh-my-pi","slug":"unsafe-embedded-addon-archive-entry-filename","errorCode":null,"errorMessage":"Unsafe embedded addon archive entry: ${filename}","messagePattern":"Unsafe embedded addon archive entry: (.+?)","errorType":"exception","errorClass":"Error","httpStatus":null,"severity":"critical","filePath":"packages/natives/native/loader-state.js","lineNumber":527,"sourceCode":"\n\tconst archive = zlib.gunzipSync(fs.readFileSync(archivePath));\n\tconst writtenPaths = [];\n\tlet offset = 0;\n\n\twhile (offset + 512 <= archive.length) {\n\t\tif (isZeroTarBlock(archive, offset)) break;\n\t\tconst header = archive.subarray(offset, offset + 512);\n\t\tconst filename = getTarEntryName(header);\n\t\tconst size = readTarOctal(header, 124, 12);\n\t\tconst typeflag = header[156] === 0 ? \"0\" : String.fromCharCode(header[156]);\n\t\toffset += 512;\n\n\t\tif (offset + size > archive.length) {\n\t\t\tthrow new Error(`Truncated embedded addon archive entry: ${filename}`);\n\t\t}\n\n\t\tif (!isSafeEmbeddedAddonFilename(filename)) {\n\t\t\tthrow new Error(`Unsafe embedded addon archive entry: ${filename}`);\n\t\t}\n\t\tif (typeflag !== \"0\") {\n\t\t\tthrow new Error(`Unsupported embedded addon archive entry type ${typeflag}: ${filename}`);\n\t\t}\n\n\t\tconst file = pending.get(filename);\n\t\tif (file) {\n\t\t\tif (typeof file.size === \"number\" && file.size !== size) {\n\t\t\t\tthrow new Error(`Embedded addon size mismatch for ${filename}: expected ${file.size}, got ${size}`);\n\t\t\t}\n\t\t\tconst targetPath = path.join(targetDir, filename);\n\t\t\twriteEmbeddedAddonFile(targetPath, archive.subarray(offset, offset + size));\n\t\t\tpending.delete(filename);\n\t\t\twrittenPaths.push(targetPath);\n\t\t}\n\n\t\toffset += Math.ceil(size / 512) * 512;\n\t}","sourceCodeStart":509,"sourceCodeEnd":545,"githubUrl":"https://github.com/can1357/oh-my-pi/blob/969062200754ea02cfac922e5ebb8c608c079e15/packages/natives/native/loader-state.js#L509-L545","documentation":"For every entry read from the embedded tar stream, the loader re-checks the entry name against isSafeEmbeddedAddonFilename (non-empty, basename-only, no '/' or '\\\\'). This defends against a hostile or corrupted archive writing outside targetDir even if the manifest side was clean. It throws instead of extracting the offending entry.","triggerScenarios":"extractEmbeddedAddonArchive encounters a tar entry whose header name (including any ustar prefix at offset 345) contains a path separator, is absolute, or is empty — e.g. an entry named '../x.node' or 'usr/lib/x.node' inside the .tar.gz.","commonSituations":"A tampered or maliciously modified embedded archive; a packaging bug that stored entries under subdirectories; manual repacking of the addon .tar.gz with paths like './dir/file'.","solutions":["Repack the embedded archive so every entry is a flat basename at the archive root (tar -czf addon.tar.gz -C staging . with files directly in staging).","Reinstall omp to restore the official embedded archive.","If building archives yourself, strip directory components with the --transform option or flatten the staging directory before packing."],"exampleFix":"// before (repacking)\n$ tar -czf addon.tar.gz out/x64/pi_natives.node\n// after\n$ cd out/x64 && tar -czf ../../addon.tar.gz pi_natives.node","handlingStrategy":"validation","validationCode":"import * as path from \"node:path\";\nfunction assertFlatArchiveEntries(entryNames) {\n  for (const name of entryNames) {\n    if (!name || path.basename(name) !== name || name.includes(\"/\") || name.includes(\"\\\\\")) {\n      throw new Error(`Unsafe embedded addon archive entry: ${name}`);\n    }\n  }\n}\n// list entries with: tar -tzf addon.tar.gz, then assertFlatArchiveEntries(names)","typeGuard":"function isFlatEntryName(name) {\n  return typeof name === \"string\" && name.length > 0 &&\n    path.basename(name) === name && !/[\\\\/]/.test(name);\n}","tryCatchPattern":"try {\n  extractEmbeddedAddonArchive({ archivePath, files, targetDir });\n} catch (err) {\n  if (String(err.message).startsWith(\"Unsafe embedded addon archive entry:\")) {\n    // treat archive as hostile/corrupt; refuse to use embedded fallback\n  } else throw err;\n}","preventionTips":["Pack archives with all files at the tar root (tar -C staging <flat files>).","Never repack addon archives with directory trees or './'-prefixed paths.","Lint the packaging script to reject nested entry names before embedding."],"tags":["path-traversal","security","tar","archive"],"backgroundTag":"unsafe-archive-entry-name","analyzedSha":"969062200754ea02cfac922e5ebb8c608c079e15","analyzedAt":"2026-08-31T10:29:35.737Z","schemaVersion":2},"datasetVersion":"2026-08-31T14:17:45.589Z"}