can1357/oh-my-pi · critical · Error
Unsafe embedded addon archive entry: ${filename}
Error message
Unsafe embedded addon archive entry: ${filename} What it means
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.
Source
Thrown at packages/natives/native/loader-state.js:527
const archive = zlib.gunzipSync(fs.readFileSync(archivePath));
const writtenPaths = [];
let offset = 0;
while (offset + 512 <= archive.length) {
if (isZeroTarBlock(archive, offset)) break;
const header = archive.subarray(offset, offset + 512);
const filename = getTarEntryName(header);
const size = readTarOctal(header, 124, 12);
const typeflag = header[156] === 0 ? "0" : String.fromCharCode(header[156]);
offset += 512;
if (offset + size > archive.length) {
throw new Error(`Truncated embedded addon archive entry: ${filename}`);
}
if (!isSafeEmbeddedAddonFilename(filename)) {
throw new Error(`Unsafe embedded addon archive entry: ${filename}`);
}
if (typeflag !== "0") {
throw new Error(`Unsupported embedded addon archive entry type ${typeflag}: ${filename}`);
}
const file = pending.get(filename);
if (file) {
if (typeof file.size === "number" && file.size !== size) {
throw new Error(`Embedded addon size mismatch for ${filename}: expected ${file.size}, got ${size}`);
}
const targetPath = path.join(targetDir, filename);
writeEmbeddedAddonFile(targetPath, archive.subarray(offset, offset + size));
pending.delete(filename);
writtenPaths.push(targetPath);
}
offset += Math.ceil(size / 512) * 512;
}View on GitHub (pinned to 9690622007)
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.
Example fix
// before (repacking) $ tar -czf addon.tar.gz out/x64/pi_natives.node // after $ cd out/x64 && tar -czf ../../addon.tar.gz pi_natives.node
Defensive patterns
Strategy: validation
Validate before calling
import * as path from "node:path";
function assertFlatArchiveEntries(entryNames) {
for (const name of entryNames) {
if (!name || path.basename(name) !== name || name.includes("/") || name.includes("\\")) {
throw new Error(`Unsafe embedded addon archive entry: ${name}`);
}
}
}
// list entries with: tar -tzf addon.tar.gz, then assertFlatArchiveEntries(names) Type guard
function isFlatEntryName(name) {
return typeof name === "string" && name.length > 0 &&
path.basename(name) === name && !/[\\/]/.test(name);
} Try / catch
try {
extractEmbeddedAddonArchive({ archivePath, files, targetDir });
} catch (err) {
if (String(err.message).startsWith("Unsafe embedded addon archive entry:")) {
// treat archive as hostile/corrupt; refuse to use embedded fallback
} else throw err;
} Prevention
- 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.
When it happens
Trigger: 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.
Common situations: 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'.
Related errors
- Archive path cannot contain '..'
- Archive entry escapes extraction directory: ${archivePath}
- Archive entry escapes extraction dir: ${entry.path}
- Archive symlink escapes extraction dir: ${link.path} -> ${li
- Destination paths cannot contain parent traversal or NUL byt
AI-assisted analysis of can1357/oh-my-pi@9690622007 (2026-08-31).
Data as JSON: /api/errors/218dc88a85a63bcd.
Report an issue: GitHub.