DEV Community

Claude code
Claude code

Posted on

Securing Claude Code in CI/CD Pipelines: What Goes Wrong and How to Lock It Down

{"@context":"https://schema.org","@type":"Article","headline":"Securing Claude Code in CI/CD Pipelines: What Goes Wrong and How to Lock It Down","keywords":"claude code ci cd security","description":"Comprehensive guide to claude code ci cd security — covering definitions, best practices, tools, and FAQs.","author":{"@type":"Organization","name":"CLaude coe ","url":"https://gtm-rho.vercel.app/"},"publisher":{"@type":"Organization","name":"CLaude coe ","url":"https://gtm-rho.vercel.app/"},"datePublished":"2026-06-15T07:30:00.549Z","dateModified":"2026-06-15T07:30:00.549Z","mainEntityOfPage":{"@type":"WebPage"}}
{"@context":"https://schema.org","@type":"FAQPage","mainEntity":[{"@type":"Question","name":"Is it safe to use --dangerously-skip-permissions inside a Docker CI container?","acceptedAnswer":{"@type":"Answer","text":"See our full guide on claude code ci cd security for a detailed answer to: Is it safe to use --dangerously-skip-permissions inside a Docker CI container?"}},{"@type":"Question","name":"How do you prevent Claude Code from leaking environment secrets during npm install in CI?","acceptedAnswer":{"@type":"Answer","text":"See our full guide on claude code ci cd security for a detailed answer to: How do you prevent Claude Code from leaking environment secrets during npm install in CI?"}}]}

Claude Code CI/CD Security: What Actually Goes Wrong in Automated Pipelines

Claude Code CI/CD security refers to the set of controls, configurations, and operational practices that prevent Claude Code — Anthropic's AI coding assistant — from becoming an unintended attack surface or data exfiltration path when run inside automated CI/CD pipelines. This includes secrets isolation, permission scoping, network egress control, and container teardown patterns that apply specifically to unattended, non-interactive execution environments.

Running Claude Code locally, with a human watching the terminal, carries its own risks. Running it headlessly in GitHub Actions or a Jenkins pipeline is a different threat model entirely. The agent executes without a human in the loop, it may have access to every secret your CI runner inherits, and if something goes wrong, there's no one watching the screen. Most CI security incidents involving AI coding tools aren't dramatic exploits. They're configuration oversights — an environment variable that shouldn't have been there, a permission flag set for convenience during testing that never got reverted.

The Unique Risks of Running Claude Code Unattended

When you trigger Claude Code in a CI job, the process inherits the full environment of the runner. On GitHub Actions, that means every secret you've exposed via env: blocks, every token injected by the runner itself, and any credentials your build system automatically provisions. Claude Code can read environment variables. Its subprocesses can read environment variables. Any tool it invokes — npm scripts, shell commands, test runners — can read environment variables.

The second risk is prompt injection. If Claude Code is processing any external input as part of your CI workflow — a PR description, a commit message, a test fixture file, an API response pulled during the build — an attacker who controls that input can attempt to redirect Claude Code's behavior. In a local session, a developer would notice. In CI, the job completes, logs get archived, and nobody reads them until something breaks in production.

Third: the blast radius of a misconfigured CI job scales with your pipeline's access. A developer laptop has access to one set of credentials. A CI runner with production deployment keys, npm publish tokens, and a cloud provider role has access to far more. That's the environment Claude Code is running in when you add it to your release pipeline without explicitly scoping what it can touch.

Isolating Secrets So ANTHROPIC_API_KEY Never Reaches Subprocesses

The most straightforward mistake is injecting ANTHROPIC_API_KEY into the top-level job environment and then wondering why it shows up in subprocess logs or gets read by a malicious npm postinstall script. The fix is step-level scoping.

In GitHub Actions, instead of setting the key at the job level:

jobs:
  ai-review:
    env:
      ANTHROPIC_API_KEY: ${{ secrets.ANTHROPIC_API_KEY }}  # exposes to all steps

Enter fullscreen mode Exit fullscreen mode

Set it only on the step that needs it:

    steps:
      - name: Run Claude Code review
        env:
          ANTHROPIC_API_KEY: ${{ secrets.ANTHROPIC_API_KEY }}
        run: claude --print "Review the diff for security issues"

Enter fullscreen mode Exit fullscreen mode

This doesn't prevent Claude Code itself from having access — it still does — but it stops the key from bleeding into every other step in the job, including dependency installation steps where supply chain attacks could harvest it.

The deeper issue is that even step-level scoping doesn't prevent Claude Code from passing the key down to tools it invokes. If your Claude Code session runs npm install as a tool call, that subprocess inherits the environment. The solution is to run dependency installation before the Claude Code step, and explicitly block npm install, pip install, and similar package management commands in your Claude Code allow-list. More on constructing that allow-list below.

