# Security Model ## Trust boundaries 1. **Workspace root** is the smallest authorization boundary. One bridge serves exactly one workspace; every token is bound to `workspace_id`; a token for project A returns 403 on project B's bridge. 2. **Workspace content is untrusted.** README, comments, diffs may contain prompt injection. Every MCP tool description carries an explicit warning and tools never grant capabilities based on file content. 3. **The model never sees long-lived credentials.** Computer Use only ever handles the one-time pairing code. Access/refresh tokens travel only inside the OAuth redirect/token endpoints between ChatGPT's client and the bridge. ## Threat model → mitigations | Threat | Mitigation | | --- | --- | | MCP URL leaks | URL alone is useless: every `/mcp` request requires a valid bearer token (401 without, 403 wrong workspace) | | Pairing code brute force | 8 chars from a 31-char CSPRNG alphabet (~40 bits), 5 attempts per session, per-IP rate limit (10/min), 5-minute TTL, one-time use, session destroyed on limit | | OAuth CSRF | `state` round-tripped verbatim; authorization requests are server-side records keyed by random ids | | Code interception | PKCE S256 mandatory (plain rejected); authorization codes are one-time, 5-minute TTL, bound to client + redirect URI | | Token theft | Opaque high-entropy tokens; stored only as SHA-256 hashes; access tokens live 1 h; refresh tokens rotate on every use (replay of the old one fails); revocation endpoint + `c2c unpair` | | Workspace traversal | `realpath` canonicalization of the deepest existing ancestor; containment check against the canonical root; case-insensitive comparison on macOS/Windows; rejects `..`, absolute escapes, backslash tricks, null bytes | | Symlink escape | Canonicalization resolves symlinks before the containment check (file and directory symlinks both covered by tests) | | Sensitive files | Deny-by-default patterns (.env*, keys, SSH, cloud creds, keychains…) enforced at resolve time — reads, listings, and search all pass through the same gate; `git diff` adds pathspec excludes; `.env.example` allowed | | Oversized file / diff DoS | read_file caps lines and bytes per response; git_diff paginates by byte offset with hard caps; search caps matches and file sizes | | Tunnel exposure | Bridge binds 127.0.0.1 only (refuses 0.0.0.0); the only public surface is HTTPS via the tunnel, protected by OAuth; `/health` reveals only a salted workspace hash | | Admin API abuse | Loopback-only + random admin token (0600 runtime file) + requests with proxy headers (`cf-connecting-ip`, `x-forwarded-for`) rejected; unauthenticated probes get 404 | | Log credential leakage | Logger redacts token prefixes, bearer headers, token-like parameters, and pairing-code-shaped strings before writing | | Execution output leak | Codex may nominate test/build/lint logs; a local sanitizer redacts tokens, pairing-code-shaped strings and home paths, truncates size, and refuses private-key blocks entirely. Restricted items are listed without a body. ChatGPT still cannot run commands. | | Checkpoint / resume dump | Session checkpoints store short protocol fields only (capped). Resume uses the existing chat or HANDOFF — no new protocol state, no log paste, no re-pairing. | ## Token & scope design Scopes: `workspace.read`, `workspace.search`, `git.read`, `execution.read`, `offline_access`. Tools enforce scopes individually (`INSUFFICIENT_SCOPE`). Access tokens: 1 hour. Refresh tokens: 30 days, rotated. All tokens bound to `workspace_id` and `client_id`. ## Storage State lives under the OS-convention app dir (`~/Library/Application Support/codex-with-chatgpt` on macOS), directories 0700, files 0600. Named-hostname preference and tunnel metadata live there too (`tunnels/.json`) — never in the project. Only SHA-256 hashes of tokens are persisted — a stolen state file does not yield usable bearer tokens. **V1 limitation**: client registrations and token hashes are file-based rather than OS-keychain-based. Raw tokens are never written anywhere. Keychain integration is a V2 item. ## What ChatGPT can never do (V1) Write files, delete files, run shell commands, commit, install packages — these tools do not exist on the server, so no prompt injection, scope bug, or UI confusion can enable them.