Toggle Theme

Codex Security Boundaries: Permissions, Sandboxes, and Secret-Leak Controls

Easton editorial illustration: terminal-to-editor relay

"OpenAI Codex security documentation describes sandbox modes, approval policy, Cloud setup and agent phases, network proxy behavior, and the secrets lifecycle; this is the core source for the boundary model in this article."

Your project root has a .env file with a database password and third-party API keys. You open Codex and ask it to refactor some test code. The permission selector shows three choices: read-only, workspace-write, and danger-full-access. Which one should you choose?

After you pick workspace-write, Codex asks to run npm install. You approve it. Did you check whether the postinstall script in package.json can read environment variables or call a service you did not expect?

Then you move Codex into GitHub Actions for automated PR review. The workflow includes OPENAI_API_KEY: ${{ secrets.OPENAI_API_KEY }}. GitHub Secrets feels safe, but every test script, third-party action, and dependency lifecycle hook in that same job may be able to read that key.

Three boundaries answer these questions directly: instructions are not permissions, approvals are not isolation, and a sandbox is not an audit log. The rest of this guide is a minimum-permission checklist for local, Cloud, and CI Codex usage.

Permissions are not enforced by words: understand sandbox, approval, and permission profile boundaries

Many teams assume that writing “do not read .env” in AGENTS.md prevents Codex from accessing sensitive files. That is not permission control. It is project guidance. The actual security boundary comes from three layers: the sandbox constrains what spawned commands can touch, the approval policy decides when Codex has to stop and ask, and the permission profile is where access control is enforced.

Codex security is not one layer. The sandbox determines what git, package managers, test runners, and other spawned commands can access. Approval policy determines whether Codex must ask before sensitive actions. The permission profile is the layer where rules such as "**/*.env" = "deny" can actually be expressed. The three layers together are the practical permission model.

Sandbox modes: read-only, workspace-write, and danger-full-access

Sandbox modeDefinitionGood fitRisk
read-onlyAllows file reads only; no writes or command executionCode review, architecture mapping, documentation drafting, read-only CI checksIt does not protect every secret on the runner; it still runs on that runner
workspace-writeAllows writes inside the active workspace; command network access is off by defaultEveryday local development, code changes, testsFiles under the workspace may still be readable, including .env, unless you add deny rules
danger-full-accessRemoves filesystem and network boundariesIsolated CI runners, containers, fully controlled test environmentsIt may access ~/.ssh, /tmp, environment variables, and local services; do not use it as a daily default

The sandbox constrains spawned commands, not just Codex built-in file operations. git, package managers, and test runners inherit the sandbox boundary. Platform prerequisites matter: Linux and WSL2 rely on bubblewrap or user namespaces, macOS relies on the system sandbox, and Windows support depends on the available platform mechanism.

Approval policy decides when Codex stops to ask:

Approval policyDefinitionCommon combinationPractical permission level
on-requestPause before selected actions and ask for approvalworkspace-write + on-request for local automationLower risk; the user confirms writes, commands, and network requests
neverDo not ask interactively; run directlyread-only + never for CI checks; danger-full-access + never only in controlled environmentsHigher risk; workspace-write + never is especially risky without a permission profile

Lower-risk combinations are read-only + never for CI-style read-only checks and workspace-write + on-request for local development. The high-risk combination is danger-full-access + never, and it belongs only in controlled environments.

Permission profile configuration: filesystem deny and network rules

A permission profile is enforced access control, not verbal guidance. Filesystem permissions support read, write, and deny. More specific rules override broader ones, and deny takes priority.

Example .env deny glob:

{
  "filesystem": {
    "rules": {
      "**/*.env": "deny",
      "**/.env.local": "deny",
      "**/secrets/**": "deny",
      "workspace/**": "write"
    }
  }
}

This makes .env files unreadable under workspace-write even when the broader workspace is writable. The specific deny rule wins over the broad write rule.

A network profile can set enabled = true and then apply domain allow and deny rules, with deny taking priority. Local and private networks are guarded by default; explicitly allowing localhost or a Docker socket is an exception. A Docker socket is a local escape hatch because it can reach local services, containers, and networks. Enable it only when the task truly needs it.

A network proxy constrains command network access after network access has been enabled. It does not grant network access by itself. A global * allow rule is broad network access and should be treated carefully.

Local minimum-permission checklist: from read-only review to full access