For teams deploying at scale, the CLaude coe documentation covers patterns for secrets compartmentalization in multi-step pipelines, including how to structure tool call policies that prevent credential exposure during package operations.

Constructing a Minimal Allow-List for CI-Only Workflows

Claude Code's permission system lets you define exactly which file paths and shell commands the agent can touch. In CI, you should never use --dangerously-skip-permissions as a shortcut. That flag exists for local environments where you understand the scope of what you're running. In a pipeline with production credentials, it removes the only guardrail between the agent and your secrets store.

A minimal CI allow-list for a code review workflow looks like this in .claude/settings.json:

{
  "permissions": {
    "allow": [
      "Read(**)",
      "Bash(git diff*)",
      "Bash(git log*)",
      "Bash(git show*)"
    ],
    "deny": [
      "Bash(npm install*)",
      "Bash(pip install*)",
      "Bash(curl*)",
      "Bash(wget*)",
      "Bash(ssh*)",
      "Write(**/.env*)",
      "Write(**/credentials*)",
      "Write(**/*.pem)",
      "Write(**/*.key)"
    ]
  }
}

Enter fullscreen mode Exit fullscreen mode

The deny rules matter as much as the allow rules. Explicitly blocking outbound network commands — curl, wget, anything that could exfiltrate data — means a successful prompt injection attack still can't phone home. The deny list is your backstop when the allow list isn't tight enough.

For CI pipelines that need Claude Code to run tests or build steps, create a separate settings file committed to the repo and reference it explicitly. Don't reuse your local development settings — those are typically broader and exist because you're watching the terminal.

Network Egress Controls

A permission allow-list controls what Claude Code can invoke, but it doesn't control what the AI model itself might instruct it to do in edge cases, and it doesn't control network calls made by tools that Claude Code legitimately runs. A defense-in-depth approach adds network egress controls at the infrastructure layer.

For GitHub Actions, this means running Claude Code jobs on a self-hosted runner behind a network policy that allows outbound HTTPS only to Anthropic's API endpoints. For most teams, the relevant domain is api.anthropic.com. Everything else — arbitrary outbound connections to third-party domains — should be blocked at the firewall or security group level.

On Kubernetes-based CI infrastructure, a NetworkPolicy that restricts egress to a specific CIDR range or named service achieves the same effect. The goal is that even if an attacker successfully injects a prompt that asks Claude Code to exfiltrate data via a shell command, the network layer rejects the connection before any data leaves your environment.

This isn't paranoia. It's the same control you'd apply to any process running in CI that handles sensitive credentials. Claude Code is a powerful tool, and the network controls you'd put around a deployment script should apply here too.

Runner Isolation: Docker Containers and Ephemeral Environments

The cleanest way to contain Claude Code's blast radius in CI is to run it inside an ephemeral Docker container that's destroyed at the end of the job. This limits persistent side effects — anything Claude Code writes to disk is gone when the container exits, assuming you're not mounting volumes with your secrets into the container image.

The pattern that fails is mounting ~/.aws, ~/.ssh, or similar credential directories into the container to give Claude Code access to deployment tools. If those directories are mounted, Claude Code and anything it invokes has full access to those credentials. Instead, provision short-lived credentials via your CI provider's secrets mechanism and inject them only for the specific steps that need them.

Ephemeral runners also prevent state from accumulating across jobs. A persistent runner can accumulate environment state, leftover files from previous jobs, and tool installations that weren't part of the original image. Ephemeral runners start clean every time, which eliminates an entire class of side-channel leaks.

If you're using GitHub Actions' hosted runners, you already get ephemeral environments by default. The risk there is configuration — what you inject into that ephemeral environment and how broadly you've scoped your secrets exposure.

A Concrete GitHub Actions Configuration

Here's a minimal but realistic GitHub Actions workflow for running Claude Code as a code review step, with the security controls applied:

name: AI Code Review

on:
  pull_request:
    types: [opened, synchronize]

jobs:
  claude-review:
    runs-on: ubuntu-latest
    permissions:
      contents: read
      pull-requests: write

    steps:
      - name: Checkout code
        uses: actions/checkout@v4
        with:
          fetch-depth: 0

      - name: Install dependencies
        run: npm ci  # run before Claude Code step, not inside it

      - name: Run Claude Code review
        env:
          ANTHROPIC_API_KEY: ${{ secrets.ANTHROPIC_API_KEY }}
        run: |
          claude --print \
            --settings .claude/settings.ci.json \
            "Review the changes in this PR for security issues. Output findings as JSON."

