{"record":{"id":"4acc6978e26f20d1","repo":"Yeachan-Heo/oh-my-codex","slug":"path-traversal-detected-path-is-outside-the-allow","errorCode":null,"errorMessage":"Path traversal detected: path is outside the allowed directory","messagePattern":"Path traversal detected: path is outside the allowed directory","errorType":"exception","errorClass":"Error","httpStatus":null,"severity":"critical","filePath":"src/team/state.ts","lineNumber":494,"sourceCode":"// injection verification; keep the default ack budget above that steady-state\n// control-plane cadence to avoid spurious fallback/failed confirmations.\nconst DEFAULT_DISPATCH_ACK_TIMEOUT_MS = 2_000;\nconst MIN_DISPATCH_ACK_TIMEOUT_MS = 100;\nconst MAX_DISPATCH_ACK_TIMEOUT_MS = 10_000;\n\nfunction isTerminalTaskStatus(status: TeamTaskStatus): boolean {\n  return isTerminalTeamTaskStatus(status);\n}\n\nfunction canTransitionTaskStatus(from: TeamTaskStatus, to: TeamTaskStatus): boolean {\n  return canTransitionTeamTaskStatus(from, to);\n}\n\nfunction assertPathWithinDir(filePath: string, rootDir: string): void {\n  const normalizedRoot = resolve(rootDir);\n  const normalizedPath = resolve(filePath);\n  if (normalizedPath !== normalizedRoot && !normalizedPath.startsWith(normalizedRoot + sep)) {\n    throw new Error('Path traversal detected: path is outside the allowed directory');\n  }\n}\n\nfunction validateWorkerName(name: string): void {\n  if (!WORKER_NAME_SAFE_PATTERN.test(name)) {\n    throw new Error(\n      `Invalid worker name: \"${name}\". Must match /^[a-z0-9][a-z0-9-]{0,63}$/ (lowercase alphanumeric + hyphens, max 64 chars).`\n    );\n  }\n}\n\nfunction validateTaskId(taskId: string): void {\n  if (!TASK_ID_SAFE_PATTERN.test(taskId)) {\n    throw new Error(\n      `Invalid task ID: \"${taskId}\". Must be a positive integer (digits only, max 20 digits).`\n    );\n  }\n}","sourceCodeStart":476,"sourceCodeEnd":512,"githubUrl":"https://github.com/Yeachan-Heo/oh-my-codex/blob/3ad79a8a6fe6e95fdbb8c00e40716fffe4011ce2/src/team/state.ts#L476-L512","documentation":"Generic path-traversal guard in team state helpers: a resolved file path is neither equal to nor prefixed by the allowed root directory. Thrown by assertPathWithinDir, used when building mailbox, claim-lock, approval, and task file paths.","triggerScenarios":"Calling taskClaimLockDir, mailboxPath, mailboxLockDir, approvalPath, or taskFilePath with a worker name/task id/team name containing '..', '/', or absolute path components that resolve outside the state root.","commonSituations":"Passing unsanitized user input (worker names or task ids from CLI args or network) into team state APIs; hand-constructed identifiers containing slashes or traversal sequences.","solutions":["Validate identifiers with the worker-name/task-id regexes before calling team state APIs","Reject any input containing '/', '\\\\', '..' or leading dots before constructing paths","Use the library's own sanitize/validation helpers rather than raw user strings"],"exampleFix":"// before\nconst mailbox = mailboxPath(req.body.worker, teamStateRoot); // may throw traversal\n\n// after\nif (!/^[a-z0-9][a-z0-9-]{0,63}$/.test(req.body.worker)) throw new Error('bad worker name');\nconst mailbox = mailboxPath(req.body.worker, teamStateRoot);","handlingStrategy":"validation","validationCode":"function safeSegment(s: string): boolean {\n  return typeof s === 'string' && !s.includes('/') && !s.includes('\\\\\\\\') && !s.includes('..') && !s.startsWith('.') && s.length > 0 && s.length <= 128;\n}","typeGuard":null,"tryCatchPattern":"catch (e) {\n  if ((e as Error).message.includes('Path traversal detected')) {\n    throw new Error('rejecting unsafe identifier used to build team state path');\n  }\n  throw e;\n}","preventionTips":["Never build filesystem paths from raw user/network input","Whitelist identifiers against strict regexes before any state API call"],"tags":["security","path-traversal","team","validation"],"backgroundTag":"path-traversal-detected","analyzedSha":"3ad79a8a6fe6e95fdbb8c00e40716fffe4011ce2","analyzedAt":"2026-08-27T22:18:39.783Z","schemaVersion":2},"datasetVersion":"2026-08-28T00:17:15.603Z"}