Skip to content

feat: add sandbox for running Claude Code in Docker - #75

Merged
vinodmut merged 5 commits into
AgentToolkit:mainfrom
vinodmut:sandbox
Feb 25, 2026
Merged

feat: add sandbox for running Claude Code in Docker#75
vinodmut merged 5 commits into
AgentToolkit:mainfrom
vinodmut:sandbox

Conversation

@vinodmut

@vinodmut vinodmut commented Feb 24, 2026

Copy link
Copy Markdown
Contributor

For #66

Summary by CodeRabbit

  • New Features

    • Added a Docker-based sandbox for running Claude Code with pre-configured system utilities, a non-root user, and a ready-to-use Python environment.
  • Documentation

    • Added a sandbox setup guide with build, run, and test instructions.
    • Included a sample environment file with an API key placeholder for local testing.

@coderabbitai

coderabbitai Bot commented Feb 24, 2026

Copy link
Copy Markdown
📝 Walkthrough

Walkthrough

Adds a sandbox Docker image and supporting artifacts: sandbox/Dockerfile, sandbox/README.md, sandbox/sample.env, and an updated .secrets.baseline entry. The Dockerfile creates a non-root sandbox user, Python venv, and installs Claude Code; README and sample env provide build/run/test instructions and an API key placeholder.

Changes

Cohort / File(s) Summary
Sandbox Dockerfile
sandbox/Dockerfile
New Debian bookworm-slim image: installs utilities, Python, jq; creates non-root sandbox user and /workspace; sets up Python venv at /opt/claude-venv, upgrades pip, installs Claude Code via install script, updates PATH, sets WORKDIR and default CMD.
Docs & Env
sandbox/README.md, sandbox/sample.env
Adds README with build/run/test commands and a sample env file containing ANTHROPIC_API_KEY=sk-ant-xxxx.
Secrets baseline
.secrets.baseline
Updates generated_at timestamp, adjusts README.md line_number, and adds sandbox/sample.env entry detecting a secret keyword placeholder at line 1.

Sequence Diagram(s)

sequenceDiagram
    participant Dev as Developer
    participant Docker as Docker daemon
    participant Container as Sandbox Container
    participant Claude as Claude Code (venv)
    participant API as Anthropic API

    Dev->>Docker: docker build ./sandbox
    Docker->>Container: create image & start container (non-root sandbox)
    Dev->>Container: docker run / exec shell
    Container->>Claude: invoke Claude Code in /opt/claude-venv
    Claude->>API: send request using ANTHROPIC_API_KEY
    API-->>Claude: return response
    Claude-->>Container: output results/logs
    Container-->>Dev: display logs / test output
Loading

Estimated code review effort

🎯 2 (Simple) | ⏱️ ~10 minutes

Poem

🐰 I hop into a Debian den so bright,
I spin a tiny venv and tuck it tight,
Claude hums in /opt, ready to play,
Keys in a sample—practice day by day,
Carrots and containers — hip-hop hooray! 🥕

