{"record":{"id":"429e6eaf18f54c14","repo":"vercel/ai","slug":"invalid-cline-history-file-name-input-historyfi","errorCode":null,"errorMessage":"Invalid Cline history file name: ${input.historyFileName}","messagePattern":"Invalid Cline history file name: (.+?)","errorType":"validation","errorClass":"Error","httpStatus":null,"severity":"error","filePath":"packages/harness-cline/src/cline-resume-state.ts","lineNumber":83,"sourceCode":"  return privateSessionDir;\n}\n\nfunction resolveContainedSandboxPath(input: {\n  readonly privateSessionDir: string;\n  readonly historyFileName: string;\n}): string {\n  const historyDir = path.posix.resolve(input.privateSessionDir);\n  const filePath = path.posix.resolve(\n    historyDir,\n    safeClineHistoryFileName(input.historyFileName),\n  );\n  const relativePath = path.posix.relative(historyDir, filePath);\n  if (\n    relativePath === '' ||\n    relativePath.startsWith('..') ||\n    path.posix.isAbsolute(relativePath)\n  ) {\n    throw new Error(\n      `Invalid Cline history file name: ${input.historyFileName}`,\n    );\n  }\n  return filePath;\n}\n\n/**\n * Persist the runtime's conversation history into private sandbox state so a\n * future process can resume the session after\n * `HarnessV1SandboxProvider.resume?.({ sessionId })` reattaches the sandbox.\n */\nexport async function persistHistoryToSandbox(args: {\n  readonly sandbox: Experimental_SandboxSession;\n  readonly privateSessionDir: string;\n  readonly historyFileName: string;\n  readonly messages: readonly AgentMessage[];\n  readonly abortSignal?: AbortSignal;\n}): Promise<void> {","sourceCodeStart":65,"sourceCodeEnd":101,"githubUrl":"https://github.com/vercel/ai/blob/69428b1f8b037e4d118fb4853428d5c4e620493c/packages/harness-cline/src/cline-resume-state.ts#L65-L101","documentation":"resolveContainedSandboxPath validates that a Cline history file name resolves to a file inside the history directory. It computes path.posix.relative(historyDir, filePath) and throws if the result is empty (points at the directory itself), starts with '..' (escapes the directory), or is absolute. This prevents path-traversal or out-of-sandbox access when reading Cline resume history.","triggerScenarios":"Calling the resume-state API with historyFileName such as '../other-task.json', '/etc/passwd', an absolute path, an empty name, or a name that normalizes to the history directory itself (e.g. '.' or a nested path like 'a/../../x').","commonSituations":"Storing or deriving history file names from user input or task IDs without sanitizing; joining a history file name with the wrong base directory; migrating from an older Cline state layout where file names included subdirectories or absolute paths.","solutions":["Pass only the bare file name (e.g. 'task-123.json'), not a path, and let the harness join it with the history directory.","Sanitize the input: strip directory components and reject names containing '/', '..', or leading separators before calling the API.","If the history file lives elsewhere, point the history directory configuration at its actual parent directory instead of using a relative name."],"exampleFix":"// before\nresolveClineResumeState({ historyFileName: `../../tasks/${taskId}.json` });\n// after\nif (!/^[A-Za-z0-9._-]+$/.test(taskId)) throw new Error('bad task id');\nresolveClineResumeState({ historyFileName: `${taskId}.json` });","handlingStrategy":"validation","validationCode":"function isValidHistoryFileName(name: string): boolean {\n  return /^[A-Za-z0-9][A-Za-z0-9._-]*$/.test(name) && !name.startsWith('.') && !name.includes('/') && !path.posix.isAbsolute(name);\n}\nif (!isValidHistoryFileName(historyFileName)) throw new Error(`Refusing unsafe history file name: ${historyFileName}`);","typeGuard":null,"tryCatchPattern":"try {\n  await resolveClineResumeState({ historyFileName });\n} catch (e) {\n  if (e instanceof Error && e.message.startsWith('Invalid Cline history file name')) {\n    historyFileName = path.posix.basename(historyFileName);\n    // retry or surface a user-facing validation error\n  } else throw e;\n}","preventionTips":["Never construct history file names from raw user input; whitelist a strict character set.","Always pass a bare file name, never a path, to resume-state APIs.","Reject names containing '/', '\\\\', '..', or absolute prefixes in your own input layer."],"tags":["path-traversal","validation","security","cline"],"backgroundTag":"path-traversal-detected","analyzedSha":"69428b1f8b037e4d118fb4853428d5c4e620493c","analyzedAt":"2026-08-30T12:32:21.016Z","schemaVersion":2},"datasetVersion":"2026-08-30T13:17:10.514Z"}