paperclipai/paperclip · critical

Daytona syncOut refusing tarball with an unparseable entry l

Error message

Daytona syncOut refusing tarball with an unparseable entry listing: ${line}

What it means

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.

Source

Thrown at packages/plugins/sandbox-providers/daytona/src/file-sync.ts:203

 * so `tar -xf` on the host must never be handed an archive whose entries carry
 * absolute paths or `../` traversal, nor a symlink/hardlink member whose target
 * escapes the tree — the latter would let a follow-up member be written through
 * the link to an arbitrary host path. Legitimate in-tree relative links (targets
 * that resolve back inside the archive, e.g. `shortcut -> nested/data.txt`) are
 * preserved. Parses the `-tvf` verbose listing so both member names and link
 * targets are inspected; any unparseable line fails closed.
 */
async function assertTarballEntriesConfined(archivePath: string): Promise<void> {
  const { stdout } = await execFileAsync("tar", ["-tvf", archivePath], {
    env: { ...process.env, COPYFILE_DISABLE: "1" },
    maxBuffer: 32 * 1024 * 1024,
  });
  const lines = stdout.split("\n").filter((line) => line.trim().length > 0);
  for (const line of lines) {
    // GNU tar -tvf: "<perms> <owner>/<group> <size> <date> <time> <name>[ -> target]".
    const match = line.match(/^(\S+)\s+\S+\s+\d+\s+\S+\s+\S+\s+(.*)$/);
    if (!match) {
      throw new Error(`Daytona syncOut refusing tarball with an unparseable entry listing: ${line}`);
    }
    const typeFlag = match[1][0];
    let name = match[2];
    let linkTarget: string | null = null;
    if (typeFlag === "l") {
      const idx = name.indexOf(" -> ");
      if (idx === -1) throw new Error(`Daytona syncOut refusing unparseable symlink entry: ${line}`);
      linkTarget = name.slice(idx + " -> ".length);
      name = name.slice(0, idx);
    } else if (typeFlag === "h") {
      const idx = name.indexOf(" link to ");
      if (idx === -1) throw new Error(`Daytona syncOut refusing unparseable hardlink entry: ${line}`);
      linkTarget = name.slice(idx + " link to ".length);
      name = name.slice(0, idx);
    }
    const cleanName = name.replace(/\/+$/, "");
    if (cleanName.length > 0 && posixPathEscapes(cleanName)) {
      throw new Error(`Daytona syncOut refusing tarball member that escapes the extraction dir: ${name}`);

View on GitHub (pinned to 67001ec6eb)

Solutions

  1. Ensure the sandbox image ships GNU tar producing the expected verbose listing format.
  2. Set a deterministic locale (e.g. `LC_ALL=C`) when running tar in the sandbox so column layout is stable.
  3. If the archive is legitimately structured differently, re-pack with a compatible tar before syncOut.
Defensive patterns

Strategy: try-catch

Validate before calling

// Before relying on syncOut, confirm the sandbox tar matches GNU verbose format.
async function sandboxTarIsGnuVerbose(sandbox, remoteDir) {
  const r = await sandbox.process.executeCommand('tar --version | head -1', remoteDir);
  return /GNU/.test(String(r.result ?? r.artifacts?.stdout ?? ""));
}

Try / catch

try {
  await performSyncOut({ sandbox, operations, remoteDir, timeoutSeconds });
} catch (err) {
  if (err instanceof Error && /unparseable entry listing/.test(err.message)) {
    // set LC_ALL=C in the sandbox tar invocation or switch to GNU tar, then retry
  } else throw err;
}

Prevention

When it happens

Trigger: 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.

Common situations: 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.

Related errors


AI-assisted analysis of paperclipai/paperclip@67001ec6eb (2026-08-12). Data as JSON: /api/errors/e29267493cfbb247. Report an issue: GitHub.