From 3067d82e1ad1a017444d06e8067d8be673075de9 Mon Sep 17 00:00:00 2001 From: Julian lechner Date: Thu, 1 Oct 2026 10:22:42 +0200 Subject: [PATCH] feat(claude): allow Context7 domain in enabled sandbox Where the Claude sandbox is enabled, permit network access to mcp.context7.com only, and validate that exact shape. Co-Authored-By: Claude Opus 5.5 --- scripts/checks/validate-config.py | 5 +++-- scripts/commands/build.ps1 | 2 +- scripts/commands/build.sh | 2 +- scripts/tests/test-shell-packages.ps1 | 2 +- scripts/tests/test-shell-packages.sh | 2 +- 5 files changed, 7 insertions(+), 6 deletions(-) diff --git a/scripts/checks/validate-config.py b/scripts/checks/validate-config.py index c1e1f48..8cfe0db 100644 --- a/scripts/checks/validate-config.py +++ b/scripts/checks/validate-config.py @@ -11,7 +11,7 @@ from pathlib import Path CLAUDE_TOP_LEVEL = {"permissions", "sandbox", "env", "statusLine", "hooks"} CLAUDE_PERMISSION_KEYS = {"defaultMode", "allow", "deny"} -CLAUDE_SANDBOX_KEYS = {"enabled", "allowUnsandboxedCommands", "failIfUnavailable"} +CLAUDE_SANDBOX_KEYS = {"enabled", "allowUnsandboxedCommands", "failIfUnavailable", "network"} CLAUDE_STATUS_LINE_KEYS = {"type", "command"} CLAUDE_HOOK_KEYS = {"type", "command", "timeout"} CLAUDE_PERMISSION_MODES = {"acceptEdits", "auto", "bypassPermissions", "default", "dontAsk", "plan"} @@ -40,7 +40,8 @@ def validate_claude(path: Path) -> None: if not isinstance(permissions.get(key), list) or not all(isinstance(item, str) for item in permissions[key]): fail(f"permissions.{key} must be an array of strings") sandbox = require_keys(settings.get("sandbox"), CLAUDE_SANDBOX_KEYS, "sandbox") - if sandbox != {"enabled": False} and sandbox != {"enabled": True, "allowUnsandboxedCommands": False, "failIfUnavailable": True}: + if sandbox != {"enabled": False} and sandbox != {"enabled": True, "allowUnsandboxedCommands": False, "failIfUnavailable": True, + "network": {"allowedDomains": ["mcp.context7.com"]}}: fail("sandbox must be disabled or enabled with fail-closed settings") env = settings.get("env") if not isinstance(env, dict) or not all(isinstance(key, str) and isinstance(value, str) for key, value in env.items()): diff --git a/scripts/commands/build.ps1 b/scripts/commands/build.ps1 index 6d67e03..4573184 100644 --- a/scripts/commands/build.ps1 +++ b/scripts/commands/build.ps1 @@ -9,7 +9,7 @@ if ($sandboxSupported) { $kernel = (& uname -r 2>$null | Out-String).Trim() if ($kernel -match '(?i)microsoft') { $sandboxSupported = $kernel -match '(?i)microsoft-standard-WSL2' } } -$claudeSandbox = if ($sandboxSupported) { '{"enabled": true, "allowUnsandboxedCommands": false, "failIfUnavailable": true}' } else { '{"enabled": false}' } +$claudeSandbox = if ($sandboxSupported) { '{"enabled": true, "allowUnsandboxedCommands": false, "failIfUnavailable": true, "network": {"allowedDomains": ["mcp.context7.com"]}}' } else { '{"enabled": false}' } $pluginManifest = Join-Path $root 'adapters/plugins.tsv' $capabilityManifest = Join-Path $root 'adapters/claude/capabilities.tsv' diff --git a/scripts/commands/build.sh b/scripts/commands/build.sh index a841373..9ef9e0e 100644 --- a/scripts/commands/build.sh +++ b/scripts/commands/build.sh @@ -24,7 +24,7 @@ case "$platform" in ;; esac sandbox='{"enabled": false}' -[ "$sandbox_supported" != true ] || sandbox='{"enabled": true, "allowUnsandboxedCommands": false, "failIfUnavailable": true}' +[ "$sandbox_supported" != true ] || sandbox='{"enabled": true, "allowUnsandboxedCommands": false, "failIfUnavailable": true, "network": {"allowedDomains": ["mcp.context7.com"]}}' plugin_manifest="$root/adapters/plugins.tsv" capability_manifest="$root/adapters/claude/capabilities.tsv" diff --git a/scripts/tests/test-shell-packages.ps1 b/scripts/tests/test-shell-packages.ps1 index 0f431b0..ce39969 100644 --- a/scripts/tests/test-shell-packages.ps1 +++ b/scripts/tests/test-shell-packages.ps1 @@ -62,7 +62,7 @@ foreach ($shell in @('powershell','bash')) { throw "Claude finish hook is incorrect in $package" } elseif ($content -notmatch '"defaultMode"\s*:\s*"auto"') { throw "Claude auto permission mode is missing in $package" - } elseif ($sandboxSupported -and ($settings.sandbox.enabled -ne $true -or $settings.sandbox.allowUnsandboxedCommands -ne $false -or $settings.sandbox.failIfUnavailable -ne $true -or @($settings.sandbox.PSObject.Properties).Count -ne 3)) { + } elseif ($sandboxSupported -and ($settings.sandbox.enabled -ne $true -or $settings.sandbox.allowUnsandboxedCommands -ne $false -or $settings.sandbox.failIfUnavailable -ne $true -or (@($settings.sandbox.network.allowedDomains) -join ',') -ne 'mcp.context7.com' -or @($settings.sandbox.PSObject.Properties).Count -ne 4)) { throw "Claude strict sandbox is missing in $package" } elseif (-not $sandboxSupported -and ($settings.sandbox.enabled -ne $false -or @($settings.sandbox.PSObject.Properties).Count -ne 1)) { throw "Claude sandbox must be disabled in $package" diff --git a/scripts/tests/test-shell-packages.sh b/scripts/tests/test-shell-packages.sh index 0a0fb89..42f4ccb 100644 --- a/scripts/tests/test-shell-packages.sh +++ b/scripts/tests/test-shell-packages.sh @@ -80,7 +80,7 @@ for shell in powershell bash; do ! grep -q 'flashbang-if-input' "$package/$file" ! grep -q '"async"[[:space:]]*:[[:space:]]*true' "$package/$file" grep -q '"defaultMode"[[:space:]]*:[[:space:]]*"auto"' "$package/$file" - python3 -c 'import json,sys; settings=json.load(open(sys.argv[1], encoding="utf-8-sig")); expected={"enabled": True, "allowUnsandboxedCommands": False, "failIfUnavailable": True} if sys.argv[2] == "true" else {"enabled": False}; assert settings["sandbox"] == expected' "$package/$file" "$sandbox_supported" + python3 -c 'import json,sys; settings=json.load(open(sys.argv[1], encoding="utf-8-sig")); expected={"enabled": True, "allowUnsandboxedCommands": False, "failIfUnavailable": True, "network": {"allowedDomains": ["mcp.context7.com"]}} if sys.argv[2] == "true" else {"enabled": False}; assert settings["sandbox"] == expected' "$package/$file" "$sandbox_supported" statusline_extension='sh' [ "$shell" != powershell ] || statusline_extension=ps1 test -f "$package/statusline/statusline.$statusline_extension"