An OpenAI Codex safety plugin and local PreToolUse hook that prevents
accidental file deletion and data loss by blocking destructive shell, Git,
database, cloud, infrastructure, patch, and MCP operations before execution.
Use it as a destructive-command guard for Codex CLI or the Codex app,
especially when working in Full Access mode. It protects against common
mistakes such as rm -rf, git reset --hard, database DROP/TRUNCATE,
force pushes, disk erasure, cloud resource deletion, and destructive MCP tool
calls.
When a call is denied, nothing runs. The guard identifies the matched safety rule and tells Codex to stop instead of trying another method. A genuinely necessary destructive operation must be reviewed and performed manually by the user.
Important
This is defense in depth, not a sandbox, backup system, or guarantee against data loss. It protects only calls Codex routes through the enabled, trusted hook. Keep Codex sandbox protections enabled and maintain tested backups.
- Codex gains one local
PreToolUsehook. - Ordinary reading, writing, coding, testing, and network access are unchanged.
- No custom permission profile is installed.
- The hook runs locally with Python and makes no network requests.
- No account, server, telemetry, command history, or third-party Python package is added.
- Full Access remains available, but a destructive call seen and denied by the hook cannot proceed.
- Codex prepares a supported tool call.
- Codex sends the visible tool name and JSON input to the trusted hook.
- The hook checks shell commands, SQL, MCP operations, and explicit patch deletion against local rules.
- A safe call produces no hook output and continues normally.
- A matching call receives a structured denial before execution.
- Malformed, incomplete, oversized, or internally uninspectable hook input fails closed.
Example:
| Call | Result |
|---|---|
rg -n 'rm -rf' README.md |
Allowed: the phrase is only a search pattern |
git commit -m "document git clean safety" |
Allowed: clean is commit-message text, not the Git subcommand |
git restore --staged app.py |
Allowed: only the Git index changes |
rm -rf build |
Denied with rule ID shell.file.delete |
git reset --hard HEAD~1 |
Denied with rule ID git.reset_hard |
DROP TABLE users |
Denied with rule ID sql.drop |
The git clean control tokenizes chained shell commands and parses Git global
options, environment assignments, subshells, wrappers, and command
substitutions to identify the actual Git subcommand. It does not match the word
clean merely because it appears in a quoted argument.
The context classifier remains conservative. Pipes, redirects, substitutions, backticks, malformed quotes, custom executable paths, and executable helper options retain strict scanning.
Requirements:
- Codex with plugin support;
- Python 3.9 or newer;
- permission to install and trust a local Codex hook.
Choose one installation method:
| Method | Best for |
|---|---|
| Paste the prompt below into Codex | Easiest reviewed installation |
| Run the two Codex CLI commands | Terminal users |
Run install.py |
Older clients and native Windows fallback |
Repository: github.com/nuuxcode/codex-destructive-guard
Copy this version-pinned prompt into a new Codex task:
Install Codex Destructive Guard v1.2.1 from this public repository:
https://github.com/nuuxcode/codex-destructive-guard
First inspect README.md, .agents/plugins/marketplace.json, the plugin manifest,
hooks/hooks.json, scripts/guard.py, and checksums/v1.2.1.sha256. If they match
the documented behavior, install the marketplace with --ref v1.2.1 and then
install codex-destructive-guard@codex-destructive-guard. Do not modify unrelated
settings and do not run a destructive live test. Show me the exact installed
hook command and explain how to review and trust it in /hooks.
The recommended installation is pinned to an immutable Git tag:
codex plugin marketplace add nuuxcode/codex-destructive-guard --ref v1.2.1
codex plugin add codex-destructive-guard@codex-destructive-guardAfter either plugin installation method, restart Codex or start a new task:
- Enter
/hooksin the CLI, or open the Hooks panel in the app. - Open the new
PreToolUsehook. - Verify that it runs
scripts/guard.pyfrom this plugin. - Press
tto trust that hook. - Confirm the Hooks screen shows one active guard hook.
Codex intentionally does not trust a new or changed non-managed hook
automatically. The guard provides no protection until the hook is trusted and
active. Omitting --ref follows mutable marketplace state and is therefore not
the recommended installation path.
The fallback installer copies the same reviewed script into CODEX_HOME and
merges one handler into hooks.json:
git clone --branch v1.2.1 --depth 1 https://github.com/nuuxcode/codex-destructive-guard.git
cd codex-destructive-guard
shasum -a 256 -c checksums/v1.2.1.sha256
python3 install.pyOn Windows PowerShell, use py -3 install.py and compare
Get-FileHash plugins/codex-destructive-guard/scripts/guard.py -Algorithm SHA256
with the value in checksums/v1.2.1.sha256.
The installer:
- records exact script and handler ownership in a private local manifest;
- refuses unowned path collisions and locally modified installed scripts;
- preserves unrelated hook groups and sibling handlers;
- backs up every existing file it changes;
- stages all writes and rolls the set back if replacement fails;
- preserves existing POSIX mode, ownership, timestamps, and supported extended attributes.
Windows ACL cloning is not promised; new files use the destination directory's
normal inherited ACL. Review permissions if CODEX_HOME has custom Windows
ACLs. The installer does not edit config.toml, install packages, or send
network data.
| Area | Examples |
|---|---|
| Files | rm, unlink, rmdir, find -delete, destructive rsync, PowerShell Remove-Item, Windows del/rd |
| File APIs | Python deletion APIs, Node rmSync/unlinkSync/rmdirSync, Ruby deletion APIs |
| Disks | dd ... of=, mkfs, wipefs -a, destructive diskutil, shred, truncate |
| Git | clean, reset --hard, working-tree restore, destructive checkout, branch/stash/worktree deletion, force/ref-deleting push |
| Databases | DROP, TRUNCATE, DELETE FROM, mass UPDATE without a top-level WHERE, Mongo/PyMongo deletion, Redis deletion/flush, common resets |
| Infrastructure | Terraform destroy, Kubernetes deletion, Helm deletion, container prune, compose volume deletion |
| Cloud/services | Common destructive AWS, Google Cloud, Azure, hosting, GitHub API, billing, and subscription commands |
| Codex patches | Explicit apply_patch file deletion |
| MCP/apps | Destructive camelCase or separated tool names, operation fields, SQL, and shell-like command arguments |
Staging-only git restore --staged is allowed. A restore that can change the
working tree remains blocked.
When a rule matches, the guard reports a stable rule ID and label, not the full command, and instructs Codex not to retry through another mechanism.
The guard can make a decision only after Codex invokes it with a visible tool name and JSON input.
| Boundary | Status |
|---|---|
Bash tool calls |
Documented Codex hook matcher; command text is inspected |
apply_patch calls |
Documented matcher; explicit patch file deletion is denied |
mcp__... calls |
Documented matcher family; names, operation fields, database text, and command-like arguments are inspected |
exec, exec_command, unified_exec, shell |
Compatibility aliases included for current clients; verify locally in /hooks |
Edit, Write |
Matched for compatibility, but generic overwrites are not reliably inferable from visible input |
If invoked, malformed, incomplete, oversized, or internally uninspectable input fails closed. Full Access does not override a hook denial. However, Full Access does not guarantee every action will be routed through this hook, so this is not a system-wide enforcement boundary.
Codex CLI 0.144.5 is pinned in CI for marketplace discovery and installation.
Local testing on macOS also observed exec_command reaching the hook, but that
observation is not a platform guarantee. See the current
Codex hooks documentation for the
official matcher contract.
The guard does not reliably detect:
- shell redirection or
teeoverwriting/truncating an existing file; - an overwriting
cpormvwhose danger depends on filesystem state; - a generic
EditorWritecall that replaces valuable content; - destructive behavior hidden inside an apparently safe script, executable, build step, browser action, remote service, or application;
- obfuscated, dynamically constructed, or previously unknown syntax;
- any call that Codex does not send through this enabled and trusted hook.
It also does not inspect filesystem state, validate targets, protect processes outside Codex, or replace database permissions and backup/restore controls.
No destructive live test is required:
python3 scripts/validate_repo.py
python3 -O scripts/validate_repo.py
python3 -m unittest discover -s tests -v
python3 scripts/benchmark_guard.pyThe JSON corpus at tests/cases/guard_cases.json records stable case IDs,
expected decisions, and rule attribution. The suite covers context-aware text,
substitutions, pipes, redirections, Unicode, malformed quoting, aliases, Git,
SQL lexical edge cases, MCP inputs, ownership collisions, sibling handlers,
metadata, idempotent updates, uninstall, and transaction rollback. CI runs on
macOS, Linux, and Windows and installs the plugin using a pinned Codex CLI.
An authenticated, real-Codex hook probe is available but never runs automatically:
CODEX_LIVE_E2E=1 python3 scripts/test_codex_live_e2e.pyRun it only with a Codex home where this plugin is already installed, trusted,
and active. It consumes model requests, does not change hook configuration, and
uses only pwd plus harmless rm --help.
Review the new release, remove the old plugin reference, and reinstall from the new immutable tag:
codex plugin remove codex-destructive-guard@codex-destructive-guard
codex plugin marketplace remove codex-destructive-guard
codex plugin marketplace add nuuxcode/codex-destructive-guard --ref v1.2.1
codex plugin add codex-destructive-guard@codex-destructive-guardRestart Codex and trust the changed hook again. For a fallback installation,
run the new checkout's install.py; ownership is verified before the
transaction begins.
Plugin installation:
codex plugin remove codex-destructive-guard@codex-destructive-guard
codex plugin marketplace remove codex-destructive-guardFallback installation:
python3 uninstall.pyOn Windows, use py -3 uninstall.py. The fallback uninstaller removes only the
exact handler and script recorded as owned, preserves siblings, backs up changed
files, and refuses to remove a locally modified or unrecognized script.
Use this alongside:
- Codex's Ask for approval or Approve for me modes for routine work;
- version control and tested, restorable backups;
- disposable local databases for tests and migrations;
- separate least-privilege production credentials;
- manual review of unfamiliar scripts and irreversible external actions.
See SECURITY.md for the threat model. OpenAI's sandbox and approvals guidance explains why a hook should complement rather than replace sandboxing.
For false positives, missed destructive commands, or installation problems, open an issue. For vulnerabilities, use GitHub private vulnerability reporting as described in SECURITY.md.
MIT. Relevant portions are derived from or adapted from the MIT-licensed
linearuncle/codex-agentguard; see THIRD_PARTY_NOTICES.md
for the pinned provenance and complete upstream notice.