Local development is not safer just because permissions are broader. Approval prompts can catch some actions, but the sandbox and permission profile are the isolation layers. Start narrow, then widen only when the task requires it.

Permission decision table: when to use read-only and when to use workspace-write

ScenarioSandbox modeApproval policyPermission profileRisk level
Code review or architecture mappingread-onlyneverNo extra config neededLow
Everyday development or code editsworkspace-writeon-requestDeny .envMedium-low
Running tests or installing dependenciesworkspace-writeon-requestDeny .env, review scriptsMedium
Read-only CI checkread-onlyneverNo extra config neededLow
CI job that must write filesworkspace-writeneverAdd deny rules and use an isolated runnerMedium-high
Task truly needs full accessdanger-full-accessneverControlled environment onlyHigh

Steps for protecting .env and secret-bearing files:

  1. Identify the permission profile configuration used by your Codex setup.
  2. Add "**/*.env" = "deny" to the filesystem permission rules.
  3. Confirm that the more specific deny rule overrides broader rules.
  4. Test it by trying to read .env in workspace-write; access should be denied.
  5. Extend the pattern with rules such as "**/.env.local" = "deny" and "**/secrets/**" = "deny".

Do not rely on written guidance alone. AGENTS.md is guidance, not mandatory access control. Codex may follow it, but enforcement belongs in the permission profile.

Dependency install risk: npm postinstall, pip hooks, and Docker socket

Installing dependencies is not just downloading files. postinstall and prepare in npm or pnpm, and package hooks in pip install, can execute during installation. Those scripts may read environment variables, make network requests, or modify system-level files.

Risk checklist:

  • Unknown postinstall scripts may read environment variables: even if .env is denied, a lifecycle script runs in the spawned command environment and may still see environment variables.
  • They may make network requests: downloading extra binaries, reporting telemetry, or connecting to a private registry.
  • They may modify system files: writing global config or changing PATH.

Review recommendations:

  1. Review scripts and sources first: check the scripts field in package.json and package setup hooks such as setup.py.
  2. Use trusted sources: pin dependency versions and avoid accidental upgrades to unknown versions.
  3. Approve inside a controlled boundary: use workspace-write + on-request locally, and approve only after Codex has shown what it wants to install.

A Docker socket is a local escape hatch. Allowing it is an explicit exception because it can reach local services, containers, and networks. Configure it only when the task needs it; do not leave it enabled by default.

Cloud boundaries: setup vs agent phase and the secrets lifecycle

A Cloud task does not run on your local machine. It runs in an isolated container managed by OpenAI. The sandbox constrains spawned commands, but secret safety mainly comes from the Cloud secrets lifecycle: secrets are available during setup and removed before the agent phase. The sandbox is not an audit system; security comes from layering.

Cloud container lifecycle:

  1. Create the container
  2. Check out the repo
  3. Run the setup script
  4. Apply network settings
  5. Let the agent run its command loop
  6. Return the answer or diff

setup vs agent phase: secrets are setup-only

PhaseNetwork accessSecrets availableEnvironment variablesDependency install
setup scriptsAvailableAvailablePresent throughoutDependencies can be installed
agent phaseOffline by defaultRemovedPresent throughoutOffline by default

The setup phase can access the network, install dependencies, and read secrets. The agent phase is offline by default, secrets have been removed, and only environment variables remain.

Setup scripts run in a separate Bash session, so export does not automatically carry into the agent phase. If you run export MY_KEY=xxx in setup, the agent phase will not inherit it. Only Cloud secrets and environment settings are passed according to their lifecycle rules.

secrets vs environment variables: the important difference

TypeEncryptedAvailable phaseUse caseLifecycle
environment variablesNo extra encryptionsetup + agentNon-sensitive config, paths, switchesPresent for the container lifecycle
secretsExtra encryptionsetup scripts onlyPrivate repo access and dependency authenticationRemoved before the agent phase

Secrets are setup-only, which makes them a fit for dependency installation and private registry access. The agent phase should not need production secrets. Environment variables persist throughout the task and should be used only for non-sensitive configuration.

Dependency install boundary:

  • The setup phase can install dependencies with network access.
  • The agent phase is offline by default.
  • Setup scripts can access secrets, so unknown scripts may leak them.
  • Review scripts and sources first, use trusted sources, and avoid running unknown setup scripts with sensitive secrets.

Container cache can last up to 12 hours. Changes to setup, maintenance, env, or secrets can invalidate the cache.

