{"record":{"id":"638e68a04c6f3169","repo":"paperclipai/paperclip","slug":"daytona-syncout-refusing-tarball-member-that-escap","errorCode":null,"errorMessage":"Daytona syncOut refusing tarball member that escapes the extraction dir: ${name}","messagePattern":"Daytona syncOut refusing tarball member that escapes the extraction dir: (.+?)","errorType":"exception","errorClass":null,"httpStatus":null,"severity":"critical","filePath":"packages/plugins/sandbox-providers/daytona/src/file-sync.ts","lineNumber":221,"sourceCode":"      throw new Error(`Daytona syncOut refusing tarball with an unparseable entry listing: ${line}`);\n    }\n    const typeFlag = match[1][0];\n    let name = match[2];\n    let linkTarget: string | null = null;\n    if (typeFlag === \"l\") {\n      const idx = name.indexOf(\" -> \");\n      if (idx === -1) throw new Error(`Daytona syncOut refusing unparseable symlink entry: ${line}`);\n      linkTarget = name.slice(idx + \" -> \".length);\n      name = name.slice(0, idx);\n    } else if (typeFlag === \"h\") {\n      const idx = name.indexOf(\" link to \");\n      if (idx === -1) throw new Error(`Daytona syncOut refusing unparseable hardlink entry: ${line}`);\n      linkTarget = name.slice(idx + \" link to \".length);\n      name = name.slice(0, idx);\n    }\n    const cleanName = name.replace(/\\/+$/, \"\");\n    if (cleanName.length > 0 && posixPathEscapes(cleanName)) {\n      throw new Error(`Daytona syncOut refusing tarball member that escapes the extraction dir: ${name}`);\n    }\n    if (linkTarget !== null) {\n      const resolved = path.posix.join(path.posix.dirname(cleanName), linkTarget);\n      if (path.posix.isAbsolute(linkTarget) || posixPathEscapes(resolved)) {\n        throw new Error(\n          `Daytona syncOut refusing tarball link whose target escapes the extraction dir: ${name} -> ${linkTarget}`,\n        );\n      }\n    }\n  }\n}\n\nasync function extractHostTarball(input: { archivePath: string; localDir: string }): Promise<void> {\n  // The archive is sandbox-authored and untrusted: validate every member (and\n  // link target) is confined before letting host-side tar write a single byte.\n  await assertTarballEntriesConfined(input.archivePath);\n  await fs.mkdir(input.localDir, { recursive: true });\n  await execFileAsync(\"tar\", [\"-xf\", input.archivePath, \"-C\", input.localDir], {","sourceCodeStart":203,"sourceCodeEnd":239,"githubUrl":"https://github.com/paperclipai/paperclip/blob/67001ec6eb96ae601aa27bc91d9b2415d665334a/packages/plugins/sandbox-providers/daytona/src/file-sync.ts#L203-L239","documentation":"Thrown by assertTarballEntriesConfined when a tarball member name (after stripping trailing slashes) escapes the extraction directory: it is absolute, equals '..', or begins with '../'. This is a path-traversal guard run against an untrusted, sandbox-authored archive before the host extracts it, preventing writes outside the target tree.","triggerScenarios":"A syncOut tarball contains an entry whose cleaned name is an absolute POSIX path, '..', or starts with '../'. The check posixPathEscapes normalizes the path and rejects any breakout.","commonSituations":"A malicious or buggy sandbox writes files with absolute paths or parent-directory traversal into the tar; a misconfigured source path that resolves outside the workspace remote dir; symlink/hardlink redirection creating out-of-tree member names.","solutions":["Inspect the offending member `name` in the error message to confirm which sandbox file produced the escaping path.","Fix the sandbox-side process so it only writes files within the workspace remote dir (no absolute or ../ paths).","If the entry is legitimate, ensure the syncOut source mapping is rooted inside the confinement directory.","Re-run syncOut after correcting the source tree; the guard will pass once all members are confined."],"exampleFix":"// before: sandbox writes /etc/override into synced dir\n// after: sandbox writes only under $remoteCwd\nprocess.chdir(remoteCwd); fs.writeFileSync('./local-file', data);","handlingStrategy":"validation","validationCode":"function memberIsConfined(name: string): boolean {\n  const clean = name.replace(/\\/+$/, '');\n  if (clean.length === 0) return true;\n  const norm = path.posix.normalize(clean);\n  return !(norm === '..' || norm.startsWith('../') || path.posix.isAbsolute(norm));\n}","typeGuard":null,"tryCatchPattern":"try {\n  await extractHostTarball({ archivePath, localDir });\n} catch (e) {\n  if (e instanceof Error && e.message.includes('escapes the extraction dir')) {\n    // quarantine the archive; do not extract anywhere on the host\n  }\n  throw e;\n}","preventionTips":["Treat any 'escapes the extraction dir' error as a potential sandbox escape attempt; do not bypass.","Ensure sandbox processes only write relative paths rooted under the workspace dir.","Audit sandbox-authored archives before host extraction; the built-in guard is intentional and should stay enabled."],"tags":["daytona","file-sync","tar","security","path-traversal","syncout"],"backgroundTag":null,"analyzedSha":"67001ec6eb96ae601aa27bc91d9b2415d665334a","analyzedAt":"2026-08-12T12:05:45.408Z","schemaVersion":2},"datasetVersion":"2026-08-12T18:17:37.767Z"}