fix: SHELL path, PATH isolation, --shell flag, nix-claude-code input

- Resolve SHELL to nix store bash path (was /bin/bash which doesn't exist in sandbox)
- Inject clean SANDBOX_PATH via makeBinPath (was leaking entire host PATH)
- Add --shell flag to drop into sandboxed bash for manual verification
- Use nix-claude-code flake for claude-code binary instead of host PATH discovery

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
This commit is contained in:
Christopher Mühl 2026-04-09 14:59:43 +02:00
parent dd6742abef
commit 613d015cc1
No known key found for this signature in database
GPG key ID: 925AC7D69955293F
3 changed files with 68 additions and 25 deletions

View file

@ -1,12 +1,17 @@
# Resolve claude binary from host PATH (before clearenv strips it) # Parse claudebox flags
CLAUDE_BIN=$(readlink -f "$(command -v claude)") || { SHELL_MODE=false
echo "error: claude not found in PATH" >&2 for arg in "$@"; do
echo "Install Claude Code first: https://docs.anthropic.com/en/docs/claude-code" >&2 case "$arg" in
exit 1 --shell) SHELL_MODE=true; shift; break ;;
} --) shift; break ;;
*) break ;;
esac
done
# Capture sandbox PATH (runtimeInputs-constructed) # SANDBOX_PATH is injected by flake.nix via makeBinPath (only runtimeInputs, no host PATH)
SANDBOX_PATH="$PATH" # Resolve binary paths from runtimeInputs
SANDBOX_BASH="$(command -v bash)"
CLAUDE_BIN="$(command -v claude)"
# Record CWD # Record CWD
CWD=$(pwd) CWD=$(pwd)
@ -35,7 +40,7 @@ ENV_ARGS=(
--setenv HOME "$HOME" --setenv HOME "$HOME"
--setenv USER "$USER" --setenv USER "$USER"
--setenv PATH "$SANDBOX_PATH" --setenv PATH "$SANDBOX_PATH"
--setenv SHELL /bin/bash --setenv SHELL "$SANDBOX_BASH"
--setenv TMPDIR /tmp --setenv TMPDIR /tmp
--setenv XDG_RUNTIME_DIR /tmp --setenv XDG_RUNTIME_DIR /tmp
--setenv NIX_SSL_CERT_FILE /etc/ssl/certs/ca-certificates.crt --setenv NIX_SSL_CERT_FILE /etc/ssl/certs/ca-certificates.crt
@ -61,6 +66,13 @@ if [[ -v CLAUDEBOX_EXTRA_ENV ]]; then
done done
fi fi
# Build sandbox command
if [[ "$SHELL_MODE" == true ]]; then
SANDBOX_CMD=("$SANDBOX_BASH" "$@")
else
SANDBOX_CMD=("$CLAUDE_BIN" --dangerously-skip-permissions "$@")
fi
# exec bwrap (SAND-04 through SAND-15, UX-06, D-01) # exec bwrap (SAND-04 through SAND-15, UX-06, D-01)
exec bwrap \ exec bwrap \
--clearenv \ --clearenv \
@ -85,4 +97,4 @@ exec bwrap \
--ro-bind "$GITCONFIG_TMP" "$HOME/.gitconfig" \ --ro-bind "$GITCONFIG_TMP" "$HOME/.gitconfig" \
--bind "$CWD" "$CWD" \ --bind "$CWD" "$CWD" \
--chdir "$CWD" \ --chdir "$CWD" \
-- "$CLAUDE_BIN" --dangerously-skip-permissions "$@" -- "${SANDBOX_CMD[@]}"

21
flake.lock generated
View file

@ -1,5 +1,25 @@
{ {
"nodes": { "nodes": {
"nix-claude-code": {
"inputs": {
"nixpkgs": [
"nixpkgs"
]
},
"locked": {
"lastModified": 1775702549,
"narHash": "sha256-33oPZsvyI41U8ygJbzgb6+GkyAaKEyVUQ3VcFNckeJY=",
"owner": "ryoppippi",
"repo": "nix-claude-code",
"rev": "729a851a87d66cebc50953e9509602e28ecb9520",
"type": "github"
},
"original": {
"owner": "ryoppippi",
"repo": "nix-claude-code",
"type": "github"
}
},
"nix-index-database": { "nix-index-database": {
"inputs": { "inputs": {
"nixpkgs": [ "nixpkgs": [
@ -38,6 +58,7 @@
}, },
"root": { "root": {
"inputs": { "inputs": {
"nix-claude-code": "nix-claude-code",
"nix-index-database": "nix-index-database", "nix-index-database": "nix-index-database",
"nixpkgs": "nixpkgs" "nixpkgs": "nixpkgs"
} }

View file

@ -3,35 +3,45 @@
inputs = { inputs = {
nixpkgs.url = "github:NixOS/nixpkgs/nixpkgs-unstable"; nixpkgs.url = "github:NixOS/nixpkgs/nixpkgs-unstable";
nix-claude-code = {
url = "github:ryoppippi/nix-claude-code";
inputs.nixpkgs.follows = "nixpkgs";
};
nix-index-database = { nix-index-database = {
url = "github:nix-community/nix-index-database"; url = "github:nix-community/nix-index-database";
inputs.nixpkgs.follows = "nixpkgs"; inputs.nixpkgs.follows = "nixpkgs";
}; };
}; };
outputs = { self, nixpkgs, nix-index-database, ... }: outputs = { self, nixpkgs, nix-claude-code, nix-index-database, ... }:
let let
system = "x86_64-linux"; system = "x86_64-linux";
pkgs = nixpkgs.legacyPackages.${system}; pkgs = nixpkgs.legacyPackages.${system};
claude-code = nix-claude-code.packages.${system}.default;
comma-with-db = nix-index-database.packages.${system}.comma-with-db; comma-with-db = nix-index-database.packages.${system}.comma-with-db;
runtimeDeps = [
pkgs.bubblewrap
pkgs.coreutils
pkgs.git
pkgs.curl
pkgs.jq
pkgs.ripgrep
pkgs.fd
pkgs.nix
comma-with-db
pkgs.bash
pkgs.nodejs
claude-code
];
sandboxPath = pkgs.lib.makeBinPath runtimeDeps;
in { in {
packages.${system} = { packages.${system} = {
claudebox = pkgs.writeShellApplication { claudebox = pkgs.writeShellApplication {
name = "claudebox"; name = "claudebox";
runtimeInputs = [ runtimeInputs = runtimeDeps;
pkgs.bubblewrap text = ''
pkgs.coreutils SANDBOX_PATH="${sandboxPath}"
pkgs.git '' + builtins.readFile ./claudebox.sh;
pkgs.curl
pkgs.jq
pkgs.ripgrep
pkgs.fd
pkgs.nix
comma-with-db
pkgs.bash
pkgs.nodejs
];
text = builtins.readFile ./claudebox.sh;
}; };
default = self.packages.${system}.claudebox; default = self.packages.${system}.claudebox;
}; };