{"record":{"id":"a9f72a4f09d61c25","repo":"paperclipai/paperclip","slug":"acpx-provider-package-issuer-for-packagename-re","errorCode":null,"errorMessage":"ACPX provider package issuer for ${packageName} resolves outside the selected provider root","messagePattern":"ACPX provider package issuer for (.+?) 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":333,"sourceCode":"    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(\n        `ACPX provider package issuer for ${packageName} resolves outside the selected provider root`,\n      );\n    }\n    const packageJsonPath = realpathSync(\n      resolvePackageJsonFromIssuer(packageName, canonicalIssuer),\n    );\n    if (!pathIsInside(canonicalNodeModules, packageJsonPath)) {\n      throw new Error(\n        `ACPX provider package ${packageName} resolves outside the selected provider root`,\n      );\n    }\n    return packageJsonPath;\n  };\n}\n\nfunction resolvePackageJsonFromIssuer(\n  packageName: string,\n  issuerPackageJsonPath: string,","sourceCodeStart":315,"sourceCodeEnd":351,"githubUrl":"https://github.com/paperclipai/paperclip/blob/01ad8584922b5d85292b1723cae71fa0d9b07a19/packages/paperclip-runner/src/drivers/acpx/installation-integrity.ts#L315-L351","documentation":"createAcpxPackageJsonResolver returns a resolver closure that, for each requested package, canonicalizes the issuer package.json path via realpathSync and verifies it lives inside the selected provider root. This error is thrown when the issuer manifest (explicitly passed, or defaulting to the provider root manifest) canonicalizes to a path outside canonicalRoot, typically via symlinks. It is a fail-closed containment check ensuring dependency resolution never anchors outside the verified provider installation.","triggerScenarios":"Calling the resolver (or defaultPackageJsonResolver) with an issuerPackageJsonPath that is a symlink into another location, points to a package outside providerPackageRoot, or refers to a manifest that was moved/relinked after createAcpxPackageJsonResolver computed canonicalRoot.","commonSituations":"Global/pnpm-style symlinked node_modules where package dirs are symlinked into a virtual store outside the provider root; a stale or hand-built provider root where the issuer manifest is a relative path resolved wrongly; passing a workspace package.json from outside the ACPX provider installation as the issuer.","solutions":["Inspect the issuerPackageJsonPath with `realpathSync` and confirm it is inside the provider root passed to createAcpxPackageJsonResolver; if not, pass the correct issuer or correct provider root.","If the issuer is a symlink into a store outside the root, either relocate the installation so symlinks stay inside the root, or pass the in-root manifest path explicitly instead of relying on the default.","Recreate the provider root (clean install of the ACPX provider packages) so realpath of the manifest lands inside it.","Verify the root argument is the canonical (realpath) root, not a path whose realpath differs (e.g. macOS /var vs /private/var)."],"exampleFix":"// before\nconst resolver = createAcpxPackageJsonResolver(providerRoot);\nresolver(pkg, \"/opt/other-store/pkg/package.json\"); // outside providerRoot\n// after\nconst issuer = resolve(providerRoot, \"node_modules/pkg/package.json\");\nconst resolver = createAcpxPackageJsonResolver(realpathSync(providerRoot));\nresolver(pkg, issuer); // issuer realpath stays inside canonicalRoot","handlingStrategy":"validation","validationCode":"import { realpathSync } from \"node:fs\";\nimport { relative, isAbsolute } from \"node:path\";\nfunction isInside(root: string, candidate: string): boolean {\n  const r = relative(realpathSync(root), realpathSync(candidate));\n  return r !== \"\" && !r.startsWith(\"..\") && !isAbsolute(r);\n}\nif (!isInside(providerRoot, issuerPath)) throw new Error(\"issuer outside provider root\");","typeGuard":"function issuerInsideRoot(providerRoot: string, issuerPath?: string): boolean {\n  if (issuerPath === undefined) return true;\n  try { return isInside(providerRoot, issuerPath); } catch { return false; }\n}","tryCatchPattern":"try {\n  const manifestPath = resolver(packageName, issuerPath);\n} catch (err) {\n  if (err instanceof Error && err.message.includes(\"issuer\") && err.message.includes(\"outside the selected provider root\")) {\n    // re-anchor issuer inside provider root or rebuild provider installation\n  } else throw err;\n}","preventionTips":["Always pass issuer manifests that live inside the provider root; prefer the default (root manifest) when unsure.","Canonicalize both root and issuer with realpathSync before validating containment yourself.","Avoid installations that symlink package dirs to a store outside the root (e.g. shared pnpm stores).","Log realpathSync(issuer) alongside the configured root when wiring profiles to catch drift early."],"tags":["security","path-containment","symlink","filesystem"],"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"}