{"record":{"id":"e29267493cfbb247","repo":"paperclipai/paperclip","slug":"daytona-syncout-refusing-tarball-with-an-unparseab","errorCode":null,"errorMessage":"Daytona syncOut refusing tarball with an unparseable entry listing: ${line}","messagePattern":"Daytona syncOut refusing tarball with an unparseable entry listing: (.+?)","errorType":"exception","errorClass":null,"httpStatus":null,"severity":"critical","filePath":"packages/plugins/sandbox-providers/daytona/src/file-sync.ts","lineNumber":203,"sourceCode":" * so `tar -xf` on the host must never be handed an archive whose entries carry\n * absolute paths or `../` traversal, nor a symlink/hardlink member whose target\n * escapes the tree — the latter would let a follow-up member be written through\n * the link to an arbitrary host path. Legitimate in-tree relative links (targets\n * that resolve back inside the archive, e.g. `shortcut -> nested/data.txt`) are\n * preserved. Parses the `-tvf` verbose listing so both member names and link\n * targets are inspected; any unparseable line fails closed.\n */\nasync function assertTarballEntriesConfined(archivePath: string): Promise<void> {\n  const { stdout } = await execFileAsync(\"tar\", [\"-tvf\", archivePath], {\n    env: { ...process.env, COPYFILE_DISABLE: \"1\" },\n    maxBuffer: 32 * 1024 * 1024,\n  });\n  const lines = stdout.split(\"\\n\").filter((line) => line.trim().length > 0);\n  for (const line of lines) {\n    // GNU tar -tvf: \"<perms> <owner>/<group> <size> <date> <time> <name>[ -> target]\".\n    const match = line.match(/^(\\S+)\\s+\\S+\\s+\\d+\\s+\\S+\\s+\\S+\\s+(.*)$/);\n    if (!match) {\n      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}`);","sourceCodeStart":185,"sourceCodeEnd":221,"githubUrl":"https://github.com/paperclipai/paperclip/blob/67001ec6eb96ae601aa27bc91d9b2415d665334a/packages/plugins/sandbox-providers/daytona/src/file-sync.ts#L185-L221","documentation":"Thrown by `assertTarballEntriesConfined` (file-sync.ts:203) when a `tar -tvf` verbose line from a sandbox-authored (untrusted) tarball does not match the expected GNU-tar listing format `<perms> <owner/group> <size> <date> <time> <name>`. The syncOut guard parses every entry to verify confinement; an unparseable line fails the whole extraction closed rather than risking an uninspected member.","triggerScenarios":"During outbound sync (sandbox -> host), the sandbox produces a tarball whose `tar -tvf` listing contains a line the regex cannot parse — e.g. a different tar variant (BSD format), locale-altered column layout, unusual owner/group tokens, or a corrupted/malicious listing.","commonSituations":"The sandbox image's `tar` is BSD-flavored or a different version emitting a different verbose format; a locale setting changes whitespace/columns; or a genuinely malformed/tampered archive is presented to the host.","solutions":["Ensure the sandbox image ships GNU tar producing the expected verbose listing format.","Set a deterministic locale (e.g. `LC_ALL=C`) when running tar in the sandbox so column layout is stable.","If the archive is legitimately structured differently, re-pack with a compatible tar before syncOut."],"exampleFix":null,"handlingStrategy":"try-catch","validationCode":"// Before relying on syncOut, confirm the sandbox tar matches GNU verbose format.\nasync function sandboxTarIsGnuVerbose(sandbox, remoteDir) {\n  const r = await sandbox.process.executeCommand('tar --version | head -1', remoteDir);\n  return /GNU/.test(String(r.result ?? r.artifacts?.stdout ?? \"\"));\n}","typeGuard":null,"tryCatchPattern":"try {\n  await performSyncOut({ sandbox, operations, remoteDir, timeoutSeconds });\n} catch (err) {\n  if (err instanceof Error && /unparseable entry listing/.test(err.message)) {\n    // set LC_ALL=C in the sandbox tar invocation or switch to GNU tar, then retry\n  } else throw err;\n}","preventionTips":["Use a sandbox base image with GNU tar producing the documented verbose format.","Pin LC_ALL=C (and LANG=C) in the sandbox when running tar so columns are stable.","Treat this as fail-closed: never relax the parser to accept unknown formats."],"tags":["daytona","sandbox","security","file-sync","tar","fail-closed"],"backgroundTag":null,"analyzedSha":"67001ec6eb96ae601aa27bc91d9b2415d665334a","analyzedAt":"2026-08-12T12:05:45.408Z","schemaVersion":2},"datasetVersion":"2026-08-12T18:17:37.767Z"}