Skip to content

Security: prevent env var leakage into AppContainer child process#460

Merged
kanismohammed merged 4 commits into
mainfrom
jsidewhite/mxc_warp_ACR_env_inherits_PR
May 29, 2026
Merged

Security: prevent env var leakage into AppContainer child process#460
kanismohammed merged 4 commits into
mainfrom
jsidewhite/mxc_warp_ACR_env_inherits_PR

Conversation

@jsidewhite

@jsidewhite jsidewhite commented May 29, 2026

Copy link
Copy Markdown
Member
  • I have signed the Contributor License Agreement.
  • This pull request is related to an issue.
  • If this PR changes build commands, project architecture, or key conventions, I have updated [.github/copilot-instructions.md](.github/copilot-instructions.md).

Summary

Fixes a security bug where the AppContainer child process inherited the parent's full environment block (secrets, tokens, internal vars from wxc-exec) when no explicit env was specified in config.json.

Fix: Always build an explicit environment block. When no env is provided by the caller, use CreateEnvironmentBlock(token, bInherit=FALSE) to get only the clean system/user profile variables.

New behavior for all four cases:

  • Explicit env, no proxy: use caller's env vars verbatim
  • Explicit env, with proxy: use caller's env vars, strip any existing proxy vars, inject HTTP_PROXY/HTTPS_PROXY from config
  • No explicit env, no proxy: call CreateEnvironmentBlock(FALSE) for a clean default user env block (no inheritance from parent process)
  • No explicit env, with proxy: call CreateEnvironmentBlock(FALSE), strip any profile-level proxy vars, inject HTTP_PROXY/HTTPS_PROXY from config
Microsoft Reviewers: Open in CodeFlow

Previously, when no explicit env was specified in config.json, NULL was
passed to CreateProcessW -- inheriting the parent process's full
environment into the sandboxed child. This is a security bug: secrets,
tokens, and internal vars from wxc-exec leak into the AppContainer.

Fix: always build an explicit environment block via
CreateEnvironmentBlock(token, bInherit=FALSE) which returns only the
clean system/user profile variables.

New behavior for all four cases:

- Explicit env, no proxy: use caller's env vars verbatim
- Explicit env, with proxy: use caller's env vars, strip any existing
  proxy vars, inject HTTP_PROXY/HTTPS_PROXY from config
- No explicit env, no proxy: call CreateEnvironmentBlock(FALSE) for a
  clean default user env block (no inheritance from parent process)
- No explicit env, with proxy: call CreateEnvironmentBlock(FALSE), strip
  any profile-level proxy vars, inject HTTP_PROXY/HTTPS_PROXY from config

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Copilot AI review requested due to automatic review settings May 29, 2026 18:55

Copilot AI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

This PR addresses a security issue in the Windows AppContainer runner where the sandboxed child process could inherit the full wxc-exec process environment (potentially leaking secrets) when no explicit env was provided. It changes the runner to always pass an explicit environment block to CreateProcessW, using CreateEnvironmentBlock(..., bInherit=FALSE) as the “clean default” baseline when the caller doesn’t specify env vars.

Changes:

  • Always builds and passes a Unicode environment block to CreateProcessW (never NULL inheritance).
  • Adds logic to construct a clean default env block via CreateEnvironmentBlock and optionally strip/re-inject proxy vars.
  • Introduces env-block parsing from OS-provided UTF-16 blocks into (key, value) pairs to support re-encoding.
Show a summary per file
File Description
src/wxc_common/src/appcontainer_runner.rs Builds an explicit child environment block in all cases; adds default-env creation via CreateEnvironmentBlock plus proxy override handling.

Copilot's findings

  • Files reviewed: 1/1 changed files
  • Comments generated: 2

Comment on lines +108 to +116
// Split on the first '=' (env vars can have '=' in the value).
// Skip entries that start with '=' (hidden drive-letter vars like "=C:=C:\").
if let Some(eq_pos) = entry.find('=') {
if eq_pos > 0 {
let key = entry[..eq_pos].to_string();
let value = entry[eq_pos + 1..].to_string();
entries.push((key, value));
}
}

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

fixed

Comment on lines +613 to +618
let env_block: Vec<u16> = if !request.env.is_empty() {
let entries = build_explicit_entries(&request.env, self.proxy_address.as_ref());
Some(encode_env_block(&entries))
} else if let Some(addr) = self.proxy_address.as_ref() {
// No explicit env but proxy is active -- inject only proxy vars.
let proxy_url = addr.to_url();
let entries = vec![
("HTTP_PROXY".to_string(), proxy_url.clone()),
("HTTPS_PROXY".to_string(), proxy_url),
];
Some(encode_env_block(&entries))
encode_env_block(&entries)
} else {
None
// Get clean default user env without inheriting process env vars.
let mut entries = create_default_env_entries()?;

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

fixed

@jsidewhite

Copy link
Copy Markdown
Member Author

/azp run

@azure-pipelines

Copy link
Copy Markdown
Azure Pipelines successfully started running 1 pipeline(s).

@jsidewhite

Copy link
Copy Markdown
Member Author

/azp run

@azure-pipelines

Copy link
Copy Markdown
Azure Pipelines successfully started running 1 pipeline(s).

@jsidewhite

Copy link
Copy Markdown
Member Author

/azp run

@azure-pipelines

Copy link
Copy Markdown
Azure Pipelines successfully started running 1 pipeline(s).

@jsidewhite

Copy link
Copy Markdown
Member Author

/azp run

@azure-pipelines

Copy link
Copy Markdown
Azure Pipelines successfully started running 1 pipeline(s).

Comment on lines +106 to +109
while unsafe { *block.add(offset) } != 0 {
offset += 1;
}
let slice = unsafe { std::slice::from_raw_parts(block.add(start), offset - start) };

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

note: missing safety statements although I don't expect this to actually be malformed since we get them directly from the WIndows API

@kanismohammed
kanismohammed merged commit fc8bbf2 into main May 29, 2026
20 checks passed
@kanismohammed
kanismohammed deleted the jsidewhite/mxc_warp_ACR_env_inherits_PR branch May 29, 2026 21:14
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

5 participants