{"record":{"id":"9fdf62820e05791e","repo":"Yeachan-Heo/oh-my-codex","slug":"mode-must-not-contain","errorCode":null,"errorMessage":"mode must not contain \"..\"","messagePattern":"mode must not contain \"\\.\\.\"","errorType":"validation","errorClass":"Error","httpStatus":null,"severity":"error","filePath":"src/mcp/state-paths.ts","lineNumber":139,"sourceCode":"    throw new Error('session_id must be a string');\n  }\n  if (!SESSION_ID_PATTERN.test(sessionId)) {\n    throw new Error('session_id must match ^[A-Za-z0-9_-]{1,64}$');\n  }\n  return sessionId;\n}\n\n\nexport function validateStateModeSegment(mode: unknown): string {\n  if (typeof mode !== 'string') {\n    throw new Error('mode must be a string');\n  }\n  const normalized = mode.trim();\n  if (!normalized) {\n    throw new Error('mode must be a non-empty string');\n  }\n  if (normalized.includes('..')) {\n    throw new Error('mode must not contain \"..\"');\n  }\n  if (normalized.includes('/') || normalized.includes('\\\\')) {\n    throw new Error('mode must not contain path separators');\n  }\n  if (!STATE_MODE_SEGMENT_PATTERN.test(normalized)) {\n    throw new Error('mode must match ^[A-Za-z0-9_-]{1,64}$');\n  }\n  return normalized;\n}\n\nexport function getStateFilename(mode: string): string {\n  return `${validateStateModeSegment(mode)}${STATE_FILE_SUFFIX}`;\n}\n\nexport function validateStateFileName(fileName: unknown): string {\n  if (typeof fileName !== 'string') {\n    throw new Error('fileName must be a string');\n  }","sourceCodeStart":121,"sourceCodeEnd":157,"githubUrl":"https://github.com/Yeachan-Heo/oh-my-codex/blob/3ad79a8a6fe6e95fdbb8c00e40716fffe4011ce2/src/mcp/state-paths.ts#L121-L157","documentation":"Thrown when the trimmed mode string contains \"..\" anywhere. Because mode is concatenated into filesystem paths, \"..\" would allow directory traversal in the state directory, so it is explicitly banned.","triggerScenarios":"mode: \"a..b\", mode: \"..\", mode: \"team..night\" — any occurrence of two consecutive dots, even innocuous ones inside a word.","commonSituations":"Ellipsis-style naming (\"draft..final\"); attempts at traversal from untrusted input; accidental double-dot typos.","solutions":["Remove the double dots: use single dots only — note dots are rejected by the final pattern anyway, prefer hyphens","Use \"a-b\" style separators instead of \"..\"","Reject user input containing '..' before it reaches state APIs"],"exampleFix":"// before\ngetStateFilename(\"run..final\");\n// after\ngetStateFilename(\"run-final\");","handlingStrategy":"validation","validationCode":"if (mode.includes('..')) mode = mode.replace(/\\.\\./g, '-');","typeGuard":"function hasNoTraversal(v: string): boolean { return !v.includes('..'); }","tryCatchPattern":null,"preventionTips":["Use hyphens not double dots in slugs","Reject '..' in any untrusted segment early"],"tags":["path-traversal","security","state","mcp"],"backgroundTag":"path-traversal-blocked","analyzedSha":"3ad79a8a6fe6e95fdbb8c00e40716fffe4011ce2","analyzedAt":"2026-08-27T22:18:39.783Z","schemaVersion":2},"datasetVersion":"2026-08-28T00:17:15.603Z"}