From 90e3280503e109bc82820a42dd13ba410f596985 Mon Sep 17 00:00:00 2001 From: Dan Guido Date: Tue, 10 Feb 2026 03:20:33 -0500 Subject: [PATCH] Mount .devcontainer/ read-only to prevent container escape on rebuild (#13) A process inside the container could modify .devcontainer/devcontainer.json to inject malicious mounts or initializeCommand entries that execute on the host during the next rebuild. Bind-mounting .devcontainer/ as read-only blocks this privilege escalation vector. Uses startswith() for the jq filter to be precise and consistent with other mount filters, and documents the SYS_ADMIN guard requirement. Based on PR #12 with review feedback from @dguido addressed. Co-authored-by: Claude Opus 4.6 --- devcontainer.json | 3 ++- install.sh | 10 +++++++++- 2 files changed, 11 insertions(+), 2 deletions(-) diff --git a/devcontainer.json b/devcontainer.json index fde560d..effdf98 100644 --- a/devcontainer.json +++ b/devcontainer.json @@ -46,7 +46,8 @@ "source=claude-code-bashhistory-${devcontainerId},target=/commandhistory,type=volume", "source=claude-code-config-${devcontainerId},target=/home/vscode/.claude,type=volume", "source=claude-code-gh-${devcontainerId},target=/home/vscode/.config/gh,type=volume", - "source=${localEnv:HOME}/.gitconfig,target=/home/vscode/.gitconfig,type=bind,readonly" + "source=${localEnv:HOME}/.gitconfig,target=/home/vscode/.gitconfig,type=bind,readonly", + "source=${localWorkspaceFolder}/.devcontainer,target=/workspace/.devcontainer,type=bind,readonly" ], "containerEnv": { "NODE_OPTIONS": "--max-old-space-size=4096", diff --git a/install.sh b/install.sh index ee056aa..decd4b1 100755 --- a/install.sh +++ b/install.sh @@ -82,6 +82,12 @@ get_workspace_folder() { # Extract custom mounts from devcontainer.json to a temp file # Returns the temp file path, or empty string if no custom mounts +# +# Security: .devcontainer/ is mounted read-only inside the container to prevent +# a compromised process from injecting malicious mounts or commands into +# devcontainer.json that execute on the host during rebuild. This protection +# requires that SYS_ADMIN is never added to runArgs (it would allow remounting +# read-write). extract_mounts_to_file() { local devcontainer_json="$1" local temp_file @@ -98,7 +104,9 @@ extract_mounts_to_file() { (startswith("source=claude-code-bashhistory-") | not) and (startswith("source=claude-code-config-") | not) and (startswith("source=claude-code-gh-") | not) and - (startswith("source=${localEnv:HOME}/.gitconfig,") | not) + (startswith("source=${localEnv:HOME}/.gitconfig,") | not) and + # Security: read-only .devcontainer mount prevents container escape on rebuild + (startswith("source=${localWorkspaceFolder}/.devcontainer,") | not) ) ) | if length > 0 then . else empty end ' "$devcontainer_json" 2>/dev/null) || true