Skip to content

Security: icesixgod/codex-trajectory

Security

SECURITY.md

Security Policy

Supported versions

Security fixes are provided for the latest released version.

Reporting a vulnerability

Do not open a public issue for a vulnerability that may expose local task content or escape the authorized Codex session directories. Use GitHub's private vulnerability reporting for this repository. Include the affected version, platform, reproduction steps, and expected impact. You should receive an acknowledgement within seven days.

Security model

All user-facing trajectory and app-only live-update tools are read-only and accept session identifiers rather than file paths. They resolve each configured root, then use descriptor-checked reads for regular, single-link descendants below the active or archived root; descendant symbolic links/reparse points, file hardlinks, non-regular sources, root escapes, ambiguous ID prefixes, and inconsistent paginated lineage boundaries fail closed. Plain and Zstandard-compressed logical rollouts share byte, line, decompression-window, discovery, and lineage bounds. Session and JSON-RPC inputs have byte, collection, nesting, and numeric conversion bounds; encoded JSON-RPC responses are capped at 8 MiB; public filesystem failures do not include local paths. Two local actions are app-private and hidden from the model: the setting tool writes atomic mode-0600 CDP settings/heartbeat files below the plugin-owned CODEX_HOME/codex-trajectory directory, while the stop tool can pause the exact bounded task's active Goal and interrupt its active turn only after the persisted CDP opt-in is enabled. Neither writes task history; settings and recovery are serialized across processes and refuse symbolic-link destinations.

On POSIX, every path component is opened descriptor-relative with O_NOFOLLOW. Windows uses reparse-point rejection, pre/post component identities, regular/single-link fstat, and final HANDLE-path containment because Python does not expose an equivalent directory-relative no-reparse primitive. It does not yet use native NtCreateFile with a RootDirectory handle, so a theoretical same-user component-replacement race within an already authorized session root remains; the final HANDLE check still rejects a source whose opened object resolves outside that root.

Safe summary mode excludes raw inputs, outputs, metadata, base instructions, and encrypted reasoning. Full details remain bounded and still exclude base instructions and encrypted reasoning, but the plugin cannot protect content after a user explicitly loads those details into a Codex conversation. The viewer uses a restrictive Content Security Policy, escapes log-derived markup, and times out unanswered host requests.

The experimental CDP direct-stop integration is disabled by default, allows only loopback HTTP/WebSocket endpoints, refuses redirects and cross-port debugger URLs, bounds HTTP/WebSocket messages and target counts, and carries one absolute deadline across every operation. Disabled status and stop paths do not probe the port. Settings, heartbeat, process-lock, and cross-process control-lock reads reject symbolic links, hardlinks, non-regular files, and oversized content. Watcher replacement verifies the lock owner, uses a process handle on Windows rather than a console signal, and restores a temporary setting only when its file revision still matches.

The token-bearing in-app Browser shortcut is available only on macOS and Windows and only after exact TCP peer process authentication. The watcher pins the Codex desktop ancestor that launched the plugin; on Windows it selects the outer qualifying packaged desktop ancestor when both codex.exe and ChatGPT.exe are present so the parent-owned CDP listener remains inside the accepted ancestry. It rejects PID reuse, resolves the owner of every connected CDP TCP flow before its WebSocket handshake, and accepts only that authenticated host or a verified descendant. macOS additionally requires the OpenAI-signed com.openai.codex app; Windows requires the same authenticated OpenAI.Codex package family across the accepted ancestry. Failure, ambiguity, an unsupported platform, or host exit prevents the local viewer URL/token from leaving the watcher and prevents token-bearing injection. Complete shell-target authentication failure emits only the fixed public error category. The fixed legacy-removal script and the independently bounded direct-stop path do not carry trajectory bearer tokens. A runtime revision causes an installed update to replace older watchers.

The plugin bundles no lifecycle command hook. MCP startup restores the same persisted opt-in through the existing watcher lock and windowless supervisor, which avoids creating a Windows cmd.exe terminal tab. It does not enable CDP, contain a viewer token, emit task data, or weaken the per-connection authentication above. Failure is silent and fail-closed while the read-only MCP server remains available.

The app-private direct-stop tool rejects arbitrary prompts and accepts only bounded identifiers, a fixed source enum, integer threshold, and locale; it permits an omitted turn candidate only so it can bind the current active turn itself. It pauses an active Goal before calling turn/interrupt, rebinds a rejected stale turn at most once, and never reads or writes composer content or attachments. The standard Apps component does not fall back to sendFollowUpMessage; disconnected controls fail closed. CDP itself grants broad control over the application renderer to any local process that can reach the debugging port, and direct stop necessarily trusts the explicitly configured local endpoint. Users must never bind or forward that port beyond loopback.

There aren't any published security advisories