BaseContainer: pass explicit env block to CreateProcessInSandbox#385
Merged
Conversation
When process.env is specified in the config, encode it as a UTF-16 environment block and pass it to Experimental_CreateProcessInSandbox with CREATE_UNICODE_ENVIRONMENT. When empty, pass NULL (OS default). SDK: inject env dict into config.process.env instead of node-pty process inheritance to prevent secret leakage into the sandbox. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
bbonaby
approved these changes
May 22, 2026
Contributor
There was a problem hiding this comment.
Pull request overview
This PR updates MXC’s Windows BaseContainer execution path and the TypeScript SDK spawn helpers to pass environment variables explicitly via the JSON config (and into Experimental_CreateProcessInSandbox), rather than inheriting them from the parent process environment.
Changes:
- Rust: Build and pass a UTF-16 environment block to
Experimental_CreateProcessInSandboxwhenprocess.envis present. - SDK: Inject the
envargument intoconfig.process.envand stop passing it vianode-pty/child_process.spawnenvironment inheritance. - Workspace: Adds a Windows crate feature intended for environment-related Win32 APIs.
Show a summary per file
| File | Description |
|---|---|
| src/wxc_common/src/base_container_runner.rs | Adds env-block encoding + passes env to BaseContainer create call. |
| src/Cargo.toml | Enables an additional windows crate feature for environment APIs. |
| sdk/src/sandbox.ts | Changes env propagation to flow through config (process.env) instead of executor process env inheritance. |
Copilot's findings
- Files reviewed: 3/3 changed files
- Comments generated: 6
Comment on lines
+47
to
+62
| fn encode_env_block(env_vars: &[String]) -> Vec<u16> { | ||
| let mut entries: Vec<(&str, &str)> = | ||
| env_vars.iter().filter_map(|e| e.split_once('=')).collect(); | ||
|
|
||
| entries.sort_by(|(a, _), (b, _)| a.to_ascii_uppercase().cmp(&b.to_ascii_uppercase())); | ||
|
|
||
| let mut block = Vec::new(); | ||
| for (key, value) in &entries { | ||
| for ch in format!("{}={}", key, value).encode_utf16() { | ||
| block.push(ch); | ||
| } | ||
| block.push(0); | ||
| } | ||
| block.push(0); | ||
| block | ||
| } |
Comment on lines
+47
to
+62
| fn encode_env_block(env_vars: &[String]) -> Vec<u16> { | ||
| let mut entries: Vec<(&str, &str)> = | ||
| env_vars.iter().filter_map(|e| e.split_once('=')).collect(); | ||
|
|
||
| entries.sort_by(|(a, _), (b, _)| a.to_ascii_uppercase().cmp(&b.to_ascii_uppercase())); | ||
|
|
||
| let mut block = Vec::new(); | ||
| for (key, value) in &entries { | ||
| for ch in format!("{}={}", key, value).encode_utf16() { | ||
| block.push(ch); | ||
| } | ||
| block.push(0); | ||
| } | ||
| block.push(0); | ||
| block | ||
| } |
Comment on lines
+47
to
+62
| fn encode_env_block(env_vars: &[String]) -> Vec<u16> { | ||
| let mut entries: Vec<(&str, &str)> = | ||
| env_vars.iter().filter_map(|e| e.split_once('=')).collect(); | ||
|
|
||
| entries.sort_by(|(a, _), (b, _)| a.to_ascii_uppercase().cmp(&b.to_ascii_uppercase())); | ||
|
|
||
| let mut block = Vec::new(); | ||
| for (key, value) in &entries { | ||
| for ch in format!("{}={}", key, value).encode_utf16() { | ||
| block.push(ch); | ||
| } | ||
| block.push(0); | ||
| } | ||
| block.push(0); | ||
| block | ||
| } |
Comment on lines
38
to
46
| windows = { version = "0.62", features = [ | ||
| "Win32_Foundation", | ||
| "Win32_Security", | ||
| "Win32_System_Com", | ||
| "Win32_System_Console", | ||
| "Win32_System_Environment", | ||
| "Win32_System_Ole", | ||
| "Win32_System_Threading", | ||
| "Win32_System_Pipes", |
Comment on lines
+446
to
+460
| function injectEnvIntoConfig( | ||
| config: ContainerConfig, | ||
| env: { [key: string]: string | undefined }, | ||
| ): void { | ||
| if (!config.process) { | ||
| config.process = { commandLine: '' }; | ||
| } | ||
| const entries: string[] = config.process.env ? [...config.process.env] : []; | ||
| for (const [key, value] of Object.entries(env)) { | ||
| if (value !== undefined) { | ||
| entries.push(`${key}=${value}`); | ||
| } | ||
| } | ||
| config.process.env = entries; | ||
| } |
Comment on lines
+471
to
+475
| // Inject env vars into config.process.env so they are passed explicitly to | ||
| // the sandboxed child via the JSON config (not via process inheritance). | ||
| if (env) { | ||
| injectEnvIntoConfig(config, env); | ||
| } |
Member
Author
|
Closes: #218 |
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.
When process.env is specified in the config, encode it as a UTF-16 environment block and pass it to Experimental_CreateProcessInSandbox with CREATE_UNICODE_ENVIRONMENT. When empty, pass NULL (OS default).
SDK: inject env dict into config.process.env instead of node-pty process inheritance to prevent secret leakage into the sandbox.
#218
Microsoft Reviewers: Open in CodeFlow