Skip to content

Disable dynamic PowerShell execution by default - #3082

Open
jebrans wants to merge 16 commits into
mainfrom
dev/jebransyed/31000000728707-powershell-containment
Open

jebrans wants to merge 16 commits into
mainfrom
dev/jebransyed/31000000728707-powershell-containment

Conversation

@jebrans

@jebrans jebrans commented Sep 25, 2026 •

Copy link
Copy Markdown
Contributor

Summary

  • Disable dynamic PowerShell execution by default.
  • Require both powershell.dynamicExecution.enabled: true and powershell.brokerExecution.enabled: true before any generated, imported, edited, seeded, or manually stored script can run.
  • Run all dynamic scripts in a Windows AppContainer with no network capability, a sanitized environment, explicit inherited handles, and a kill-on-close Job Object limited to one process.
  • Apply ConstrainedLanguage and positive AST/command validation inside the brokered host.
  • Preserve reviewed built-in namespace actions on a separate trusted execution path.
  • Track script provenance and fail closed for missing or unknown origins.

Security boundary

Dynamic script text no longer reaches the legacy FullLanguage host directly. The TypeScript runner sends a bounded JSON request over stdin to an architecture-specific broker. The broker:

  • creates or derives a dedicated AppContainer profile;
  • writes the request only into the AppContainer profile;
  • launches Windows PowerShell using PROC_THREAD_ATTRIBUTE_SECURITY_CAPABILITIES;
  • inherits only stdin/stdout/stderr handles;
  • assigns PowerShell to a Job Object with ACTIVE_PROCESS_LIMIT=1, DIE_ON_UNHANDLED_EXCEPTION, and KILL_ON_JOB_CLOSE;
  • supplies a minimal environment with no parent-process credentials or API keys;
  • terminates the complete job on timeout or broker shutdown;
  • cleans the profile before and after execution.

The brokered host rejects type expressions, unsafe type conversions, member invocation, redirection, invocation operators, dynamic commands, using statements, module requirements, and ForEach-Object member-invocation parameter sets. It accepts only a small server-defined command set for data transformation and output.

Capability policy

Dynamic PowerShell currently receives no external filesystem, module, network, executable, service, registry, COM, or child-process capability. Model-authored allowedModules, networkAccess, and path grants are no longer accepted as authority. Requests containing unsupported commands or capabilities fail with a non-retryable policy denial.

This intentionally means custom file/network/process PowerShell flows remain unavailable. Reviewed built-in actions such as listFiles remain available through their separate static registry. Additional dynamic capabilities require future broker-mediated implementations and security review; they must not be added by widening the model-authored policy.

Original incident validation

With both opt-in gates enabled:

  • The reported sibling-path [System.IO.File]::ReadAllText case is denied and returns no file contents.
  • Static .NET file writes and process creation are denied with no marker side effect.
  • ForEach-Object -MemberName and positional member invocation are denied.
  • using module and #requires -Modules are denied.
  • Network, module, filesystem, and process capability requests are denied.
  • Parent-process secrets are absent from the AppContainer environment.
  • Timeout and cancellation terminate the sandbox job.
  • Missing or failed broker startup has no legacy fallback.

Packaging

  • Adds a trimmed self-contained .NET 8 broker for win-x64 and win-arm64.
  • Adds platform/architecture-aware agent asset mapping so non-Windows bundles exclude the broker and cross-target bundles select the requested architecture.
  • Extends packaged-artifact validation to require the matching broker on Windows.

Validation

  • pnpm run build packages/agents/powershell
  • pnpm --filter @typeagent/powershell-typeagent test (79 tests)
  • pnpm --filter @typeagent/config test (199 tests)
  • Broker Debug and Release builds, zero warnings
  • Dedicated Windows broker integration lane and fast monorepo Windows unit lane
  • Windows x64 bundle includes only the x64 broker; Linux bundle excludes broker assets
  • pnpm run prettier:changed:fix
  • lint, complexity, circular-dependency, and test-debt PR ratchets

Rollback

Turning either execution gate off disables dynamic PowerShell. There is no fallback from brokered dynamic execution to the legacy host. Reviewed static actions remain available independently.

jebrans and others added 4 commits September 26, 2026 19:41
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
@jebrans
jebrans force-pushed the dev/jebransyed/31000000728707-powershell-containment branch from 3fe64e5 to 6cd4398 Compare September 27, 2026 02:52
@jebrans
jebrans enabled auto-merge September 27, 2026 06:25
@jebrans
jebrans disabled auto-merge September 27, 2026 06:25
Preserve explicit false overrides, reject non-YAML opt-ins and invalid configuration, and enforce the dynamic execution gate at the final runner boundary.\n\nCo-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
@jebrans
jebrans enabled auto-merge September 27, 2026 07:07
@jebrans
jebrans disabled auto-merge September 27, 2026 08:13
@jebrans
jebrans enabled auto-merge September 27, 2026 08:14
Route untrusted scripts through a fail-closed Windows AppContainer and Job Object broker, constrain the PowerShell language and commands, sanitize the environment, package architecture-specific broker binaries, and add exploit regressions.\n\nCo-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Use per-execution AppContainer profiles and a cancellation pipe instead of a global broker lock so parallel workers remain isolated without timing out.\n\nCo-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Run the PowerShell package tests in-band and use production-like broker timeouts so Windows CI does not starve concurrent AppContainer processes.\n\nCo-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Keep the monorepo Windows test fan-out on fast unit coverage and run the AppContainer integration suite in the isolated Release broker job, while preserving full local coverage.\n\nCo-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Use the request-path contract rather than mutable runspace state when reporting untrusted broker failures, avoiding dependency on removed PowerShell commands.\n\nCo-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Keep host-side reporting and output cmdlets available while AST validation continues to prevent attacker scripts from invoking unapproved commands.\n\nCo-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Avoid mutating InitialSessionState for untrusted scripts, enforce the command boundary through positive AST validation, and reject user-defined functions and types that could shadow approved commands.\n\nCo-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Expose internal host initialization details only in the dedicated CI lane so Windows runner-specific failures can be diagnosed without changing production error disclosure.\n\nCo-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Pass the diagnostics switch on the fixed broker command line so request-file and host initialization failures are visible in the isolated CI lane without exposing production details.\n\nCo-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Stop treating command-table mutation as a boundary for reviewed static actions while untrusted scripts remain constrained by AppContainer, CLM, and positive AST validation.\n\nCo-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Construct the untrusted runspace variable entry through the framework API so the broker can initialize on Windows images where outer-host utility cmdlets are unavailable.\n\nCo-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>

This branch has not been deployed

No deployments
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.

2 participants