🚥 Pre-merge checks | ✅ 3
✅ Passed checks (3 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed The title accurately describes the main objective: adding a sandbox Docker setup for Claude Code.
Docstring Coverage ✅ Passed No functions found in the changed files to evaluate docstring coverage. Skipping docstring coverage check.

✏️ Tip: You can configure your own custom pre-merge checks in the settings.

✨ Finishing Touches
  • 📝 Generate docstrings (stacked PR)
  • 📝 Generate docstrings (commit on current branch)
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Post copyable unit tests in a comment

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands and usage tips.

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 3

🧹 Nitpick comments (2)
sandbox/Dockerfile (2)

1-1: Pin the base image to a digest for reproducible builds.

debian:bookworm-slim is a floating tag that silently changes on every upstream push, making builds non-reproducible and potentially pulling in breaking OS-level changes.

♻️ Example — pin by digest
-FROM debian:bookworm-slim
+FROM debian:bookworm-slim@sha256:<current-digest>

Obtain the current digest with:

docker pull debian:bookworm-slim
docker inspect debian:bookworm-slim --format '{{index .RepoDigests 0}}'
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@sandbox/Dockerfile` at line 1, The Dockerfile uses the floating tag
"debian:bookworm-slim"; replace this with the pinned image digest returned by
docker inspect (e.g., change the FROM line to use the full repo@sha256:<digest>)
so builds are reproducible and won’t change when upstream updates; run docker
pull debian:bookworm-slim and docker inspect to obtain the RepoDigest and update
the FROM line accordingly.

4-17: Add --no-install-recommends to reduce image size.

Trivy DS-0029 flags the missing flag. Without it, apt-get pulls in a large set of transitive recommended packages that are not needed, increasing image size and attack surface.

♻️ Proposed fix
-RUN apt-get update && apt-get install -y \
+RUN apt-get update && apt-get install -y --no-install-recommends \
     # Basics & editors
     bash coreutils findutils grep sed gawk less nano vim tree file which procps psmisc sudo \
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@sandbox/Dockerfile` around lines 4 - 17, The apt-get installation in the
Dockerfile (the RUN line that starts with "apt-get update && apt-get install
-y") is missing the --no-install-recommends flag; update that RUN command to use
apt-get install -y --no-install-recommends so recommended packages aren’t pulled
in, keeping the existing packages list (bash, coreutils, curl, git, python3, jq,
build-essential, etc.) and preserving the subsequent rm -rf /var/lib/apt/lists/*
cleanup.
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Inline comments:
In `@sandbox/Dockerfile`:
- Around line 23-25: The Dockerfile currently uses an unsafe "RUN curl -fsSL
https://claude.ai/install.sh | bash" which pipes a remote script to bash as root
and leaves a commented alternative URL; replace this with a secure flow:
download the installer to a file (e.g., via curl), fetch the published SHA256
checksum from the versioned release manifest, verify the checksum matches
(sha256sum), then execute the installer using a pinned VERSION argument (bash
install.sh <VERSION>) rather than piping, and remove the commented
"https://install.anthropic.com/claude-code/linux" line; reference the RUN curl
-fsSL https://claude.ai/install.sh | bash command in Dockerfile to locate the
change.
- Line 1: Add a non-root user and switch to it: create a sandbox group/user
(e.g., "sandbox"), create its home directory, chown any app/workspace
directories that need write access, set HOME / ENV accordingly, and add a USER
sandbox line near the end of the Dockerfile so the container no longer runs as
root; also update the PATH reference from /root/.local/bin to the new user's
home (e.g., /home/sandbox/.local/bin) so any user-local installs resolve
correctly and ensure CMD/ENTRYPOINT continue to work for the new user.

In `@sandbox/README.md`:
- Around line 7-9: The README's docker build example uses '.' as the build
context which fails unless run from the sandbox/ directory; update the README to
prepend a step that changes into the sandbox directory (e.g., add "cd sandbox/"
before the existing "docker build -t claude-sandbox ." line) or otherwise
clarify that the command must be executed from the sandbox/ CWD so users don't
encounter a "Dockerfile not found" error.

---

Nitpick comments:
In `@sandbox/Dockerfile`:
- Line 1: The Dockerfile uses the floating tag "debian:bookworm-slim"; replace
this with the pinned image digest returned by docker inspect (e.g., change the
FROM line to use the full repo@sha256:<digest>) so builds are reproducible and
won’t change when upstream updates; run docker pull debian:bookworm-slim and
docker inspect to obtain the RepoDigest and update the FROM line accordingly.
- Around line 4-17: The apt-get installation in the Dockerfile (the RUN line
that starts with "apt-get update && apt-get install -y") is missing the
--no-install-recommends flag; update that RUN command to use apt-get install -y
--no-install-recommends so recommended packages aren’t pulled in, keeping the
existing packages list (bash, coreutils, curl, git, python3, jq,
build-essential, etc.) and preserving the subsequent rm -rf /var/lib/apt/lists/*
cleanup.

ℹ️ Review info

Configuration used: defaults

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 9721a05 and 3caa73d.

📒 Files selected for processing (3)
  • sandbox/Dockerfile
  • sandbox/README.md
  • sandbox/sample.env

Comment thread sandbox/Dockerfile
Comment thread sandbox/Dockerfile Outdated
Comment thread sandbox/README.md

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

♻️ Duplicate comments (1)
sandbox/Dockerfile (1)

30-32: ⚠️ Potential issue | 🟠 Major

Supply-chain hardening is incomplete — version pinning and checksum verification still absent.

Downloading to a file before executing (rather than curl | bash) is an improvement, but the two root concerns from the previous review remain open:

  1. No version pin — the install always pulls the latest release, so a future breaking or malicious version is silently adopted.
  2. No integrity check — the downloaded script is executed without verifying a SHA-256 checksum, leaving a MITM/CDN-compromise window.
🛡️ Proposed hardening
-# Install Claude Code as sandbox user
-USER sandbox
-RUN curl -fsSL -o /tmp/install.sh https://claude.ai/install.sh \
-    && bash /tmp/install.sh \
-    && rm /tmp/install.sh
+# Install Claude Code as sandbox user (pin version; update CLAUDE_VERSION to upgrade)
+ARG CLAUDE_VERSION=2.0.30
+USER sandbox
+RUN curl -fsSL -o /tmp/install.sh https://claude.ai/install.sh \
+    && EXPECTED_SHA256="<sha256-of-release-script>" \
+    && echo "${EXPECTED_SHA256}  /tmp/install.sh" | sha256sum -c - \
+    && bash /tmp/install.sh "${CLAUDE_VERSION}" \
+    && rm /tmp/install.sh

Replace <sha256-of-release-script> with the hash published in the release manifest for CLAUDE_VERSION.

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@sandbox/Dockerfile` around lines 30 - 32, The RUN step that downloads and
executes the installer (/tmp/install.sh) must pin the installer to a specific
release and verify its SHA-256 checksum before execution: change the download
URL to include a CLAUDE_VERSION variable (or concrete tag) and fetch the
published SHA-256 for that version, then verify the downloaded /tmp/install.sh
against that checksum (failing the build if it doesn't match) before running and
removing the file; update any references in the Dockerfile to use CLAUDE_VERSION
and the expected <sha256-of-release-script>.
🧹 Nitpick comments (2)
sandbox/Dockerfile (2)

1-1: Consider pinning the base image to a digest for reproducible builds.

debian:bookworm-slim is a floating tag; the image pulled today may differ from one pulled in six months, silently changing the build environment.

♻️ Proposed fix
-FROM debian:bookworm-slim
+FROM debian:bookworm-slim@sha256:<digest>

Obtain the current digest via docker pull debian:bookworm-slim && docker inspect debian:bookworm-slim --format='{{index .RepoDigests 0}}'.

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@sandbox/Dockerfile` at line 1, The Dockerfile uses a floating base tag ("FROM
debian:bookworm-slim"); pin the base image to a specific digest to ensure
reproducible builds by replacing that FROM line with the repo@sha256 digest you
obtain (use docker pull debian:bookworm-slim && docker inspect
debian:bookworm-slim --format='{{index .RepoDigests 0}}' to get the exact
repo@sha256 value) so the Dockerfile's FROM instruction references the immutable
digest instead of the floating tag.

4-17: Add --no-install-recommends to reduce image size (Trivy DS-0029).

Without this flag, apt-get pulls in a significant tail of suggested/recommended packages that are unused in this image.

♻️ Proposed fix
-RUN apt-get update && apt-get install -y \
+RUN apt-get update && apt-get install -y --no-install-recommends \
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@sandbox/Dockerfile` around lines 4 - 17, The apt-get install command in the
Dockerfile RUN block is currently pulling recommended packages; update the RUN
line that starts with "apt-get update && apt-get install -y" to include the
--no-install-recommends flag (i.e., use "apt-get install -y
--no-install-recommends") so the package install step in the Dockerfile does not
pull unnecessary recommended packages, and leave the rest of the multi-line
install and the trailing "&& rm -rf /var/lib/apt/lists/*" intact.
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Duplicate comments:
In `@sandbox/Dockerfile`:
- Around line 30-32: The RUN step that downloads and executes the installer
(/tmp/install.sh) must pin the installer to a specific release and verify its
SHA-256 checksum before execution: change the download URL to include a
CLAUDE_VERSION variable (or concrete tag) and fetch the published SHA-256 for
that version, then verify the downloaded /tmp/install.sh against that checksum
(failing the build if it doesn't match) before running and removing the file;
update any references in the Dockerfile to use CLAUDE_VERSION and the expected
<sha256-of-release-script>.

---

Nitpick comments:
In `@sandbox/Dockerfile`:
- Line 1: The Dockerfile uses a floating base tag ("FROM debian:bookworm-slim");
pin the base image to a specific digest to ensure reproducible builds by
replacing that FROM line with the repo@sha256 digest you obtain (use docker pull
debian:bookworm-slim && docker inspect debian:bookworm-slim --format='{{index
.RepoDigests 0}}' to get the exact repo@sha256 value) so the Dockerfile's FROM
instruction references the immutable digest instead of the floating tag.
- Around line 4-17: The apt-get install command in the Dockerfile RUN block is
currently pulling recommended packages; update the RUN line that starts with
"apt-get update && apt-get install -y" to include the --no-install-recommends
flag (i.e., use "apt-get install -y --no-install-recommends") so the package
install step in the Dockerfile does not pull unnecessary recommended packages,
and leave the rest of the multi-line install and the trailing "&& rm -rf
/var/lib/apt/lists/*" intact.

ℹ️ Review info

Configuration used: defaults

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 3caa73d and 67bac1c.

📒 Files selected for processing (3)
  • .secrets.baseline
  • sandbox/Dockerfile
  • sandbox/README.md
🚧 Files skipped from review as they are similar to previous changes (1)
  • sandbox/README.md

Add a dedicated sandbox user so the container no longer runs as root.
Also download the installer to a file before executing and update
README paths to use sandbox/myenv.

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🧹 Nitpick comments (2)
sandbox/Dockerfile (2)

4-17: Add --no-install-recommends to reduce image size and attack surface.

Without this flag, apt-get install pulls recommended packages in addition to the required ones, inflating the image with unnecessary software. --no-install-recommends is part of the well-formed apt-get pattern recommended by Docker's official best practices documentation.

♻️ Proposed fix
-RUN apt-get update && apt-get install -y \
+RUN apt-get update && apt-get install -y --no-install-recommends \
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@sandbox/Dockerfile` around lines 4 - 17, The apt-get install invocation in
the Dockerfile RUN step should include --no-install-recommends to avoid pulling
recommended packages; update the RUN command that calls "apt-get update &&
apt-get install -y \ bash coreutils ..." so that the apt-get install includes
the --no-install-recommends flag (i.e., "apt-get install -y
--no-install-recommends ..."), preserving the existing package list and the
trailing "&& rm -rf /var/lib/apt/lists/*" cleanup.

1-1: Consider pinning the base image to a SHA256 digest for reproducibility.

FROM debian:bookworm-slim tags are mutable. To fully secure supply chain integrity you can pin the image version to a specific digest, which guarantees you always use the same image version even if a publisher replaces the tag with a new image. For a developer sandbox this trade-off (manual digest updates vs. auto-received security patches) may be acceptable to leave as-is, but it's worth noting.

♻️ Example (retrieve digest via `docker manifest inspect debian:bookworm-slim`)
-FROM debian:bookworm-slim
+FROM debian:bookworm-slim@sha256:<digest>
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@sandbox/Dockerfile` at line 1, The Dockerfile uses a mutable tag "FROM
debian:bookworm-slim"; pin the base image to a specific SHA256 digest instead to
ensure reproducible builds and supply-chain integrity by replacing the tag with
the digest from `docker manifest inspect debian:bookworm-slim` (e.g., change the
FROM line to use the returned sha256 digest), and document the chosen digest so
future updates are intentional.
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Nitpick comments:
In `@sandbox/Dockerfile`:
- Around line 4-17: The apt-get install invocation in the Dockerfile RUN step
should include --no-install-recommends to avoid pulling recommended packages;
update the RUN command that calls "apt-get update && apt-get install -y \ bash
coreutils ..." so that the apt-get install includes the --no-install-recommends
flag (i.e., "apt-get install -y --no-install-recommends ..."), preserving the
existing package list and the trailing "&& rm -rf /var/lib/apt/lists/*" cleanup.
- Line 1: The Dockerfile uses a mutable tag "FROM debian:bookworm-slim"; pin the
base image to a specific SHA256 digest instead to ensure reproducible builds and
supply-chain integrity by replacing the tag with the digest from `docker
manifest inspect debian:bookworm-slim` (e.g., change the FROM line to use the
returned sha256 digest), and document the chosen digest so future updates are
intentional.

ℹ️ Review info

Configuration used: defaults

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 67bac1c and c91a15b.

📒 Files selected for processing (4)
  • .secrets.baseline
  • sandbox/Dockerfile
  • sandbox/README.md
  • sandbox/sample.env
🚧 Files skipped from review as they are similar to previous changes (2)
  • sandbox/sample.env
  • sandbox/README.md

@vinodmut
vinodmut merged commit 647aea0 into AgentToolkit:main Feb 25, 2026
15 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants