{"record":{"id":"3d06f5e3e9f8db7d","repo":"paperclipai/paperclip","slug":"codex-auth-cache-label-is-a-relative-path-segm","errorCode":null,"errorMessage":"codex auth cache: ${label} is a relative path segment","messagePattern":"codex auth cache: (.+?) is a relative path segment","errorType":"exception","errorClass":"Error","httpStatus":null,"severity":"error","filePath":"packages/adapters/codex-local/src/server/codex-auth-cache.ts","lineNumber":75,"sourceCode":"  const raw = env[CODEX_AUTH_CACHE_OFF_SWITCH_ENV];\n  if (typeof raw !== \"string\") return true;\n  return !FALSY_ENV_RE.test(raw.trim());\n}\n\n/**\n * Sanitizes one raw value to a single safe path segment. Rejects an empty value,\n * a relative segment (`.` or `..`), a path separator (`/` or `\\`), and a NUL\n * byte, so the value can never become a path traversal. Returns the trimmed,\n * safe segment. The `label` names the value in the error message. (Security\n * condition 3.)\n */\nfunction toSafePathSegment(value: string, label: string): string {\n  const trimmed = typeof value === \"string\" ? value.trim() : \"\";\n  if (trimmed.length === 0) {\n    throw new Error(`codex auth cache: ${label} is empty`);\n  }\n  if (trimmed === \".\" || trimmed === \"..\") {\n    throw new Error(`codex auth cache: ${label} is a relative path segment`);\n  }\n  if (trimmed.includes(\"/\") || trimmed.includes(\"\\\\\") || trimmed.includes(\"\\0\")) {\n    throw new Error(`codex auth cache: ${label} contains a path separator`);\n  }\n  // Defense in depth: a safe segment is exactly its own basename. Anything else\n  // carries a separator or a relative segment the checks above must have caught.\n  if (path.basename(trimmed) !== trimmed) {\n    throw new Error(`codex auth cache: ${label} is not a single path segment`);\n  }\n  return trimmed;\n}\n\n/**\n * Sanitizes an `account_id` to one safe path segment. Rejects an empty value, a\n * relative segment (`.` or `..`), a path separator, and a NUL byte, so a raw\n * `account_id` can never become a path traversal. Returns the trimmed, safe\n * segment. (Security condition 3.)\n */","sourceCodeStart":57,"sourceCodeEnd":93,"githubUrl":"https://github.com/paperclipai/paperclip/blob/67001ec6eb96ae601aa27bc91d9b2415d665334a/packages/adapters/codex-local/src/server/codex-auth-cache.ts#L57-L93","documentation":"Thrown by toSafePathSegment when a value used as a path segment is exactly \".\" or \"..\" after trimming. This is the relative-segment guard in the same path-traversal defense chain (Security condition 3) that protects the companies/<companyId>/.../<accountId>/auth.json cache layout from escaping its directory.","triggerScenarios":"toCacheKey(\".\") or toCacheKey(\"..\") is called (an account_id of \".\" or \"..\"), or resolveCodexAuthCacheDir is called with companyId equal to \".\" or \"..\".","commonSituations":"Corrupted or hand-edited auth.json where account_id was set to a dot segment; a test fixture using \".\" as a placeholder id; malformed upstream data feeding the cache key.","solutions":["Re-generate the Codex auth.json so account_id is a real opaque id (re-run codex login).","Validate/sanitize the account_id upstream before it reaches the cache; reject \".\" and \"..\" explicitly.","If the value is genuinely unusable, disable the cache (PAPERCLIP_CODEX_AUTH_CACHE=0) until the credential is replaced."],"exampleFix":null,"handlingStrategy":"validation","validationCode":"function isSafePathSegment(value: unknown): value is string {\n  if (typeof value !== \"string\") return false;\n  const t = value.trim();\n  if (t.length === 0 || t === \".\" || t === \"..\") return false;\n  if (t.includes(\"/\") || t.includes(\"\\\\\") || t.includes(\"\\0\")) return false;\n  return path.basename(t) === t;\n}","typeGuard":"function isRelativeSegment(value: unknown): boolean {\n  return typeof value === \"string\" && (value.trim() === \".\" || value.trim() === \"..\");\n}","tryCatchPattern":"try {\n  const key = toCacheKey(accountId);\n} catch (e) {\n  if (e instanceof Error && /is a relative path segment/.test(e.message)) {\n    // treat as corrupt credential; refuse to cache, log a fixed line\n  } else throw e;\n}","preventionTips":["Sanitize upstream data sources that populate account_id; reject dot-segments early.","Add a schema check on auth.json so account_id is an opaque id before caching.","Keep PAPERCLIP_CODEX_AUTH_CACHE=0 as an escape hatch for untrusted credential sources."],"tags":["codex","auth-cache","security","path-traversal","validation"],"backgroundTag":null,"analyzedSha":"67001ec6eb96ae601aa27bc91d9b2415d665334a","analyzedAt":"2026-08-12T12:05:45.408Z","schemaVersion":2},"datasetVersion":"2026-08-12T18:17:37.767Z"}