{"record":{"id":"fa8adcf72c38fa84","repo":"Yeachan-Heo/oh-my-codex","slug":"invalid-detached-leader-parent-environment","errorCode":null,"errorMessage":"invalid detached leader parent environment","messagePattern":"invalid detached leader parent environment","errorType":"exception","errorClass":"Error","httpStatus":null,"severity":"error","filePath":"src/cli/index.ts","lineNumber":6963,"sourceCode":"\nexport function decodeDetachedLeaderPayload(encoded: string | undefined): DetachedLeaderPayload {\n  if (!encoded || !/^[A-Za-z0-9_-]+$/.test(encoded)) throw new Error(\"invalid detached leader payload\");\n  let value: unknown;\n  try { value = JSON.parse(Buffer.from(encoded, \"base64url\").toString(\"utf8\")); } catch { throw new Error(\"invalid detached leader payload\"); }\n  if (!value || typeof value !== \"object\") throw new Error(\"invalid detached leader payload\");\n  const payload = value as Record<string, unknown>;\n  const options = payload.preLaunchOptions;\n  if (typeof payload.cwd !== \"string\" || typeof payload.sessionName !== \"string\" || typeof payload.sessionId !== \"string\" ||\n    typeof payload.codexCmd !== \"string\" || !options || typeof options !== \"object\" ||\n    typeof (options as Record<string, unknown>).enableNotifyFallbackAuthority !== \"boolean\" ||\n    typeof (options as Record<string, unknown>).worktreeDirty !== \"boolean\" ||\n    typeof (options as Record<string, unknown>).shouldAttach !== \"boolean\") throw new Error(\"invalid detached leader payload\");\n  const notifyTempContract = (options as Record<string, unknown>).notifyTempContract;\n  if (notifyTempContract !== undefined && (!notifyTempContract || typeof notifyTempContract !== \"object\")) throw new Error(\"invalid detached leader payload\");\n  const parentEnv = payload.parentEnv;\n  if (parentEnv !== undefined && (!parentEnv || typeof parentEnv !== \"object\" ||\n    Object.entries(parentEnv).some(([key, value]) => !SHELL_ENV_NAME_PATTERN.test(key) || DETACHED_SESSION_PANE_ENV_KEYS.has(key) || typeof value !== \"string\" || value.includes(\"\\0\")))) {\n    throw new Error(\"invalid detached leader parent environment\");\n  }\n  return {\n    cwd: payload.cwd, sessionName: payload.sessionName, sessionId: payload.sessionId, codexCmd: payload.codexCmd,\n    ...(typeof payload.codexHomeOverride === \"string\" ? { codexHomeOverride: payload.codexHomeOverride } : {}),\n    ...(typeof payload.projectLocalCodexHomeForCleanup === \"string\" ? { projectLocalCodexHomeForCleanup: payload.projectLocalCodexHomeForCleanup } : {}),\n    ...(typeof payload.runtimeCodexHomeForCleanup === \"string\" ? { runtimeCodexHomeForCleanup: payload.runtimeCodexHomeForCleanup } : {}),\n    ...(parentEnv ? { parentEnv: parentEnv as Record<string, string> } : {}),\n    ...(typeof payload.readyPath === \"string\" ? { readyPath: payload.readyPath } : {}),\n    preLaunchOptions: {\n      ...(notifyTempContract ? { notifyTempContract: notifyTempContract as NotifyTempContract } : {}),\n      enableNotifyFallbackAuthority: (options as DetachedLeaderPreLaunchOptions).enableNotifyFallbackAuthority,\n      worktreeDirty: (options as DetachedLeaderPreLaunchOptions).worktreeDirty,\n      shouldAttach: (options as DetachedLeaderPreLaunchOptions).shouldAttach,\n    },\n  };\n}\n\nfunction parseDetachedHudAuthorityProof(value: unknown): DetachedHudAuthority | undefined {","sourceCodeStart":6945,"sourceCodeEnd":6981,"githubUrl":"https://github.com/Yeachan-Heo/oh-my-codex/blob/3ad79a8a6fe6e95fdbb8c00e40716fffe4011ce2/src/cli/index.ts#L6945-L6981","documentation":"Thrown when the optional parentEnv map contains an invalid entry: a key failing SHELL_ENV_NAME_PATTERN, a key in the forbidden DETACHED_SESSION_PANE_ENV_KEYS set, a non-string value, or a value containing a NUL byte.","triggerScenarios":"payload.parentEnv includes keys with invalid shell names (e.g. containing '=' or leading digits depending on the pattern), reserved pane keys like TMUX_PANE, numeric values, or embedded '\\0'.","commonSituations":"Forwarding the entire process.env verbatim into the detached payload, which picks up odd variables set by CI systems or shells; NUL bytes appear when env values were built from binary buffers.","solutions":["Filter parentEnv through the same SHELL_ENV_NAME_PATTERN and drop DETACHED_SESSION_PANE_ENV_KEYS before encoding","Coerce all values to strings and strip/reject NUL bytes","Pass only a curated allowlist of env vars instead of the full environment"],"exampleFix":"// before\nparentEnv: { ...process.env }\n// after\nparentEnv: Object.fromEntries(Object.entries(process.env).filter(([k, v]) => SHELL_ENV_NAME_PATTERN.test(k) && !DETACHED_SESSION_PANE_ENV_KEYS.has(k) && typeof v === 'string' && !v.includes('\\0')))","handlingStrategy":"validation","validationCode":"const env: Record<string,string> = {};\nfor (const [k, v] of Object.entries(process.env)) {\n  if (!/^[A-Za-z_][A-Za-z0-9_]*$/.test(k)) continue;\n  if (['TMUX_PANE','TMUX_SESSION'].includes(k)) continue;\n  if (typeof v !== 'string' || v.includes('\\0')) continue;\n  env[k] = v;\n}","typeGuard":"function isSafeParentEnv(v: unknown): v is Record<string, string> {\n  return !!v && typeof v === 'object' && Object.entries(v).every(([k, val]) =>\n    typeof val === 'string' && !val.includes('\\0') && /^[A-Za-z_][A-Za-z0-9_]*$/.test(k));\n}","tryCatchPattern":null,"preventionTips":["Never forward process.env wholesale into the payload","Maintain an explicit env allowlist per session","Strip NUL bytes at the source when building env from buffers"],"tags":["environment-variables","validation","security"],"backgroundTag":"env-var-sanitization-failed","analyzedSha":"3ad79a8a6fe6e95fdbb8c00e40716fffe4011ce2","analyzedAt":"2026-08-27T22:18:39.783Z","schemaVersion":2},"datasetVersion":"2026-08-28T00:17:15.603Z"}