paperclipai/paperclip · critical
Daytona sync ${label} path escapes the workspace remote dir:
Error message
Daytona sync ${label} path escapes the workspace remote dir: ${candidate} What it means
Thrown by `assertConfinedSandboxPath` (file-sync.ts:121) — the second fail-closed check — when the candidate path is absolute and free of `..` but does NOT equal the workspace `remoteDir` and does not start with `remoteDir + "/"`. I.e. the path is absolute but outside the permitted workspace root, so a transfer would read/write outside confinement.
Source
Thrown at packages/plugins/sandbox-providers/daytona/src/file-sync.ts:121
* orchestrator's own confinement. Every sandbox-side path (the sync target for
* inbound, the sync source for outbound) MUST canonicalize inside the workspace
* remote dir; absolute escapes and `..` traversal are rejected fail-closed before
* any bytes move. Sandbox paths on the server are POSIX.
*/
export function assertConfinedSandboxPath(remoteDir: string, candidate: string, label: string): void {
const normalizedRoot = path.posix.normalize(remoteDir);
const normalized = path.posix.normalize(candidate);
if (
!path.posix.isAbsolute(normalized) ||
normalized === ".." ||
normalized.includes("/../") ||
normalized.endsWith("/..")
) {
throw new Error(`Daytona sync ${label} path is not a confined absolute path: ${candidate}`);
}
const prefix = normalizedRoot.endsWith("/") ? normalizedRoot : `${normalizedRoot}/`;
if (normalized !== normalizedRoot && !normalized.startsWith(prefix)) {
throw new Error(`Daytona sync ${label} path escapes the workspace remote dir: ${candidate}`);
}
}
async function withHostTempDir<T>(fn: (dir: string) => Promise<T>): Promise<T> {
const dir = await fs.mkdtemp(path.join(os.tmpdir(), "paperclip-daytona-sync-"));
try {
return await fn(dir);
} finally {
await fs.rm(dir, { recursive: true, force: true }).catch(() => undefined);
}
}
/**
* Build a host-side tarball of a directory, mirroring the runtime's own
* `createTarballFromDirectory`: archive top-level entries by name (no "." self
* entry), suppress AppleDouble/xattr sidecars, honor `exclude`, and reproduce the
* `followSymlinks` → `-h` mapping so the native path is observationally identical
* to the base64 fallback's tar.View on GitHub (pinned to 67001ec6eb)
Solutions
- Scope every path to the current workspace's `remoteDir` prefix; build target/source paths as `remoteDir`-rooted.
- Confirm the `remoteDir` passed to the sync matches the workspace the paths belong to.
- Reject any caller-supplied absolute path that is not under the active remote dir.
Example fix
// before
cwd: "/shared/build" // outside remoteDir
// after
cwd: `${remoteDir}/build` Defensive patterns
Strategy: validation
Validate before calling
import path from "node:path";
function assertInsideRoot(remoteDir, candidate) {
const root = path.posix.normalize(remoteDir);
const n = path.posix.normalize(candidate);
const prefix = root.endsWith("/") ? root : `${root}/`;
if (n !== root && !n.startsWith(prefix)) {
throw new Error(`path escapes workspace remote dir: ${candidate}`);
}
return n;
} Type guard
function isInsideRemoteRoot(remoteDir: string, candidate: string): boolean {
const root = path.posix.normalize(remoteDir);
const n = path.posix.normalize(candidate);
const prefix = root.endsWith("/") ? root : `${root}/`;
return n === root || n.startsWith(prefix);
} Prevention
- Derive every sync path from the active workspace remoteDir, never hard-code absolutes.
- Do not mix paths across workspaces; re-resolve remoteDir per workspace.
- Treat this throw as a security stop — never widen remoteDir to silence it.
When it happens
Trigger: A sync mapping or post-upload command `cwd` points to an absolute path outside the workspace remote dir — e.g. `/etc/...`, `/root/...`, or a different workspace's directory. The lexical check (536) passed (absolute, no traversal) but this prefix check rejects the escape.
Common situations: Cross-workspace path leakage (using another workspace's remoteDir), a hard-coded absolute path in config, or untrusted input that resolves under a sibling root rather than the current workspace's remote dir.
Related errors
- Daytona sync ${label} path is not a confined absolute path:
- sync operation ${label} path is not a confined absolute path
- sync operation ${label} path escapes its confinement root: $
- provision stageFile.name must be a simple basename, got: ${s
- Daytona syncOut refusing tarball with an unparseable entry l
AI-assisted analysis of paperclipai/paperclip@67001ec6eb (2026-08-12).
Data as JSON: /api/errors/c0b3210d5055349a.
Report an issue: GitHub.