feat(sandbox): inject user env vars and secret refs into execution#12655
Open
anticorrelator wants to merge 6 commits intoversion-sandboxesfrom
Open
feat(sandbox): inject user env vars and secret refs into execution#12655anticorrelator wants to merge 6 commits intoversion-sandboxesfrom
anticorrelator wants to merge 6 commits intoversion-sandboxesfrom
Conversation
…box execution Extends per-adapter config schemas with env_vars (literal values or references to the encrypted Secret table), internet_access, and dependencies fields. Wires resolution through get_or_create_backend and forwards merged env to all seven adapters. Adds SandboxConfigDialog UI for managing entries with a secret picker, capability advertisement per adapter, and end-to-end tests.
Add user_env parameter to test-only SandboxAdapter subclasses to match the updated base signature, and apply ruff formatting to env_var_end_to_end.
Generated subprocess.run args were embedding self._packages as a nested list literal, which raises TypeError at runtime. Use * unpacking so each package becomes its own argv element. Reported by Claude Code Review on PR #12655.
…back with GQL mutations Adds 2-tier credential resolution (DB Secret → env var) for sandbox provider authentication, centralized in `get_or_create_backend()` so adapters stay credential-agnostic. Introduces `EnvVarSpec` as the declarative contract between the factory and each adapter, and exposes `setSandboxCredential` / `deleteSandboxCredential` GraphQL mutations (with `env_var_specs` validation and `_BACKEND_CACHE` invalidation) so admins can rotate sandbox credentials via the UI without a server restart.
…ve-on-save UI Lock the unified env_vars/dependencies/internet_access contract across all sandbox adapters. AdapterMetadata is the canonical source of capability advertisement; build_backend MUST raise UnsupportedOperation for any capability-gated input the adapter cannot fulfill (no silent drops). Backend reconciliation: - MODAL: flip supports_env_vars=True (runtime already worked); make ModalSandboxBackend.execute() raise UnsupportedOperation for per-call env overrides (was a silent warning); session-level user_env preserved - E2B/Vercel-Py/Vercel-TS/Modal: reject non-empty `dependencies` in build_backend - E2B/Daytona/Modal/Vercel-Py/Vercel-TS/Deno: reject non-"none" `internet_access` in build_backend - VercelPython/VercelTypescript/Deno configs: add Optional internet_access field for shape uniformity Frontend (SandboxConfigDialog): - formValuesToConfigPatch (extracted to utils.tsx for testability): deep-merge form state into a clone of the raw stored config; preserves capability-gated keys when flag is false or activeBackend is undefined, closing the False→True→False data-loss path - Advanced Config JSON editor explicitly strips capability-gated keys on submit; structured editors are the only authors - Hidden capability sections render a single muted "Not supported by the selected backend." placeholder instead of being omitted - ALLOWLIST internet_access mode marked reserved-for-future Tests: - New parametrized test_unified_config_contract.py iterates over SANDBOX_ADAPTER_METADATA.keys() and enforces flag/config-model/runtime agreement for all three capabilities; new adapters must opt-in to pass - test_capability_advertisement.py and test_queries.py refactored to parametrize from the same registry source — eliminates duplicate hard-coded matrices - test_user_env_forwarding.py covers MODAL's new execute-time policy - New formValuesToConfigPatch.test.ts covers the three preserve-on-save data-loss scenarios Documents the contract on AdapterMetadata with cross-references from SandboxAdapter.build_backend and validate_config so future adapters inherit the rules from source.
…d WASM The unified config contract tests expect adapters with dependencies_language=None or internet_access="none" in SANDBOX_ADAPTER_METADATA to raise UnsupportedOperation when build_backend is called with a conflicting config. DENO was missing the packages guard; WASM was missing both packages and internet_access guards. Mirrors the existing pattern in e2b/modal/vercel backends.
Contributor
Code reviewNo issues found. Checked for bugs and CLAUDE.md compliance. |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Adds user-defined environment variables to sandbox code execution and locks down the broader unified config contract that governs how every per-adapter capability (env vars, dependencies, internet access) is advertised, stored, rendered, and enforced. Each sandbox config carries a list of env var entries — either literal
name/valuepairs or secret references that point to an encrypted key in the Secret table — plusinternet_accessanddependenciesfields.SANDBOX_ADAPTER_METADATAadvertises which capabilities each adapter supports so the frontend renders only the editors that apply and the runtime fails closed on anything else.At resolution time literals pass through directly, secret refs are decrypted from the Secret table (with a clear error when a referenced key is missing), and the merged env dict is forwarded to the underlying provider SDK for all seven adapters (E2B, Deno, Daytona, Vercel Python/TypeScript, Modal, WASM). The settings dialog gets a new env-vars editor with a secret picker populated from the existing secrets query, so admins can bind a sandbox env var to an existing secret without re-typing the plaintext.
PR #12655 originally introduced the shape of a unified config (three structured sections:
env_vars,dependencies,internet_access, plus three capability flags onAdapterMetadata) but never locked it as a contract. This change writes the contract down and reconciles every layer so the rules are enforced rather than emergent:build_backendmust do, and how the UI should render.SandboxAdapter.build_backendandvalidate_configcross-reference it so future adapter authors find the rules from source.build_backendraisesUnsupportedOperationon non-empty input. MODALsupports_env_varsflipped to True (the runtime always worked);ModalSandboxBackend.execute()now raises on per-callenvoverrides instead of silently warning.formValuesToConfigPatch(extracted toutils.tsx) deep-merges form state into a clone of the raw stored config, preserving capability-gated keys when the flag is false oractiveBackendis undefined. The Advanced Config JSON editor cannot create or mutateenv_vars/internet_access/dependencies— only the structured editors author them. Hidden capability sections render a single muted "Not supported by the selected backend." placeholder so users see the gap is intentional.InternetAccessMode.ALLOWLISTis reserved-for-future and not user-selectable.test_unified_config_contract.pyiterates overSANDBOX_ADAPTER_METADATA.keys()and asserts flag/config-model/runtime agreement for all three capabilities;test_capability_advertisement.pyandtest_queries.pywere refactored to parametrize from the same registry source. Adding a new adapter automatically participates — no hand-maintained per-adapter list can drift.Env vars land on the sandbox config and resolve into the dict that feeds
build_backend(), rather than as a per-call argument toexecute(). This was driven by Modal's lifecycle:Sandbox.create()acceptsenv_dictbutsandbox.exec()does not, so env must land at sandbox-creation time. Routing env through the config dict gives every adapter a single injection point that maps cleanly to whatever its SDK supports — and makes env vars uniform with the other two capabilities the contract now covers.Test plan
supports_env_vars,internet_access,dependencies_language)test_unified_config_contract.py) iteratingSANDBOX_ADAPTER_METADATA.keys()— flag/config-model/runtime agreement for all three capabilitiestest_capability_advertisement.pyandtest_queries.pyrefactored to parametrize from the same registry source (no duplicate hard-coded matrix)test_user_env_forwarding.pycovers MODAL's new execute-time policy (per-callexecute(env=...)raisesUnsupportedOperation; session-leveluser_envstill forwarded)formValuesToConfigPatch.test.ts) covering the three preserve-on-save data-loss scenarios: flag flip False→True→False,activeBackendundefined, non-UI-path stored datainternet_accessset to a non-"none" mode via the API and confirmbuild_backendraisesUnsupportedOperation(fail-closed)