{"record":{"id":"17cd9b1f855453a5","repo":"paperclipai/paperclip","slug":"acpx-provider-package-manifest-resolves-outside-th","errorCode":null,"errorMessage":"ACPX provider package manifest resolves outside the selected provider root","messagePattern":"ACPX provider package manifest resolves outside the selected provider root","errorType":"exception","errorClass":null,"httpStatus":null,"severity":"error","filePath":"packages/paperclip-runner/src/drivers/acpx/installation-integrity.ts","lineNumber":315,"sourceCode":"    );\n  }\n  const manifest = (\n    providerPackageManifest ?? resolve(root, \"package.json\")\n  ).trim();\n  if (\n    !manifest ||\n    !isAbsolute(manifest) ||\n    manifest.includes(\"\\0\") ||\n    resolve(manifest) !== manifest\n  ) {\n    throw new Error(\n      \"ACPX provider package manifest must be an explicit normalized absolute path\",\n    );\n  }\n  const canonicalRoot = realpathSync(root);\n  const canonicalManifest = realpathSync(manifest);\n  if (!pathIsInside(canonicalRoot, canonicalManifest)) {\n    throw new Error(\n      \"ACPX provider package manifest resolves outside the selected provider root\",\n    );\n  }\n  const canonicalNodeModules = realpathSync(\n    resolve(canonicalRoot, \"node_modules\"),\n  );\n  if (!pathIsInside(canonicalRoot, canonicalNodeModules)) {\n    throw new Error(\n      \"ACPX provider node_modules resolves outside the selected provider root\",\n    );\n  }\n  return (packageName, issuerPackageJsonPath) => {\n    const canonicalIssuer =\n      issuerPackageJsonPath === undefined\n        ? canonicalManifest\n        : realpathSync(issuerPackageJsonPath);\n    if (!pathIsInside(canonicalRoot, canonicalIssuer)) {\n      throw new Error(","sourceCodeStart":297,"sourceCodeEnd":333,"githubUrl":"https://github.com/paperclipai/paperclip/blob/01ad8584922b5d85292b1723cae71fa0d9b07a19/packages/paperclip-runner/src/drivers/acpx/installation-integrity.ts#L297-L333","documentation":"After validating that root and manifest are normalized absolute paths, the resolver canonicalizes both with realpathSync and requires the canonical manifest to live inside the canonical root (pathIsInside). If the package.json resolves (through symlinks) to a location outside the provider root directory, the provider layout is considered tampered with or misconfigured and resolution is refused.","triggerScenarios":"root is a symlink whose target's manifest itself symlinks (or realpath-resolves) outside the root, e.g. root=/opt/provider -> /opt/provider-v2 while the manifest points into a shared cache; passing a manifest from a different package than root.","commonSituations":"pnpm/yarn store layouts where package files are symlinked from a central store outside the provider directory; a user copied a package.json from another project to a path outside root; version-switching symlinks that point the manifest outside the selected root.","solutions":["Ensure the package.json physically lives inside the resolved provider root directory (compare realpathSync of both)","If using symlinked store layouts, select the provider root as the real (realpathSync) directory so canonical root contains the canonical manifest","Pass the correct matching manifest for the chosen root instead of one from another package/version","Update the layout so root and manifest share the same realpath ancestor, or remove symlink indirection from the provider install"],"exampleFix":"// before\ncreateAcpxPackageJsonResolver('/opt/acpx/provider', '/opt/acpx/other/package.json');\n// after\nconst canonicalRoot = fs.realpathSync('/opt/acpx/provider');\ncreateAcpxPackageJsonResolver(canonicalRoot, path.join(canonicalRoot, 'package.json'));","handlingStrategy":"validation","validationCode":"import fs from 'node:fs';\nimport path from 'node:path';\nfunction manifestInsideRoot(root: string, manifest: string): boolean {\n  const canonicalRoot = fs.realpathSync(root);\n  const canonicalManifest = fs.realpathSync(manifest);\n  return canonicalManifest.startsWith(canonicalRoot + path.sep);\n}","typeGuard":null,"tryCatchPattern":"try {\n  const resolver = createAcpxPackageJsonResolver(root, manifest);\n} catch (err) {\n  if (err instanceof Error && /manifest resolves outside the selected provider root/.test(err.message)) {\n    const cr = fs.realpathSync(root), cm = fs.realpathSync(manifest);\n    throw new Error(`Provider layout mismatch: manifest realpath ${cm} is outside root realpath ${cr}; fix symlinks or pass the matching manifest`);\n  }\n  throw err;\n}","preventionTips":["Realpath both root and manifest before wiring them together","Avoid symlink-switched install layouts (provider -> provider-vN) that place files outside the selected root","Copy, don't symlink, package.json when staging provider installs","Audit provider directories for symlinks escaping the root before registration"],"tags":["acpx","path-validation","symlink","security"],"backgroundTag":"path-traversal-blocked","analyzedSha":"01ad8584922b5d85292b1723cae71fa0d9b07a19","analyzedAt":"2026-09-10T03:14:50.855Z","contentChangedAt":"2026-09-10T03:14:50.855Z","schemaVersion":2},"datasetVersion":"2026-09-14T00:17:10.932Z"}