Claude Code can leak into Slack, WordPress, and .env in production: 10 guardrails to enable before first run
I’m Denis Shokhirev, an Agentic AI Systems Architect based in Freiburg im Breisgau, Germany, and at DennisCraft AI Studio I ship production AI systems for DACH B2B clients using Claude, Supabase, n8n, Doppler, and self-hosted Postgres. The ugliest production failure in these systems usually doesn’t look like an “AI failure” at all: an agent reads the wrong file, posts the wrong draft to WordPress, or copies part of a .env into Slack because nobody constrained its tool access before first run. O
I’m Denis Shokhirev, an Agentic AI Systems Architect based in Freiburg im Breisgau, Germany, and at DennisCraft AI Studio I ship production AI systems for DACH B2B clients using Claude, Supabase, n8n, Doppler, and self-hosted Postgres. The ugliest production failure in these systems usually doesn’t look like an “AI failure” at all: an agent reads the wrong file, posts the wrong draft to WordPress, or copies part of a .env into Slack because nobody constrained its tool access before first run.
Once Claude Code can touch a repo, shell, CMS, database, and chat channel, data leakage becomes a routing problem. OWASP’s 2025 LLM Top 10 calls out sensitive information disclosure and excessive agency as distinct risks in LLM applications: https://genai.owasp.org/llm-top-10/. Anthropic’s tool-use guidance also pushes structured tool access rather than open-ended environment access: https://docs.anthropic.com/. Here are the 10 guardrails I put in place before I let an agent write code, run commands, or publish externally in production.
1. Reduce scope before you grant tools
The first mistake is giving Claude Code the whole workspace and hoping the prompt will carry the rest. In production I do the opposite: separate working directory, separate service account, separate credentials, separate storage paths. If the agent is generating release notes, it does not need billing exports, HR files, or the root .env.
Guardrail 1: isolated workspace
Run the agent in a dedicated directory or container. Do not mount the home directory, SSH keys, or shared volumes. This matters even more when the same host contains WordPress themes, old SQL dumps, or backup archives.
Guardrail 2: allowlist commands and paths
Give read/write access only to the directories needed for the task. For shell commands, use an allowlist, not a blacklist. Blacklists miss edge cases in production.
#!/usr/bin/env bash
set -euo pipefail
export WORKDIR="/srv/agent-task"
mkdir -p "$WORKDIR/out"
allowed_cmds=("python3" "pytest" "git" "ls" "cat")
cmd="${1:-}"
if [[ ! " ${allowed_cmds[*]} " =~ " ${cmd} " ]]; then
echo "blocked command: $cmd" >&2
exit 1
fi
cd "$WORKDIR"
exec "$@"2. Secrets should never enter the model context
I still see the same pattern over and over: the agent gets repo access, a .env file is sitting nearby, the model reads it “for debugging,” and then fragments show up in Slack output or generated content. That is not a model bug. It is a system design error.
Guardrail 3: inject secrets at runtime only
Doppler, process-level environment variables, and short-lived tokens are fine. A .env file sitting inside the agent’s working directory is not. Even if the file is read-only, the content can still be copied into output.
Guardrail 4: pre-run secret scanning
Run gitleaks against the workspace before every execution and fail hard on matches. This catches the “temporary file” problem that shows up in real teams more often than people admit.
#!/usr/bin/env bash
set -euo pipefail
gitleaks detect \
--source /srv/agent-task \
--no-banner \
--exit-code 1 \
--redact
echo "No secrets detected in workspace"3. External publishing needs a policy layer
Slack and WordPress are risky because they are external sinks with no natural pause. If the model misclassifies text, you can leak a secret, publish unreviewed claims, or expose personal data. For European teams, that is already a GDPR problem before anyone starts debating prompt quality.
Guardrail 5: two-step publishing
The agent should not publish directly. It creates a draft in Postgres or Supabase, then a separate process evaluates policy checks and only then posts to Slack or the WordPress API.
Guardrail 6: content filtering for common leak patterns
I check for email addresses, API keys, JWTs, lines that look like password assignments, connection strings, and .env-like fragments. This is not full DLP, but it closes common production holes.
import re
RISK_PATTERNS = [
r"AKIA[0-9A-Z]{16}",
r"-----BEGIN [A-Z ]+ PRIVATE KEY-----",
r"postgres(ql)?://[^\\s]+",
r"(?i)password\\s*=",
r"(?i)api[_-]?key\\s*[:=]",
r"[A-Za-z0-9-_]+\\.[A-Za-z0-9-_]+\\.[A-Za-z0-9-_]+"
]
def is_publishable(text: str) -> bool:
return not any(re.search(pattern, text) for pattern in RISK_PATTERNS)
sample = "draft message for Slack"
if not is_publishable(sample):
raise ValueError("Blocked by content policy")4. Treat generated code as code, not as model output
Claude Code can produce working code that passes a happy-path test and still introduces SQL injection, shell injection, or unsafe file handling. Static analysis catches things your prompt will not. In my stack, semgrep and bandit are worth far more than adding another paragraph to the system instruction.
Guardrail 7: static analysis on every generated diff
Semgrep is useful across Python, JavaScript, TypeScript, and YAML. Bandit is a simple Python layer. If the agent writes n8n workflow JSON or SQL migrations, add checks that fit those artifacts too.
Guardrail 8: policy on dangerous sinks
Block generated changes from auto-merging or auto-running if they use subprocess with shell=True, raw SQL string interpolation, .env writes, direct outbound webhooks, or bulk update/delete without a where clause.
| Artifact | Tool | What to catch |
|---|---|---|
| Python | bandit, semgrep | shell injection, unsafe temp files, weak crypto |
| JS/TS | semgrep | eval, command execution, SSRF patterns |
| Secrets | gitleaks | tokens, keys, connection strings |
| Policies | OWASP checklist | excessive agency, data exposure, tool misuse |
version: "3"
tasks:
scan:
cmds:
- semgrep --config p/owasp-top-ten .
- bandit -r . -q
- gitleaks detect --source . --no-banner --exit-code 1 --redact
silent: true5. Runtime controls matter more than elegant prompts
Anthropic’s documentation on tool use is a good anchor here: let the model operate through described tools and structured calls, not through broad implicit access. That pattern limits blast radius. A polished prompt does not replace execution controls.
Guardrail 9: log every tool invocation
I want a trace of the prompt, tool name, arguments, tool output, and side effect. Without that, a postmortem turns into guesswork: did the data leave through Slack, WordPress, Supabase, or shell output?
Guardrail 10: kill switch and budget limits
Every agent run should have limits on tool calls, runtime, and spend. If the chain drifts, you stop it automatically instead of discovering 20 minutes later that it already posted garbage into four channels.
type ToolEvent = {
tool: string;
args: Record;
timestamp: string;
};
const MAX_CALLS = 12;
const events: ToolEvent[] = [];
export function recordToolCall(tool: string, args: Record) {
if (events.length >= MAX_CALLS) {
throw new Error("tool budget exceeded");
}
events.push({ tool, args, timestamp: new Date().toISOString() });
console.log(JSON.stringify(events[events.length - 1]));
}FAQ
Is a strong system prompt enough?
No. A prompt helps the model interpret the task, but it does not constrain filesystem access, network egress, or publishing rights. Production controls need to exist outside the model.
Do I need human approval for every external post?
Not always. Low-risk drafts can flow through policy checks and a staging table. But anything involving personal data, financial actions, or public brand statements usually deserves explicit approval.
What matters more: sandboxing or static analysis?
They solve different problems. Sandboxing limits runtime damage, while static analysis catches dangerous patterns in generated code before execution. Production systems need both.
How does this relate to regulated European markets?
If an agent can leak personal data into Slack, CMS content, or logs, you already have a GDPR and auditability issue. In DACH client environments I document those data flows directly, including which tools can see or emit which classes of data.
Where should a team start if the system is already live?
Start with the tool map and output sinks. List every tool the agent can call, every directory it can read, and every destination it can write to. Then add allowlists, secret scanning, and a publishing gate in that order.
Which stage in your LLM pipeline catches the most issues in production right now: static analysis on generated diffs, runtime policy checks, or human review after content reaches Slack or CMS? I’d genuinely like to compare that with what I’m seeing in DACH B2B environments. I run a free 30-min stack audit for DACH founders building AI in regulated markets. DM me on LinkedIn or write to @ger_dennis_ai.
I build agents like this for businesses in Germany and the EU.
Tell me in two sentences what eats your team’s time – reply within 24 hours.
Turn your process into an AI system
Production quality. DACH B2B focus.