The security tool I was testing added itself to my agent config
In early October, I was comparing guardrails for my coding agent. One of them added itself to my configuration. It stayed active in all my sessions for about eight minutes. It was not malware. It was a documented featur
In early October, I was comparing guardrails for my coding agent. One of them added itself to my configuration. It stayed active in all my sessions for about eight minutes.
It was not malware. It was a documented feature. And that is exactly what made me think.
TL;DR: any program that runs with your rights can change your AI agent's config. A hook added to
settings.jsonruns before every command, in every session. I saw it happen with dcg, a guardrail that registers itself again. Treat that file like a secret: backed up, compared, never changed in silence. And test unknown tools far from your real config.
This article is for developers and teams who use Claude Code or Codex on their own machines. You do not need to know hooks already: I explain them.
The context: a test bench for guardrails
A coding agent runs commands on your machine. A guardrail reads each command before it runs and blocks the dangerous ones, like rm -rf ~.
In Claude Code, that guardrail takes the form of a hook. A hook is a command the agent runs automatically at a given moment, for example before each shell command. Hooks are declared in the file ~/.claude/settings.json.
The hook is part of the harness, the software around the model. I cover it in the article on the harness.
On 2 October, I tested three guardrails against 33 commands: 16 legitimate and 17 destructive. The bench has its own article. Here, I tell what happened on the side.
What happened
One of the candidates is called dcg, short for destructive command guard. It is an open source tool written in Rust. I tested version 0.15.2.
The binary sat in a temporary folder of my session. I ran it once in hook mode, the way the agent would, without installing it.
On that run, dcg wrote its own entry into my ~/.claude/settings.json. The entry pointed at the binary in the temporary folder. It did not ask me anything.
A hook entry looks like this. The path is an example:
This is the change to look for in a diff. The path is a generic example, not the one from my machine.
+ { "type": "command", "command": "/temporary-evaluation/dcg" }
{
"hooks": {
"PreToolUse": [
{
"matcher": "Bash",
"hooks": [
{ "type": "command", "command": "/path/to/temporary/folder/dcg" }
]
}
]
}
}
From 08:17 to about 08:25, all my sessions sent their commands through that binary. Then I removed the entry and deleted its config folder, ~/.config/dcg.
It is not malware, it is a feature
The dcg documentation describes this behaviour. The tool has a self_heal_hook option, on by default. Self-heal means the tool repairs itself.
On every call in hook mode, dcg checks that its entry is still in settings.json. If it is missing, it writes it back. Since version 0.7.7, it registers the absolute path of the binary that is running. In my case, that was the one in the temporary folder.
The intent makes sense. A guardrail that another program can quietly remove does not protect much. The dcg docs say the option protects against a settings.json overwritten during a session.
The maintainers ran into the same case. According to the 0.14.0 changelog, their own tests could install the development binary as a global hook. They turned the feature off during tests with an environment variable.
For you, the option turns off with DCG_NO_SELF_HEAL=1 or self_heal_hook = false in its config.
So my problem is not dcg. I ran a tool to evaluate it, and it changed the config of all my sessions.
Why this file matters so much
settings.json holds more than preferences. It says which commands run before each agent action. It can also say which server your requests go to.
The Claude Code docs are clear about hooks. They run shell commands with your full user permissions. They can read, change or delete any file your account can reach.
The docs also say that direct edits to hooks are normally picked up automatically. No restart needed. So a single write to the file is enough to change how your sessions behave.
Any program run with your rights can write your agent's config. One write is enough, and every session applies it at once. Version the file and test unknown tools elsewhere.
The hook's path matters too. A hook pointing at a temporary folder runs whatever sits there at the time of the call. If the file is gone, the docs say the action goes ahead with a plain error notice. The guardrail then blocks nothing.
How to spot a silent change
There is no perfect recipe. Here is what I recommend, from simplest to strongest.
Put the file under version control
The simplest step is to put the file under git. An allowlist keeps the rest of the folder out, since it holds session history and caches.
cd ~/.claude
git init
printf '*\n!.gitignore\n!settings.json\n' > .gitignore
git add .gitignore settings.json
git commit -m "agent config: baseline"
Do the same in ~/.codex with config.toml and hooks.json. From then on, git diff shows you exactly what changed, line by line.
Never push that repository to a server. settings.json can hold keys in its env section. A committed secret stays in the history, even after you delete it. I cover that in the article on GhostAction.
Check every time you open a terminal
A check stored in the file it watches can disappear with it. Better to put it somewhere else, for example in your shell startup. This line in ~/.bashrc or ~/.zshrc warns you when the file has moved:
git -C ~/.claude diff --quiet HEAD -- settings.json || echo "settings.json changed: git -C ~/.claude diff"
Listen for changes during the session
Claude Code has a hook event made for this, called ConfigChange. It fires when a settings file changes during a session. The docs suggest it for auditing or blocking those changes. This example logs the time of each change:
"ConfigChange": [
{
"matcher": "user_settings",
"hooks": [
{ "type": "command", "command": "date -Is >> ~/.claude/config-changes.log" }
]
}
]
It has two limits. Blocking only stops the current session from applying the change. The file stays modified on disk. And a block shows no message, neither to you nor to the agent.
Review your hooks
In Claude Code, the /hooks command opens a read-only list. It shows each hook, what it runs and the file that declares it. On the command line, jq prints the list of commands:
jq -r '.hooks[]?[].hooks[].command' ~/.claude/settings.json
For each line, ask two questions. Do I recognize this command? Is its path stable, outside /tmp?
Also look at the .claude/settings.json of the repositories you clone. The docs warn that in claude -p mode, those hooks run without any trust dialog.
Codex does better here. It records a fingerprint of each approved hook. A new or changed hook is set aside until you approve it in /hooks.
That review covers hooks. Also review changes to the sandbox and the server address.
File permissions are not enough
You can make the file read-only with chmod 444. That blocks a direct write. It does not block a replacement.
Many tools write a copy, then rename it over the original. That rename depends on the folder's permissions, not the file's. According to its changelog, dcg rewrites settings.json exactly this way.
And locking the whole ~/.claude folder would break Claude Code, which stores its sessions there. Permissions slow things down. They are not a barrier.
Test unknown tools far from your real config
Detection tells you a change happened. Isolation keeps it from reaching your real config.
Claude Code reads its config folder from the CLAUDE_CONFIG_DIR variable when it is set. According to its docs, dcg honours it too. For a trial run, point it at a throwaway folder:
CLAUDE_CONFIG_DIR="$(mktemp -d)" ./tool-under-test
That variable only protects you from tools that read it. A tool can hard-code ~/.claude. Changing HOME removes no file permissions. For an unknown tool, use an isolated environment with no access to your real configuration.
Also turn off automatic write features when the tool documents them, like DCG_NO_SELF_HEAL=1 for dcg.
My rule since that incident is simple. I do not run dcg again without isolating it. And before plugging in my new guardrail, I had kept a dated copy of settings.json.
The checklist
Your agent's config is code that runs. Nobody should change it without you seeing it.
- [ ] List your agents' config files:
~/.claude/settings.json,~/.codex/config.toml,~/.codex/hooks.json - [ ] Version them locally with an allowlist, and never push that repository
- [ ] Add a
git diff --quietcheck to your shell startup - [ ] Log settings changes with a
ConfigChangehook - [ ] Review your hooks with
/hooks: known command, stable path, nothing in/tmp - [ ] Search your config files for
danger-full-accessand unknown server addresses - [ ] Test an unknown tool in an isolated environment with no access to your real configuration
- [ ] Check staged changes too with
git diff HEAD
What to remember
dcg did what its docs say. The lesson is not about one tool. It is about a habit.
A file that decides what your agent runs deserves the same care as your code. You back it up, you compare its versions, and you know who touched it last.
Do you want to check what runs with your agents' rights across your team? Let's talk.
Sources: dcg, self_heal_hook option ยท dcg, changelog ยท Claude Code, hooks ยท Claude Code, updates ยท Codex, hooks
Originally published by Dev.to Security. Aggregated on AIWithGhost for educational purposes โ full credit and traffic to the original publisher.