{"record":{"id":"ffb0db15ff1c6770","repo":"paperclipai/paperclip","slug":"refusing-to-remove-install-store-with-an-invalid-m","errorCode":null,"errorMessage":"Refusing to remove install store with an invalid manifest at ${paths.cliRoot}.","messagePattern":"Refusing to remove install store with an invalid manifest at (.+?)\\.","errorType":"exception","errorClass":"Error","httpStatus":null,"severity":"critical","filePath":"cli/src/install-store.ts","lineNumber":142,"sourceCode":"    markerStat = fs.lstatSync(paths.markerPath);\n  } catch (error) {\n    if ((error as NodeJS.ErrnoException).code === \"ENOENT\") {\n      throw new Error(`Refusing to remove unverified install store ${paths.cliRoot}.`);\n    }\n    throw error;\n  }\n  if (!markerStat.isFile() || markerStat.isSymbolicLink() || markerStat.nlink > 1) {\n    throw new Error(`Refusing to remove unverified install store ${paths.cliRoot}.`);\n  }\n  assertOwnedByCurrentUser(markerStat, paths.markerPath);\n  if (fs.readFileSync(paths.markerPath, \"utf8\") !== MANAGED_STORE_MARKER) {\n    throw new Error(`Refusing to remove unverified install store ${paths.cliRoot}.`);\n  }\n  const manifest = readInstallManifest(paths);\n  if (!manifest) throw new Error(`Refusing to remove install store without a manifest at ${paths.cliRoot}.`);\n  const relativePayload = path.relative(paths.installsRoot, path.resolve(manifest.payloadPath));\n  if (!relativePayload || relativePayload.startsWith(\"..\") || path.isAbsolute(relativePayload)) {\n    throw new Error(`Refusing to remove install store with an invalid manifest at ${paths.cliRoot}.`);\n  }\n  return manifest;\n}\n\nexport async function withInstallStoreLock<T>(\n  callback: () => Promise<T>,\n  paths = resolveInstallStorePaths(),\n  options: { initialize?: boolean } = {},\n): Promise<T> {\n  if (options.initialize !== false) initializeInstallStore(paths);\n  const token = `${process.pid}:${Date.now()}:${Math.random().toString(16).slice(2)}`;\n  const processIsAlive = (pid: number): boolean => {\n    try {\n      process.kill(pid, 0);\n      return true;\n    } catch (error) {\n      return (error as NodeJS.ErrnoException).code === \"EPERM\";\n    }","sourceCodeStart":124,"sourceCodeEnd":160,"githubUrl":"https://github.com/paperclipai/paperclip/blob/67001ec6eb96ae601aa27bc91d9b2415d665334a/cli/src/install-store.ts#L124-L160","documentation":"Thrown by assertManagedInstallStore() when the manifest was read successfully but its payloadPath, resolved and made relative to installsRoot, either is empty, starts with '..' (escapes installsRoot), or is absolute. This is a path-confusion / path-traversal guard: a manifest pointing outside the install payloads directory could cause the removal logic to delete or act on arbitrary locations.","triggerScenarios":"Called assertManagedInstallStore() where readInstallManifest returns a manifest whose payloadPath resolves to a location outside paths.installsRoot (e.g. '/etc', '../../..', or an absolute path).","commonSituations":"1) Manifest was hand-edited to point payloadPath at an external directory. 2) installsRoot was moved/renamed but the manifest still holds the old absolute path. 3) Tampering to trick prune/remove into touching files outside the store. 4) A bug in an older version wrote a bad payloadPath.","solutions":["Inspect install.json and check manifest.payloadPath; it must be inside ~/.paperclip/cli/installs/<source>/<id>.","If the store is corrupted, remove cliRoot and reinitialize with 'paperclipai install'.","If the path is merely stale due to a moved store, correct payloadPath to the new in-store location or reinitialize.","Never set payloadPath to an absolute or parent-relative path."],"exampleFix":"// before: install.json has \"payloadPath\": \"/usr/local/lib\"\n$ rm -rf ~/.paperclip/cli\n$ paperclipai install   # manifest gets correct in-store payloadPath","handlingStrategy":"validation","validationCode":"import path from \"node:path\";\nimport fs from \"node:fs\";\nimport { resolveInstallStorePaths, readInstallManifest } from \"./install-store.js\";\n\nfunction manifestPayloadInStore(paths = resolveInstallStorePaths()): boolean {\n  const m = readInstallManifest(paths);\n  if (!m) return false;\n  const rel = path.relative(paths.installsRoot, path.resolve(m.payloadPath));\n  return !!rel && !rel.startsWith(\"..\") && !path.isAbsolute(rel);\n}","typeGuard":"import path from \"node:path\";\n\nfunction isWithin(installsRoot: string, payloadPath: string): boolean {\n  const rel = path.relative(installsRoot, path.resolve(payloadPath));\n  return !!rel && !rel.startsWith(\"..\") && !path.isAbsolute(rel);\n}","tryCatchPattern":"try {\n  assertManagedInstallStore(paths);\n} catch (err) {\n  if (err instanceof Error && err.message.includes(\"invalid manifest\")) {\n    console.error(\"Manifest payloadPath escapes installsRoot. Reset store and reinstall.\");\n    process.exit(1);\n  }\n  throw err;\n}","preventionTips":["Never hand-edit install.json's payloadPath to an external location.","If moving the store, reinitialize rather than editing paths in the manifest.","Treat a manifest with an external payloadPath as tampering."],"tags":["install-store","security","path-traversal","manifest","cli"],"backgroundTag":null,"analyzedSha":"67001ec6eb96ae601aa27bc91d9b2415d665334a","analyzedAt":"2026-08-12T12:05:45.408Z","schemaVersion":2},"datasetVersion":"2026-08-12T18:17:37.767Z"}