feat: add sandbox for running Claude Code in Docker - #75
Conversation
📝 WalkthroughWalkthroughAdds a sandbox Docker image and supporting artifacts: Changes
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
Estimated code review effort🎯 2 (Simple) | ⏱️ ~10 minutes Poem
🚥 Pre-merge checks | ✅ 3✅ Passed checks (3 passed)
✏️ Tip: You can configure your own custom pre-merge checks in the settings. ✨ Finishing Touches
🧪 Generate unit tests (beta)
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. Comment |
There was a problem hiding this comment.
Actionable comments posted: 3
🧹 Nitpick comments (2)
sandbox/Dockerfile (2)
1-1: Pin the base image to a digest for reproducible builds.
debian:bookworm-slimis 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-recommendsto reduce image size.Trivy DS-0029 flags the missing flag. Without it,
apt-getpulls 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.
There was a problem hiding this comment.
♻️ Duplicate comments (1)
sandbox/Dockerfile (1)
30-32:⚠️ Potential issue | 🟠 MajorSupply-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:
- No version pin — the install always pulls the latest release, so a future breaking or malicious version is silently adopted.
- 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.shReplace
<sha256-of-release-script>with the hash published in the release manifest forCLAUDE_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-slimis 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-recommendsto reduce image size (Trivy DS-0029).Without this flag,
apt-getpulls 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
📒 Files selected for processing (3)
.secrets.baselinesandbox/Dockerfilesandbox/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.
There was a problem hiding this comment.
🧹 Nitpick comments (2)
sandbox/Dockerfile (2)
4-17: Add--no-install-recommendsto reduce image size and attack surface.Without this flag,
apt-get installpulls recommended packages in addition to the required ones, inflating the image with unnecessary software.--no-install-recommendsis part of the well-formedapt-getpattern 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-slimtags 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
📒 Files selected for processing (4)
.secrets.baselinesandbox/Dockerfilesandbox/README.mdsandbox/sample.env
🚧 Files skipped from review as they are similar to previous changes (2)
- sandbox/sample.env
- sandbox/README.md
For #66
Summary by CodeRabbit
New Features
Documentation