can1357/oh-my-pi · critical · Error

Unsafe embedded addon filename: ${file.filename}

Error message

Unsafe embedded addon filename: ${file.filename}

What it means

extractEmbeddedAddonArchive validates every filename in the embedded-addon manifest before doing any I/O. A filename is unsafe if it is empty, contains a path separator ('/' or '\\'), or differs from its own path.basename — i.e. it could escape the target directory. The library throws this to prevent path traversal when writing files extracted from the bundled tar.gz into the native version directory.

Source

Thrown at packages/natives/native/loader-state.js:501

	const tempPath = `${targetPath}.tmp.${process.pid}.${Date.now()}`;
	try {
		fs.writeFileSync(tempPath, content, { mode: 0o755 });
		fs.renameSync(tempPath, targetPath);
	} catch (err) {
		try {
			fs.unlinkSync(tempPath);
		} catch {
			// Best-effort cleanup only.
		}
		throw err;
	}
}

export function extractEmbeddedAddonArchive({ archivePath, files, targetDir }) {
	const pending = new Map();
	for (const file of files) {
		if (!isSafeEmbeddedAddonFilename(file.filename)) {
			throw new Error(`Unsafe embedded addon filename: ${file.filename}`);
		}
		const targetPath = path.join(targetDir, file.filename);
		if (!isEmbeddedAddonFileCurrent(targetPath, file)) {
			pending.set(file.filename, file);
		}
	}
	if (pending.size === 0) return [];

	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]);

View on GitHub (pinned to 9690622007)

Solutions

  1. Fix the manifest so each files[].filename is a bare filename with no directory components (e.g. 'pi_natives-linux-x64.node').
  2. Rebuild/reinstall the compiled binary so the embedded addon metadata is regenerated from the official build.
  3. If you construct the manifest yourself, normalize entries with path.basename(filename) and reject entries where the result differs from the input.

Example fix

// before
extractEmbeddedAddonArchive({ archivePath, files: [{ filename: "sub/pi_natives.node" }], targetDir });
// after
const files = [{ filename: "pi_natives.node" }];
if (!files.every(f => f.filename && path.basename(f.filename) === f.filename)) throw new Error("bad manifest");
extractEmbeddedAddonArchive({ archivePath, files, targetDir });
Defensive patterns

Strategy: validation

Validate before calling

import * as path from "node:path";
function assertSafeAddonFilenames(files) {
  for (const file of files) {
    if (!file.filename || path.basename(file.filename) !== file.filename ||
        file.filename.includes("/") || file.filename.includes("\\")) {
      throw new Error(`Unsafe embedded addon filename: ${file.filename}`);
    }
  }
}
assertSafeAddonFilenames(files); // run before extractEmbeddedAddonArchive

Type guard

function isSafeAddonFilename(filename) {
  return typeof filename === "string" && filename.length > 0 &&
    path.basename(filename) === filename &&
    !filename.includes("/") && !filename.includes("\\");
}

Try / catch

try {
  extractEmbeddedAddonArchive({ archivePath, files, targetDir });
} catch (err) {
  if (String(err.message).startsWith("Unsafe embedded addon filename:")) {
    // reject/treat manifest as untrusted; skip embedded path and fall back to disk lookup
  } else throw err;
}

Prevention

When it happens

Trigger: Calling extractEmbeddedAddonArchive({archivePath, files, targetDir}) where any entry of files has file.filename that is empty, absolute, or contains '/' or '\\' (e.g. '../evil.node' or 'sub/dir/x.node'). The check runs before the archive is even read, so a single bad manifest entry aborts extraction.

Common situations: A corrupted or hand-edited embedded-addon manifest; a custom build pipeline that records filenames with directory prefixes; a tampered/partially overwritten compiled binary's embedded metadata; tests feeding synthetic manifest data with paths.

Related errors


AI-assisted analysis of can1357/oh-my-pi@9690622007 (2026-08-31). Data as JSON: /api/errors/f0dcd2e2eac5d5c5. Report an issue: GitHub.