Yeachan-Heo/oh-my-codex · error · Error
artifact path must not traverse directories
Error message
artifact path must not traverse directories
What it means
Thrown when a relative artifact path contains ../ traversal (or is exactly ".." or contains NUL bytes) after backslash-to-slash normalization. This is a path-traversal guard: the bridge must never read files outside the working directory via crafted relative paths.
Source
Thrown at src/mcp/hermes-bridge.ts:505
stdio: "ignore",
env: { ...bridgeEnv, OMX_HERMES_MCP_BRIDGE: "1" },
}) as ChildProcess;
child.unref();
if (!child.pid) return failure("command_failed", "OMX session launcher did not report a pid");
return jsonResult({ pid: child.pid, command, args: launchArgs, workingDirectory: cwd });
} catch (error) {
const message = error instanceof Error ? error.message : String(error);
if (message.includes("allow_mutation")) return failure("mutation_not_allowed", message);
return failure("invalid_input", message);
}
}
function normalizeArtifactRelativePath(pathValue: unknown): string {
const raw = normalizeString(pathValue, "path", { required: true })!;
if (isAbsolute(raw)) throw new Error("artifact path must be relative");
const normalized = raw.replace(/\\/g, "/").replace(/^\.\//, "");
if (normalized.includes("../") || normalized === ".." || normalized.includes("\0")) {
throw new Error("artifact path must not traverse directories");
}
if (!SAFE_ARTIFACT_PREFIXES.some((prefix) => normalized.startsWith(prefix))) {
throw new Error(`artifact path must be under ${SAFE_ARTIFACT_PREFIXES.join(", ")}`);
}
return normalized;
}
function isInsideDirectory(parent: string, candidate: string): boolean {
const rel = relative(parent, candidate);
return rel === "" || (!rel.startsWith("..") && !isAbsolute(rel));
}
async function resolveSafeArtifactPath(cwd: string, rel: string): Promise<string> {
const cwdRealPath = await realpath(cwd);
const full = resolve(cwd, rel);
const relativeToCwd = relative(resolve(cwd), full);
if (relativeToCwd.startsWith("..") || isAbsolute(relativeToCwd)) {
throw new Error("artifact resolved outside working directory");View on GitHub (pinned to 3ad79a8a6f)
Solutions
- Remove any ../ segments and pass a path directly under an allowed prefix
- If you need a file outside the working directory, copy it into the working directory first
- Validate/escape user-supplied filenames before forwarding them as artifact paths
Example fix
// before
await tool({ path: "logs/../../config.env" });
// after
await tool({ path: "logs/run.log" }); Defensive patterns
Strategy: validation
Validate before calling
if (/(^|\/)\.\.(\/|$)/.test(p) || p.includes('\0')) throw new TypeError('unsafe artifact path'); Type guard
function isSafeRelativePath(p: string): boolean { const n = p.replace(/\\/g, '/'); return !(n.split('/').includes('..') || n.includes('\0')); } Prevention
- Build paths with path.posix.join and pre-normalized segments
- Never concatenate raw user input into artifact paths
When it happens
Trigger: path="../../etc/passwd", path="logs/../../../../secrets", path="..", or a path containing \0. Also triggers for Windows-style backslash traversal like "..\\..\\etc" because backslashes are normalized to slashes first.
Common situations: Attempting to fetch files outside the artifact directory; malicious or buggy client input; tests probing traversal behavior.
Understand the failure class
Background: Path traversal blocked: "path escapes the workspace" and "outside site root" errors when a path will not stay inside its allowed directory — this error's family across 26 libraries.
Related errors
- artifact resolved outside working directory
- artifact path must be relative
- mode must not contain ".."
- fileName must not contain ".."
- run directory escapes the authorized runs root
AI-assisted analysis of Yeachan-Heo/oh-my-codex@3ad79a8a6f (2026-08-27).
Data as JSON: /api/errors/b0550c2739d9f21b.
Report an issue: GitHub.