CI and GitHub Actions: codex exec, API key mistakes, and official Action safety controls

codex exec defaults to a read-only sandbox, but many workflows still set OPENAI_API_KEY as a job-level environment variable. Test scripts, third-party actions, and dependency lifecycle hooks in that same job may all be able to read the key. The sandbox is not an audit system; minimum permission and key isolation matter more.

API key rule: do not use job-level env

Do not set OPENAI_API_KEY or CODEX_API_KEY as a job-level environment variable in a workflow that checks out or runs repository code. Repository code, tests, dependency lifecycle scripts, and third-party actions in that same job may still reach process environment variables.

Do-not list:

  • Do not set OPENAI_API_KEY as job-level env.
  • Do not set a job-level key in a workflow that checks out or runs repository code.
  • Do not use auth.json or ChatGPT-managed auth for public or open-source repo workflows.
  • Do not run untrusted code in the same process environment as the key.

Safer patterns:

  1. Single-invocation inline injection: set CODEX_API_KEY only for one codex exec invocation.
- name: Run Codex
  run: CODEX_API_KEY=${{ secrets.CODEX_API_KEY }} codex exec "review PR #${{ github.event.number }}"
  1. Official Action proxy: use the proxy provided by openai/codex-action@v1.
- uses: openai/codex-action@v1
  with:
    prompt: "review PR #${{ github.event.number }}"
    sandbox: read-only
    safety-strategy: drop-sudo

In CI, danger-full-access only belongs in an isolated CI runner or container. Do not use it as the default because it can access runner-level resources.

GitHub Action safety checklist: restrict triggers, protect keys, rotate keys

Official Action parameters:

ParameterDefaultMeaning
safety-strategydrop-sudoRemoves sudo privileges
sandbox-Chooses read-only, workspace-write, or danger-full-access
allow-usersUsers with write accessLimits who can trigger the Action
allow-bots-Controls whether bots may trigger it

read-only does not mean every runner secret is protected. The Action still runs on the runner; it only constrains filesystem access. drop-sudo removes sudo privileges. sandbox should be the narrowest mode that can complete the task.

GitHub Action checklist:

  • Restrict trigger users: use allow-users for specific users, and enable allow-bots only deliberately.
  • Sanitize PR, issue, and prompt input: treat untrusted text as prompt-injection input, not as a command.
  • Protect API keys: do not use job-level env; use single-invocation inline injection or the proxy.
  • Run Codex as the final step: reduce the time window in which the key exists.
  • Rotate the key if leakage is suspected: stop the exposure before investigating.

Treat PR, issue, and prompt text as untrusted input. Do not pass raw PR comments or issue bodies directly into a Codex prompt without a boundary.

Secret leak response: the first step after a suspected leak

If a leak is suspected, the first step is not investigation. It is key rotation. Stop the exposure first, then audit. A sandbox can constrain spawned commands, but it cannot prevent Codex from outputting a key into logs, a PR comment, or an answer.

Secret leak response steps: rotate, audit, revoke

Step one: rotate the key. Do not wait for root-cause analysis before invalidating the old key.

Follow-up steps:

  1. Audit logs: check key usage records for abnormal calls.
  2. Revoke tokens: make sure the old key is fully invalid and there are no surviving sessions.
  3. Trace the source: inspect Codex logs, GitHub Action output, dependency install scripts, and third-party actions.

Prevention:

  • Do not put API keys in frontend code or repositories.
  • Do not set API keys as job-level env in CI.
  • Use permission profile deny rules for .env.
  • Rotate keys regularly, following OWASP Secrets Management Cheat Sheet guidance.

Codex Security boundary: it does not replace SAST or automatically apply patches

Codex Security is an LLM-driven security analysis toolkit that runs in an ephemeral isolated container and returns structured findings with patch suggestions. It can help discover and validate vulnerabilities, but it does not replace SAST or manual security review.

Limitations:

  • It does not replace SAST.
  • It does not replace manual security review.
  • Proposed patches require user review and are not applied automatically.
  • Its role is to assist discovery, validation, and remediation suggestions, not to approve fixes automatically.

Do not treat Codex Security as a universal security scanner. AI-driven analysis can catch some issues, but actual safety still comes from layered control: readable scope, writable scope, network access, secrets, approval, review, and revocation.

Conclusion

