{"record":{"id":"12c94b337d42d3f1","repo":"vercel/ai","slug":"invalid-pi-session-file-name-input-sessionfilen","errorCode":null,"errorMessage":"Invalid Pi session file name: ${input.sessionFileName}","messagePattern":"Invalid Pi session file name: (.+?)","errorType":"exception","errorClass":null,"httpStatus":null,"severity":"error","filePath":"packages/harness-pi/src/pi-resume-state.ts","lineNumber":78,"sourceCode":"  return privateSessionDir;\n}\n\nfunction resolveContainedHostPath(input: {\n  readonly baseDir: string;\n  readonly sessionFileName: string;\n}): string {\n  const baseDir = path.resolve(input.baseDir);\n  const filePath = path.resolve(\n    baseDir,\n    safePiSessionFileName(input.sessionFileName),\n  );\n  const relativePath = path.relative(baseDir, filePath);\n  if (\n    relativePath === '' ||\n    relativePath.startsWith('..') ||\n    path.isAbsolute(relativePath)\n  ) {\n    throw new Error(`Invalid Pi session file name: ${input.sessionFileName}`);\n  }\n  return filePath;\n}\n\nfunction resolveContainedSandboxPath(input: {\n  readonly privateSessionDir: string;\n  readonly sessionFileName: string;\n}): string {\n  const sessionDir = path.posix.resolve(input.privateSessionDir);\n  const filePath = path.posix.resolve(\n    sessionDir,\n    safePiSessionFileName(input.sessionFileName),\n  );\n  const relativePath = path.posix.relative(sessionDir, filePath);\n  if (\n    relativePath === '' ||\n    relativePath.startsWith('..') ||\n    path.posix.isAbsolute(relativePath)","sourceCodeStart":60,"sourceCodeEnd":96,"githubUrl":"https://github.com/vercel/ai/blob/69428b1f8b037e4d118fb4853428d5c4e620493c/packages/harness-pi/src/pi-resume-state.ts#L60-L96","documentation":"resolveContainedHostPath validates that the Pi session file name resolves to a path strictly contained in the host's local session mirror directory. The name must be a safe basename ending in .jsonl or .json and must not escape the base dir. It throws for empty paths, absolute paths, parent escapes ('..'), or names failing the safe-file-name pattern.","triggerScenarios":"persistSessionFileToSandbox or pullSessionFileFromSandbox receives a sessionFileName that is absolute, empty, contains path traversal ('../'), or is not a `<safe-name>.jsonl`/`.json` basename — typically because it came from a corrupted or tampered resume state's `data` payload.","commonSituations":"Hand-editing session state to use a full path instead of a basename; restoring state written by another harness version; a hostile/legacy sessionFileName like '../../etc/passwd' or 'session.txt'.","solutions":["Use only basenames matching /^[A-Za-z0-9][A-Za-z0-9._-]*\\.jsonl?$/ for sessionFileName","Read sessionFileName from piResumeStateSchema-validated state (it enforces the same pattern)","Strip any directory components from the stored name before calling resume APIs"],"exampleFix":"// before\nawait persistSessionFileToSandbox({\n  ...\n  sessionFileName: '/sessions/abc.jsonl', // absolute path -> throws\n});\n// after\nawait persistSessionFileToSandbox({\n  ...\n  sessionFileName: 'abc.jsonl', // safe basename\n});","handlingStrategy":"validation","validationCode":"const SAFE = /^[A-Za-z0-9][A-Za-z0-9._-]*\\.jsonl?$/;\nexport function isValidPiSessionFileName(name: unknown): name is string {\n  return typeof name === 'string' && SAFE.test(name);\n}","typeGuard":"export function isPiResumeState(v: unknown): v is { sessionFileName?: string } {\n  return typeof v === 'object' && v !== null &&\n    (!('sessionFileName' in v) || isValidPiSessionFileName((v as any).sessionFileName));\n}","tryCatchPattern":"try {\n  await persistSessionFileToSandbox({ ...args });\n} catch (e) {\n  if (e instanceof Error && e.message.startsWith('Invalid Pi session file name')) {\n    // reject/repair the stored sessionFileName\n  }\n  throw e;\n}","preventionTips":["Always read sessionFileName from piResumeStateSchema-validated state","Store basenames only, never absolute or relative directory paths","Add the regex check at the boundary where state is loaded from disk"],"tags":["path-traversal","validation","sessions"],"backgroundTag":"invalid-session-file-name","analyzedSha":"69428b1f8b037e4d118fb4853428d5c4e620493c","analyzedAt":"2026-08-30T12:32:21.016Z","schemaVersion":2},"datasetVersion":"2026-08-30T13:17:10.514Z"}