Enter fullscreen mode Exit fullscreen mode

The key decisions here: npm ci runs before the Claude Code step so package installation never happens inside the agent's execution context. The API key is scoped to a single step. A separate CI-specific settings file controls what the agent can do. The runner permissions are minimal — contents: read plus pull-requests: write only if you want Claude Code to post review comments.

At CLaude coe, we built our review tooling around this pattern after encountering CI pipelines where ANTHROPIC_API_KEY was exposed at the job level alongside production database credentials. The combination is dangerous, and it's surprisingly common. See the CLaude coe product overview for how we approach policy enforcement across CI pipelines at scale.

Container Teardown Patterns That Contain Blast Radius

Teardown matters as much as setup. After a Claude Code CI job completes, you want to ensure no sensitive data persists in the runner environment, no temporary files containing secrets were written to unprotected paths, and no background processes were spawned that outlive the job.

Add an explicit cleanup step that runs regardless of job success or failure:

      - name: Cleanup
        if: always()
        run: |
          rm -f /tmp/claude-* 2>/dev/null || true
          unset ANTHROPIC_API_KEY

Enter fullscreen mode Exit fullscreen mode

For Docker-based pipelines, the container itself handles most of this — exit the container and the filesystem is gone. The risk is bind mounts. Audit every -v flag in your Docker run command and ask whether that mount needs to exist for this specific job. If Claude Code only needs to read source code, a read-only bind mount of the repository is sufficient: -v $(pwd):/workspace:ro.

The write scope of your Claude Code session should match the write scope of the CI job. If the job's purpose is code review, Claude Code shouldn't have write access to anything. If it's generating code that gets committed back, the write scope should be limited to the files in the PR branch, not the entire repository and not any infrastructure configuration.

Teams managing multiple AI-assisted pipelines benefit from centralized policy enforcement rather than per-repo settings files. That's the use case the CLaude coe pricing tiers are built around — managing consistent security policy across dozens of pipelines without requiring each team to get the settings right independently.

Frequently Asked Questions

Is Claude Code safe to run in GitHub Actions?

Claude Code can be run safely in GitHub Actions, but not with default settings. The hosted runners are ephemeral, which helps, but you still need to scope ANTHROPIC_API_KEY to a single step, use a CI-specific settings file with a minimal allow-list, and block package installation from running inside the agent's execution context. Without these controls, you're running a capable AI agent in an environment with access to every secret your CI system holds.

How do I prevent prompt injection in CI pipelines?

The primary defense is to avoid passing untrusted external input directly to Claude Code. Don't pipe PR descriptions, commit messages, or external API responses into a Claude prompt without sanitizing them first. Secondary defenses include deny rules in your settings file that block network egress commands, so even a successful injection can't exfiltrate data. Reviewing Claude Code's output logs from CI runs is also valuable — injection attempts that fail still leave evidence.

Should I use --dangerously-skip-permissions in production CI?

No. --dangerously-skip-permissions removes the allow-list and deny-list controls entirely, which is the main security layer between Claude Code and everything your CI runner can access. This flag exists for local development workflows where a human is watching. In a headless CI environment with production credentials on the runner, it means Claude Code has unrestricted shell access and file system access for the duration of the job. Build a proper settings file instead.

What secrets does Claude Code expose in CI?

Claude Code itself doesn't exfiltrate secrets, but it can expose them indirectly. If secrets are in the environment and Claude Code runs commands that print environment variables — or if a subprocess does — those values appear in logs. npm postinstall scripts, for example, can read and transmit environment variables during installation. The pattern to avoid is running package installation inside a Claude Code tool call when ANTHROPIC_API_KEY or deployment credentials are present in the environment.

How do I scope ANTHROPIC_API_KEY to a single step?

In GitHub Actions, set the key in the env: block of the specific step that runs Claude Code, not in the job-level env: block. Environment variables set at the step level are only available to that step's process, not to preceding or subsequent steps. This won't prevent Claude Code's own subprocess calls from inheriting it during that step, but it limits exposure to exactly the step that legitimately needs it, rather than every step in the job including dependency installation and test execution.

The controls described here — secrets scoping, allow-lists with explicit deny rules, network egress restrictions, ephemeral containers, and disciplined teardown — aren't Claude Code-specific. They're the same controls you should apply to any automated process with access to production credentials. What makes Claude Code worth specific attention is that it's a general-purpose agent capable of taking actions you didn't explicitly script. That capability is what makes it useful. In CI, it's also what makes misconfiguration consequential. CLaude coe exists specifically to help teams get this right without requiring every engineer to become an expert in AI agent security policy. Secure Claude Code.

Top comments (0)