Codex security boundaries come from three layers: the sandbox constrains spawned commands, approval policy decides when Codex stops to ask, and the permission profile enforces access control. Together, those layers become practical permissions.

Remember three boundaries:

  • Instructions are not permissions: AGENTS.md is guidance, not mandatory access control. Do not protect .env or API keys with words alone.
  • Approval is not isolation: approval policy can stop some actions, but the sandbox and permission profile provide the real isolation boundary.
  • A sandbox is not an audit log: it constrains spawned commands, but it cannot prevent Codex from printing a key into logs, PR comments, or answers. Real safety comes from layering.

Quick self-check:

  • Did you configure a .env deny glob?
  • Did you avoid job-level API keys in CI?
  • Did you restrict who can trigger the GitHub Action?
  • Did you review dependency install scripts?
  • Do you have a key rotation plan?

Security boundaries do not catch logic errors. Codex may follow every permission rule and still generate buggy code, fail a test, or require rollback. Reviewing the diff, running tests, and preparing a rollback plan are the second line of defense beyond the permission boundary.

Suggested next reads:

  • Failure review and verification workflow: understand how to handle Codex mistakes, validate diffs, and prepare rollback.
  • codex exec automation and CI: go deeper into non-interactive CI workflows, workflow design, and error handling.
  • AGENTS.md project rules: learn how to write project rules, while remembering that they are guidance, not a permission system.

Related reading:

Set the smallest safe boundary for a Codex task

Choose sandbox, approval, permission, secrets, and CI job boundaries by task risk so write access, network access, dependency scripts, and API keys do not share one unbounded environment.

⏱️ Estimated time: 30 min

  1. 1

    Step 1: Decide whether the task needs read, write, or network access

    Use read-only for review and planning, workspace-write + on-request for code changes, and full access only inside an isolated runner or controlled container.
  2. 2

    Step 2: Move sensitive files out of scope or explicitly deny them

    Add deny-read rules for .env, *.pem, credentials.json, and secrets directories. Do not rely on AGENTS.md wording alone.
  3. 3

    Step 3: Review scripts before approving dependency installs

    Check package.json, postinstall, prepare, pip hooks, download scripts, and network targets before allowing install or network access.
  4. 4

    Step 4: Separate Cloud setup from the agent phase

    Put private registry tokens and dependency credentials in Cloud secrets for setup scripts only. Keep only necessary non-sensitive env vars in the agent phase.
  5. 5

    Step 5: Split the Codex job from write-permission jobs in CI

    Do not set API keys as job-level env. Keep the Codex job as read-only when possible, generate a patch artifact, and move comments, PR updates, or merges to later controlled jobs.
  6. 6

    Step 6: Review outputs and prepare key rotation

    Inspect diffs, logs, artifacts, and test results. If a leak is suspected, rotate the key first, then audit the source.

FAQ

Is writing 'do not read .env' in AGENTS.md enough?
No. AGENTS.md is custom instruction text that guides Codex behavior; it is not filesystem or network permission enforcement. To make a file unreadable, use a permission profile deny rule or move the sensitive file outside the workspace.
Can Codex read .env, ~/.ssh, or /tmp under workspace-write?
Do not assume those paths are unreadable by default. workspace-write mainly limits where writes can happen. If a sensitive file is inside a readable scope and there is no deny rule, treat it as accessible. danger-full-access removes even more filesystem and network boundaries.
When is danger-full-access acceptable for Codex?
Only consider it in an isolated CI runner, a container, or another explicitly controlled one-off environment, and only when that environment has no production secrets, personal SSH keys, unnecessary private-network access, or browser login state.
Can the Codex agent read Cloud secrets?
According to the OpenAI Codex Cloud environment documentation, secrets are available only to setup scripts and are removed before the agent phase. Environment variables, however, persist across both setup and agent phases.
How should CI protect an API key when running codex exec?
Do not set OPENAI_API_KEY or CODEX_API_KEY as job-level env in a job that checks out or runs repository code. Prefer the official Codex GitHub Action proxy, or inject CODEX_API_KEY only for a single codex exec invocation.
Can Codex Security replace SAST or manual security review?
No. It can help discover, validate, and suggest patches, but it does not replace SAST, manual security review, threat modeling, or final human validation.

13 min read · Published on: Jul 26, 2026 · Modified on: Jul 27, 2026

Comments

Sign in with GitHub to leave a comment

Easton BlogEaston Blog