paperclipai/paperclip · critical

sync operation ${label} path is not a confined absolute path

Error message

sync operation ${label} path is not a confined absolute path: ${candidate}

What it means

Thrown by assertSyncOperationsConfined when a sync mapping's source or target path is not a safe absolute POSIX path. The guard rejects relative paths, the literal `..`, and any path containing `/../` segments or ending in `/..`. This is the first half of a two-stage confinement check performed at the orchestrator trust boundary before handing file/directory transfers to a provider.

Source

Thrown at packages/adapter-utils/src/sandbox-managed-runtime.ts:273

  syncOut?(operations: SandboxSyncOperation[]): Promise<SandboxSyncResult>;
}

/**
 * Host-side complete-mediation guard for native sync operations. The orchestrator
 * authors every `targetPath`, but the native transport crosses the host↔sandbox
 * trust boundary, so we canonicalize and confine each mapping's source and target
 * to an orchestrator-owned root before handing the operation to a provider.
 * Absolute escapes and `..` traversal are rejected fail-closed. Sandbox and host
 * paths on the server are POSIX.
 */
export function assertSyncOperationsConfined(
  operations: SandboxSyncOperation[],
  roots: { sourceRoots: string[]; targetRoots: string[] },
): void {
  const confine = (candidate: string, allowed: string[], label: string): void => {
    const normalized = path.posix.normalize(candidate);
    if (!path.posix.isAbsolute(normalized) || normalized === ".." || normalized.includes("/../") || normalized.endsWith("/..")) {
      throw new Error(`sync operation ${label} path is not a confined absolute path: ${candidate}`);
    }
    const within = allowed.some((root) => {
      const normalizedRoot = path.posix.normalize(root);
      const prefix = normalizedRoot.endsWith("/") ? normalizedRoot : `${normalizedRoot}/`;
      return normalized === normalizedRoot || normalized.startsWith(prefix);
    });
    if (!within) {
      throw new Error(`sync operation ${label} path escapes its confinement root: ${candidate}`);
    }
  };
  for (const operation of operations) {
    for (const mapping of operation.files) {
      confine(mapping.sourcePath, roots.sourceRoots, "source");
      confine(mapping.targetPath, roots.targetRoots, "target");
    }
  }
}

View on GitHub (pinned to 67001ec6eb)

Solutions

  1. Ensure every sourcePath/targetPath passed to sync operations is absolute and normalize() it before submission (path.posix.resolve or path.resolve).
  2. Strip or reject `..` in any user-supplied filename before joining it into a target path.
  3. For additional sources, pass an absolute localPath (matches the error at line 1011) computed with path.resolve at the call site.
  4. Add a unit test feeding a `..`-bearing path and confirm it throws this exact message.

Example fix

// before
files: [{ sourcePath: relPath, targetPath: `${root}/../asset.tar`, ... }]
// after
const safeTarget = path.posix.join(runtimeRootDir, 'asset.tar');
files: [{ sourcePath: path.resolve(relPath), targetPath: safeTarget, ... }]
Defensive patterns

Strategy: validation

Validate before calling

function assertConfinedAbsolute(candidate: string): void {
  const n = path.posix.normalize(candidate);
  if (!path.posix.isAbsolute(n) || n === '..' || n.includes('/../') || n.endsWith('/..')) {
    throw new Error(`not a confined absolute path: ${candidate}`);
  }
}
// run before assertSyncOperationsConfined:
for (const op of operations) for (const m of op.files) { assertConfinedAbsolute(m.sourcePath); assertConfinedAbsolute(m.targetPath); }

Type guard

function isConfinedAbsolutePath(p: string): boolean {
  const n = path.posix.normalize(p);
  return path.posix.isAbsolute(n) && n !== '..' && !n.includes('/../') && !n.endsWith('/..');
}

Prevention

When it happens

Trigger: Calling assertSyncOperationsConfined(operations, roots) where any operation.files[].sourcePath or targetPath is relative (no leading /), contains `..`, equals `..`, or has a trailing `/..`. Triggered during prepareSandboxManagedRuntime staging when asset or additional-source paths are assembled incorrectly.

Common situations: A caller builds a targetPath with a template that can yield `../`; an additionalSource localPath is passed as a workspace-relative string instead of absolute; a tar extraction target is computed with a missing prefix producing a relative path; portability bug where a Windows backslash path leaks into the POSIX check.

Related errors


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