mirror of
https://github.com/aphrody-labs/PowerShell.git
synced 2026-10-17 12:45:47 +00:00
Integrate changes between [SD:717473] and [SD:725290]
This commit is contained in:
1 parent
2280bea11e
commit
0dd425da19
28 files changed
+848
-342
No files matched your search
+143
-14
@@ -110,17 +110,15 @@ namespace Microsoft.PowerShell.Commands
|
||||
protected override void ProcessRecord()
|
||||
{
|
||||
// In ConstrainedLanguage, XAML workflows are not supported (even from a trusted FullLanguage state),
|
||||
// since we can't prevent tampering.
|
||||
// unless they are signed in-box OS binaries.
|
||||
bool checkSignatures = false;
|
||||
if ((SystemPolicy.GetSystemLockdownPolicy() == SystemEnforcementMode.Enforce) ||
|
||||
(this.SessionState.LanguageMode == PSLanguageMode.ConstrainedLanguage))
|
||||
{
|
||||
// However, this static internal property can be changed by tests that can already run a script
|
||||
// in full-language mode.
|
||||
PropertyInfo xamlProperty = typeof(SystemPolicy).GetProperty("XamlWorkflowSupported", BindingFlags.NonPublic | BindingFlags.Static);
|
||||
if (! ((bool) xamlProperty.GetValue(null, null)))
|
||||
{
|
||||
throw new NotSupportedException(Resources.XamlWorkflowsNotSupported);
|
||||
}
|
||||
checkSignatures = !((bool)xamlProperty.GetValue(null, null));
|
||||
}
|
||||
|
||||
string dependentWorkflowAssemblyPath = string.Empty;
|
||||
@@ -174,8 +172,10 @@ namespace Microsoft.PowerShell.Commands
|
||||
WriteError(er);
|
||||
Tracer.TraceErrorRecord(er);
|
||||
continue;
|
||||
|
||||
}
|
||||
|
||||
CheckFileSignatureAsNeeded(checkSignatures, resolvedPath);
|
||||
|
||||
try
|
||||
{
|
||||
// Finally load the file. If there is an access violation, write a
|
||||
@@ -251,6 +251,8 @@ namespace Microsoft.PowerShell.Commands
|
||||
continue;
|
||||
}
|
||||
|
||||
CheckFileSignatureAsNeeded(checkSignatures, resolvedPath);
|
||||
|
||||
FunctionDetails detailsToUseForUpdate = null;
|
||||
try
|
||||
{
|
||||
@@ -302,7 +304,8 @@ namespace Microsoft.PowerShell.Commands
|
||||
requiredAssemblies,
|
||||
dependentWorkflowContent.ToArray(),
|
||||
dependentWorkflowAssemblyPath,
|
||||
resolvedPath);
|
||||
resolvedPath,
|
||||
this.SessionState.LanguageMode);
|
||||
|
||||
// check if the function cache already has the entry to this file
|
||||
// If detailsToUseForUpdate is not null it is a forced import module
|
||||
@@ -342,6 +345,14 @@ namespace Microsoft.PowerShell.Commands
|
||||
|
||||
} //ProcessRecord
|
||||
|
||||
private static void CheckFileSignatureAsNeeded(bool checkSignatures, string filePath)
|
||||
{
|
||||
if (checkSignatures && !System.Management.Automation.Internal.SecuritySupport.IsProductBinary(filePath))
|
||||
{
|
||||
throw new NotSupportedException(Resources.XamlWorkflowsNotSupported);
|
||||
}
|
||||
}
|
||||
|
||||
private static readonly ConcurrentDictionary<string, FunctionDetails> FunctionCache =
|
||||
new ConcurrentDictionary<string, FunctionDetails>(StringComparer.OrdinalIgnoreCase);
|
||||
|
||||
@@ -358,7 +369,15 @@ namespace Microsoft.PowerShell.Commands
|
||||
/// <param name="dependentWorkflows">Any workflows required by this workflow.</param>
|
||||
/// <param name="dependentAssemblyPath">Path to the dependent assembly.</param>
|
||||
/// <param name="resolvedPath">Resolved Path of the xaml</param>
|
||||
private static FunctionDetails GenerateFunctionFromXaml(string name, string xaml, Dictionary<string, string> requiredAssemblies, string[] dependentWorkflows, string dependentAssemblyPath, string resolvedPath)
|
||||
/// <param name="sourceLanguageMode">Language mode of source in which workflow should run</param>
|
||||
private static FunctionDetails GenerateFunctionFromXaml(
|
||||
string name,
|
||||
string xaml,
|
||||
Dictionary<string, string> requiredAssemblies,
|
||||
string[] dependentWorkflows,
|
||||
string dependentAssemblyPath,
|
||||
string resolvedPath,
|
||||
PSLanguageMode sourceLanguageMode)
|
||||
{
|
||||
if (name == null)
|
||||
{
|
||||
@@ -368,7 +387,21 @@ namespace Microsoft.PowerShell.Commands
|
||||
}
|
||||
|
||||
string modulePath = System.IO.Path.GetDirectoryName(resolvedPath);
|
||||
string functionDefinition = CreateFunctionFromXaml(name, xaml, requiredAssemblies, dependentWorkflows, dependentAssemblyPath, null, modulePath, false, "[CmdletBinding()]");
|
||||
string functionDefinition = CreateFunctionFromXaml(
|
||||
name,
|
||||
xaml,
|
||||
requiredAssemblies,
|
||||
dependentWorkflows,
|
||||
dependentAssemblyPath,
|
||||
null,
|
||||
modulePath,
|
||||
false,
|
||||
"[CmdletBinding()]",
|
||||
null, /* scriptContent */
|
||||
null, /* fullScript */
|
||||
null, /* rootWorkflowName */
|
||||
sourceLanguageMode,
|
||||
null);
|
||||
|
||||
FunctionDetails details = new FunctionDetails
|
||||
{Name = name, FunctionDefinition = functionDefinition, Xaml = xaml};
|
||||
@@ -438,6 +471,24 @@ namespace Microsoft.PowerShell.Commands
|
||||
[SuppressMessage("Microsoft.Naming", "CA2204:Literals should be spelled correctly", MessageId = "workFlowDefinition")]
|
||||
public static ContainerParentJob StartWorkflowApplication(PSCmdlet command, string jobName, string workflowGuid, bool startAsync,
|
||||
bool parameterCollectionProcessed, Hashtable[] parameters, bool debuggerActive)
|
||||
{
|
||||
return StartWorkflowApplication(command, jobName, workflowGuid, startAsync, parameterCollectionProcessed, parameters, false, null);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Executes an instance of the workflow object graph identified by the passed
|
||||
/// GUID, binding parameters from the Parameters hastable.
|
||||
/// </summary>
|
||||
/// <param name="command">The powershell command.</param>
|
||||
/// <param name="workflowGuid">The GUID used to identify the workflow to run.</param>
|
||||
/// <param name="parameters">The parameters to pass to the workflow instance.</param>
|
||||
/// <param name="jobName">The friendly name for the job</param>
|
||||
/// <param name="parameterCollectionProcessed">True if there was a PSParameters collection</param>
|
||||
/// <param name="startAsync"></param>
|
||||
/// <param name="debuggerActive">True if debugger is in active state.</param>
|
||||
/// <param name="SourceLanguageMode">Language mode of source creating workflow.</param>
|
||||
public static ContainerParentJob StartWorkflowApplication(PSCmdlet command, string jobName, string workflowGuid, bool startAsync,
|
||||
bool parameterCollectionProcessed, Hashtable[] parameters, bool debuggerActive, string SourceLanguageMode)
|
||||
{
|
||||
Guid trackingGuid = Guid.NewGuid();
|
||||
_structuredTracer.BeginStartWorkflowApplication(trackingGuid);
|
||||
@@ -573,6 +624,15 @@ namespace Microsoft.PowerShell.Commands
|
||||
myJob = command.JobManager.NewJob(specification) as ContainerParentJob;
|
||||
_structuredTracer.EndCreateNewJob(trackingGuid);
|
||||
|
||||
// Pass the source language mode to the workflow job so that it can be
|
||||
// applied during activity execution.
|
||||
PSLanguageMode sourceLanguageModeValue;
|
||||
PSLanguageMode? sourceLanguageMode = null;
|
||||
if (!string.IsNullOrEmpty(SourceLanguageMode) && Enum.TryParse<PSLanguageMode>(SourceLanguageMode, out sourceLanguageModeValue))
|
||||
{
|
||||
sourceLanguageMode = sourceLanguageModeValue;
|
||||
}
|
||||
|
||||
// Raise engine event of new WF job start for debugger, if
|
||||
// debugger is active (i.e., has breakpoints set).
|
||||
if (debuggerActive)
|
||||
@@ -582,12 +642,13 @@ namespace Microsoft.PowerShell.Commands
|
||||
|
||||
if (startAsync)
|
||||
{
|
||||
if (!PSSessionConfigurationData.IsServerManager)
|
||||
foreach(PSWorkflowJob childjob in myJob.ChildJobs)
|
||||
{
|
||||
foreach(PSWorkflowJob childjob in myJob.ChildJobs)
|
||||
if (!PSSessionConfigurationData.IsServerManager)
|
||||
{
|
||||
childjob.EnableStreamUnloadOnPersistentState();
|
||||
}
|
||||
childjob.SourceLanguageMode = sourceLanguageMode;
|
||||
}
|
||||
myJob.StartJobAsync();
|
||||
}
|
||||
@@ -598,6 +659,7 @@ namespace Microsoft.PowerShell.Commands
|
||||
foreach (PSWorkflowJob childJob in myJob.ChildJobs)
|
||||
{
|
||||
childJob.SynchronousExecution = true;
|
||||
childJob.SourceLanguageMode = sourceLanguageMode;
|
||||
}
|
||||
myJob.StartJob();
|
||||
}
|
||||
@@ -605,8 +667,8 @@ namespace Microsoft.PowerShell.Commands
|
||||
// write an event specifying that job creation is done
|
||||
_structuredTracer.EndStartWorkflowApplication(trackingGuid);
|
||||
_structuredTracer.TrackingGuidContainerParentJobCorrelation(trackingGuid, myJob.InstanceId);
|
||||
return myJob;
|
||||
|
||||
return myJob;
|
||||
}
|
||||
|
||||
private static void RaiseWFJobEvent(PSCmdlet command, ContainerParentJob job, bool startAsync)
|
||||
@@ -1010,6 +1072,46 @@ namespace Microsoft.PowerShell.Commands
|
||||
string fullScript,
|
||||
string rootWorkflowName
|
||||
)
|
||||
{
|
||||
return CreateFunctionFromXaml(name, xaml, requiredAssemblies, dependentWorkflows, dependentAssemblyPath, parameterValidation, modulePath,
|
||||
scriptWorkflow, workflowAttributes, scriptContent, fullScript, rootWorkflowName, null, null);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Creates a workflow activity based on the provided Xaml and returns PowerShell script that will
|
||||
/// run the workflow.
|
||||
/// </summary>
|
||||
/// <param name="name">Workflow name</param>
|
||||
/// <param name="xaml">Workflow Xaml definition</param>
|
||||
/// <param name="requiredAssemblies">Required assemblies</param>
|
||||
/// <param name="dependentWorkflows">Dependent workflows</param>
|
||||
/// <param name="dependentAssemblyPath">Path for dependent assemblies</param>
|
||||
/// <param name="parameterValidation">Workflow parameters</param>
|
||||
/// <param name="modulePath">Module path</param>
|
||||
/// <param name="scriptWorkflow">True if this is script workflow</param>
|
||||
/// <param name="workflowAttributes">the attribute string to use for the workflow, should be '[CmdletBinding()]'</param>
|
||||
/// <param name="scriptContent">File path containing script content.</param>
|
||||
/// <param name="fullScript">Full source script.</param>
|
||||
/// <param name="rootWorkflowName">Only root Workflow will be compiled</param>
|
||||
/// <param name="sourceLanguageMode">Language mode of source that is creating the workflow</param>
|
||||
/// <param name="attributeAstCollection">Optional collection of parameter attribute Asts</param>
|
||||
/// <returns></returns>
|
||||
public static string CreateFunctionFromXaml(
|
||||
string name,
|
||||
string xaml,
|
||||
Dictionary<string, string> requiredAssemblies,
|
||||
string[] dependentWorkflows,
|
||||
string dependentAssemblyPath,
|
||||
Dictionary<string, ParameterAst> parameterValidation,
|
||||
string modulePath,
|
||||
bool scriptWorkflow,
|
||||
string workflowAttributes,
|
||||
string scriptContent,
|
||||
string fullScript,
|
||||
string rootWorkflowName,
|
||||
PSLanguageMode? sourceLanguageMode,
|
||||
ReadOnlyCollection<AttributeAst> attributeAstCollection
|
||||
)
|
||||
{
|
||||
// check to see if the specified name is allowed
|
||||
if (!Regex.IsMatch(name, functionNamePattern))
|
||||
@@ -1304,9 +1406,15 @@ namespace Microsoft.PowerShell.Commands
|
||||
completeFunctionDefinitionTemplate.AppendLine(" }}");
|
||||
completeFunctionDefinitionTemplate.AppendLine(FunctionBodyTemplate);
|
||||
|
||||
// Mark the function definition with sourceLanguageMode (language mode that function can run under, i.e.,
|
||||
// as trusted or not trusted), unless the workflow script is marked with the "SecurityCritical" attribute in
|
||||
// which case the function will always be run under the current system lock down setting.
|
||||
bool isSecurityCritical = ContainsSecurityCriticalAttribute(attributeAstCollection);
|
||||
string sourceLanguageModeStr = (!isSecurityCritical && (sourceLanguageMode != null)) ? sourceLanguageMode.ToString() : string.Empty;
|
||||
|
||||
// Combine the pieces to create the complete function
|
||||
string functionDefinition = String.Format(CultureInfo.InvariantCulture, completeFunctionDefinitionTemplate.ToString(),
|
||||
defaultUpdates, workflowGuid, modulePath);
|
||||
defaultUpdates, workflowGuid, modulePath, sourceLanguageModeStr);
|
||||
|
||||
#if DEBUG
|
||||
// Verify that the generated function is valid powershell. This is only an issue when changing the
|
||||
@@ -1334,6 +1442,23 @@ namespace Microsoft.PowerShell.Commands
|
||||
return functionDefinition;
|
||||
}
|
||||
|
||||
private static Type securityCriticalAttributeType = typeof(System.Security.SecurityCriticalAttribute);
|
||||
private static bool ContainsSecurityCriticalAttribute(ReadOnlyCollection<AttributeAst> attributeAsts)
|
||||
{
|
||||
if (attributeAsts != null)
|
||||
{
|
||||
foreach (var attributeAst in attributeAsts)
|
||||
{
|
||||
if (attributeAst.TypeName.GetReflectionAttributeType() == securityCriticalAttributeType)
|
||||
{
|
||||
return true;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
|
||||
/// <summary>
|
||||
///
|
||||
@@ -1515,6 +1640,9 @@ namespace Microsoft.PowerShell.Commands
|
||||
# which uses this as a base path to find localized content files.
|
||||
$psBoundParameters['" + Constants.ModulePath + @"'] = '{2}'
|
||||
|
||||
# Variable that contains the source language mode.
|
||||
[string] $SourceLanguageMode = '{3}'
|
||||
|
||||
if (Test-Path variable:\PSSenderInfo)
|
||||
{{
|
||||
$psBoundParameters['" + Constants.PSSenderInfo + @"'] = $PSSenderInfo
|
||||
@@ -1693,7 +1821,8 @@ namespace Microsoft.PowerShell.Commands
|
||||
$AsJob,
|
||||
$parameterCollectionProcessed,
|
||||
$finalParameterCollection,
|
||||
$debuggerActive)
|
||||
$debuggerActive,
|
||||
$SourceLanguageMode)
|
||||
}}
|
||||
catch
|
||||
{{
|
||||
|
||||
+34
-7
@@ -80,23 +80,29 @@ namespace Microsoft.PowerShell.Workflow
|
||||
return EndGetRunspace(asyncResult);
|
||||
}
|
||||
|
||||
private Runspace AssignRunspaceIfPossible()
|
||||
private Runspace AssignRunspaceIfPossible(PSLanguageMode? sourceLanguageMode = null)
|
||||
{
|
||||
Runspace runspace = null;
|
||||
PSLanguageMode languageMode = (sourceLanguageMode != null) ? sourceLanguageMode.Value :
|
||||
(_languageMode != null) ? _languageMode.Value : GetSystemLanguageMode();
|
||||
lock (_runspaceCache.TimerServicingSyncObject)
|
||||
{
|
||||
// Retrieve or create a local runspace having the same language mode as the source, if provided.
|
||||
foreach (Item<Runspace> item in _runspaceCache.Cast<Item<Runspace>>().Where(item => !item.Busy))
|
||||
{
|
||||
item.Idle = false;
|
||||
item.Busy = true;
|
||||
runspace = item.Value;
|
||||
break;
|
||||
if (item.Value.SessionStateProxy.LanguageMode == languageMode)
|
||||
{
|
||||
item.Idle = false;
|
||||
item.Busy = true;
|
||||
runspace = item.Value;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if ((runspace == null || runspace.RunspaceStateInfo.State != RunspaceState.Opened) &&
|
||||
(_maxRunspaces == MaxRunspacesPossible || _runspaceCache.Cache.Count < _maxRunspaces))
|
||||
{
|
||||
runspace = CreateLocalActivityRunspace(_languageMode);
|
||||
runspace = CreateLocalActivityRunspace(languageMode);
|
||||
|
||||
runspace.Open();
|
||||
_tracer.WriteMessage("New local runspace created");
|
||||
@@ -108,6 +114,11 @@ namespace Microsoft.PowerShell.Workflow
|
||||
return runspace;
|
||||
}
|
||||
|
||||
private static PSLanguageMode GetSystemLanguageMode()
|
||||
{
|
||||
return (SystemPolicy.GetSystemLockdownPolicy() == SystemEnforcementMode.Enforce) ?
|
||||
PSLanguageMode.ConstrainedLanguage : PSLanguageMode.FullLanguage;
|
||||
}
|
||||
|
||||
private void TraceThreadPoolInfo(string message)
|
||||
{
|
||||
@@ -208,7 +219,23 @@ namespace Microsoft.PowerShell.Workflow
|
||||
|
||||
LocalRunspaceAsyncResult asyncResult = new LocalRunspaceAsyncResult(state, callback, Guid.Empty);
|
||||
|
||||
Runspace runspace = AssignRunspaceIfPossible();
|
||||
// Get the source language mode from the activity arguments if available and pass to runspace fetching.
|
||||
PSLanguageMode? sourceLanguageMode = null;
|
||||
RunCommandsArguments args = state as RunCommandsArguments;
|
||||
if (args != null)
|
||||
{
|
||||
PSWorkflowRuntime wfRuntime = args.WorkflowHost as PSWorkflowRuntime;
|
||||
if (wfRuntime != null)
|
||||
{
|
||||
PSWorkflowJob wfJob = wfRuntime.JobManager.GetJob(args.PSActivityContext.JobInstanceId);
|
||||
if (wfJob != null)
|
||||
{
|
||||
sourceLanguageMode = wfJob.SourceLanguageMode;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Runspace runspace = AssignRunspaceIfPossible(sourceLanguageMode);
|
||||
if (runspace != null)
|
||||
{
|
||||
asyncResult.Runspace = runspace;
|
||||
|
||||
@@ -1205,6 +1205,8 @@ namespace Microsoft.PowerShell.Workflow
|
||||
|
||||
internal bool? IsSuspendable = null;
|
||||
|
||||
internal PSLanguageMode? SourceLanguageMode { get; set; }
|
||||
|
||||
#endregion Internal Accessors
|
||||
|
||||
#region Internal Methods
|
||||
|
||||
@@ -413,7 +413,7 @@ For more information about how to add checkpoints, see the help topics for Windo
|
||||
<comment>{0} is a number and should not be localized.</comment>
|
||||
</data>
|
||||
<data name="XamlWorkflowsNotSupported" xml:space="preserve">
|
||||
<value>Cannot load workflow. XAML-based workflows are not supported in this language mode. Only script-based workflows are supported.</value>
|
||||
<value>Cannot load the workflow. Only signed in-box XAML-based workflows or script-based workflows are supported in the current language mode.</value>
|
||||
</data>
|
||||
<data name="RetryingConnection" xml:space="preserve">
|
||||
<value>Retrying activity connection: attempt {0} of {1}.</value>
|
||||
|
||||
Reference in new issue
Block a user