{"record":{"id":"61ad191bed632fbb","repo":"Yeachan-Heo/oh-my-codex","slug":"scale-down-cleanup-debt-path-escape-path","errorCode":null,"errorMessage":"scale_down_cleanup_debt_path_escape:${path}","messagePattern":"scale_down_cleanup_debt_path_escape:(.+?)","errorType":"exception","errorClass":"Error","httpStatus":null,"severity":"critical","filePath":"src/team/scaling.ts","lineNumber":1552,"sourceCode":"): boolean {\n  const relativePath = pathSemantics.relative(root, path);\n  return relativePath === ''\n    || (!pathSemantics.isAbsolute(relativePath)\n      && relativePath !== '..'\n      && !relativePath.startsWith(`..${pathSemantics.sep}`));\n}\n\nasync function assertExistingPathParentContained(path: string, root: string): Promise<void> {\n  const canonicalRoot = await realpath(root);\n  let candidate = resolve(path);\n  while (!existsSync(candidate)) {\n    const parent = dirname(candidate);\n    if (parent === candidate) throw new Error(`scale_down_cleanup_debt_path_missing_parent:${path}`);\n    candidate = parent;\n  }\n  const canonicalExistingPath = await realpath(candidate);\n  if (!isSameOrInsidePath(canonicalExistingPath, canonicalRoot)) {\n    throw new Error(`scale_down_cleanup_debt_path_escape:${path}`);\n  }\n}\n\nasync function validateScaleDownCleanupResources(\n  teamName: string,\n  leaderCwd: string,\n  teamStateRoot: string,\n  workers: readonly ScaleDownCleanupDebtResource[],\n): Promise<void> {\n  const canonicalLeaderCwd = await realpath(leaderCwd);\n  let repoRoot = canonicalLeaderCwd;\n  const repoRootResult = spawnSync('git', ['rev-parse', '--show-toplevel'], {\n    cwd: leaderCwd,\n    encoding: 'utf-8',\n    windowsHide: true,\n  });\n  const reportedRepoRoot = (repoRootResult.stdout || '').trim();\n  if (repoRootResult.status === 0 && reportedRepoRoot) {","sourceCodeStart":1534,"sourceCodeEnd":1570,"githubUrl":"https://github.com/Yeachan-Heo/oh-my-codex/blob/3ad79a8a6fe6e95fdbb8c00e40716fffe4011ce2/src/team/scaling.ts#L1534-L1570","documentation":"The closest existing ancestor of a scale-down cleanup path resolves (via realpath) outside the allowed root, so cleanup would touch paths escaping the team/repo root. The message embeds the path.","triggerScenarios":"assertExistingPathParentContained finds an existing ancestor whose realpath is not inside the canonical root, e.g. the path traverses a symlink pointing outside the root.","commonSituations":"Symlinked team state directory pointing elsewhere on disk; worker metadata with crafted relative/absolute paths attempting traversal; moved repo where stale absolute paths now resolve outside the root.","solutions":["Remove or fix symlinks in the team state / worktree paths so they resolve inside the root","Update worker records in team config with correct absolute paths under the team state root","Re-run scale-down after normalizing paths"],"exampleFix":null,"handlingStrategy":"validation","validationCode":"import { realpathSync } from 'node:fs';\nimport { resolve, sep } from 'node:path';\nfunction resolvesWithin(p: string, root: string): boolean {\n  let cur = p;\n  while (!existsSync(cur)) cur = dirname(cur);\n  const real = realpathSync(cur);\n  return real === root || real.startsWith(root + sep);\n}","typeGuard":null,"tryCatchPattern":"catch (e) {\n  if ((e as Error).message.startsWith('scale_down_cleanup_debt_path_escape')) {\n    throw new Error('cleanup path escapes root: fix symlinks in team state paths');\n  }\n  throw e;\n}","preventionTips":["Never symlink team state or worktree directories to other volumes","Audit worker metadata paths after moving repos between machines"],"tags":["team","scale-down","path-traversal","security","symlink"],"backgroundTag":"path-traversal-detected","analyzedSha":"3ad79a8a6fe6e95fdbb8c00e40716fffe4011ce2","analyzedAt":"2026-08-27T22:18:39.783Z","schemaVersion":2},"datasetVersion":"2026-08-28T00:17:15.603Z"}