Yeachan-Heo/oh-my-codex · error · Error

invalid auth slot name: use 1-64 letters, numbers, '.', '_'

Error message

invalid auth slot name: use 1-64 letters, numbers, '.', '_' or '-' and start with a letter or number

What it means

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.

Source

Thrown at src/auth/paths.ts:20

import { basename, join, resolve } from "path";
import { lstat, mkdir, stat } from "fs/promises";
import { resolveCodexHomeForLaunch } from "../cli/codex-home.js";

export const AUTH_DIR_MODE = 0o700;
export const AUTH_FILE_MODE = 0o600;

export function resolveOmxAuthDir(home = homedir()): string {
  return join(home, ".omx", "auth");
}

export function resolveAuthMetadataPath(home = homedir()): string {
  return join(resolveOmxAuthDir(home), "slots.json");
}

export function validateSlotName(slot: string): string {
  const trimmed = slot.trim();
  if (!/^[A-Za-z0-9][A-Za-z0-9._-]{0,63}$/.test(trimmed)) {
    throw new Error(
      "invalid auth slot name: use 1-64 letters, numbers, '.', '_' or '-' and start with a letter or number",
    );
  }
  if (trimmed === "." || trimmed === ".." || basename(trimmed) !== trimmed) {
    throw new Error("invalid auth slot name: path traversal is not allowed");
  }
  return trimmed;
}

export function resolveSlotPath(slot: string, home = homedir()): string {
  const safeSlot = validateSlotName(slot);
  const authDir = resolveOmxAuthDir(home);
  const candidate = resolve(authDir, `${safeSlot}.json`);
  const expected = join(resolve(authDir), `${safeSlot}.json`);
  if (candidate !== expected) {
    throw new Error("invalid auth slot path");
  }
  return candidate;

View on GitHub (pinned to 3ad79a8a6f)

Solutions

  1. Strip the leading slash or drive prefix and pass only the relative member path
  2. When creating archives, never use absolute source paths — tar without -P, and relative dirs for zip
  3. If inspecting an untrusted archive, reject archives containing absolute entry names before any extraction
  4. Keep config for 'where to install' separate from 'which member to extract' so absolute install paths never reach the member-path API

Example fix

// before
await writeSelectedNativeArchiveMember(archive, '/bin/mytool', dest); // throws archive_path_absolute

// after
const member = '/bin/mytool'.replace(/^\/+|[A-Za-z]:/, '');
await writeSelectedNativeArchiveMember(archive, member, dest);
Defensive patterns

Strategy: validation

Validate before calling

if (memberPath.startsWith('/') || /^[A-Za-z]:/.test(memberPath)) throw new Error('member path must be relative');

Type guard

const isRelativeMemberPath = (p: string): boolean => !p.startsWith('/') && !/^[A-Za-z]:/.test(p);

Prevention

When it happens

Trigger: 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).

Common situations: 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.

Related errors


AI-assisted analysis of Yeachan-Heo/oh-my-codex@3ad79a8a6f (2026-08-27). Data as JSON: /api/errors/521e368de0f4dbca. Report an issue: GitHub.