{"record":{"id":"b58bb53f4ca66839","repo":"Yeachan-Heo/oh-my-codex","slug":"filename-must-not-contain-path-separators","errorCode":null,"errorMessage":"fileName must not contain path separators","messagePattern":"fileName must not contain path separators","errorType":"validation","errorClass":"Error","httpStatus":null,"severity":"error","filePath":"src/mcp/state-paths.ts","lineNumber":166,"sourceCode":"}\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  }\n  const normalized = fileName.trim();\n  if (!normalized) {\n    throw new Error('fileName must be a non-empty string');\n  }\n  if (normalized.includes('..')) {\n    throw new Error('fileName must not contain \"..\"');\n  }\n  if (normalized.includes('/') || normalized.includes('\\\\')) {\n    throw new Error('fileName must not contain path separators');\n  }\n  if (!STATE_FILE_NAME_PATTERN.test(normalized)) {\n    throw new Error('fileName must match ^[A-Za-z0-9._-]{1,128}$');\n  }\n  return normalized;\n}\n\nfunction convertWindowsToWslPath(raw: string): string {\n  const m = /^([a-zA-Z]):[\\\\/](.*)$/.exec(raw);\n  if (!m) return raw;\n  const drive = m[1].toLowerCase();\n  const rest = String(m[2] || '').replace(/\\\\/g, '/');\n  const mountRoot = `/mnt/${drive}`;\n  if (!existsSync(mountRoot)) return raw;\n  return rest ? `${mountRoot}/${rest}` : mountRoot;\n}\n\nfunction convertWslToWindowsPath(raw: string): string {","sourceCodeStart":148,"sourceCodeEnd":184,"githubUrl":"https://github.com/Yeachan-Heo/oh-my-codex/blob/3ad79a8a6fe6e95fdbb8c00e40716fffe4011ce2/src/mcp/state-paths.ts#L148-L184","documentation":"Thrown when fileName contains a / or \\\\ separator. fileName must be a bare file name, not a path — separators would create or reference subdirectories inside/around the state directory.","triggerScenarios":"fileName: \"sub/state.json\", fileName: \"logs\\\\state.json\", or passing a full path like \"/etc/x\" (also fails the earlier checks).","commonSituations":"Passing path.join(...) output where a basename is expected; Windows clients using backslashes; directory structure encoded into the name.","solutions":["Pass only the basename: path.basename(fullPath)","Normalize separators to hyphens if you need hierarchy encoded: p.split(/[\\\\/]/).join(\"-\")","Keep fileName a flat slug"],"exampleFix":"// before\ngetStateFilePath(\"team/state.json\");\n// after\ngetStateFilePath(path.basename(\"team/state.json\")); // \"state.json\"","handlingStrategy":"validation","validationCode":"import { basename } from 'node:path';\nfileName = basename(fileName.replace(/\\\\/g, '/'));","typeGuard":"function isBareFileName(v: string): boolean { return !/[\\\\/]/.test(v); }","tryCatchPattern":null,"preventionTips":["Always basename() paths before passing as fileName","Encode hierarchy with hyphens, not slashes"],"tags":["path-validation","security","state","mcp"],"backgroundTag":"path-separator-in-segment","analyzedSha":"3ad79a8a6fe6e95fdbb8c00e40716fffe4011ce2","analyzedAt":"2026-08-27T22:18:39.783Z","schemaVersion":2},"datasetVersion":"2026-08-28T00:17:15.603Z"}