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
- 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.
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
- 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.
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
- Daytona syncOut refusing unparseable symlink entry: ${line}
- Daytona sync ${label} path is not a confined absolute path:
- Daytona sync ${label} path escapes the workspace remote dir:
- Daytona syncOut refusing unparseable hardlink entry: ${line}
- Daytona syncOut refusing tarball member that escapes the ext
AI-assisted analysis of paperclipai/paperclip@67001ec6eb (2026-08-12).
Data as JSON: /api/errors/e29267493cfbb247.
Report an issue: GitHub.