Skip to content

Fix the dot-sourcing behavior of pwsh -file for advanced-function scripts#27727

Open
daxian-dbw wants to merge 1 commit into
PowerShell:masterfrom
daxian-dbw:dot-source
Open

Fix the dot-sourcing behavior of pwsh -file for advanced-function scripts#27727
daxian-dbw wants to merge 1 commit into
PowerShell:masterfrom
daxian-dbw:dot-source

Conversation

@daxian-dbw

Copy link
Copy Markdown
Member

PR Summary

Context

The behavior between signed advanced script and signed simple script is inconsistent in a WDAC enforced environment -- signed advanced script is rejected when running with pwsh.exe -f while the signed simple script works fine.

It turns out advanced script and simple script take different code paths to calling ValidateCompatibleLanguageMode, and they use different condition checks to decide whether they will actually call into that method.

For simple script, PowerShell uses the DlrScriptCommandProcessor, which calls ValidateCompatibleLanguageMode at this location with the following check:

// If the script has been dotted, throw an error if it's from a different language mode.
// Unless it was a script loaded through -File, in which case the danger of dotting other
// language modes (getting internal functions in the user's state) isn't a danger
if ((!this.UseLocalScope) && (!this._rethrowExitException))
{
    ValidateCompatibleLanguageMode(_scriptBlock, context, Command.MyInvocation);
}

For advanced script, PowerShell uses CommandProcessor, which calls ValidateCompatibleLanguageMode at this location, with the following check:

// If the script has been dotted, throw an error if it's from a different language mode.
if (!this.UseLocalScope)
{
    ValidateCompatibleLanguageMode(scriptCommandInfo.ScriptBlock, _context, Command.MyInvocation);
}

As you can see, the check for simple script code path has && !this._rethrowExitException, but that is missing from the advance script code path. _rethrowExitException gets set at startup in ConsoleHost, as follows:

// If we're not going to continue, then get the exit code out of the runspace and
// and indicate that it should be returned...
if (!_noExit && this.Runspace is not RemoteRunspace)
{
    this.Runspace.ExecutionContext.ScriptCommandProcessorShouldRethrowExit = true;
}

The value of _rethrowExitException will be true if it's just pwsh -file xxx.ps1. In this case, even though the global scope is CLM, there is no harm to dot-source the FLM script file, because the process will be gone right after the script finishes execution, so cross boundary is fine in this case. We believe that's why the DlrScriptCommandProcessor code path has that check.

So, for a simple script, the check returns false for pwsh -file xxx.ps1, and thus it never calls to ValidateCompatibleLanguageMode. While for an advanced script, it always calls to ValidateCompatibleLanguageMode. This is why in a WDAC enforced environment, pwsh -file allows a signed simple script to work but errors out for a signed advanced script.

Fix

Given the above, the fix should be adding the && !this._rethrowExitException check to the CommandProcessor code path too (the advance script code path). With such a fix,

  • The existing behavior in FLM environment will be kept.
  • In WDAC enforced environment, pwsh -file xxx.ps1 will work for both signed advanced script and simple script.
  • In WDAC enforced environment, pwsh -noexit -file xxx.ps1 will continue to fail for signed scripts as it is today.

PR Checklist

@daxian-dbw
daxian-dbw requested a review from a team as a code owner July 24, 2026 01:27
Copilot AI review requested due to automatic review settings July 24, 2026 01:27
@azure-pipelines

Copy link
Copy Markdown
Azure Pipelines:
There may be pipelines that require an authorized user to comment /azp run to run.

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 aligns the language-mode compatibility validation behavior between the “simple script” and “advanced script” execution paths when scripts are invoked via pwsh -File, addressing inconsistent failures under WDAC/system lockdown scenarios.

Changes:

  • Exposes whether a PSScriptCmdlet instance was created with ScriptCommandProcessorShouldRethrowExit enabled (the -File console-host scenario).
  • Updates the advanced-script CommandProcessor path to skip ValidateCompatibleLanguageMode(...) when the script is being run via pwsh -File, matching the existing behavior of the simple-script path.

Reviewed changes

Copilot reviewed 2 out of 2 changed files in this pull request and generated no comments.

File Description
src/System.Management.Automation/engine/runtime/CompiledScriptBlock.cs Adds an internal accessor to surface the script-cmdlet “rethrow exit” flag to other engine components.
src/System.Management.Automation/engine/CommandProcessor.cs Updates the dot-sourcing language-mode validation guard to also consider the -File (“rethrow exit”) case.

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