can1357/oh-my-pi · critical · ToolError
Archive path cannot contain '..'
Error message
Archive path cannot contain '..'
What it means
Path traversal is forbidden inside archive targets: any '..' segment in the in-archive sub-path is rejected outright to prevent writing outside the archive entry namespace (zip-slip style attacks).
Source
Thrown at packages/coding-agent/src/tools/write.ts:473
if (isEnoent(error)) return true;
return typeof error === "object" && error !== null && "code" in error && error.code === "ENOTDIR";
}
function normalizeArchiveWriteSubPath(rawPath: string): string {
const normalized = rawPath.replace(/\\/g, "/");
if (normalized.length === 0) {
throw new ToolError("Archive write path must target a file inside the archive");
}
if (normalized.endsWith("/")) {
throw new ToolError("Archive write path must target a file, not a directory");
}
const parts = normalized.split("/");
const normalizedParts: string[] = [];
for (const part of parts) {
if (!part || part === ".") continue;
if (part === "..") {
throw new ToolError("Archive path cannot contain '..'");
}
normalizedParts.push(part);
}
if (normalizedParts.length === 0) {
throw new ToolError("Archive write path must target a file inside the archive");
}
return normalizedParts.join("/");
}
function parseSqliteWriteTarget(subPath: string, queryString: string): { table: string; key?: string } {
if (queryString.trim().length > 0) {
throw new ToolError("SQLite write paths do not support query parameters");
}
const normalized = subPath.replace(/^:+/, "").trim();
if (!normalized) {View on GitHub (pinned to 9690622007)
Solutions
- Remove '..' segments; archive entries are always relative to the archive root
- Sanitize/normalize user-supplied entry names before composing the write target
Example fix
// before
write({ path: "bundle.zip/../escape.txt", content: "x" })
// after
write({ path: "bundle.zip/escape.txt", content: "x" }) Defensive patterns
Strategy: validation
Validate before calling
function assertNoTraversal(entry: string): void {
const parts = entry.replace(/\\/g, "/").split("/");
if (parts.includes("..")) throw new Error(`archive path traversal rejected: ${entry}`);
}
assertNoTraversal(inArchiveSubPath); Try / catch
try {
await write({ path: `bundle.zip/${entry}`, content });
} catch (e) {
if (e instanceof ToolError && e.message.includes("cannot contain '..'")) {
throw new Error(`Refusing unsafe archive entry: ${entry}`);
}
throw e;
} Prevention
- Sanitize any user-supplied entry names: reject or resolve '..' before composing targets
- Treat '..' in archive targets as a security signal, never a legit path
- Normalize entry names to root-relative form before write
When it happens
Trigger: write({ path: "bundle.zip/../evil.txt", content }) or any target whose sub-path contains a '..' segment.
Common situations: Relative-path mistakes when constructing archive entry names; attempts to escape the archive root, possibly via untrusted input.
Related errors
- Unsafe embedded addon archive entry: ${filename}
- 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/6150bb3f7242e15c.
Report an issue: GitHub.