Codex Security Hardening skill for Codex
Least privilege for your coding agent. Codex audits its own effective configuration, applies a least-privilege baseline (workspace-write, on-request approvals, filtered environment, keyring credentials), denies secret files with a permission profile, routes network through a domain allowlist, leaves unfamiliar repos untrusted, and proves each control with an allowed and a blocked action.
curl -fsSL codexskills.dev/install.sh \ | sh -s -- codex-security-hardening Installs to ~/.agents/skills/codex-security-hardening/ for every project on this machine.
curl -fsSL codexskills.dev/cli.mjs -o codex-skills.mjsnode codex-skills.mjs install codex-security-hardening The CLI also searches, updates and removes skills. Run it with no arguments to pick from a list. Needs Node 20.12+.
curl -fsSL codexskills.dev/install.sh \ | sh -s -- --project codex-security-hardening Installs to .agents/skills/codex-security-hardening/ in the current repository. Commit it and everyone who runs Codex there gets it.
curl -fsSLO codexskills.dev/downloads/codex-security-hardening.zipunzip -o codex-security-hardening.zip -d ~/.agents/skills/ The archive holds the skill folder with its generated agents/openai.yaml. Unzip into .agents/skills/ to scope it to a repo.
& ([scriptblock]::Create((irm codexskills.dev/install.ps1))) codex-security-hardening PowerShell 5.1 or 7. Installs to %USERPROFILE%\.agents\skills\. Add -Project for the current repo.
104-line POSIX script. No sudo, no telemetry. Read install.sh
Use it in Codex
Start a prompt with $codex-security-hardening, or pick it from /skills.
You can also just describe the task: Codex reads the description below and loads the playbook when your request matches.
What Codex reads before loading it
Hardens a local Codex setup with least-privilege sandbox and approval settings, permission profiles that deny secret files, filtered shell environments, a domain-allowlisted network proxy, project trust for untrusted repos, execpolicy rules, MCP tool approval modes, keyring credential storage and safe CI key handling. Use when the user asks to lock down Codex, run it on an unfamiliar or untrusted repository, keep secrets away from the agent, review a teammate's risky config, or decide whether --yolo or danger-full-access is acceptable.
Use it when
- Running Codex on an unfamiliar or untrusted repository
- Keeping API keys, tokens and .env files away from the agent
- Reviewing a config that uses danger-full-access, never or --yolo
- Restricting network access to specific domains
Use something else when
- You only need to pick sandbox and approval mode for one task: use codex-sandbox-approvals
- Setting policy for a whole organization's rollout: use codex-team-rollout
How to check the result
From a hardened session, writing outside the workspace fails, env shows no token variables, an unlisted domain is blocked, and execpolicy check reports forbidden for destructive commands.
The mistake it prevents
Assuming secret-looking environment variables are filtered by default, when ignore_default_excludes defaults to true and passes them to every command.
A Codex setup is hardened when the agent can do the task and nothing more: writes stay in the workspace, secrets are neither readable nor in the command environment, network goes only to named hosts, untrusted repos cannot load their own config or hooks, and every widening of access is an explicit, reviewable choice.
Threat model in one table
| Threat | Primary control |
|---|---|
| Agent edits or deletes outside the project | workspace-write sandbox (or :workspace permission profile) |
| Prompt injection from repo files, issues, web results, MCP output | Approvals on, network off or allowlisted, web_search = "cached" |
| Secret exfiltration via env vars | shell_environment_policy filters |
Secret exfiltration via files (.env, cloud creds) | Permission profile deny rules, protected paths |
| Malicious repo config, hooks or rules | Leave the project untrusted |
Destructive commands (rm -rf, force push, prod CLIs) | execpolicy forbidden / prompt rules |
| Side-effecting MCP tools | default_tools_approval_mode, tool allowlists |
| Leaked Codex credentials | Keyring storage, inline-only API keys in CI |
Workflow
- [ ] 1. Inspect the effective configuration
- [ ] 2. Apply the baseline
- [ ] 3. Lock down secrets
- [ ] 4. Constrain network and tools
- [ ] 5. Decide trust for each repository
- [ ] 6. Verify with deliberate allowed and blocked actions
1. Inspect
codex --version
codex doctor --summary # install, config, auth and runtime health
codex features list # effective feature flags
In a session, /status shows sandbox, approval policy and writable roots;
/debug-config shows every config layer and any admin requirements. Check
~/.codex/config.toml, any ~/.codex/*.config.toml profile files and the repo’s
.codex/config.toml.
2. Baseline (user config)
# ~/.codex/config.toml
sandbox_mode = "workspace-write"
approval_policy = "on-request"
allow_login_shell = false # shell tools don't source login profiles
web_search = "cached" # pre-indexed results, not live pages
cli_auth_credentials_store = "keyring"
[sandbox_workspace_write]
network_access = false # default; opt in per task
[shell_environment_policy]
inherit = "core"
ignore_default_excludes = false # default is true: KEY/SECRET/TOKEN vars pass through!
[shell_environment_policy.filters]
"AWS_*" = "exclude"
"GITHUB_TOKEN" = "exclude"
"NPM_TOKEN" = "exclude"
"*_API_KEY" = "exclude"
approval_policy = "untrusted" is retired and can stop Codex from starting; remove it.
For the old “approve every command” behaviour, mark the project untrusted (step 5).
3. Secrets on disk
In workspace-write, .git, .agents, .codex and .aws inside writable roots are
read-only, but readable. To make secret files unreadable, use a permission profile
(beta; do not mix with sandbox_mode in the same setup, the older keys win):
default_permissions = "hardened"
[permissions.hardened]
extends = ":workspace"
[permissions.hardened.filesystem.":workspace_roots"]
"**/*.env" = "deny"
"**/*.pem" = "deny"
"config/credentials*" = "deny"
Keep real secrets out of the repo entirely; use a secret manager the agent cannot query.
4. Network and tools
Allow only the hosts a task needs, with enforcement through the network proxy:
[sandbox_workspace_write]
network_access = true
[features.network_proxy]
enabled = true
domains = { "registry.npmjs.org" = "allow", "**.github.com" = "allow" }
Without network_proxy, network_access = true means unrestricted egress, and domain
rules do nothing. deny beats allow; local and private addresses stay blocked unless
explicitly allowed. The proxy does not cover web search, MCP connections or model
traffic.
MCP servers:
[mcp_servers.jira]
enabled_tools = ["get_issue", "search_issues"]
default_tools_approval_mode = "writes" # prompt for any tool not marked read-only
Command rules decide what happens when Codex asks to run a command outside the sandbox
(~/.codex/rules/default.rules, Starlark, experimental):
prefix_rule(pattern = ["git", "push", ["--force", "-f"]], decision = "forbidden",
justification = "Never force-push; open a PR instead.")
prefix_rule(pattern = ["terraform", ["apply", "destroy"]], decision = "forbidden")
prefix_rule(pattern = ["kubectl", "delete"], decision = "prompt")
Test them: codex execpolicy check --pretty --rules ~/.codex/rules/default.rules -- git push -f origin main.
5. Repository trust
Untrusted projects skip project .codex/ layers: config, hooks and rules. Use that for
any repo you did not write or review:
[projects."/Users/me/src/third-party-sdk"]
trust_level = "untrusted" # commands need approval unless a rule allows them
Before trusting a repo, read its .codex/config.toml, .codex/hooks.json,
.codex/rules/, AGENTS.md files and .agents/skills/: they shape what the agent is
told and what runs automatically. Project config cannot change providers, base URLs,
notify or otel, but it can change model, sandbox defaults and MCP servers.
For an untrusted repo, run read-only first:
codex --sandbox read-only --ask-for-approval on-request -C ~/src/third-party-sdk
6. Automation and CI
- Never put
OPENAI_API_KEYorCODEX_API_KEYin job-level env when the job runs repo code; setCODEX_API_KEYinline on the singlecodex execcommand, or use the Codex GitHub Action, which proxies the key. --dangerously-bypass-approvals-and-sandbox(--yolo) anddanger-full-accessare acceptable only inside a disposable container or VM that is itself the boundary, with no credentials beyond what the task needs. Inside such a container a malicious repo can still read everything in it, including Codex auth.--dangerously-bypass-hook-trustonly for automation that already vets hook sources.
Gotchas
ignore_default_excludesdefaults totrue, so variables named*KEY*,*SECRET*,*TOKEN*are passed to commands unless you set it tofalseor filter them.setinshell_environment_policyruns after exclusions and can re-add a secret.auth.json(file storage) holds live tokens; never commit or share it.- Rules match argument prefixes only:
["git", "push", ["--force", "-f"]]blocksgit push -f origin mainbut notgit push origin main -f. Keep the sandbox (no network, read-only.git) as the real boundary and treat rules as a second layer. - Approving a suggested
prefix_rulein the TUI writes an allow rule to~/.codex/rules/default.rules; review that file periodically. web_search = "live"and--searchfetch arbitrary pages: higher injection risk.- Docker without the capabilities
bwrapneeds breaks the Linux sandbox; fix the container rather than reaching for--yoloon a host. - Telemetry: keep
[otel] log_user_prompt = falseunless policy allows storing prompts.
Verification
Run each check and record the result:
codex exec --sandbox workspace-write "Run: touch ../outside.txt; report the error" # expect denial
codex exec --sandbox workspace-write "Run: env | grep -ci token; report the number" # expect 0
codex exec --sandbox workspace-write "Run: curl -sI https://example.com; report status" # expect blocked
codex execpolicy check --pretty --rules ~/.codex/rules/default.rules -- rm -rf /
Use codex sandbox to run one command directly under the sandbox (newer releases take a
platform subcommand, e.g. codex sandbox macos --log-denials <cmd>; check
codex sandbox --help).
Output template
## Codex hardening report
| Control | Before | After | Verified by |
|---|---|---|---|
| Sandbox / approvals | danger-full-access / never | workspace-write / on-request | touch ../outside.txt denied |
| Env secrets | 14 *_TOKEN vars passed | 0 | env grep count |
| Network | unrestricted | proxy, 2 domains | curl example.com blocked |
| Repo trust | trusted by default | third-party-sdk untrusted | /debug-config |
Remaining risks: <list> What the zip contains
SKILL.md plus the interface file below. Nothing runs on install.
agents/openai.yaml
Gives the Codex app a display name, a one-line description and a starter prompt for this skill.
interface:
display_name: "Codex Security Hardening"
short_description: "Least privilege for your coding agent"
default_prompt: "$codex-security-hardening Lock down my Codex config before I point it at this third-party repo" About this skill
Written and maintained by the Codex Skills editors to the same editorial standard as every skill here: a selective description, a real workflow, known pitfalls and a way to verify the work. First published , last updated . MIT licensed. Something wrong or out of date? Tell us.