Skip to content

security(providers): codex vendor binary resolver lacks hash/signature pinning — LPE via writable vendor dir #1245

@shaun0927

Description

@shaun0927

Summary

packages/providers/src/codex/binary-resolver.ts resolves the Codex CLI binary from three sources (env, config, ~/.archon/vendor/codex/<binary>) and passes it to the SDK's codexPathOverride. The only guard is fileExists() — there is no hash verification, signature check, or directory permission validation.

If ~/.archon/vendor/codex/ is world-writable or writable by another local user (shared workstation, misconfigured container image, prior malware dropping a file), the next Archon workflow that uses the Codex provider will execute the attacker's binary with the user's full privileges.

Evidence

Verified on dev branch (3dedc22), file packages/providers/src/codex/binary-resolver.ts:

# grep for any integrity check
$ grep -n "hash\|sha\|checksum\|chmod\|permission\|sign" binary-resolver.ts
61: 'Please verify the path...'   # error message text, not a check
73: 'Please verify the path...'   # same

# only guard is fileExists
$ grep -n "fileExists" binary-resolver.ts
58:    if (!fileExists(envPath)) {          # source 1: env
70:    if (!fileExists(configCodexBinaryPath)) {  # source 2: config
86:    if (fileExists(vendorBinaryPath)) {   # source 3: vendor dir — exists = trusted

No hash, no signature, no stat() for permissions, no file-mode check — fileExists() is the entire trust decision.

Impact

  • Local Privilege Escalation gateway: any process that can write to ~/.archon/vendor/codex/ can hijack the next Codex invocation
  • Silent execution: no log entry distinguishes "expected binary" from "replaced binary"
  • Supply chain adjacent: if Archon ever ships a codex vendor binary in releases or install scripts, there's no integrity anchor to detect tampering

Threat Model Context

Archon is primarily a single-user local CLI, so this requires a co-resident attacker or misconfigured permissions. The risk increases for:

  • archon serve deployments on shared infrastructure
  • Docker images where ~/.archon/ is a shared volume
  • CI runners where the vendor dir persists across jobs

Suggested Fix

Minimum viable (low effort):

  1. On first successful resolution, compute SHA-256 of the binary and write it to ~/.archon/vendor/codex/SHA256SUMS
  2. On subsequent loads, verify the hash matches — reject with a clear error if mismatched
  3. Log codex.binary_hash_verified / codex.binary_hash_mismatch for auditability
import { createHash } from 'node:crypto';
import { readFileSync, writeFileSync } from 'node:fs';

function verifyOrPin(binaryPath: string, sumPath: string): void {
  const actual = createHash('sha256').update(readFileSync(binaryPath)).digest('hex');
  if (existsSync(sumPath)) {
    const expected = readFileSync(sumPath, 'utf-8').trim();
    if (actual !== expected) throw new Error(
      `Codex binary hash mismatch:\n  expected: ${expected}\n  actual:   ${actual}\n` +
      `The binary at ${binaryPath} may have been tampered with.`
    );
  } else {
    writeFileSync(sumPath, actual + '\n', { mode: 0o644 });
    getLog().info({ binaryPath, hash: actual }, 'codex.binary_hash_pinned');
  }
}

Better (medium effort):

  • Also stat() the vendor directory and warn if group/other-writable
  • Check binary is owned by current uid

Environment

  • Archon dev branch (3dedc22)
  • File: packages/providers/src/codex/binary-resolver.ts (107 lines, no integrity code)

Metadata

Metadata

Assignees

No one assigned

    Labels

    P2Medium priority - Backlog, when time permitsarea: clientsAI assistant clientsbugSomething is brokeneffort/lowSingle file or function, one responsibility, isolated changesecuritySecurity vulnerabilities and hardening

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions