{"record":{"id":"db2232eb93b38f3a","repo":"paperclipai/paperclip","slug":"sync-operation-label-path-escapes-its-confineme","errorCode":null,"errorMessage":"sync operation ${label} path escapes its confinement root: ${candidate}","messagePattern":"sync operation (.+?) path escapes its confinement root: (.+?)","errorType":"validation","errorClass":null,"httpStatus":null,"severity":"critical","filePath":"packages/adapter-utils/src/sandbox-managed-runtime.ts","lineNumber":281,"sourceCode":" * 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\nexport interface PreparedSandboxManagedRuntime {\n  spec: SandboxRemoteExecutionSpec;\n  workspaceLocalDir: string;\n  workspaceRemoteDir: string;\n  runtimeRootDir: string;\n  assetDirs: Record<string, string>;\n  /**\n   * Remote directory of each additional (referenced) project that staged","sourceCodeStart":263,"sourceCodeEnd":299,"githubUrl":"https://github.com/paperclipai/paperclip/blob/67001ec6eb96ae601aa27bc91d9b2415d665334a/packages/adapter-utils/src/sandbox-managed-runtime.ts#L263-L299","documentation":"The second confinement check in assertSyncOperationsConfined: the path is a clean absolute POSIX path but does not fall under any of the allowed roots supplied for that label (source or target). It fails closed to prevent a sync operation from reading or writing outside orchestrator-owned directories, blocking absolute-path escapes that are technically valid but unauthorized.","triggerScenarios":"Calling assertSyncOperationsConfined where a mapping path is absolute and traversal-free but starts with a root not present in roots.sourceRoots (for source) or roots.targetRoots (for target). E.g. targetPath under /etc while targetRoots only lists the runtime root.","commonSituations":"roots arrays were not updated when a new staging directory was introduced; a symlink-resolved real path points outside the declared root; misconfigured runtimeRootDir or tempDir; caller passed an asset dir outside the asset root.","solutions":["Compare the offending candidate (logged in the message) against roots.sourceRoots/targetRoots and add the missing owning root.","Canonicalize paths with fs.realpath before submitting so symlinks resolve to their true root before the check.","Confirm runtimeRootDir/tempDir used to build targetPaths are included in the targetRoots you pass.","Restrict caller-supplied paths to known prefixes at the API boundary so they never escape."],"exampleFix":null,"handlingStrategy":"validation","validationCode":"function assertWithinRoots(candidate: string, roots: string[]): void {\n  const n = path.posix.normalize(candidate);\n  const ok = roots.some((r) => {\n    const nr = path.posix.normalize(r);\n    const prefix = nr.endsWith('/') ? nr : nr + '/';\n    return n === nr || n.startsWith(prefix);\n  });\n  if (!ok) throw new Error(`path escapes confinement roots: ${candidate}`);\n}","typeGuard":null,"tryCatchPattern":null,"preventionTips":["Keep roots.sourceRoots/targetRoots in sync with every directory you stage from/into.","Resolve symlinks (fs.realpath) before the confinement check so real paths are evaluated.","Document which roots each staging step declares."],"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"}