{"record":{"id":"521e368de0f4dbca","repo":"Yeachan-Heo/oh-my-codex","slug":"invalid-auth-slot-name-use-1-64-letters-numbers","errorCode":null,"errorMessage":"invalid auth slot name: use 1-64 letters, numbers, '.', '_' or '-' and start with a letter or number","messagePattern":"invalid auth slot name: use 1-64 letters, numbers, '\\.', '_' or '-' and start with a letter or number","errorType":"validation","errorClass":"Error","httpStatus":null,"severity":"error","filePath":"src/auth/paths.ts","lineNumber":20,"sourceCode":"import { basename, join, resolve } from \"path\";\nimport { lstat, mkdir, stat } from \"fs/promises\";\nimport { resolveCodexHomeForLaunch } from \"../cli/codex-home.js\";\n\nexport const AUTH_DIR_MODE = 0o700;\nexport const AUTH_FILE_MODE = 0o600;\n\nexport function resolveOmxAuthDir(home = homedir()): string {\n  return join(home, \".omx\", \"auth\");\n}\n\nexport function resolveAuthMetadataPath(home = homedir()): string {\n  return join(resolveOmxAuthDir(home), \"slots.json\");\n}\n\nexport function validateSlotName(slot: string): string {\n  const trimmed = slot.trim();\n  if (!/^[A-Za-z0-9][A-Za-z0-9._-]{0,63}$/.test(trimmed)) {\n    throw new Error(\n      \"invalid auth slot name: use 1-64 letters, numbers, '.', '_' or '-' and start with a letter or number\",\n    );\n  }\n  if (trimmed === \".\" || trimmed === \"..\" || basename(trimmed) !== trimmed) {\n    throw new Error(\"invalid auth slot name: path traversal is not allowed\");\n  }\n  return trimmed;\n}\n\nexport function resolveSlotPath(slot: string, home = homedir()): string {\n  const safeSlot = validateSlotName(slot);\n  const authDir = resolveOmxAuthDir(home);\n  const candidate = resolve(authDir, `${safeSlot}.json`);\n  const expected = join(resolve(authDir), `${safeSlot}.json`);\n  if (candidate !== expected) {\n    throw new Error(\"invalid auth slot path\");\n  }\n  return candidate;","sourceCodeStart":2,"sourceCodeEnd":38,"githubUrl":"https://github.com/Yeachan-Heo/oh-my-codex/blob/3ad79a8a6fe6e95fdbb8c00e40716fffe4011ce2/src/auth/paths.ts#L2-L38","documentation":"Thrown when the path is absolute — starting with '/' or a Windows drive letter pattern like 'C:'. Archive member paths must be relative so extraction stays inside the destination directory; absolute paths would escape it and are also the canonical zip-slip attack vector.","triggerScenarios":"Passing '/usr/local/bin/mytool' or 'C:\\\\tools\\\\mytool.exe' as a memberPath, or processing an archive whose entries are stored with absolute names (a known hazard with some old tar files).","commonSituations":"Reusing filesystem paths from config (installDir + binaryName) as archive member paths; archives created with 'tar -P' or malformed tools that preserve leading slashes; untrusted archives crafted for path traversal.","solutions":["Strip the leading slash or drive prefix and pass only the relative member path","When creating archives, never use absolute source paths — tar without -P, and relative dirs for zip","If inspecting an untrusted archive, reject archives containing absolute entry names before any extraction","Keep config for 'where to install' separate from 'which member to extract' so absolute install paths never reach the member-path API"],"exampleFix":"// before\nawait writeSelectedNativeArchiveMember(archive, '/bin/mytool', dest); // throws archive_path_absolute\n\n// after\nconst member = '/bin/mytool'.replace(/^\\/+|[A-Za-z]:/, '');\nawait writeSelectedNativeArchiveMember(archive, member, dest);","handlingStrategy":"validation","validationCode":"if (memberPath.startsWith('/') || /^[A-Za-z]:/.test(memberPath)) throw new Error('member path must be relative');","typeGuard":"const isRelativeMemberPath = (p: string): boolean => !p.startsWith('/') && !/^[A-Za-z]:/.test(p);","tryCatchPattern":null,"preventionTips":["Keep install destinations and archive member paths in separate config fields","Strip leading slashes/drive prefixes from user-supplied member paths","Reject archives whose internal entries have absolute names when handling untrusted input"],"tags":["native-assets","path","security","zip-slip"],"backgroundTag":"absolute-path-rejected","analyzedSha":"3ad79a8a6fe6e95fdbb8c00e40716fffe4011ce2","analyzedAt":"2026-08-27T22:18:39.783Z","schemaVersion":2},"datasetVersion":"2026-08-28T00:17:15.603Z"}