{"record":{"id":"d84eb513dbeb1d79","repo":"paperclipai/paperclip","slug":"sync-operation-label-path-is-not-a-confined-abs","errorCode":null,"errorMessage":"sync operation ${label} path is not a confined absolute path: ${candidate}","messagePattern":"sync operation (.+?) path is not a confined absolute path: (.+?)","errorType":"validation","errorClass":null,"httpStatus":null,"severity":"critical","filePath":"packages/adapter-utils/src/sandbox-managed-runtime.ts","lineNumber":273,"sourceCode":"  syncOut?(operations: SandboxSyncOperation[]): Promise<SandboxSyncResult>;\n}\n\n/**\n * Host-side complete-mediation guard for native sync operations. The orchestrator\n * authors every `targetPath`, but the native transport crosses the host↔sandbox\n * trust boundary, so we canonicalize and confine each mapping's source and target\n * to an orchestrator-owned root before handing the operation to a provider.\n * Absolute escapes and `..` traversal are rejected fail-closed. Sandbox and host\n * paths on the server are POSIX.\n */\nexport function assertSyncOperationsConfined(\n  operations: SandboxSyncOperation[],\n  roots: { sourceRoots: string[]; targetRoots: string[] },\n): void {\n  const confine = (candidate: string, allowed: string[], label: string): void => {\n    const normalized = path.posix.normalize(candidate);\n    if (!path.posix.isAbsolute(normalized) || normalized === \"..\" || normalized.includes(\"/../\") || normalized.endsWith(\"/..\")) {\n      throw new Error(`sync operation ${label} path is not a confined absolute path: ${candidate}`);\n    }\n    const within = allowed.some((root) => {\n      const normalizedRoot = path.posix.normalize(root);\n      const prefix = normalizedRoot.endsWith(\"/\") ? normalizedRoot : `${normalizedRoot}/`;\n      return normalized === normalizedRoot || normalized.startsWith(prefix);\n    });\n    if (!within) {\n      throw new Error(`sync operation ${label} path escapes its confinement root: ${candidate}`);\n    }\n  };\n  for (const operation of operations) {\n    for (const mapping of operation.files) {\n      confine(mapping.sourcePath, roots.sourceRoots, \"source\");\n      confine(mapping.targetPath, roots.targetRoots, \"target\");\n    }\n  }\n}\n","sourceCodeStart":255,"sourceCodeEnd":291,"githubUrl":"https://github.com/paperclipai/paperclip/blob/67001ec6eb96ae601aa27bc91d9b2415d665334a/packages/adapter-utils/src/sandbox-managed-runtime.ts#L255-L291","documentation":"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.","triggerScenarios":"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.","commonSituations":"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.","solutions":["Ensure every sourcePath/targetPath passed to sync operations is absolute and normalize() it before submission (path.posix.resolve or path.resolve).","Strip or reject `..` in any user-supplied filename before joining it into a target path.","For additional sources, pass an absolute localPath (matches the error at line 1011) computed with path.resolve at the call site.","Add a unit test feeding a `..`-bearing path and confirm it throws this exact message."],"exampleFix":"// before\nfiles: [{ sourcePath: relPath, targetPath: `${root}/../asset.tar`, ... }]\n// after\nconst safeTarget = path.posix.join(runtimeRootDir, 'asset.tar');\nfiles: [{ sourcePath: path.resolve(relPath), targetPath: safeTarget, ... }]","handlingStrategy":"validation","validationCode":"function assertConfinedAbsolute(candidate: string): void {\n  const n = path.posix.normalize(candidate);\n  if (!path.posix.isAbsolute(n) || n === '..' || n.includes('/../') || n.endsWith('/..')) {\n    throw new Error(`not a confined absolute path: ${candidate}`);\n  }\n}\n// run before assertSyncOperationsConfined:\nfor (const op of operations) for (const m of op.files) { assertConfinedAbsolute(m.sourcePath); assertConfinedAbsolute(m.targetPath); }","typeGuard":"function isConfinedAbsolutePath(p: string): boolean {\n  const n = path.posix.normalize(p);\n  return path.posix.isAbsolute(n) && n !== '..' && !n.includes('/../') && !n.endsWith('/..');\n}","tryCatchPattern":null,"preventionTips":["Always build sync paths with path.posix.resolve/join from trusted prefixes.","Reject any user-supplied path segment containing '..' at the input boundary.","Unit-test the confinement helper with traversal payloads."],"tags":["sandbox","security","path-traversal","validation","sync"],"backgroundTag":null,"analyzedSha":"67001ec6eb96ae601aa27bc91d9b2415d665334a","analyzedAt":"2026-08-12T12:05:45.408Z","schemaVersion":2},"datasetVersion":"2026-08-12T18:17:37.767Z"}