From 0bc793fc1341a5930e2a0efd4a66c200a9f2a4ab Mon Sep 17 00:00:00 2001 From: PowerShell Team Bot <69177312+pwshBot@users.noreply.github.com> Date: Wed, 6 Aug 2025 14:25:19 -0700 Subject: [PATCH 01/16] [release/v7.5] Fix Out-GridView by replacing use of obsolete BinaryFormatter with custom implementation. (#25559) Co-authored-by: Matthias Wolf <78562192+mawosoft@users.noreply.github.com> Co-authored-by: Travis Plunk --- .../FilterRules/ComparableValueFilterRule.cs | 17 +++++++++ .../FilterRules/DoesNotEqualFilterRule.cs | 11 +++++- .../FilterRules/EqualsFilterRule.cs | 11 +++++- .../FilterCore/FilterRules/FilterRule.cs | 20 +++++++++- .../FilterRules/FilterRuleExtensions.cs | 28 +------------- .../FilterRules/IsBetweenFilterRule.cs | 16 +++++++- .../FilterRules/IsEmptyFilterRule.cs | 11 +++++- .../FilterRules/IsGreaterThanFilterRule.cs | 11 +++++- .../FilterRules/IsLessThanFilterRule.cs | 11 +++++- .../FilterRules/IsNotEmptyFilterRule.cs | 11 +++++- .../FilterRules/IsNotEmptyValidationRule.cs | 8 ++++ .../PropertiesTextContainsFilterRule.cs | 11 ++++++ .../PropertyValueSelectorFilterRule.cs | 11 ++++++ .../FilterRules/SelectorFilterRule.cs | 16 +++++++- .../SingleValueComparableValueFilterRule.cs | 13 ++++++- .../FilterRules/TextContainsFilterRule.cs | 11 +++++- .../TextDoesNotContainFilterRule.cs | 11 +++++- .../FilterRules/TextDoesNotEqualFilterRule.cs | 11 +++++- .../FilterRules/TextEndsWithFilterRule.cs | 11 +++++- .../FilterRules/TextEqualsFilterRule.cs | 11 +++++- .../FilterCore/FilterRules/TextFilterRule.cs | 13 ++++++- .../FilterRules/TextStartsWithFilterRule.cs | 11 +++++- .../FilterCore/IDeepCloneable.cs | 18 +++++++++ .../FilterCore/ValidatingSelectorValue.cs | 37 +++++++++++++++++++ .../FilterCore/ValidatingValue.cs | 23 ++++++++++++ .../FilterCore/ValidatingValueBase.cs | 26 ++++++++++++- .../DataErrorInfoValidationRule.cs | 5 ++- 27 files changed, 346 insertions(+), 48 deletions(-) create mode 100644 src/Microsoft.Management.UI.Internal/ManagementList/FilterCore/IDeepCloneable.cs diff --git a/src/Microsoft.Management.UI.Internal/ManagementList/FilterCore/FilterRules/ComparableValueFilterRule.cs b/src/Microsoft.Management.UI.Internal/ManagementList/FilterCore/FilterRules/ComparableValueFilterRule.cs index e7ef648e3fe..dca6c08d535 100644 --- a/src/Microsoft.Management.UI.Internal/ManagementList/FilterCore/FilterRules/ComparableValueFilterRule.cs +++ b/src/Microsoft.Management.UI.Internal/ManagementList/FilterCore/FilterRules/ComparableValueFilterRule.cs @@ -16,6 +16,23 @@ namespace Microsoft.Management.UI.Internal [Serializable] public abstract class ComparableValueFilterRule : FilterRule where T : IComparable { + /// + /// Initializes a new instance of the class. + /// + protected ComparableValueFilterRule() + { + } + + /// + /// Initializes a new instance of the class. + /// + /// The source to initialize from. + protected ComparableValueFilterRule(ComparableValueFilterRule source) + : base(source) + { + this.DefaultNullValueEvaluation = source.DefaultNullValueEvaluation; + } + #region Properties /// diff --git a/src/Microsoft.Management.UI.Internal/ManagementList/FilterCore/FilterRules/DoesNotEqualFilterRule.cs b/src/Microsoft.Management.UI.Internal/ManagementList/FilterCore/FilterRules/DoesNotEqualFilterRule.cs index ae209d0e60f..94451623c3a 100644 --- a/src/Microsoft.Management.UI.Internal/ManagementList/FilterCore/FilterRules/DoesNotEqualFilterRule.cs +++ b/src/Microsoft.Management.UI.Internal/ManagementList/FilterCore/FilterRules/DoesNotEqualFilterRule.cs @@ -17,7 +17,7 @@ namespace Microsoft.Management.UI.Internal public class DoesNotEqualFilterRule : EqualsFilterRule where T : IComparable { /// - /// Initializes a new instance of the DoesNotEqualFilterRule class. + /// Initializes a new instance of the class. /// public DoesNotEqualFilterRule() { @@ -25,6 +25,15 @@ public DoesNotEqualFilterRule() this.DefaultNullValueEvaluation = true; } + /// + /// Initializes a new instance of the class. + /// + /// The source to initialize from. + public DoesNotEqualFilterRule(DoesNotEqualFilterRule source) + : base(source) + { + } + /// /// Determines if item is not equal to Value. /// diff --git a/src/Microsoft.Management.UI.Internal/ManagementList/FilterCore/FilterRules/EqualsFilterRule.cs b/src/Microsoft.Management.UI.Internal/ManagementList/FilterCore/FilterRules/EqualsFilterRule.cs index 7bafd53e411..eaf647f0030 100644 --- a/src/Microsoft.Management.UI.Internal/ManagementList/FilterCore/FilterRules/EqualsFilterRule.cs +++ b/src/Microsoft.Management.UI.Internal/ManagementList/FilterCore/FilterRules/EqualsFilterRule.cs @@ -18,13 +18,22 @@ namespace Microsoft.Management.UI.Internal public class EqualsFilterRule : SingleValueComparableValueFilterRule where T : IComparable { /// - /// Initializes a new instance of the EqualsFilterRule class. + /// Initializes a new instance of the class. /// public EqualsFilterRule() { this.DisplayName = UICultureResources.FilterRule_Equals; } + /// + /// Initializes a new instance of the class. + /// + /// The source to initialize from. + public EqualsFilterRule(EqualsFilterRule source) + : base(source) + { + } + /// /// Determines if item is equal to Value. /// diff --git a/src/Microsoft.Management.UI.Internal/ManagementList/FilterCore/FilterRules/FilterRule.cs b/src/Microsoft.Management.UI.Internal/ManagementList/FilterCore/FilterRules/FilterRule.cs index 800812cdba5..7f72f33eb2d 100644 --- a/src/Microsoft.Management.UI.Internal/ManagementList/FilterCore/FilterRules/FilterRule.cs +++ b/src/Microsoft.Management.UI.Internal/ManagementList/FilterCore/FilterRules/FilterRule.cs @@ -10,7 +10,7 @@ namespace Microsoft.Management.UI.Internal /// [System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.MSInternal", "CA903:InternalNamespaceShouldNotContainPublicTypes")] [Serializable] - public abstract class FilterRule : IEvaluate + public abstract class FilterRule : IEvaluate, IDeepCloneable { /// /// Gets a value indicating whether the FilterRule can be @@ -34,12 +34,28 @@ public string DisplayName } /// - /// Initializes a new instance of the FilterRule class. + /// Initializes a new instance of the class. /// protected FilterRule() { } + /// + /// Initializes a new instance of the class. + /// + /// The source to initialize from. + protected FilterRule(FilterRule source) + { + ArgumentNullException.ThrowIfNull(source); + this.DisplayName = source.DisplayName; + } + + /// + public object DeepClone() + { + return Activator.CreateInstance(this.GetType(), new object[] { this }); + } + /// /// Gets a value indicating whether the supplied item meets the /// criteria specified by this rule. diff --git a/src/Microsoft.Management.UI.Internal/ManagementList/FilterCore/FilterRules/FilterRuleExtensions.cs b/src/Microsoft.Management.UI.Internal/ManagementList/FilterCore/FilterRules/FilterRuleExtensions.cs index bc8e0b02ca6..4a3f8dc2975 100644 --- a/src/Microsoft.Management.UI.Internal/ManagementList/FilterCore/FilterRules/FilterRuleExtensions.cs +++ b/src/Microsoft.Management.UI.Internal/ManagementList/FilterCore/FilterRules/FilterRuleExtensions.cs @@ -2,10 +2,6 @@ // Licensed under the MIT License. using System; -using System.Diagnostics; -using System.IO; -using System.Runtime.Serialization; -using System.Runtime.Serialization.Formatters.Binary; namespace Microsoft.Management.UI.Internal { @@ -28,29 +24,7 @@ public static class FilterRuleExtensions public static FilterRule DeepCopy(this FilterRule rule) { ArgumentNullException.ThrowIfNull(rule); - -#pragma warning disable SYSLIB0050 - Debug.Assert(rule.GetType().IsSerializable, "rule is serializable"); -#pragma warning disable SYSLIB0011 - BinaryFormatter formatter = new BinaryFormatter(null, new StreamingContext(StreamingContextStates.Clone)); -#pragma warning restore SYSLIB0011 - MemoryStream ms = new MemoryStream(); - - FilterRule copy = null; - try - { - formatter.Serialize(ms, rule); - - ms.Position = 0; - copy = (FilterRule)formatter.Deserialize(ms); -#pragma warning restore SYSLIB0050 - } - finally - { - ms.Close(); - } - - return copy; + return (FilterRule)rule.DeepClone(); } } } diff --git a/src/Microsoft.Management.UI.Internal/ManagementList/FilterCore/FilterRules/IsBetweenFilterRule.cs b/src/Microsoft.Management.UI.Internal/ManagementList/FilterCore/FilterRules/IsBetweenFilterRule.cs index cbe4a875dd0..d8c4a263c61 100644 --- a/src/Microsoft.Management.UI.Internal/ManagementList/FilterCore/FilterRules/IsBetweenFilterRule.cs +++ b/src/Microsoft.Management.UI.Internal/ManagementList/FilterCore/FilterRules/IsBetweenFilterRule.cs @@ -56,7 +56,7 @@ public ValidatingValue EndValue #region Ctor /// - /// Initializes a new instance of the IsBetweenFilterRule class. + /// Initializes a new instance of the class. /// public IsBetweenFilterRule() { @@ -69,6 +69,20 @@ public IsBetweenFilterRule() this.EndValue.PropertyChanged += this.Value_PropertyChanged; } + /// + /// Initializes a new instance of the class. + /// + /// The source to initialize from. + public IsBetweenFilterRule(IsBetweenFilterRule source) + : base(source) + { + this.StartValue = (ValidatingValue)source.StartValue.DeepClone(); + this.StartValue.PropertyChanged += this.Value_PropertyChanged; + + this.EndValue = (ValidatingValue)source.EndValue.DeepClone(); + this.EndValue.PropertyChanged += this.Value_PropertyChanged; + } + #endregion Ctor #region Public Methods diff --git a/src/Microsoft.Management.UI.Internal/ManagementList/FilterCore/FilterRules/IsEmptyFilterRule.cs b/src/Microsoft.Management.UI.Internal/ManagementList/FilterCore/FilterRules/IsEmptyFilterRule.cs index 5ad2ae1247e..a3add25eae5 100644 --- a/src/Microsoft.Management.UI.Internal/ManagementList/FilterCore/FilterRules/IsEmptyFilterRule.cs +++ b/src/Microsoft.Management.UI.Internal/ManagementList/FilterCore/FilterRules/IsEmptyFilterRule.cs @@ -14,13 +14,22 @@ namespace Microsoft.Management.UI.Internal public class IsEmptyFilterRule : FilterRule { /// - /// Initializes a new instance of the IsEmptyFilterRule class. + /// Initializes a new instance of the class. /// public IsEmptyFilterRule() { this.DisplayName = UICultureResources.FilterRule_IsEmpty; } + /// + /// Initializes a new instance of the class. + /// + /// The source to initialize from. + public IsEmptyFilterRule(IsEmptyFilterRule source) + : base(source) + { + } + /// /// Gets a values indicating whether the supplied item is empty. /// diff --git a/src/Microsoft.Management.UI.Internal/ManagementList/FilterCore/FilterRules/IsGreaterThanFilterRule.cs b/src/Microsoft.Management.UI.Internal/ManagementList/FilterCore/FilterRules/IsGreaterThanFilterRule.cs index d098d2a9383..c6d9ef762b4 100644 --- a/src/Microsoft.Management.UI.Internal/ManagementList/FilterCore/FilterRules/IsGreaterThanFilterRule.cs +++ b/src/Microsoft.Management.UI.Internal/ManagementList/FilterCore/FilterRules/IsGreaterThanFilterRule.cs @@ -18,13 +18,22 @@ namespace Microsoft.Management.UI.Internal public class IsGreaterThanFilterRule : SingleValueComparableValueFilterRule where T : IComparable { /// - /// Initializes a new instance of the IsGreaterThanFilterRule class. + /// Initializes a new instance of the class. /// public IsGreaterThanFilterRule() { this.DisplayName = UICultureResources.FilterRule_GreaterThanOrEqual; } + /// + /// Initializes a new instance of the class. + /// + /// The source to initialize from. + public IsGreaterThanFilterRule(IsGreaterThanFilterRule source) + : base(source) + { + } + /// /// Determines if item is greater than Value. /// diff --git a/src/Microsoft.Management.UI.Internal/ManagementList/FilterCore/FilterRules/IsLessThanFilterRule.cs b/src/Microsoft.Management.UI.Internal/ManagementList/FilterCore/FilterRules/IsLessThanFilterRule.cs index 8539d6edf0e..a90dcdc8a82 100644 --- a/src/Microsoft.Management.UI.Internal/ManagementList/FilterCore/FilterRules/IsLessThanFilterRule.cs +++ b/src/Microsoft.Management.UI.Internal/ManagementList/FilterCore/FilterRules/IsLessThanFilterRule.cs @@ -18,13 +18,22 @@ namespace Microsoft.Management.UI.Internal public class IsLessThanFilterRule : SingleValueComparableValueFilterRule where T : IComparable { /// - /// Initializes a new instance of the IsLessThanFilterRule class. + /// Initializes a new instance of the class. /// public IsLessThanFilterRule() { this.DisplayName = UICultureResources.FilterRule_LessThanOrEqual; } + /// + /// Initializes a new instance of the class. + /// + /// The source to initialize from. + public IsLessThanFilterRule(IsLessThanFilterRule source) + : base(source) + { + } + /// /// Determines if item is less than Value. /// diff --git a/src/Microsoft.Management.UI.Internal/ManagementList/FilterCore/FilterRules/IsNotEmptyFilterRule.cs b/src/Microsoft.Management.UI.Internal/ManagementList/FilterCore/FilterRules/IsNotEmptyFilterRule.cs index 68e501d1f68..450a1237a97 100644 --- a/src/Microsoft.Management.UI.Internal/ManagementList/FilterCore/FilterRules/IsNotEmptyFilterRule.cs +++ b/src/Microsoft.Management.UI.Internal/ManagementList/FilterCore/FilterRules/IsNotEmptyFilterRule.cs @@ -14,13 +14,22 @@ namespace Microsoft.Management.UI.Internal public class IsNotEmptyFilterRule : IsEmptyFilterRule { /// - /// Initializes a new instance of the IsNotEmptyFilterRule class. + /// Initializes a new instance of the class. /// public IsNotEmptyFilterRule() { this.DisplayName = UICultureResources.FilterRule_IsNotEmpty; } + /// + /// Initializes a new instance of the class. + /// + /// The source to initialize from. + public IsNotEmptyFilterRule(IsNotEmptyFilterRule source) + : base(source) + { + } + /// /// Gets a values indicating whether the supplied item is not empty. /// diff --git a/src/Microsoft.Management.UI.Internal/ManagementList/FilterCore/FilterRules/IsNotEmptyValidationRule.cs b/src/Microsoft.Management.UI.Internal/ManagementList/FilterCore/FilterRules/IsNotEmptyValidationRule.cs index 31722bfe1f7..98534aca0ad 100644 --- a/src/Microsoft.Management.UI.Internal/ManagementList/FilterCore/FilterRules/IsNotEmptyValidationRule.cs +++ b/src/Microsoft.Management.UI.Internal/ManagementList/FilterCore/FilterRules/IsNotEmptyValidationRule.cs @@ -51,6 +51,14 @@ public override DataErrorInfoValidationResult Validate(object value, System.Glob } } + /// + public override object DeepClone() + { + // Instance is stateless. + // return this; + return new IsNotEmptyValidationRule(); + } + #endregion Public Methods internal static bool IsStringNotEmpty(string value) diff --git a/src/Microsoft.Management.UI.Internal/ManagementList/FilterCore/FilterRules/PropertiesTextContainsFilterRule.cs b/src/Microsoft.Management.UI.Internal/ManagementList/FilterCore/FilterRules/PropertiesTextContainsFilterRule.cs index 2a1cc576b39..54532335835 100644 --- a/src/Microsoft.Management.UI.Internal/ManagementList/FilterCore/FilterRules/PropertiesTextContainsFilterRule.cs +++ b/src/Microsoft.Management.UI.Internal/ManagementList/FilterCore/FilterRules/PropertiesTextContainsFilterRule.cs @@ -29,6 +29,17 @@ public PropertiesTextContainsFilterRule() this.EvaluationResultInvalidated += this.PropertiesTextContainsFilterRule_EvaluationResultInvalidated; } + /// + /// Initializes a new instance of the class. + /// + /// The source to initialize from. + public PropertiesTextContainsFilterRule(PropertiesTextContainsFilterRule source) + : base(source) + { + this.PropertyNames = new List(source.PropertyNames); + this.EvaluationResultInvalidated += this.PropertiesTextContainsFilterRule_EvaluationResultInvalidated; + } + /// /// Gets a collection of the names of properties to search in. /// diff --git a/src/Microsoft.Management.UI.Internal/ManagementList/FilterCore/FilterRules/PropertyValueSelectorFilterRule.cs b/src/Microsoft.Management.UI.Internal/ManagementList/FilterCore/FilterRules/PropertyValueSelectorFilterRule.cs index 158ab4e0229..6699cb0e698 100644 --- a/src/Microsoft.Management.UI.Internal/ManagementList/FilterCore/FilterRules/PropertyValueSelectorFilterRule.cs +++ b/src/Microsoft.Management.UI.Internal/ManagementList/FilterCore/FilterRules/PropertyValueSelectorFilterRule.cs @@ -82,6 +82,17 @@ public PropertyValueSelectorFilterRule(string propertyName, string propertyDispl this.AvailableRules.DisplayNameConverter = new FilterRuleToDisplayNameConverter(); } + /// + /// Initializes a new instance of the class. + /// + /// The source to initialize from. + public PropertyValueSelectorFilterRule(PropertyValueSelectorFilterRule source) + : base(source) + { + this.PropertyName = source.PropertyName; + this.AvailableRules.DisplayNameConverter = new FilterRuleToDisplayNameConverter(); + } + #endregion Ctor #region Public Methods diff --git a/src/Microsoft.Management.UI.Internal/ManagementList/FilterCore/FilterRules/SelectorFilterRule.cs b/src/Microsoft.Management.UI.Internal/ManagementList/FilterCore/FilterRules/SelectorFilterRule.cs index da4a62b6f66..d7451bbd21f 100644 --- a/src/Microsoft.Management.UI.Internal/ManagementList/FilterCore/FilterRules/SelectorFilterRule.cs +++ b/src/Microsoft.Management.UI.Internal/ManagementList/FilterCore/FilterRules/SelectorFilterRule.cs @@ -40,7 +40,7 @@ public ValidatingSelectorValue AvailableRules #region Ctor /// - /// Creates a new SelectorFilterRule instance. + /// Initializes a new instance of the class. /// public SelectorFilterRule() { @@ -48,6 +48,18 @@ public SelectorFilterRule() this.AvailableRules.SelectedValueChanged += this.AvailableRules_SelectedValueChanged; } + /// + /// Initializes a new instance of the class. + /// + /// The source to initialize from. + public SelectorFilterRule(SelectorFilterRule source) + : base(source) + { + this.AvailableRules = (ValidatingSelectorValue)source.AvailableRules.DeepClone(); + this.AvailableRules.SelectedValueChanged += this.AvailableRules_SelectedValueChanged; + this.AvailableRules.SelectedValue.EvaluationResultInvalidated += this.SelectedValue_EvaluationResultInvalidated; + } + #endregion Ctor #region Public Methods @@ -86,8 +98,8 @@ protected void OnSelectedValueChanged(FilterRule oldValue, FilterRule newValue) FilterRuleCustomizationFactory.FactoryInstance.TransferValues(oldValue, newValue); FilterRuleCustomizationFactory.FactoryInstance.ClearValues(oldValue); - newValue.EvaluationResultInvalidated += this.SelectedValue_EvaluationResultInvalidated; oldValue.EvaluationResultInvalidated -= this.SelectedValue_EvaluationResultInvalidated; + newValue.EvaluationResultInvalidated += this.SelectedValue_EvaluationResultInvalidated; this.NotifyEvaluationResultInvalidated(); } diff --git a/src/Microsoft.Management.UI.Internal/ManagementList/FilterCore/FilterRules/SingleValueComparableValueFilterRule.cs b/src/Microsoft.Management.UI.Internal/ManagementList/FilterCore/FilterRules/SingleValueComparableValueFilterRule.cs index 9486a126820..a34288a6530 100644 --- a/src/Microsoft.Management.UI.Internal/ManagementList/FilterCore/FilterRules/SingleValueComparableValueFilterRule.cs +++ b/src/Microsoft.Management.UI.Internal/ManagementList/FilterCore/FilterRules/SingleValueComparableValueFilterRule.cs @@ -44,7 +44,7 @@ public override bool IsValid #region Ctor /// - /// Initializes a new instance of the SingleValueComparableValueFilterRule class. + /// Initializes a new instance of the class. /// protected SingleValueComparableValueFilterRule() { @@ -52,6 +52,17 @@ protected SingleValueComparableValueFilterRule() this.Value.PropertyChanged += this.Value_PropertyChanged; } + /// + /// Initializes a new instance of the class. + /// + /// The source to initialize from. + protected SingleValueComparableValueFilterRule(SingleValueComparableValueFilterRule source) + : base(source) + { + this.Value = (ValidatingValue)source.Value.DeepClone(); + this.Value.PropertyChanged += this.Value_PropertyChanged; + } + #endregion Ctor private void Value_PropertyChanged(object sender, PropertyChangedEventArgs e) diff --git a/src/Microsoft.Management.UI.Internal/ManagementList/FilterCore/FilterRules/TextContainsFilterRule.cs b/src/Microsoft.Management.UI.Internal/ManagementList/FilterCore/FilterRules/TextContainsFilterRule.cs index fe581ca2031..acd52555498 100644 --- a/src/Microsoft.Management.UI.Internal/ManagementList/FilterCore/FilterRules/TextContainsFilterRule.cs +++ b/src/Microsoft.Management.UI.Internal/ManagementList/FilterCore/FilterRules/TextContainsFilterRule.cs @@ -18,13 +18,22 @@ public class TextContainsFilterRule : TextFilterRule private static readonly string TextContainsWordsRegexPattern = WordBoundaryRegexPattern + TextContainsCharactersRegexPattern + WordBoundaryRegexPattern; /// - /// Initializes a new instance of the TextContainsFilterRule class. + /// Initializes a new instance of the class. /// public TextContainsFilterRule() { this.DisplayName = UICultureResources.FilterRule_Contains; } + /// + /// Initializes a new instance of the class. + /// + /// The source to initialize from. + public TextContainsFilterRule(TextContainsFilterRule source) + : base(source) + { + } + /// /// Determines if Value is contained within data. /// diff --git a/src/Microsoft.Management.UI.Internal/ManagementList/FilterCore/FilterRules/TextDoesNotContainFilterRule.cs b/src/Microsoft.Management.UI.Internal/ManagementList/FilterCore/FilterRules/TextDoesNotContainFilterRule.cs index 29bec9b4bbf..f85ca1bd125 100644 --- a/src/Microsoft.Management.UI.Internal/ManagementList/FilterCore/FilterRules/TextDoesNotContainFilterRule.cs +++ b/src/Microsoft.Management.UI.Internal/ManagementList/FilterCore/FilterRules/TextDoesNotContainFilterRule.cs @@ -14,7 +14,7 @@ namespace Microsoft.Management.UI.Internal public class TextDoesNotContainFilterRule : TextContainsFilterRule { /// - /// Initializes a new instance of the TextDoesNotContainFilterRule class. + /// Initializes a new instance of the class. /// public TextDoesNotContainFilterRule() { @@ -22,6 +22,15 @@ public TextDoesNotContainFilterRule() this.DefaultNullValueEvaluation = true; } + /// + /// Initializes a new instance of the class. + /// + /// The source to initialize from. + public TextDoesNotContainFilterRule(TextDoesNotContainFilterRule source) + : base(source) + { + } + /// /// Determines if Value is not contained within data. /// diff --git a/src/Microsoft.Management.UI.Internal/ManagementList/FilterCore/FilterRules/TextDoesNotEqualFilterRule.cs b/src/Microsoft.Management.UI.Internal/ManagementList/FilterCore/FilterRules/TextDoesNotEqualFilterRule.cs index 4e72fe16e67..0c7bb96532c 100644 --- a/src/Microsoft.Management.UI.Internal/ManagementList/FilterCore/FilterRules/TextDoesNotEqualFilterRule.cs +++ b/src/Microsoft.Management.UI.Internal/ManagementList/FilterCore/FilterRules/TextDoesNotEqualFilterRule.cs @@ -14,7 +14,7 @@ namespace Microsoft.Management.UI.Internal public class TextDoesNotEqualFilterRule : TextEqualsFilterRule { /// - /// Initializes a new instance of the TextDoesNotEqualFilterRule class. + /// Initializes a new instance of the class. /// public TextDoesNotEqualFilterRule() { @@ -22,6 +22,15 @@ public TextDoesNotEqualFilterRule() this.DefaultNullValueEvaluation = true; } + /// + /// Initializes a new instance of the class. + /// + /// The source to initialize from. + public TextDoesNotEqualFilterRule(TextDoesNotEqualFilterRule source) + : base(source) + { + } + /// /// Determines if data is not equal to Value. /// diff --git a/src/Microsoft.Management.UI.Internal/ManagementList/FilterCore/FilterRules/TextEndsWithFilterRule.cs b/src/Microsoft.Management.UI.Internal/ManagementList/FilterCore/FilterRules/TextEndsWithFilterRule.cs index baca67801bf..2d4febe058d 100644 --- a/src/Microsoft.Management.UI.Internal/ManagementList/FilterCore/FilterRules/TextEndsWithFilterRule.cs +++ b/src/Microsoft.Management.UI.Internal/ManagementList/FilterCore/FilterRules/TextEndsWithFilterRule.cs @@ -18,13 +18,22 @@ public class TextEndsWithFilterRule : TextFilterRule private static readonly string TextEndsWithWordsRegexPattern = WordBoundaryRegexPattern + TextEndsWithCharactersRegexPattern; /// - /// Initializes a new instance of the TextEndsWithFilterRule class. + /// Initializes a new instance of the class. /// public TextEndsWithFilterRule() { this.DisplayName = UICultureResources.FilterRule_TextEndsWith; } + /// + /// Initializes a new instance of the class. + /// + /// The source to initialize from. + public TextEndsWithFilterRule(TextEndsWithFilterRule source) + : base(source) + { + } + /// /// Determines if data ends with Value. /// diff --git a/src/Microsoft.Management.UI.Internal/ManagementList/FilterCore/FilterRules/TextEqualsFilterRule.cs b/src/Microsoft.Management.UI.Internal/ManagementList/FilterCore/FilterRules/TextEqualsFilterRule.cs index e49dd9b4a0d..a6e74dca622 100644 --- a/src/Microsoft.Management.UI.Internal/ManagementList/FilterCore/FilterRules/TextEqualsFilterRule.cs +++ b/src/Microsoft.Management.UI.Internal/ManagementList/FilterCore/FilterRules/TextEqualsFilterRule.cs @@ -17,13 +17,22 @@ public class TextEqualsFilterRule : TextFilterRule private static readonly string TextEqualsCharactersRegexPattern = "^{0}$"; /// - /// Initializes a new instance of the TextEqualsFilterRule class. + /// Initializes a new instance of the class. /// public TextEqualsFilterRule() { this.DisplayName = UICultureResources.FilterRule_Equals; } + /// + /// Initializes a new instance of the class. + /// + /// The source to initialize from. + public TextEqualsFilterRule(TextEqualsFilterRule source) + : base(source) + { + } + /// /// Determines if data is equal to Value. /// diff --git a/src/Microsoft.Management.UI.Internal/ManagementList/FilterCore/FilterRules/TextFilterRule.cs b/src/Microsoft.Management.UI.Internal/ManagementList/FilterCore/FilterRules/TextFilterRule.cs index 0dc75cf24e4..c4cd5dba9f7 100644 --- a/src/Microsoft.Management.UI.Internal/ManagementList/FilterCore/FilterRules/TextFilterRule.cs +++ b/src/Microsoft.Management.UI.Internal/ManagementList/FilterCore/FilterRules/TextFilterRule.cs @@ -62,7 +62,7 @@ public bool CultureInvariant } /// - /// Initializes a new instance of the TextFilterRule class. + /// Initializes a new instance of the class. /// protected TextFilterRule() { @@ -70,6 +70,17 @@ protected TextFilterRule() this.CultureInvariant = false; } + /// + /// Initializes a new instance of the class. + /// + /// The source to initialize from. + protected TextFilterRule(TextFilterRule source) + : base(source) + { + this.IgnoreCase = source.IgnoreCase; + this.CultureInvariant = source.CultureInvariant; + } + /// /// Gets the current value and determines whether it should be evaluated as an exact match. /// diff --git a/src/Microsoft.Management.UI.Internal/ManagementList/FilterCore/FilterRules/TextStartsWithFilterRule.cs b/src/Microsoft.Management.UI.Internal/ManagementList/FilterCore/FilterRules/TextStartsWithFilterRule.cs index e97deb0fd46..3b39d0f5660 100644 --- a/src/Microsoft.Management.UI.Internal/ManagementList/FilterCore/FilterRules/TextStartsWithFilterRule.cs +++ b/src/Microsoft.Management.UI.Internal/ManagementList/FilterCore/FilterRules/TextStartsWithFilterRule.cs @@ -18,13 +18,22 @@ public class TextStartsWithFilterRule : TextFilterRule private static readonly string TextStartsWithWordsRegexPattern = TextStartsWithCharactersRegexPattern + WordBoundaryRegexPattern; /// - /// Initializes a new instance of the TextStartsWithFilterRule class. + /// Initializes a new instance of the class. /// public TextStartsWithFilterRule() { this.DisplayName = UICultureResources.FilterRule_TextStartsWith; } + /// + /// Initializes a new instance of the class. + /// + /// The source to initialize from. + public TextStartsWithFilterRule(TextStartsWithFilterRule source) + : base(source) + { + } + /// /// Determines if data starts with Value. /// diff --git a/src/Microsoft.Management.UI.Internal/ManagementList/FilterCore/IDeepCloneable.cs b/src/Microsoft.Management.UI.Internal/ManagementList/FilterCore/IDeepCloneable.cs new file mode 100644 index 00000000000..3090f93a95c --- /dev/null +++ b/src/Microsoft.Management.UI.Internal/ManagementList/FilterCore/IDeepCloneable.cs @@ -0,0 +1,18 @@ +// Copyright (c) Microsoft Corporation. +// Licensed under the MIT License. + +namespace Microsoft.Management.UI.Internal +{ + /// + /// Defines a generalized method for creating a deep copy of an instance. + /// + [System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.MSInternal", "CA903:InternalNamespaceShouldNotContainPublicTypes")] + public interface IDeepCloneable + { + /// + /// Creates a deep copy of the current instance. + /// + /// A new object that is a deep copy of the current instance. + object DeepClone(); + } +} diff --git a/src/Microsoft.Management.UI.Internal/ManagementList/FilterCore/ValidatingSelectorValue.cs b/src/Microsoft.Management.UI.Internal/ManagementList/FilterCore/ValidatingSelectorValue.cs index 30b2fe8fac1..ff981a74751 100644 --- a/src/Microsoft.Management.UI.Internal/ManagementList/FilterCore/ValidatingSelectorValue.cs +++ b/src/Microsoft.Management.UI.Internal/ManagementList/FilterCore/ValidatingSelectorValue.cs @@ -19,6 +19,37 @@ namespace Microsoft.Management.UI.Internal [Serializable] public class ValidatingSelectorValue : ValidatingValueBase { + /// + /// Initializes a new instance of the class. + /// + public ValidatingSelectorValue() + { + } + + /// + /// Initializes a new instance of the class. + /// + /// The source to initialize from. + public ValidatingSelectorValue(ValidatingSelectorValue source) + : base(source) + { + availableValues.EnsureCapacity(source.availableValues.Count); + if (typeof(IDeepCloneable).IsAssignableFrom(typeof(T))) + { + foreach (var value in source.availableValues) + { + availableValues.Add((T)((IDeepCloneable)value).DeepClone()); + } + } + else + { + availableValues.AddRange(source.availableValues); + } + + selectedIndex = source.selectedIndex; + displayNameConverter = source.displayNameConverter; + } + #region Properties #region Consts @@ -150,6 +181,12 @@ public IValueConverter DisplayNameConverter #region Public Methods + /// + public override object DeepClone() + { + return new ValidatingSelectorValue(this); + } + #region Validate /// diff --git a/src/Microsoft.Management.UI.Internal/ManagementList/FilterCore/ValidatingValue.cs b/src/Microsoft.Management.UI.Internal/ManagementList/FilterCore/ValidatingValue.cs index fe21d2fee37..eee8ebc6e4a 100644 --- a/src/Microsoft.Management.UI.Internal/ManagementList/FilterCore/ValidatingValue.cs +++ b/src/Microsoft.Management.UI.Internal/ManagementList/FilterCore/ValidatingValue.cs @@ -18,6 +18,23 @@ namespace Microsoft.Management.UI.Internal [Serializable] public class ValidatingValue : ValidatingValueBase { + /// + /// Initializes a new instance of the class. + /// + public ValidatingValue() + { + } + + /// + /// Initializes a new instance of the class. + /// + /// The source to initialize from. + public ValidatingValue(ValidatingValue source) + : base(source) + { + value = source.Value is IDeepCloneable deepClone ? deepClone.DeepClone() : source.Value; + } + #region Properties #region Value @@ -50,6 +67,12 @@ public object Value #region Public Methods + /// + public override object DeepClone() + { + return new ValidatingValue(this); + } + /// /// Gets the raw value cast/transformed into /// type T. diff --git a/src/Microsoft.Management.UI.Internal/ManagementList/FilterCore/ValidatingValueBase.cs b/src/Microsoft.Management.UI.Internal/ManagementList/FilterCore/ValidatingValueBase.cs index d1c349dc32c..6b4c2daa10d 100644 --- a/src/Microsoft.Management.UI.Internal/ManagementList/FilterCore/ValidatingValueBase.cs +++ b/src/Microsoft.Management.UI.Internal/ManagementList/FilterCore/ValidatingValueBase.cs @@ -16,8 +16,29 @@ namespace Microsoft.Management.UI.Internal /// [System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.MSInternal", "CA903:InternalNamespaceShouldNotContainPublicTypes")] [Serializable] - public abstract class ValidatingValueBase : IDataErrorInfo, INotifyPropertyChanged + public abstract class ValidatingValueBase : IDataErrorInfo, INotifyPropertyChanged, IDeepCloneable { + /// + /// Initializes a new instance of the class. + /// + protected ValidatingValueBase() + { + } + + /// + /// Initializes a new instance of the class. + /// + /// The source to initialize from. + protected ValidatingValueBase(ValidatingValueBase source) + { + ArgumentNullException.ThrowIfNull(source); + validationRules.EnsureCapacity(source.validationRules.Count); + foreach (var rule in source.validationRules) + { + validationRules.Add((DataErrorInfoValidationRule)rule.DeepClone()); + } + } + #region Properties #region ValidationRules @@ -129,6 +150,9 @@ public string Error #region Public Methods + /// + public abstract object DeepClone(); + #region AddValidationRule /// diff --git a/src/Microsoft.Management.UI.Internal/ManagementList/FilterCore/ValidationRules/DataErrorInfoValidationRule.cs b/src/Microsoft.Management.UI.Internal/ManagementList/FilterCore/ValidationRules/DataErrorInfoValidationRule.cs index 652592aec04..78b54a9c045 100644 --- a/src/Microsoft.Management.UI.Internal/ManagementList/FilterCore/ValidationRules/DataErrorInfoValidationRule.cs +++ b/src/Microsoft.Management.UI.Internal/ManagementList/FilterCore/ValidationRules/DataErrorInfoValidationRule.cs @@ -10,7 +10,7 @@ namespace Microsoft.Management.UI.Internal /// [System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.MSInternal", "CA903:InternalNamespaceShouldNotContainPublicTypes")] [Serializable] - public abstract class DataErrorInfoValidationRule + public abstract class DataErrorInfoValidationRule : IDeepCloneable { /// /// When overridden in a derived class, performs validation checks on a value. @@ -25,5 +25,8 @@ public abstract class DataErrorInfoValidationRule /// A DataErrorInfoValidationResult object. /// public abstract DataErrorInfoValidationResult Validate(object value, System.Globalization.CultureInfo cultureInfo); + + /// + public abstract object DeepClone(); } } From b69a4798068fe64bd2aac654b07c41f2999fca50 Mon Sep 17 00:00:00 2001 From: PowerShell Team Bot <69177312+pwshBot@users.noreply.github.com> Date: Fri, 29 Aug 2025 16:47:05 -0700 Subject: [PATCH 02/16] [release/v7.5] Fix updatable help test for new content (#25944) Co-authored-by: Aditya Patwardhan --- .../engine/Help/UpdatableHelpSystem.Tests.ps1 | 6 +++--- ...3e-98dc-74d7b4d63d59_en-US_helpcontent.cab | Bin 202345 -> 198346 bytes ...3e-98dc-74d7b4d63d59_en-US_helpcontent.zip | Bin 203864 -> 198989 bytes 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/test/powershell/engine/Help/UpdatableHelpSystem.Tests.ps1 b/test/powershell/engine/Help/UpdatableHelpSystem.Tests.ps1 index 439dc3be989..d4cbb420066 100644 --- a/test/powershell/engine/Help/UpdatableHelpSystem.Tests.ps1 +++ b/test/powershell/engine/Help/UpdatableHelpSystem.Tests.ps1 @@ -47,7 +47,7 @@ else } # default values for system modules -[string] $myUICulture = 'en-US' +[string] $myUICulture = 'en-US' [string] $HelpInstallationPath = Join-Path $PSHOME $myUICulture [string] $HelpInstallationPathHome = Join-Path $userHelpRoot $myUICulture @@ -118,7 +118,7 @@ else } "Microsoft.PowerShell.Utility" = @{ - HelpFiles = "Microsoft.PowerShell.Commands.Utility.dll-Help.xml", "Microsoft.PowerShell.Utility-help.xml" + HelpFiles = "Microsoft.PowerShell.Commands.Utility.dll-Help.xml" HelpInfoFiles = "Microsoft.PowerShell.Utility_1da87e53-152b-403e-98dc-74d7b4d63d59_HelpInfo.xml" CompressedFiles = "Microsoft.PowerShell.Utility_1da87e53-152b-403e-98dc-74d7b4d63d59_en-US_helpcontent$extension" HelpInstallationPath = $HelpInstallationPath @@ -216,7 +216,7 @@ function RunUpdateHelpTests # Delete the whole help directory Remove-Item ($moduleHelpPath) -Recurse - + [hashtable] $UICultureParam = $(if ((Get-UICulture).Name -ne $myUICulture) { @{ UICulture = $myUICulture } } else { @{} }) [hashtable] $sourcePathParam = $(if ($useSourcePath) { @{ SourcePath = Join-Path $PSScriptRoot assets } } else { @{} }) Update-Help -Module:$moduleName -Force @UICultureParam @sourcePathParam -Scope:$updateScope diff --git a/test/powershell/engine/Help/assets/Microsoft.PowerShell.Utility_1da87e53-152b-403e-98dc-74d7b4d63d59_en-US_helpcontent.cab b/test/powershell/engine/Help/assets/Microsoft.PowerShell.Utility_1da87e53-152b-403e-98dc-74d7b4d63d59_en-US_helpcontent.cab index 949471140cebe33bf9c372e4ee2b261f131d81f1..ec0d3294f798823316d9c9d11643763c0b29a5c9 100644 GIT binary patch delta 923 zcmV;M17!T^tPIMH3@J@hLq-4q007Dc0{{R3001li00000000940RRC200000003_Q z0029YDMo>%g{1+7r2@630|x_tt)_r-OamhpLrd*l!EW0y480fVKL|by>*jR`(35v4 zd|GqqVI5EeK@P5KYzERKh?V8ij~_`%wB#s~oi*DK;ymcD zVT49~#aQku*CQ)&+Z%c{!ktj4uh!yS2x?BK2fgkKXYET& zH|$Iv^KbX#`>&Y@QagHoXUJkFKarBaU3 zW8n=uNmeqdy=-uJJK$5RM_WyVEg&@lde}y~ya@eUglMDKbwN{q4>H|%9_@RsxO&OT zG2%1UAW)T z*sg&PpM_OeVr27wTI8r{)zO-^SLSv0Ve0b~+z6{H18i7?miK6=QQ})Qh@+|JYk2vq z2ZvSu0Mi)O+mh&RR6Dp~t=D}AWHVw^q5L-%FWlm*5JO^-=@2_2w;&u%x!<(P-NkZR z@AxpqK16+qPcH58g51!ER^#tWCkAY^7whPt(jYoc`5MxHTY*KslbM)+fQV_XegTD* zgKiVkyP3EvU^^O(D5eiH@vvzSGJ`*E;osGKCElBD3qvQ9UCjnAuaEt$C`eDGf#-We)^-_Mj0?ls`7?%HF*Jft{04sFw_N}L delta 4946 zcmV-Y6RqsZjST6m3@J@hLq-4q003zg0{{R3001li00000000940RRF3003$s005-` z002CZDMl{Sp#T6Q799Wp07XePjvxR{X=8G4b8lvJE>LfGWpYz!Wo&FNRdi`=X>@rl zXn|*iX90z00<~uY4+DQ2V&iY54FG^cOYI$NZ`;W6yMg?Ng@9|5tE^DAWaZrJ1&SZB z(M#ak35*~JdK-z7h*G4Ek0kEpepx^CC-pCMX7<6QM3Is$Do$<#$4I1o@4RPr<{Pm7 zs?8Nxf7L-qt1mlGmOa-aTjITVw6~y(>+r-f{_4dn)6@%U(W!rv&~bbN*&3e@xPvrN zCODn_=2d7|TVWyI&hmOcb4JAp z<)E!q%~{I=)75`*Hm>`ZFV9~-KV+q|)m|BrFE}XkB3r4c@FSWAE7qTD{&{|mbgPpRkS=rnOcJoHl z0?YKO>&W`$!L@MOB%Ix0s#2`iMr`%!MvZ!iuTu&BB?AHNErx~Q^9{4QcT5VVzUsyT z7&OcW0ZA?_|V1Z_CNF*9*`PZLDlsA62|~Un;NWxo(2&+ z)oDndGJNFw<{f(j0K(q(#Xo>x^?|mrw`cFz83%M!6p_8XddIHP7)@sIpQrG(>(AIj zg|;ws<4xny;pWIuxiPJb2x(bE`!~ynqSK*((#-Rz^#oX5k$F+3*`O1XM;{wPy?ofq z6GMOdSYb~eKpF7V`{63;LANh7DVPUFNj!W~6|F{$4QX%gUD=%NHRlJ`P`1Ku#Ia`E zkZ>%eHkocoJjR4MW^iJ&L>pfh1BQ%3mueNar#|INFAFjHhJC$l>uj~oR_kp2=AK&> z6S6H%T~E-*T5jgY=ypZd2)6j&fByU9(@%d>ALDppky5bbG#D%}9Fj{TO-8#<8c%=P z`B8Uc!V~as_90JwEF? zoVv>n@E%sE#^tTyt+W2P`;(n@qmMdUDvk9nSwa;`E-OQ5k?{57kM^gVa_mVRh z#9v`&kIQi3&eTpM6j_(rsb03$ndNkr(|y4!{JPM$I(TTUkea&%med{d&^}zju)mVM z`&%^`_PQM#5xzCG`jkv@yJzmF3RQnvG_25~^TVv5J|AcHcY1mjMmn!3CJy8=n*gK^ z-GMjdevJX**)b>&#c-XKsc5QvG#I5l9gWauFH%{m=q;l-8(Afrm>bYcQ^$ov!FNZ= z6caS6+8~%qH7sa|E?4bD_0wsPxp4HP)t(!f?wtn=frtFg`OVNlAOldTIir6lonf{; zbwcv2gcE(rZ?YP2@*7H;3berukYcphWg3_z5Sp2f*b68Dpv+i}E-(>MN5ia8G4oEg z5)&&{wvTLC?l_T##ao9ptBcIQQrRj+${#NoUc;I=gw8?+#SEu`XRV{i|4W5)Xc;<~yf}zKQ*MWx|xqP(BHWjff z`G6HTFgfC#V$L^XAffxHQ3a;Utatogy~97Lark*KtaB&TGt_H+`06##dY-scas(P- zxfZvCDg5@^DTap|ote?{_us9;T5fNuL6X?0{b?R-F0L+kGRXnX&mMow&wc#-wq=QV z6miOy?TfL1(v(5-L0aqF1=R|b9~Pt zHxYqhdWKwGWgO8Rs%U?7t``&ocu+m3Wf_b}p8Exp)>9On@RI)nMuTzwd6|N;|H~Gi zuDyMCI|7dVEJEMfpu7P=Mt81cgGoY8c8@fYwG_jhu3`t+p>yRNJ z&cl{?4`SfFDud{UA`wznYju%au!rC0`g7km)+hCxg#j(DY{h@sV%27=Hs4FN*{Um6 zUHOXD6{}!a1;Z*BUsPzaDuGoA{s1b$SCReYzL#2mq5bzpg%9~qGHyM)N!+8*_uYZ< zY-a7FDMs4-*i7QKtaGQaI3|a$ZiV$epSIu%I~48dVya94>RPB`BJBHf0Xt=mlcTkucTI)5~rKc z(r^P`H2=*OQ?opmZ8z4RSA-fJEAH>Gb#%0Rv1>%q6FsIF&J?88)r^B2s za}Jo(W6l%i{J@+(bDlBhh&jj1IbqJ}gPjLEj{F0?J5PVuL6;pI(a$N{b6B^B|LE<} zC!K|&!p5T3Pi>-*srsCZL?gzGwPu<=2Q-Wm44%?q7)chhSawqen(Yz}HQ)=N&@=kg zN_wNl2Cr@f-8da~W78fupIfUevu51E#@N|G)C9IJS`k)oM$#ib!T-$>|UZ+~Nt93}1@rE!9f*S7GiQqK6Zx24y>a=uq6 z18;M}Yj3!L`E2y*2BJ^VmVghh&Uiejh(Cp<6~U(k1FaB&iuHukMB4U&E>4VWf|6u{ zz$t&~$8oKs4nwzb@#I zUu?K!sunv{lf*I!lbk3fMe)5WGawBqy9j?~^WZ+{33H1#cPrvlRqI?;!t6j>?8*b? z$|F^u5qthQ*5fUiJ2x6JWH=2YWcCcy!w;y%=!RI8Iy3KOCJtrASUh}{@o5fx%=@hH zxXjXM7K$X**#MM3NTgl)03d)GBfX#-M|?IBl`3~*caTPy6N(b-!A6A6#*<}hrSN}Y zG;UIzZh2ZQc0Iu_<8bQEL3P}e`Y0aFi{q}SQhwHNH>rYD5Hdf6AxJ!lVU<_{TP}(c zU}359B;kX9iAx)YAjb;@M>NXMCU+dU0ZKd`$T?Fum)Juu9(-=(?ONR0uImwy11j*r zfN+LDAN~&_oHy{oG^XN}0Z#!oBJY3Sm_;l>kuTZBT}@%RQo@3x;L|+xuMru1DcIRZl<}rRH8LzISV6MMxK1WJQ`yZzsP@cg_&|i ziXn-@==ZH94n5Bw?`tG_Umf7Bed;+fA#P=q2EtyH2;fE5CfMF0rOn`aS}T^@(< zqJQ)XfX8=_Pp--v9(PN>zdXM9$GV- z1z9NJTXB9>nZ}6Uz|{g`^B7>HImgY!E1L?Q*2GkJsV@sAN?Juw5qp1RAd(!4l0qvv zOC;;XUV`uH`q3JuO`@+}?s$Od4U}c%9Q6ck4{)q3DZ!HE}ULcg=ETG-f6oXdnv}&hSJFVJj)lRE+TD8-voqvQp4OZr{GLMyctjx1r znP(X<_&@5wtEUMC_BTUI?On@mqe>9nN6J62$mU9M#=Hzfx(jGv?jtW+c@=9BE z4;ywOmb*uj@x+A(r7RR~7qf!GE&VKn6R6hjN^WC`93_AJk4k6@B7@4DUd^x{G7U$w zLUy1kK|}!w(3ckie~@m4Yf4@xzguZw8>Vi%sILrEMKxX9Q9VaBd=1H_j!fUN4c~Kg zS5tJiuWHD)%z@_1jwd78(G2zSg4iOgHr!6OgO8{>P!;~5=<(=PzUkMRy*;CV$c<{$ z3aesQ8h(FbX;nw9-~y=SQc*6{a%a?Ju`G&-H2nUptpHXB8$W!=@hFb$WRk`C$W52YOBzh_5yA=?Yc}&ye)R5s z+{Ae*^$JO(_(xq*S7eo7BxPAu`nDq@#q(s#M;3pUTr?PXs%puGD_gRyXr78Z%e9eZ zxsHJhb6_Lnsj{c*maJ*MXZvVi=}0#{y>EJo=Qzl6Ra}*?$iBTQ%dLC2XYu9AlnqoV zvu~qZm*$e{cd(8o(>Q@^I)gbb0dqfL{Q%X@@9!Ccah-(oV-V?6AjmMy3h zI{7&Mqux$?e6sMxZh?0 zt{xy?z9uE$n1X(Qb?h3D4|wlDEK0=;R!;Qldr37t1W%bD_O(0x!29T(&Cj}K9PV@ z@=pUn_|1GeYoZ3PUI0U>RRu%&6Mlc`=??#%;Pod6Dg^}tPbkSO(`RL-{A4lx9cJ_x zrPe%AFV;|4+hx*~m%QFWkEAGlnc%lJa+Ma%6ctuRo<*2)%LJ4n#9@)qY#aQry**MB zp(7!@8Alc89i>SWXWN8q-e)P6q@0W<4X!6O(2%v?VFWu=wn!4etxK-Ae@6Rk82|tP diff --git a/test/powershell/engine/Help/assets/Microsoft.PowerShell.Utility_1da87e53-152b-403e-98dc-74d7b4d63d59_en-US_helpcontent.zip b/test/powershell/engine/Help/assets/Microsoft.PowerShell.Utility_1da87e53-152b-403e-98dc-74d7b4d63d59_en-US_helpcontent.zip index 47643e90de6000705f6ecb69f4a032253a19e6f9..480f920114fcee4f8339fe5d781f50f1d418b152 100644 GIT binary patch delta 41 rcmcbygXiodo`x327N#xCH#w$Hlx9v4VP#_g0!AQA0Mgevn1Lh!45SG( delta 4952 zcma)=Wl+=&w8nQ?IwhrEy56NjM1iFj3F+=eI(7+R**_qNba$6DN_RKXNC;9A0s?}R zlzY81_uiTN;m+K1W_~B;IiJtWGnquZ+)5lA1`fk&gnYpVAlOtO5C|XiBu+_%BA>d@ zhXMqeF9d-&|88G8*?M~*JRFb$S{{D(-ny^sU0ntAkxs5o$k+UT2TuWiH`in7Rd0qi z@1K>wWD4{GHGFj`hN@m1EUy&}m|LDM>G^>XT2N#82^3Va2_MG)TId}KEz_ww!F z3F5bmz!_VliHaoDR9sjVvLE;~pciwOj|qC%e!9WF#77}TyneI@T`=zt)!sj~x_5Y* zc7k@i9!bt!qTG8q&2f%HUQBR)kZqoSyM8h@Rh7F`Jag@|$(Rig$Py0iTkG!El!8zFXS-D}=J zFvkjAB5QY)XC&#DQSz0N+etw-$ajtiB3sP}FYE!fB-X=YKvf^eymR?@W^TrrQZDs0 ztMlY&@wmfT&cMjQwZ6(ho^QGYDbln2y7by8l-B9kNo?$SVBn*20xe-8jt5_nYdU~# zqSq{{w>koc;`?Q$9eL#PPvLml(}WnCX0=+U!GqKhv_qefM$%&4V+-?t1^g zCbq$nMDOcR4U%Ftg_}BKxF7K^C8*UqzmW6?AFrql?N0Km2km==)u4r&=V~5QyE1CN z=C4nWNqOuPvY@Omw$HNbzrv$Dt6{((IX52n2eLVB+|U6t`am6pAhLI}=H8Ry3Z*}c zl%y5X@z|J8hK%9;YRmKNiKk^eF=gCU2Z2Pm#32!1Z6Aw*<bl*mf>S;^@@Rqc9^f9(^Mr5dy* z7g2)ujmGY>pkVfpUYJffBx~v<$3>%AUivgdFhi;iRx8J}CWs*~G^(=ND3`g+C^b=! z1`D=@VV2@Z@E>MbDC8A=z0rVNz(_vkc^WYPN-y@k0=Br|?Mpt(inUp|XMJ2~^E0Uc zJ(2H@OeLZ?7g_QDdUG~s!5^h>2z+_^`j=X8k^*`G>`~uvQ8xtR=E7o(K%C_VzF8wf zE1o(|WovXTx<4UdVN5^qcIUjZ@cz-voBbJTcxohT-xZx( zE#l|jnW;-ZrwpTb%;-r0=SZQ{`mQ=hKy8PWbNG?`y29{_#>b#%)*K2Dx|_{lm2a-~ zB3gsx_Rt*>4$?{y=3T7zMeR|nusSLo$S z?WxL8IZL49`We^;LgN6D9IhaE;p027mvxsnR{Xrm<2uLU;Ap#TcKSzlOd>?vZIe8T z1z@2&f?}S$(4A6Rk=FLi)7h}!NO-7L547^NLQR(l9exiEA`ff2NW^p;gDctj4$ z8lsv=Tz^Vwr5U!8f;9j!Bqz3ZWjC}@8(O(ZRKAkL<*I89`3ZP|X?F!2*=+a-^N53ZE6NPI$TVMx-EFx|PkI!wgvrkCjKP&LG%SX8No6}0)K5IP+Imk;$q~h5N*qEh z+(OjTWqz8=4}|7B4!E7zT@tTO!ldr-FJL!s)b^FSWpXPHfaOs(9{d(%uNK+_vSsnKLuG9kmZ4bekN#d|bj32-9sPB$KHMbD3r{M^ewqm8V2kDZ_#CW4m z^RB)F-G2rXR(pfW3?G@HZVZEQJ-bowSeG#Yf=_KkFCTtlyq@C3ZL5ZKBjj)7>{_*a zW4AvvIMdRso_P!g*WeJv@MRe3k+j%E5Irae>j487hMjwWvFw<>kRAC~f`H=`3P+C3kizkBLe%b8pq^5|gz(A^q+3VpgRXVE_ zFr3cnzEDG#jv|G%@6+xH384hdibhmR!J!~;c&)bk@uxThhCgP3j%%D4DqXYpxz1_4 zK0A-Y@rQt`xlXLkj7jIU-@#4L`(<8fX%jsS$D~X7X$T(HqOaj+Dk3)~apC%ZoHw9A zs!bs&YTkln%20?@tS@syox!t@0~v#gNA|t;hDys&s3avRo6?D?F=1lK;mVXn&T0iM zF!rZJZB?HYPvwB)FwfCRe01#GSp#7VFAl%cc!**W_t~`^*dYlUclgg(u> zml6~11>6d8l_sUih>{hq1=0$pMCWupO_e6)${?M85)Mxn{(im6n~>Q_(r2!T19`Wi z9G6h1rZbu2c4w!g8)g@6msCq9W03{LNTHfx$}1r$k4d9mH5V#GjY99Oa2TMvr2c|0 zPxX_Kok2ruj80m{F)!UpI9@?v*`c9Q^V%97hjEvZiT<2x(jIHYwMsGk5Hk0w;=~*k z;QSB*|CN6J$Fs-4NgoQ_kfWg-U_%8@AzV2AlUBw3LNYMWgIDmC63FlgBjCeP$RhDJVn62kvv}mQY*0{HSsJ^xLXwm6@eNbz zXQC0j@5qV4y&U_L@(f+&8HI3j4pwaEc{BLI1l1z#-m;oX`~bHeBnFax%P2KcUM>~E z(+h$~y{Bd5I8DMYF!NdXmp5Hmo+5pXHr1ULh1EyiY6N%8q88-;0=#A|jXoX@D2;aE zn;Eqbq@l31=m>gECcQmvHae2?lOkwyB zg*@&n-|u5P-LOS~fJFc9N7&I$v=VIp!jy*Myy|v24M*`R$8D|EWo7%Z2&P)2I_tsm zfl*_(5@|>-2S>pM$w1>e{>|`sNHS35@PM^S^O#{>L_eH3StYDe@Fh0|RI5{`t<{S7 zcN@4d%xLY0T#itSe_%_y#*>2@ICqz5n87Fx-nJ^cM{{}{aL=9C-(i2kC}y7kW-u5# zGQ}tRARMJ4*{{som0l{5#5c_L>oSMv$aWqv6!}Rk?tMmG7%LqpL<$?30pEJ^5d@Dp zBx(0jETe?UKen;_sLd$st>Cy!DPCkN4)rjtf-(>9>`}jQqj%LGEhB(Y6;AK9njB^N zQ)!>qZs?Q%?f$DZ$6H%;vTPRkEGdC(R;lEB@|Q*XvL~X#nmm}YiNgs5Gkf3Qf$NwdqXs$EER);j)TxcJQVMH ztSV?Z?{wSZJAPTW4G&=HX~|3nsjw-Aw`S}$6L12@@;;=qb~>mr%u_FMUiM-bg*(gJaGVf6Y)}LfSteQnSKH#i4k{Z*0Re6 zf)Yj;zbO~hu#_{q^*y2^#ZEiybtG=hm$m~4z|c?n|HxJv_&6c6s@fZ!KmB;L?h}~a zE5-DKOd!qgs3i@C@$`lHFMasO;HQBtsFL&ue#ljdD*gP#@8!CX^Z^9-;+cbJ(_52r zjhWx3?{`i?8ebS#`0>T5d)WgKi|Rf04gK$r$-#$Hh<7%8#Z^>>__Y*@!}5JAt;xRu zW1(=m(&JhtY{vXwjWrj^E#AZ%3{S!+I9`q$cAb>OLfO>i?`ywSai1DMioCP^sCiU9 z{IC)ljx|6!?UQQkd~_RoVh@YrA1+>8sNF0M&P$M6ez?W=o!(ztqj`XqYsoVGa5bv& zv!&mt|5T=HU>?w3T4wq`0RFq>B|Mpq{emW&Nnc#^C}fo6PuAfygQ{sZc+V2v(3V4M zhsK@UIKA`Kts`l&NJ}G=k3@r)@(AWeDiW>>&DW|$CRGkZ_ePbx+O9LR`5y|$>jVi` z^&|bM-W2O;)yBTZ6-uXeSvCEaX9Qg``YlsG7bcH9BX$NF-L#1zJ_hB4*OOyW{Hggg zO7bdGRGJ|(5xjJ6<~SB_bM2Rn|2J-s{Zo$-nhFQ!DKO3d#Ps&9l;Ai`O2n}s=3|$- zXu8v$$mBh0r&66r1~Kf9Zn2>1VN2{;2wtD!*TmEk7oLp&=4p!%{>1+skaDpViKxCv zi(zRK>TfMRsG)5x;eQ)}lQ!DfQD!-;)^pk$K*V_#{c=Q%@1em=Vm5o#?_>@B!<*{r zC1RicG2PCq+cm0hkMe`^96H|=%tiB@;_MqrI{gV@EGw*_uSLg=S+}e?`P_9mKYY~o zK;Ev+4{tWkGKmK^X;dxI0eQZw1AR_a8`Ukh3-E>|=|e{Gh^BeD8be99W`+;GDS|cy zea~ZYFx3lk#d%_Ol7CR7982=fUDnG}Gv!cl;0Q{`ux4k*;1Um!0 z3}NrL@9vw5W~h^hry-AZlu;)k^07BRGGkO<#^(9r^yFj zO_#1)D=kU-tGHl9U_`_l_;&f?$x@v2S^#2ZpCs;fZGBc7)41m1N7~V~UZBm;XrkQY zfRNZH4UAB(v8WOSUg=A|x!t^I^!w}qD!1+uKN9%$Yo#O5>gr`mg$(PcH<0OAMG%)H zllMp4qe40l+b;#{&yh;2T%}jB)av{ccIrk#Cp7Bt^NPLR`77_14b23;MiC*fRRRdNiqzF$bP)8Ikf40=7y5(Ig|vI|R}7iROiV6-JPa0XP?K|j!i^#A#ijH;YG}){uAGXRxOrdA zAr^o&c#Mvg>ht~>&$ zQuTn&vRTV-YXo2y7yhT<>sJ)iVP`M&+Jd%KYNf>PT#7IxIjsM^K&{~;rsi{kHZhPZQCC&>v6)T=1J0v$6kkSN=5(|gvFYo{Gytx40*-J+*Y@ue#zhJ2~0W| zQc@XqJ=FJ+ymsdozgZ?S zC;jJu&SC)6DWSnqZ|l49=3BCC5|sjhDokfSFXjlreRKRFQR#-X%aS^>(>O*$T+=j# zLok80tv;ADlBRm0;uR}@8wqk)5r;$F93`XLqr?^xjF~pb9(DI^L04OBZ0-f}3Blv% zA}vt@yQh8L|K3*Fu~u(u)Ba{Lv_wBCM;~#FgSn_@R0fzZWa&%z5F1A@78U6Kwz&Sf eT>lk+#s5FUOG^z0_djp2{$|798qWav%l`o5UQY)A From add10d83e520326b210f6eb1fdb84446b7dfa106 Mon Sep 17 00:00:00 2001 From: PowerShell Team Bot <69177312+pwshBot@users.noreply.github.com> Date: Tue, 2 Sep 2025 14:10:17 -0700 Subject: [PATCH 03/16] [release/v7.5] Update branch for release (#25942) Co-authored-by: Travis Plunk --- DotnetRuntimeMetadata.json | 2 +- global.json | 2 +- ...oft.PowerShell.Commands.Diagnostics.csproj | 8 +- ...soft.PowerShell.Commands.Management.csproj | 4 +- ...crosoft.PowerShell.Commands.Utility.csproj | 6 +- ...crosoft.PowerShell.CoreCLR.Eventing.csproj | 2 +- .../Microsoft.PowerShell.SDK.csproj | 98 ++++++++--------- .../Microsoft.WSMan.Management.csproj | 4 +- .../System.Management.Automation.csproj | 24 ++--- .../BenchmarkDotNet.Extensions.csproj | 3 +- .../ResultsComparer/ResultsComparer.csproj | 3 +- ...soft.PowerShell.NamedPipeConnection.csproj | 26 ++--- test/tools/TestService/TestService.csproj | 94 ++++++++-------- test/tools/WebListener/WebListener.csproj | 6 +- tools/cgmanifest.json | 102 +++++++++--------- 15 files changed, 193 insertions(+), 191 deletions(-) diff --git a/DotnetRuntimeMetadata.json b/DotnetRuntimeMetadata.json index a97818844d0..eb802bfec36 100644 --- a/DotnetRuntimeMetadata.json +++ b/DotnetRuntimeMetadata.json @@ -4,7 +4,7 @@ "quality": "daily", "qualityFallback": "preview", "packageVersionPattern": "9.0.0-preview.6", - "sdkImageVersion": "9.0.301", + "sdkImageVersion": "9.0.304", "nextChannel": "9.0.0-preview.7", "azureFeed": "", "sdkImageOverride": "" diff --git a/global.json b/global.json index 2808b362fea..4c6e2601f69 100644 --- a/global.json +++ b/global.json @@ -1,5 +1,5 @@ { "sdk": { - "version": "9.0.301" + "version": "9.0.304" } } diff --git a/src/Microsoft.PowerShell.Commands.Diagnostics/Microsoft.PowerShell.Commands.Diagnostics.csproj b/src/Microsoft.PowerShell.Commands.Diagnostics/Microsoft.PowerShell.Commands.Diagnostics.csproj index af93c4c2bf4..0b8a4c35cfc 100644 --- a/src/Microsoft.PowerShell.Commands.Diagnostics/Microsoft.PowerShell.Commands.Diagnostics.csproj +++ b/src/Microsoft.PowerShell.Commands.Diagnostics/Microsoft.PowerShell.Commands.Diagnostics.csproj @@ -7,11 +7,11 @@ - - - + + + - + diff --git a/src/Microsoft.PowerShell.Commands.Management/Microsoft.PowerShell.Commands.Management.csproj b/src/Microsoft.PowerShell.Commands.Management/Microsoft.PowerShell.Commands.Management.csproj index d4c5da64bae..d9f8e52e762 100644 --- a/src/Microsoft.PowerShell.Commands.Management/Microsoft.PowerShell.Commands.Management.csproj +++ b/src/Microsoft.PowerShell.Commands.Management/Microsoft.PowerShell.Commands.Management.csproj @@ -47,8 +47,8 @@ - - + + diff --git a/src/Microsoft.PowerShell.Commands.Utility/Microsoft.PowerShell.Commands.Utility.csproj b/src/Microsoft.PowerShell.Commands.Utility/Microsoft.PowerShell.Commands.Utility.csproj index 8fe2b24b05f..3e270da4164 100644 --- a/src/Microsoft.PowerShell.Commands.Utility/Microsoft.PowerShell.Commands.Utility.csproj +++ b/src/Microsoft.PowerShell.Commands.Utility/Microsoft.PowerShell.Commands.Utility.csproj @@ -13,7 +13,7 @@ runtime; build; native; contentfiles; analyzers; buildtransitive all - + @@ -41,8 +41,8 @@ - - + + diff --git a/src/Microsoft.PowerShell.CoreCLR.Eventing/Microsoft.PowerShell.CoreCLR.Eventing.csproj b/src/Microsoft.PowerShell.CoreCLR.Eventing/Microsoft.PowerShell.CoreCLR.Eventing.csproj index b88db6c5e62..a02a0637693 100644 --- a/src/Microsoft.PowerShell.CoreCLR.Eventing/Microsoft.PowerShell.CoreCLR.Eventing.csproj +++ b/src/Microsoft.PowerShell.CoreCLR.Eventing/Microsoft.PowerShell.CoreCLR.Eventing.csproj @@ -8,7 +8,7 @@ - + diff --git a/src/Microsoft.PowerShell.SDK/Microsoft.PowerShell.SDK.csproj b/src/Microsoft.PowerShell.SDK/Microsoft.PowerShell.SDK.csproj index f1feac7c371..e951707c28b 100644 --- a/src/Microsoft.PowerShell.SDK/Microsoft.PowerShell.SDK.csproj +++ b/src/Microsoft.PowerShell.SDK/Microsoft.PowerShell.SDK.csproj @@ -17,55 +17,55 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - + + + + + + + + + + + + + + + + + + + + + + + + + + - - - - - - + + + + + + - - - - - - - - - - - - - - + + + + + + + + + + + + + + - - + + - + diff --git a/src/Microsoft.WSMan.Management/Microsoft.WSMan.Management.csproj b/src/Microsoft.WSMan.Management/Microsoft.WSMan.Management.csproj index 087d4562069..c820e3f235b 100644 --- a/src/Microsoft.WSMan.Management/Microsoft.WSMan.Management.csproj +++ b/src/Microsoft.WSMan.Management/Microsoft.WSMan.Management.csproj @@ -7,11 +7,11 @@ - + - + diff --git a/src/System.Management.Automation/System.Management.Automation.csproj b/src/System.Management.Automation/System.Management.Automation.csproj index 030f6654237..f3b62eafb49 100644 --- a/src/System.Management.Automation/System.Management.Automation.csproj +++ b/src/System.Management.Automation/System.Management.Automation.csproj @@ -32,25 +32,25 @@ - - - - - - + + + + + + - + - - - - + + + + - + diff --git a/test/perf/dotnet-tools/BenchmarkDotNet.Extensions/BenchmarkDotNet.Extensions.csproj b/test/perf/dotnet-tools/BenchmarkDotNet.Extensions/BenchmarkDotNet.Extensions.csproj index b8568a0e373..9b5fb753394 100644 --- a/test/perf/dotnet-tools/BenchmarkDotNet.Extensions/BenchmarkDotNet.Extensions.csproj +++ b/test/perf/dotnet-tools/BenchmarkDotNet.Extensions/BenchmarkDotNet.Extensions.csproj @@ -13,10 +13,11 @@ - + + diff --git a/test/perf/dotnet-tools/ResultsComparer/ResultsComparer.csproj b/test/perf/dotnet-tools/ResultsComparer/ResultsComparer.csproj index a7ec51fdcf7..3eacb22eb51 100644 --- a/test/perf/dotnet-tools/ResultsComparer/ResultsComparer.csproj +++ b/test/perf/dotnet-tools/ResultsComparer/ResultsComparer.csproj @@ -13,11 +13,12 @@ - + + diff --git a/test/tools/NamedPipeConnection/src/code/Microsoft.PowerShell.NamedPipeConnection.csproj b/test/tools/NamedPipeConnection/src/code/Microsoft.PowerShell.NamedPipeConnection.csproj index a813503467c..7fa3f53c83d 100644 --- a/test/tools/NamedPipeConnection/src/code/Microsoft.PowerShell.NamedPipeConnection.csproj +++ b/test/tools/NamedPipeConnection/src/code/Microsoft.PowerShell.NamedPipeConnection.csproj @@ -17,21 +17,21 @@ - + - - - - - - - + + + + + + + - - - - - + + + + + diff --git a/test/tools/TestService/TestService.csproj b/test/tools/TestService/TestService.csproj index cae146e8f5e..5508aa6cd9b 100644 --- a/test/tools/TestService/TestService.csproj +++ b/test/tools/TestService/TestService.csproj @@ -15,56 +15,56 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + - - - - - + + + + + - - - - - - - - + + + + + + + + - + diff --git a/test/tools/WebListener/WebListener.csproj b/test/tools/WebListener/WebListener.csproj index 9f6fb2ce79a..c65c90a3ff7 100644 --- a/test/tools/WebListener/WebListener.csproj +++ b/test/tools/WebListener/WebListener.csproj @@ -7,9 +7,9 @@ - - + + - + diff --git a/tools/cgmanifest.json b/tools/cgmanifest.json index 13f35eb8dd2..9b00c6ac230 100644 --- a/tools/cgmanifest.json +++ b/tools/cgmanifest.json @@ -115,7 +115,7 @@ "Type": "nuget", "Nuget": { "Name": "Microsoft.Extensions.ObjectPool", - "Version": "8.0.17" + "Version": "8.0.19" } }, "DevelopmentDependency": false @@ -155,7 +155,7 @@ "Type": "nuget", "Nuget": { "Name": "Microsoft.Win32.Registry.AccessControl", - "Version": "9.0.6" + "Version": "9.0.8" } }, "DevelopmentDependency": false @@ -165,7 +165,7 @@ "Type": "nuget", "Nuget": { "Name": "Microsoft.Win32.SystemEvents", - "Version": "9.0.6" + "Version": "9.0.8" } }, "DevelopmentDependency": false @@ -175,7 +175,7 @@ "Type": "nuget", "Nuget": { "Name": "Microsoft.Windows.Compatibility", - "Version": "9.0.6" + "Version": "9.0.8" } }, "DevelopmentDependency": false @@ -195,7 +195,7 @@ "Type": "nuget", "Nuget": { "Name": "runtime.android-arm.runtime.native.System.IO.Ports", - "Version": "9.0.6" + "Version": "9.0.8" } }, "DevelopmentDependency": false @@ -205,7 +205,7 @@ "Type": "nuget", "Nuget": { "Name": "runtime.android-arm64.runtime.native.System.IO.Ports", - "Version": "9.0.6" + "Version": "9.0.8" } }, "DevelopmentDependency": false @@ -215,7 +215,7 @@ "Type": "nuget", "Nuget": { "Name": "runtime.android-x64.runtime.native.System.IO.Ports", - "Version": "9.0.6" + "Version": "9.0.8" } }, "DevelopmentDependency": false @@ -225,7 +225,7 @@ "Type": "nuget", "Nuget": { "Name": "runtime.android-x86.runtime.native.System.IO.Ports", - "Version": "9.0.6" + "Version": "9.0.8" } }, "DevelopmentDependency": false @@ -235,7 +235,7 @@ "Type": "nuget", "Nuget": { "Name": "runtime.linux-arm.runtime.native.System.IO.Ports", - "Version": "9.0.6" + "Version": "9.0.8" } }, "DevelopmentDependency": false @@ -245,7 +245,7 @@ "Type": "nuget", "Nuget": { "Name": "runtime.linux-arm64.runtime.native.System.IO.Ports", - "Version": "9.0.6" + "Version": "9.0.8" } }, "DevelopmentDependency": false @@ -255,7 +255,7 @@ "Type": "nuget", "Nuget": { "Name": "runtime.linux-bionic-arm64.runtime.native.System.IO.Ports", - "Version": "9.0.6" + "Version": "9.0.8" } }, "DevelopmentDependency": false @@ -265,7 +265,7 @@ "Type": "nuget", "Nuget": { "Name": "runtime.linux-bionic-x64.runtime.native.System.IO.Ports", - "Version": "9.0.6" + "Version": "9.0.8" } }, "DevelopmentDependency": false @@ -275,7 +275,7 @@ "Type": "nuget", "Nuget": { "Name": "runtime.linux-musl-arm.runtime.native.System.IO.Ports", - "Version": "9.0.6" + "Version": "9.0.8" } }, "DevelopmentDependency": false @@ -285,7 +285,7 @@ "Type": "nuget", "Nuget": { "Name": "runtime.linux-musl-arm64.runtime.native.System.IO.Ports", - "Version": "9.0.6" + "Version": "9.0.8" } }, "DevelopmentDependency": false @@ -295,7 +295,7 @@ "Type": "nuget", "Nuget": { "Name": "runtime.linux-musl-x64.runtime.native.System.IO.Ports", - "Version": "9.0.6" + "Version": "9.0.8" } }, "DevelopmentDependency": false @@ -305,7 +305,7 @@ "Type": "nuget", "Nuget": { "Name": "runtime.linux-x64.runtime.native.System.IO.Ports", - "Version": "9.0.6" + "Version": "9.0.8" } }, "DevelopmentDependency": false @@ -315,7 +315,7 @@ "Type": "nuget", "Nuget": { "Name": "runtime.maccatalyst-arm64.runtime.native.System.IO.Ports", - "Version": "9.0.6" + "Version": "9.0.8" } }, "DevelopmentDependency": false @@ -325,7 +325,7 @@ "Type": "nuget", "Nuget": { "Name": "runtime.maccatalyst-x64.runtime.native.System.IO.Ports", - "Version": "9.0.6" + "Version": "9.0.8" } }, "DevelopmentDependency": false @@ -345,7 +345,7 @@ "Type": "nuget", "Nuget": { "Name": "runtime.native.System.IO.Ports", - "Version": "9.0.6" + "Version": "9.0.8" } }, "DevelopmentDependency": false @@ -355,7 +355,7 @@ "Type": "nuget", "Nuget": { "Name": "runtime.osx-arm64.runtime.native.System.IO.Ports", - "Version": "9.0.6" + "Version": "9.0.8" } }, "DevelopmentDependency": false @@ -365,7 +365,7 @@ "Type": "nuget", "Nuget": { "Name": "runtime.osx-x64.runtime.native.System.IO.Ports", - "Version": "9.0.6" + "Version": "9.0.8" } }, "DevelopmentDependency": false @@ -425,7 +425,7 @@ "Type": "nuget", "Nuget": { "Name": "System.CodeDom", - "Version": "9.0.6" + "Version": "9.0.8" } }, "DevelopmentDependency": false @@ -445,7 +445,7 @@ "Type": "nuget", "Nuget": { "Name": "System.ComponentModel.Composition.Registration", - "Version": "9.0.6" + "Version": "9.0.8" } }, "DevelopmentDependency": false @@ -455,7 +455,7 @@ "Type": "nuget", "Nuget": { "Name": "System.ComponentModel.Composition", - "Version": "9.0.6" + "Version": "9.0.8" } }, "DevelopmentDependency": false @@ -465,7 +465,7 @@ "Type": "nuget", "Nuget": { "Name": "System.Configuration.ConfigurationManager", - "Version": "9.0.6" + "Version": "9.0.8" } }, "DevelopmentDependency": false @@ -475,7 +475,7 @@ "Type": "nuget", "Nuget": { "Name": "System.Data.Odbc", - "Version": "9.0.6" + "Version": "9.0.8" } }, "DevelopmentDependency": false @@ -485,7 +485,7 @@ "Type": "nuget", "Nuget": { "Name": "System.Data.OleDb", - "Version": "9.0.6" + "Version": "9.0.8" } }, "DevelopmentDependency": false @@ -505,7 +505,7 @@ "Type": "nuget", "Nuget": { "Name": "System.Diagnostics.DiagnosticSource", - "Version": "9.0.6" + "Version": "9.0.8" } }, "DevelopmentDependency": false @@ -515,7 +515,7 @@ "Type": "nuget", "Nuget": { "Name": "System.Diagnostics.EventLog", - "Version": "9.0.6" + "Version": "9.0.8" } }, "DevelopmentDependency": false @@ -525,7 +525,7 @@ "Type": "nuget", "Nuget": { "Name": "System.Diagnostics.PerformanceCounter", - "Version": "9.0.6" + "Version": "9.0.8" } }, "DevelopmentDependency": false @@ -535,7 +535,7 @@ "Type": "nuget", "Nuget": { "Name": "System.DirectoryServices.AccountManagement", - "Version": "9.0.6" + "Version": "9.0.8" } }, "DevelopmentDependency": false @@ -545,7 +545,7 @@ "Type": "nuget", "Nuget": { "Name": "System.DirectoryServices.Protocols", - "Version": "9.0.6" + "Version": "9.0.8" } }, "DevelopmentDependency": false @@ -555,7 +555,7 @@ "Type": "nuget", "Nuget": { "Name": "System.DirectoryServices", - "Version": "9.0.6" + "Version": "9.0.8" } }, "DevelopmentDependency": false @@ -565,7 +565,7 @@ "Type": "nuget", "Nuget": { "Name": "System.Drawing.Common", - "Version": "9.0.6" + "Version": "9.0.8" } }, "DevelopmentDependency": false @@ -575,7 +575,7 @@ "Type": "nuget", "Nuget": { "Name": "System.IO.Packaging", - "Version": "9.0.6" + "Version": "9.0.8" } }, "DevelopmentDependency": false @@ -585,7 +585,7 @@ "Type": "nuget", "Nuget": { "Name": "System.IO.Ports", - "Version": "9.0.6" + "Version": "9.0.8" } }, "DevelopmentDependency": false @@ -595,7 +595,7 @@ "Type": "nuget", "Nuget": { "Name": "System.Management", - "Version": "9.0.6" + "Version": "9.0.8" } }, "DevelopmentDependency": false @@ -605,7 +605,7 @@ "Type": "nuget", "Nuget": { "Name": "System.Net.Http.WinHttpHandler", - "Version": "9.0.6" + "Version": "9.0.8" } }, "DevelopmentDependency": false @@ -635,7 +635,7 @@ "Type": "nuget", "Nuget": { "Name": "System.Reflection.Context", - "Version": "9.0.6" + "Version": "9.0.8" } }, "DevelopmentDependency": false @@ -665,7 +665,7 @@ "Type": "nuget", "Nuget": { "Name": "System.Runtime.Caching", - "Version": "9.0.6" + "Version": "9.0.8" } }, "DevelopmentDependency": false @@ -685,7 +685,7 @@ "Type": "nuget", "Nuget": { "Name": "System.Security.Cryptography.Pkcs", - "Version": "9.0.6" + "Version": "9.0.8" } }, "DevelopmentDependency": false @@ -695,7 +695,7 @@ "Type": "nuget", "Nuget": { "Name": "System.Security.Cryptography.ProtectedData", - "Version": "9.0.6" + "Version": "9.0.8" } }, "DevelopmentDependency": false @@ -705,7 +705,7 @@ "Type": "nuget", "Nuget": { "Name": "System.Security.Cryptography.Xml", - "Version": "9.0.6" + "Version": "9.0.8" } }, "DevelopmentDependency": false @@ -715,7 +715,7 @@ "Type": "nuget", "Nuget": { "Name": "System.Security.Permissions", - "Version": "9.0.6" + "Version": "9.0.8" } }, "DevelopmentDependency": false @@ -785,7 +785,7 @@ "Type": "nuget", "Nuget": { "Name": "System.ServiceModel.Syndication", - "Version": "9.0.6" + "Version": "9.0.8" } }, "DevelopmentDependency": false @@ -795,7 +795,7 @@ "Type": "nuget", "Nuget": { "Name": "System.ServiceProcess.ServiceController", - "Version": "9.0.6" + "Version": "9.0.8" } }, "DevelopmentDependency": false @@ -805,7 +805,7 @@ "Type": "nuget", "Nuget": { "Name": "System.Speech", - "Version": "9.0.6" + "Version": "9.0.8" } }, "DevelopmentDependency": false @@ -815,7 +815,7 @@ "Type": "nuget", "Nuget": { "Name": "System.Text.Encoding.CodePages", - "Version": "9.0.6" + "Version": "9.0.8" } }, "DevelopmentDependency": false @@ -825,7 +825,7 @@ "Type": "nuget", "Nuget": { "Name": "System.Text.Encodings.Web", - "Version": "9.0.6" + "Version": "9.0.8" } }, "DevelopmentDependency": false @@ -835,7 +835,7 @@ "Type": "nuget", "Nuget": { "Name": "System.Threading.AccessControl", - "Version": "9.0.6" + "Version": "9.0.8" } }, "DevelopmentDependency": false @@ -855,7 +855,7 @@ "Type": "nuget", "Nuget": { "Name": "System.Windows.Extensions", - "Version": "9.0.6" + "Version": "9.0.8" } }, "DevelopmentDependency": false From 18fa315361602d5a97a13b919a9c70bdd721366f Mon Sep 17 00:00:00 2001 From: PowerShell Team Bot <69177312+pwshBot@users.noreply.github.com> Date: Tue, 2 Sep 2025 14:31:44 -0700 Subject: [PATCH 04/16] [release/v7.5] Fix typo in CHANGELOG for script filename suggestion (#25963) Co-authored-by: Travis Plunk --- CHANGELOG/7.5.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/CHANGELOG/7.5.md b/CHANGELOG/7.5.md index 8cf3dba6282..d2071727790 100644 --- a/CHANGELOG/7.5.md +++ b/CHANGELOG/7.5.md @@ -10,7 +10,7 @@ - Set standard handles explicitly when starting a process with `-NoNewWindow` (#25324) - Make inherited protected internal instance members accessible in class scope. (#25547) (Thanks @mawosoft!) -- Remove the old fuzzy suggestion and fix the local script file name suggestion (#25330) +- Remove the old fuzzy suggestion and fix the local script filename suggestion (#25330) - Fix `PSMethodInvocationConstraints.GetHashCode` method (#25306) (Thanks @crazyjncsu!) ### Build and Packaging Improvements From 964a77b46f52c6b35308bbee83db7f878a816fec Mon Sep 17 00:00:00 2001 From: PowerShell Team Bot <69177312+pwshBot@users.noreply.github.com> Date: Tue, 2 Sep 2025 14:51:58 -0700 Subject: [PATCH 05/16] [release/v7.5] Remove AsyncSDL from Pipelines Toggle Official/NonOfficial Runs (#25964) Co-authored-by: Justin Chung <124807742+jshigetomi@users.noreply.github.com> Co-authored-by: Travis Plunk --- .pipelines/MSIXBundle-vPack-Official.yml | 3 +- ...werShell-Coordinated_Packages-Official.yml | 35 +++++++++++-------- .pipelines/PowerShell-Packages-Official.yml | 31 ++++++++-------- .../PowerShell-Release-Official-Azure.yml | 13 ++++--- .pipelines/PowerShell-Release-Official.yml | 19 +++++++--- .pipelines/PowerShell-vPack-Official.yml | 3 +- 6 files changed, 62 insertions(+), 42 deletions(-) diff --git a/.pipelines/MSIXBundle-vPack-Official.yml b/.pipelines/MSIXBundle-vPack-Official.yml index f20e8a31114..ef96f63f045 100644 --- a/.pipelines/MSIXBundle-vPack-Official.yml +++ b/.pipelines/MSIXBundle-vPack-Official.yml @@ -68,11 +68,10 @@ extends: suppressionsFile: $(Build.SourcesDirectory)\.config\suppress.json binskim: enabled: false + exactToolVersion: 4.4.2 # APIScan requires a non-Ready-To-Run build apiscan: enabled: false - asyncSDL: - enabled: false tsaOptionsFile: .config/tsaoptions.json stages: diff --git a/.pipelines/PowerShell-Coordinated_Packages-Official.yml b/.pipelines/PowerShell-Coordinated_Packages-Official.yml index 11215302e46..8de89b0c508 100644 --- a/.pipelines/PowerShell-Coordinated_Packages-Official.yml +++ b/.pipelines/PowerShell-Coordinated_Packages-Official.yml @@ -26,6 +26,10 @@ parameters: displayName: Enable MSBuild Binary Logs type: boolean default: false + - name: OfficialBuild + type: boolean + default: false + resources: repositories: @@ -74,9 +78,17 @@ variables: - group: mscodehub-feed-read-akv - name: ENABLE_MSBUILD_BINLOGS value: ${{ parameters.ENABLE_MSBUILD_BINLOGS }} + - name: templateFile + value: ${{ iif ( parameters.OfficialBuild, 'v2/OneBranch.Official.CrossPlat.yml@onebranchTemplates', 'v2/OneBranch.NonOfficial.CrossPlat.yml@onebranchTemplates' ) }} + # Fix for BinSkim ICU package error in Linux containers + - name: DOTNET_SYSTEM_GLOBALIZATION_INVARIANT + value: true + # Disable BinSkim at job level to override NonOfficial template defaults + - name: ob_sdl_binskim_enabled + value: false extends: - template: v2/OneBranch.Official.CrossPlat.yml@onebranchTemplates + template: ${{ variables.templateFile }} parameters: customTags: 'ES365AIMigrationTooling' featureFlags: @@ -84,6 +96,7 @@ extends: Network: KS3 WindowsHostVersion: Network: KS3 + incrementalSDLBinaryAnalysis: true globalSdl: disableLegacyManifest: true # disabled Armorty as we dont have any ARM templates to scan. It fails on some sample ARM templates. @@ -103,19 +116,13 @@ extends: cg: enabled: true ignoreDirectories: '.devcontainer,demos,docker,docs,src,test,tools/packaging' - asyncSdl: - enabled: true - forStages: [prep, macos, linux, windows, SignFiles, test_and_release_artifacts] - credscan: - enabled: true - scanFolder: $(Build.SourcesDirectory) - suppressionsFile: $(Build.SourcesDirectory)\PowerShell\.config\suppress.json - binskim: - enabled: false - # APIScan requires a non-Ready-To-Run build - apiscan: - enabled: false - tsaOptionsFile: .config\tsaoptions.json + binskim: + enabled: false + exactToolVersion: 4.4.2 + # APIScan requires a non-Ready-To-Run build + apiscan: + enabled: false + tsaOptionsFile: .config\tsaoptions.json stages: - stage: prep diff --git a/.pipelines/PowerShell-Packages-Official.yml b/.pipelines/PowerShell-Packages-Official.yml index 487e8cb9c6a..f0d428bf1d6 100644 --- a/.pipelines/PowerShell-Packages-Official.yml +++ b/.pipelines/PowerShell-Packages-Official.yml @@ -24,7 +24,10 @@ parameters: # parameters are shown up in ADO UI in a build queue time displayName: Skip Signing type: string default: 'NO' - + - name: OfficialBuild + type: boolean + default: false + name: pkgs-$(BUILD.SOURCEBRANCHNAME)-$(Build.BuildId) variables: @@ -61,6 +64,9 @@ variables: - name: branchCounter value: $[counter(variables['branchCounterKey'], 1)] - group: MSIXSigningProfile + - name: templateFile + value: ${{ iif ( parameters.OfficialBuild, 'v2/OneBranch.Official.CrossPlat.yml@onebranchTemplates', 'v2/OneBranch.NonOfficial.CrossPlat.yml@onebranchTemplates' ) }} + resources: pipelines: @@ -79,7 +85,7 @@ resources: ref: refs/heads/main extends: - template: v2/OneBranch.Official.CrossPlat.yml@templates + template: ${{ variables.templateFile }} parameters: cloudvault: enabled: false @@ -88,6 +94,7 @@ extends: Version: 2022 Network: KS3 linuxEsrpSigning: true + incrementalSDLBinaryAnalysis: true globalSdl: disableLegacyManifest: true # disabled Armorty as we dont have any ARM templates to scan. It fails on some sample ARM templates. @@ -104,19 +111,13 @@ extends: cg: enabled: true ignoreDirectories: '.devcontainer,demos,docker,docs,src,test,tools/packaging' - asyncSdl: - enabled: true - forStages: ['build'] - credscan: - enabled: true - scanFolder: $(Build.SourcesDirectory) - suppressionsFile: $(Build.SourcesDirectory)\PowerShell\.config\suppress.json - binskim: - enabled: false - # APIScan requires a non-Ready-To-Run build - apiscan: - enabled: false - tsaOptionsFile: .config\tsaoptions.json + binskim: + enabled: false + exactToolVersion: 4.4.2 + # APIScan requires a non-Ready-To-Run build + apiscan: + enabled: false + tsaOptionsFile: .config\tsaoptions.json stages: - stage: prep jobs: diff --git a/.pipelines/PowerShell-Release-Official-Azure.yml b/.pipelines/PowerShell-Release-Official-Azure.yml index 2d644c7a5dd..8e144f1ee55 100644 --- a/.pipelines/PowerShell-Release-Official-Azure.yml +++ b/.pipelines/PowerShell-Release-Official-Azure.yml @@ -13,6 +13,9 @@ parameters: # parameters are shown up in ADO UI in a build queue time displayName: Skip Signing type: string default: 'NO' + - name: OfficialBuild + type: boolean + default: false name: ev2-$(BUILD.SOURCEBRANCHNAME)-$(Build.BuildId) @@ -46,6 +49,9 @@ variables: - name: LinuxContainerImage value: mcr.microsoft.com/onebranch/cbl-mariner/build:2.0 - group: PoolNames + - name: templateFile + value: ${{ iif ( parameters.OfficialBuild, 'v2/OneBranch.Official.CrossPlat.yml@onebranchTemplates', 'v2/OneBranch.NonOfficial.CrossPlat.yml@onebranchTemplates' ) }} + resources: repositories: @@ -67,13 +73,14 @@ resources: - releases/* extends: - template: v2/OneBranch.Official.CrossPlat.yml@templates + template: ${{ variables.templateFile }} parameters: featureFlags: WindowsHostVersion: Version: 2022 Network: Netlock linuxEsrpSigning: true + incrementalSDLBinaryAnalysis: true cloudvault: enabled: false globalSdl: @@ -81,9 +88,6 @@ extends: # disabled Armory as we dont have any ARM templates to scan. It fails on some sample ARM templates. armory: enabled: false - asyncSdl: - enabled: true - tsaOptionsFile: .config/tsaoptions.json tsa: enabled: true credscan: @@ -92,6 +96,7 @@ extends: suppressionsFile: $(Build.SourcesDirectory)\.config\suppress.json binskim: break: false # always break the build on binskim issues in addition to TSA upload + exactToolVersion: 4.4.2 policheck: break: true # always break the build on policheck issues. You can disable it by setting to 'false' tsaOptionsFile: .config\tsaoptions.json diff --git a/.pipelines/PowerShell-Release-Official.yml b/.pipelines/PowerShell-Release-Official.yml index bfc475785aa..9d543eae3a9 100644 --- a/.pipelines/PowerShell-Release-Official.yml +++ b/.pipelines/PowerShell-Release-Official.yml @@ -25,6 +25,9 @@ parameters: # parameters are shown up in ADO UI in a build queue time displayName: Skip Copying Archives and Installers to PSInfrastructure Public Location type: boolean default: false + - name: OfficialBuild + type: boolean + default: false name: release-$(BUILD.SOURCEBRANCHNAME)-$(Build.BuildId) @@ -58,6 +61,13 @@ variables: - name: ReleaseTagVar value: ${{ parameters.ReleaseTagVar }} - group: PoolNames + - name: templateFile + value: ${{ iif ( parameters.OfficialBuild, 'v2/OneBranch.Official.CrossPlat.yml@onebranchTemplates', 'v2/OneBranch.NonOfficial.CrossPlat.yml@onebranchTemplates' ) }} + - name: releaseEnvironment + value: ${{ iif ( parameters.OfficialBuild, 'Production', 'Test' ) }} + # Fix for BinSkim ICU package error in Linux containers + - name: DOTNET_SYSTEM_GLOBALIZATION_INVARIANT + value: true resources: repositories: @@ -83,7 +93,7 @@ resources: - releases/* extends: - template: v2/OneBranch.Official.CrossPlat.yml@templates + template: ${{ variables.templateFile }} parameters: release: category: NonAzure @@ -91,6 +101,7 @@ extends: WindowsHostVersion: Version: 2022 Network: KS3 + incrementalSDLBinaryAnalysis: true cloudvault: enabled: false globalSdl: @@ -98,9 +109,6 @@ extends: # disabled Armory as we dont have any ARM templates to scan. It fails on some sample ARM templates. armory: enabled: false - asyncSdl: - enabled: true - tsaOptionsFile: .config/tsaoptions.json tsa: enabled: true credscan: @@ -109,6 +117,7 @@ extends: suppressionsFile: $(Build.SourcesDirectory)\.config\suppress.json binskim: break: false # always break the build on binskim issues in addition to TSA upload + exactToolVersion: 4.4.2 policheck: break: true # always break the build on policheck issues. You can disable it by setting to 'false' # suppression: @@ -279,7 +288,7 @@ extends: - setReleaseTagAndChangelog - UpdateChangeLog variables: - ob_release_environment: Production + ob_release_environment: ${{ parameters.releaseEnvironment }} jobs: - template: /.pipelines/templates/release-githubNuget.yml@self parameters: diff --git a/.pipelines/PowerShell-vPack-Official.yml b/.pipelines/PowerShell-vPack-Official.yml index 9a9aceed387..05a8fefbb0f 100644 --- a/.pipelines/PowerShell-vPack-Official.yml +++ b/.pipelines/PowerShell-vPack-Official.yml @@ -93,11 +93,10 @@ extends: suppressionsFile: $(Build.SourcesDirectory)\.config\suppress.json binskim: enabled: false + exactToolVersion: 4.4.2 # APIScan requires a non-Ready-To-Run build apiscan: enabled: false - asyncSDL: - enabled: false tsaOptionsFile: .config/tsaoptions.json stages: - stage: main From 8fb0da460ddfe48fb43a1463441cd2d59b53943f Mon Sep 17 00:00:00 2001 From: PowerShell Team Bot <69177312+pwshBot@users.noreply.github.com> Date: Tue, 2 Sep 2025 16:45:51 -0700 Subject: [PATCH 06/16] [release/v7.5] Make the interface `IDeepCloneable` internal (#25830) Co-authored-by: Dongbo Wang Co-authored-by: Travis Plunk --- .../ManagementList/FilterCore/IDeepCloneable.cs | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/src/Microsoft.Management.UI.Internal/ManagementList/FilterCore/IDeepCloneable.cs b/src/Microsoft.Management.UI.Internal/ManagementList/FilterCore/IDeepCloneable.cs index 3090f93a95c..841a2424b51 100644 --- a/src/Microsoft.Management.UI.Internal/ManagementList/FilterCore/IDeepCloneable.cs +++ b/src/Microsoft.Management.UI.Internal/ManagementList/FilterCore/IDeepCloneable.cs @@ -6,8 +6,7 @@ namespace Microsoft.Management.UI.Internal /// /// Defines a generalized method for creating a deep copy of an instance. /// - [System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.MSInternal", "CA903:InternalNamespaceShouldNotContainPublicTypes")] - public interface IDeepCloneable + internal interface IDeepCloneable { /// /// Creates a deep copy of the current instance. From d03f8071174c0c0114113fe4b8f14e411e706ec3 Mon Sep 17 00:00:00 2001 From: PowerShell Team Bot <69177312+pwshBot@users.noreply.github.com> Date: Tue, 2 Sep 2025 17:12:48 -0700 Subject: [PATCH 07/16] [release/v7.5] Remove `OnDeserialized` and `Serializable` attributes from `Microsoft.Management.UI.Internal` project (#25831) Co-authored-by: Dongbo Wang Co-authored-by: Travis Plunk --- .../FilterCore/FilterRules/ComparableValueFilterRule.cs | 1 - .../FilterCore/FilterRules/DoesNotEqualFilterRule.cs | 1 - .../FilterCore/FilterRules/EqualsFilterRule.cs | 1 - .../ManagementList/FilterCore/FilterRules/FilterRule.cs | 2 -- .../FilterCore/FilterRules/IsBetweenFilterRule.cs | 8 -------- .../FilterCore/FilterRules/IsEmptyFilterRule.cs | 1 - .../FilterCore/FilterRules/IsGreaterThanFilterRule.cs | 1 - .../FilterCore/FilterRules/IsLessThanFilterRule.cs | 1 - .../FilterCore/FilterRules/IsNotEmptyFilterRule.cs | 1 - .../FilterCore/FilterRules/IsNotEmptyValidationRule.cs | 1 - .../FilterRules/PropertiesTextContainsFilterRule.cs | 7 ------- .../FilterRules/PropertyValueSelectorFilterRule.cs | 1 - .../FilterCore/FilterRules/SelectorFilterRule.cs | 8 -------- .../FilterRules/SingleValueComparableValueFilterRule.cs | 7 ------- .../FilterCore/FilterRules/TextContainsFilterRule.cs | 1 - .../FilterRules/TextDoesNotContainFilterRule.cs | 1 - .../FilterCore/FilterRules/TextDoesNotEqualFilterRule.cs | 1 - .../FilterCore/FilterRules/TextEndsWithFilterRule.cs | 1 - .../FilterCore/FilterRules/TextEqualsFilterRule.cs | 1 - .../FilterCore/FilterRules/TextFilterRule.cs | 1 - .../FilterCore/FilterRules/TextStartsWithFilterRule.cs | 1 - .../ManagementList/FilterCore/ValidatingSelectorValue.cs | 2 -- .../ManagementList/FilterCore/ValidatingValue.cs | 1 - .../ManagementList/FilterCore/ValidatingValueBase.cs | 3 --- .../ValidationRules/DataErrorInfoValidationRule.cs | 1 - .../FilterProviders/FilterRuleToDisplayNameConverter.cs | 1 - .../ManagementList/ManagementListStateDescriptor.cs | 1 - src/powershell-win-core/powershell-win-core.csproj | 1 - 28 files changed, 58 deletions(-) diff --git a/src/Microsoft.Management.UI.Internal/ManagementList/FilterCore/FilterRules/ComparableValueFilterRule.cs b/src/Microsoft.Management.UI.Internal/ManagementList/FilterCore/FilterRules/ComparableValueFilterRule.cs index dca6c08d535..8362a035156 100644 --- a/src/Microsoft.Management.UI.Internal/ManagementList/FilterCore/FilterRules/ComparableValueFilterRule.cs +++ b/src/Microsoft.Management.UI.Internal/ManagementList/FilterCore/FilterRules/ComparableValueFilterRule.cs @@ -13,7 +13,6 @@ namespace Microsoft.Management.UI.Internal /// The generic parameter. /// [System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.MSInternal", "CA903:InternalNamespaceShouldNotContainPublicTypes")] - [Serializable] public abstract class ComparableValueFilterRule : FilterRule where T : IComparable { /// diff --git a/src/Microsoft.Management.UI.Internal/ManagementList/FilterCore/FilterRules/DoesNotEqualFilterRule.cs b/src/Microsoft.Management.UI.Internal/ManagementList/FilterCore/FilterRules/DoesNotEqualFilterRule.cs index 94451623c3a..c5d4f36fe55 100644 --- a/src/Microsoft.Management.UI.Internal/ManagementList/FilterCore/FilterRules/DoesNotEqualFilterRule.cs +++ b/src/Microsoft.Management.UI.Internal/ManagementList/FilterCore/FilterRules/DoesNotEqualFilterRule.cs @@ -13,7 +13,6 @@ namespace Microsoft.Management.UI.Internal /// The generic parameter. /// [System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.MSInternal", "CA903:InternalNamespaceShouldNotContainPublicTypes")] - [Serializable] public class DoesNotEqualFilterRule : EqualsFilterRule where T : IComparable { /// diff --git a/src/Microsoft.Management.UI.Internal/ManagementList/FilterCore/FilterRules/EqualsFilterRule.cs b/src/Microsoft.Management.UI.Internal/ManagementList/FilterCore/FilterRules/EqualsFilterRule.cs index eaf647f0030..34a1ecb722d 100644 --- a/src/Microsoft.Management.UI.Internal/ManagementList/FilterCore/FilterRules/EqualsFilterRule.cs +++ b/src/Microsoft.Management.UI.Internal/ManagementList/FilterCore/FilterRules/EqualsFilterRule.cs @@ -14,7 +14,6 @@ namespace Microsoft.Management.UI.Internal /// The generic parameter. /// [System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.MSInternal", "CA903:InternalNamespaceShouldNotContainPublicTypes")] - [Serializable] public class EqualsFilterRule : SingleValueComparableValueFilterRule where T : IComparable { /// diff --git a/src/Microsoft.Management.UI.Internal/ManagementList/FilterCore/FilterRules/FilterRule.cs b/src/Microsoft.Management.UI.Internal/ManagementList/FilterCore/FilterRules/FilterRule.cs index 7f72f33eb2d..f18c89addf9 100644 --- a/src/Microsoft.Management.UI.Internal/ManagementList/FilterCore/FilterRules/FilterRule.cs +++ b/src/Microsoft.Management.UI.Internal/ManagementList/FilterCore/FilterRules/FilterRule.cs @@ -9,7 +9,6 @@ namespace Microsoft.Management.UI.Internal /// The base class for all filtering rules. /// [System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.MSInternal", "CA903:InternalNamespaceShouldNotContainPublicTypes")] - [Serializable] public abstract class FilterRule : IEvaluate, IDeepCloneable { /// @@ -69,7 +68,6 @@ public object DeepClone() /// /// Occurs when the values of this rule changes. /// - [field: NonSerialized] public event EventHandler EvaluationResultInvalidated; /// diff --git a/src/Microsoft.Management.UI.Internal/ManagementList/FilterCore/FilterRules/IsBetweenFilterRule.cs b/src/Microsoft.Management.UI.Internal/ManagementList/FilterCore/FilterRules/IsBetweenFilterRule.cs index d8c4a263c61..f51093510ec 100644 --- a/src/Microsoft.Management.UI.Internal/ManagementList/FilterCore/FilterRules/IsBetweenFilterRule.cs +++ b/src/Microsoft.Management.UI.Internal/ManagementList/FilterCore/FilterRules/IsBetweenFilterRule.cs @@ -16,7 +16,6 @@ namespace Microsoft.Management.UI.Internal /// The generic parameter. /// [System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.MSInternal", "CA903:InternalNamespaceShouldNotContainPublicTypes")] - [Serializable] public class IsBetweenFilterRule : ComparableValueFilterRule where T : IComparable { #region Properties @@ -122,13 +121,6 @@ private void Value_PropertyChanged(object sender, PropertyChangedEventArgs e) } } - [OnDeserialized] - private void Initialize(StreamingContext context) - { - this.StartValue.PropertyChanged += this.Value_PropertyChanged; - this.EndValue.PropertyChanged += this.Value_PropertyChanged; - } - #endregion Value Change Handlers } } diff --git a/src/Microsoft.Management.UI.Internal/ManagementList/FilterCore/FilterRules/IsEmptyFilterRule.cs b/src/Microsoft.Management.UI.Internal/ManagementList/FilterCore/FilterRules/IsEmptyFilterRule.cs index a3add25eae5..71bb7e23e7c 100644 --- a/src/Microsoft.Management.UI.Internal/ManagementList/FilterCore/FilterRules/IsEmptyFilterRule.cs +++ b/src/Microsoft.Management.UI.Internal/ManagementList/FilterCore/FilterRules/IsEmptyFilterRule.cs @@ -10,7 +10,6 @@ namespace Microsoft.Management.UI.Internal /// is empty or not. /// [System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.MSInternal", "CA903:InternalNamespaceShouldNotContainPublicTypes")] - [Serializable] public class IsEmptyFilterRule : FilterRule { /// diff --git a/src/Microsoft.Management.UI.Internal/ManagementList/FilterCore/FilterRules/IsGreaterThanFilterRule.cs b/src/Microsoft.Management.UI.Internal/ManagementList/FilterCore/FilterRules/IsGreaterThanFilterRule.cs index c6d9ef762b4..6c7d16f312a 100644 --- a/src/Microsoft.Management.UI.Internal/ManagementList/FilterCore/FilterRules/IsGreaterThanFilterRule.cs +++ b/src/Microsoft.Management.UI.Internal/ManagementList/FilterCore/FilterRules/IsGreaterThanFilterRule.cs @@ -14,7 +14,6 @@ namespace Microsoft.Management.UI.Internal /// The generic parameter. /// [System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.MSInternal", "CA903:InternalNamespaceShouldNotContainPublicTypes")] - [Serializable] public class IsGreaterThanFilterRule : SingleValueComparableValueFilterRule where T : IComparable { /// diff --git a/src/Microsoft.Management.UI.Internal/ManagementList/FilterCore/FilterRules/IsLessThanFilterRule.cs b/src/Microsoft.Management.UI.Internal/ManagementList/FilterCore/FilterRules/IsLessThanFilterRule.cs index a90dcdc8a82..e1dc3268cc5 100644 --- a/src/Microsoft.Management.UI.Internal/ManagementList/FilterCore/FilterRules/IsLessThanFilterRule.cs +++ b/src/Microsoft.Management.UI.Internal/ManagementList/FilterCore/FilterRules/IsLessThanFilterRule.cs @@ -14,7 +14,6 @@ namespace Microsoft.Management.UI.Internal /// The generic parameter. /// [System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.MSInternal", "CA903:InternalNamespaceShouldNotContainPublicTypes")] - [Serializable] public class IsLessThanFilterRule : SingleValueComparableValueFilterRule where T : IComparable { /// diff --git a/src/Microsoft.Management.UI.Internal/ManagementList/FilterCore/FilterRules/IsNotEmptyFilterRule.cs b/src/Microsoft.Management.UI.Internal/ManagementList/FilterCore/FilterRules/IsNotEmptyFilterRule.cs index 450a1237a97..711caee9874 100644 --- a/src/Microsoft.Management.UI.Internal/ManagementList/FilterCore/FilterRules/IsNotEmptyFilterRule.cs +++ b/src/Microsoft.Management.UI.Internal/ManagementList/FilterCore/FilterRules/IsNotEmptyFilterRule.cs @@ -10,7 +10,6 @@ namespace Microsoft.Management.UI.Internal /// is empty or not. /// [System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.MSInternal", "CA903:InternalNamespaceShouldNotContainPublicTypes")] - [Serializable] public class IsNotEmptyFilterRule : IsEmptyFilterRule { /// diff --git a/src/Microsoft.Management.UI.Internal/ManagementList/FilterCore/FilterRules/IsNotEmptyValidationRule.cs b/src/Microsoft.Management.UI.Internal/ManagementList/FilterCore/FilterRules/IsNotEmptyValidationRule.cs index 98534aca0ad..cb6eacaaff3 100644 --- a/src/Microsoft.Management.UI.Internal/ManagementList/FilterCore/FilterRules/IsNotEmptyValidationRule.cs +++ b/src/Microsoft.Management.UI.Internal/ManagementList/FilterCore/FilterRules/IsNotEmptyValidationRule.cs @@ -9,7 +9,6 @@ namespace Microsoft.Management.UI.Internal /// The IsNotEmptyValidationRule checks a value to see if a value is not empty. /// [System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.MSInternal", "CA903:InternalNamespaceShouldNotContainPublicTypes")] - [Serializable] public class IsNotEmptyValidationRule : DataErrorInfoValidationRule { #region Properties diff --git a/src/Microsoft.Management.UI.Internal/ManagementList/FilterCore/FilterRules/PropertiesTextContainsFilterRule.cs b/src/Microsoft.Management.UI.Internal/ManagementList/FilterCore/FilterRules/PropertiesTextContainsFilterRule.cs index 54532335835..8c32530be8c 100644 --- a/src/Microsoft.Management.UI.Internal/ManagementList/FilterCore/FilterRules/PropertiesTextContainsFilterRule.cs +++ b/src/Microsoft.Management.UI.Internal/ManagementList/FilterCore/FilterRules/PropertiesTextContainsFilterRule.cs @@ -12,7 +12,6 @@ namespace Microsoft.Management.UI.Internal /// Represents a filter rule that searches for text within properties on an object. /// [System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.MSInternal", "CA903:InternalNamespaceShouldNotContainPublicTypes")] - [Serializable] public class PropertiesTextContainsFilterRule : TextFilterRule { private static readonly string TextContainsCharactersRegexPattern = "{0}"; @@ -131,11 +130,5 @@ private void PropertiesTextContainsFilterRule_EvaluationResultInvalidated(object { this.OnEvaluationResultInvalidated(); } - - [OnDeserialized] - private void Initialize(StreamingContext context) - { - this.EvaluationResultInvalidated += this.PropertiesTextContainsFilterRule_EvaluationResultInvalidated; - } } } diff --git a/src/Microsoft.Management.UI.Internal/ManagementList/FilterCore/FilterRules/PropertyValueSelectorFilterRule.cs b/src/Microsoft.Management.UI.Internal/ManagementList/FilterCore/FilterRules/PropertyValueSelectorFilterRule.cs index 6699cb0e698..09c732970b0 100644 --- a/src/Microsoft.Management.UI.Internal/ManagementList/FilterCore/FilterRules/PropertyValueSelectorFilterRule.cs +++ b/src/Microsoft.Management.UI.Internal/ManagementList/FilterCore/FilterRules/PropertyValueSelectorFilterRule.cs @@ -16,7 +16,6 @@ namespace Microsoft.Management.UI.Internal /// The generic parameter. /// [System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.MSInternal", "CA903:InternalNamespaceShouldNotContainPublicTypes")] - [Serializable] public class PropertyValueSelectorFilterRule : SelectorFilterRule where T : IComparable { #region Properties diff --git a/src/Microsoft.Management.UI.Internal/ManagementList/FilterCore/FilterRules/SelectorFilterRule.cs b/src/Microsoft.Management.UI.Internal/ManagementList/FilterCore/FilterRules/SelectorFilterRule.cs index d7451bbd21f..d1627ee2281 100644 --- a/src/Microsoft.Management.UI.Internal/ManagementList/FilterCore/FilterRules/SelectorFilterRule.cs +++ b/src/Microsoft.Management.UI.Internal/ManagementList/FilterCore/FilterRules/SelectorFilterRule.cs @@ -10,7 +10,6 @@ namespace Microsoft.Management.UI.Internal /// The SelectorFilterRule represents a rule composed of other rules. /// [System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.MSInternal", "CA903:InternalNamespaceShouldNotContainPublicTypes")] - [Serializable] public class SelectorFilterRule : FilterRule { #region Properties @@ -113,13 +112,6 @@ private void SelectedValue_EvaluationResultInvalidated(object sender, EventArgs #region Private Methods - [OnDeserialized] - private void Initialize(StreamingContext context) - { - this.AvailableRules.SelectedValueChanged += this.AvailableRules_SelectedValueChanged; - this.AvailableRules.SelectedValue.EvaluationResultInvalidated += this.SelectedValue_EvaluationResultInvalidated; - } - private void AvailableRules_SelectedValueChanged(object sender, PropertyChangedEventArgs e) { this.OnSelectedValueChanged(e.OldValue, e.NewValue); diff --git a/src/Microsoft.Management.UI.Internal/ManagementList/FilterCore/FilterRules/SingleValueComparableValueFilterRule.cs b/src/Microsoft.Management.UI.Internal/ManagementList/FilterCore/FilterRules/SingleValueComparableValueFilterRule.cs index a34288a6530..b26531943fc 100644 --- a/src/Microsoft.Management.UI.Internal/ManagementList/FilterCore/FilterRules/SingleValueComparableValueFilterRule.cs +++ b/src/Microsoft.Management.UI.Internal/ManagementList/FilterCore/FilterRules/SingleValueComparableValueFilterRule.cs @@ -13,7 +13,6 @@ namespace Microsoft.Management.UI.Internal /// /// The generic parameter. [System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.MSInternal", "CA903:InternalNamespaceShouldNotContainPublicTypes")] - [Serializable] public abstract class SingleValueComparableValueFilterRule : ComparableValueFilterRule where T : IComparable { #region Properties @@ -72,11 +71,5 @@ private void Value_PropertyChanged(object sender, PropertyChangedEventArgs e) this.NotifyEvaluationResultInvalidated(); } } - - [OnDeserialized] - private void Initialize(StreamingContext context) - { - this.Value.PropertyChanged += this.Value_PropertyChanged; - } } } diff --git a/src/Microsoft.Management.UI.Internal/ManagementList/FilterCore/FilterRules/TextContainsFilterRule.cs b/src/Microsoft.Management.UI.Internal/ManagementList/FilterCore/FilterRules/TextContainsFilterRule.cs index acd52555498..beb4a29d23f 100644 --- a/src/Microsoft.Management.UI.Internal/ManagementList/FilterCore/FilterRules/TextContainsFilterRule.cs +++ b/src/Microsoft.Management.UI.Internal/ManagementList/FilterCore/FilterRules/TextContainsFilterRule.cs @@ -11,7 +11,6 @@ namespace Microsoft.Management.UI.Internal /// check if it is contains the rule's value within it. /// [System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.MSInternal", "CA903:InternalNamespaceShouldNotContainPublicTypes")] - [Serializable] public class TextContainsFilterRule : TextFilterRule { private static readonly string TextContainsCharactersRegexPattern = "{0}"; diff --git a/src/Microsoft.Management.UI.Internal/ManagementList/FilterCore/FilterRules/TextDoesNotContainFilterRule.cs b/src/Microsoft.Management.UI.Internal/ManagementList/FilterCore/FilterRules/TextDoesNotContainFilterRule.cs index f85ca1bd125..2cdbf1efcef 100644 --- a/src/Microsoft.Management.UI.Internal/ManagementList/FilterCore/FilterRules/TextDoesNotContainFilterRule.cs +++ b/src/Microsoft.Management.UI.Internal/ManagementList/FilterCore/FilterRules/TextDoesNotContainFilterRule.cs @@ -10,7 +10,6 @@ namespace Microsoft.Management.UI.Internal /// check if it is does not contain the rule's value within it. /// [System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.MSInternal", "CA903:InternalNamespaceShouldNotContainPublicTypes")] - [Serializable] public class TextDoesNotContainFilterRule : TextContainsFilterRule { /// diff --git a/src/Microsoft.Management.UI.Internal/ManagementList/FilterCore/FilterRules/TextDoesNotEqualFilterRule.cs b/src/Microsoft.Management.UI.Internal/ManagementList/FilterCore/FilterRules/TextDoesNotEqualFilterRule.cs index 0c7bb96532c..e74b371a7a6 100644 --- a/src/Microsoft.Management.UI.Internal/ManagementList/FilterCore/FilterRules/TextDoesNotEqualFilterRule.cs +++ b/src/Microsoft.Management.UI.Internal/ManagementList/FilterCore/FilterRules/TextDoesNotEqualFilterRule.cs @@ -10,7 +10,6 @@ namespace Microsoft.Management.UI.Internal /// check if it is not equal to the rule's value. /// [System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.MSInternal", "CA903:InternalNamespaceShouldNotContainPublicTypes")] - [Serializable] public class TextDoesNotEqualFilterRule : TextEqualsFilterRule { /// diff --git a/src/Microsoft.Management.UI.Internal/ManagementList/FilterCore/FilterRules/TextEndsWithFilterRule.cs b/src/Microsoft.Management.UI.Internal/ManagementList/FilterCore/FilterRules/TextEndsWithFilterRule.cs index 2d4febe058d..d7f7e05c4b8 100644 --- a/src/Microsoft.Management.UI.Internal/ManagementList/FilterCore/FilterRules/TextEndsWithFilterRule.cs +++ b/src/Microsoft.Management.UI.Internal/ManagementList/FilterCore/FilterRules/TextEndsWithFilterRule.cs @@ -11,7 +11,6 @@ namespace Microsoft.Management.UI.Internal /// check if it ends with the rule's value. /// [System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.MSInternal", "CA903:InternalNamespaceShouldNotContainPublicTypes")] - [Serializable] public class TextEndsWithFilterRule : TextFilterRule { private static readonly string TextEndsWithCharactersRegexPattern = "{0}$"; diff --git a/src/Microsoft.Management.UI.Internal/ManagementList/FilterCore/FilterRules/TextEqualsFilterRule.cs b/src/Microsoft.Management.UI.Internal/ManagementList/FilterCore/FilterRules/TextEqualsFilterRule.cs index a6e74dca622..a357575c6ab 100644 --- a/src/Microsoft.Management.UI.Internal/ManagementList/FilterCore/FilterRules/TextEqualsFilterRule.cs +++ b/src/Microsoft.Management.UI.Internal/ManagementList/FilterCore/FilterRules/TextEqualsFilterRule.cs @@ -11,7 +11,6 @@ namespace Microsoft.Management.UI.Internal /// check if it is equal to the rule's value. /// [System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.MSInternal", "CA903:InternalNamespaceShouldNotContainPublicTypes")] - [Serializable] public class TextEqualsFilterRule : TextFilterRule { private static readonly string TextEqualsCharactersRegexPattern = "^{0}$"; diff --git a/src/Microsoft.Management.UI.Internal/ManagementList/FilterCore/FilterRules/TextFilterRule.cs b/src/Microsoft.Management.UI.Internal/ManagementList/FilterCore/FilterRules/TextFilterRule.cs index c4cd5dba9f7..eacbcb8d256 100644 --- a/src/Microsoft.Management.UI.Internal/ManagementList/FilterCore/FilterRules/TextFilterRule.cs +++ b/src/Microsoft.Management.UI.Internal/ManagementList/FilterCore/FilterRules/TextFilterRule.cs @@ -14,7 +14,6 @@ namespace Microsoft.Management.UI.Internal /// evaluating string operations. /// [System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.MSInternal", "CA903:InternalNamespaceShouldNotContainPublicTypes")] - [Serializable] public abstract class TextFilterRule : SingleValueComparableValueFilterRule { /// diff --git a/src/Microsoft.Management.UI.Internal/ManagementList/FilterCore/FilterRules/TextStartsWithFilterRule.cs b/src/Microsoft.Management.UI.Internal/ManagementList/FilterCore/FilterRules/TextStartsWithFilterRule.cs index 3b39d0f5660..98eac2b9a41 100644 --- a/src/Microsoft.Management.UI.Internal/ManagementList/FilterCore/FilterRules/TextStartsWithFilterRule.cs +++ b/src/Microsoft.Management.UI.Internal/ManagementList/FilterCore/FilterRules/TextStartsWithFilterRule.cs @@ -11,7 +11,6 @@ namespace Microsoft.Management.UI.Internal /// check if it starts with the rule's value. /// [System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.MSInternal", "CA903:InternalNamespaceShouldNotContainPublicTypes")] - [Serializable] public class TextStartsWithFilterRule : TextFilterRule { private static readonly string TextStartsWithCharactersRegexPattern = "^{0}"; diff --git a/src/Microsoft.Management.UI.Internal/ManagementList/FilterCore/ValidatingSelectorValue.cs b/src/Microsoft.Management.UI.Internal/ManagementList/FilterCore/ValidatingSelectorValue.cs index ff981a74751..0fed0c42e65 100644 --- a/src/Microsoft.Management.UI.Internal/ManagementList/FilterCore/ValidatingSelectorValue.cs +++ b/src/Microsoft.Management.UI.Internal/ManagementList/FilterCore/ValidatingSelectorValue.cs @@ -16,7 +16,6 @@ namespace Microsoft.Management.UI.Internal /// The generic parameter. /// [System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.MSInternal", "CA903:InternalNamespaceShouldNotContainPublicTypes")] - [Serializable] public class ValidatingSelectorValue : ValidatingValueBase { /// @@ -174,7 +173,6 @@ public IValueConverter DisplayNameConverter /// /// Notifies listeners that the selected value has changed. /// - [field: NonSerialized] public event EventHandler> SelectedValueChanged; #endregion Events diff --git a/src/Microsoft.Management.UI.Internal/ManagementList/FilterCore/ValidatingValue.cs b/src/Microsoft.Management.UI.Internal/ManagementList/FilterCore/ValidatingValue.cs index eee8ebc6e4a..437cb3be50e 100644 --- a/src/Microsoft.Management.UI.Internal/ManagementList/FilterCore/ValidatingValue.cs +++ b/src/Microsoft.Management.UI.Internal/ManagementList/FilterCore/ValidatingValue.cs @@ -15,7 +15,6 @@ namespace Microsoft.Management.UI.Internal /// The generic parameter. /// [System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.MSInternal", "CA903:InternalNamespaceShouldNotContainPublicTypes")] - [Serializable] public class ValidatingValue : ValidatingValueBase { /// diff --git a/src/Microsoft.Management.UI.Internal/ManagementList/FilterCore/ValidatingValueBase.cs b/src/Microsoft.Management.UI.Internal/ManagementList/FilterCore/ValidatingValueBase.cs index 6b4c2daa10d..a4ffb1af77c 100644 --- a/src/Microsoft.Management.UI.Internal/ManagementList/FilterCore/ValidatingValueBase.cs +++ b/src/Microsoft.Management.UI.Internal/ManagementList/FilterCore/ValidatingValueBase.cs @@ -15,7 +15,6 @@ namespace Microsoft.Management.UI.Internal /// classes to support validation via the IDataErrorInfo interface. /// [System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.MSInternal", "CA903:InternalNamespaceShouldNotContainPublicTypes")] - [Serializable] public abstract class ValidatingValueBase : IDataErrorInfo, INotifyPropertyChanged, IDeepCloneable { /// @@ -47,7 +46,6 @@ protected ValidatingValueBase(ValidatingValueBase source) private ReadOnlyCollection readonlyValidationRules; private bool isValidationRulesCollectionDirty = true; - [field: NonSerialized] private DataErrorInfoValidationResult cachedValidationResult; /// @@ -141,7 +139,6 @@ public string Error /// /// The listeners attached to this event are not serialized. /// - [field: NonSerialized] public event PropertyChangedEventHandler PropertyChanged; #endregion PropertyChanged diff --git a/src/Microsoft.Management.UI.Internal/ManagementList/FilterCore/ValidationRules/DataErrorInfoValidationRule.cs b/src/Microsoft.Management.UI.Internal/ManagementList/FilterCore/ValidationRules/DataErrorInfoValidationRule.cs index 78b54a9c045..a92916c0717 100644 --- a/src/Microsoft.Management.UI.Internal/ManagementList/FilterCore/ValidationRules/DataErrorInfoValidationRule.cs +++ b/src/Microsoft.Management.UI.Internal/ManagementList/FilterCore/ValidationRules/DataErrorInfoValidationRule.cs @@ -9,7 +9,6 @@ namespace Microsoft.Management.UI.Internal /// Provides a way to create a custom rule in order to check the validity of user input. /// [System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.MSInternal", "CA903:InternalNamespaceShouldNotContainPublicTypes")] - [Serializable] public abstract class DataErrorInfoValidationRule : IDeepCloneable { /// diff --git a/src/Microsoft.Management.UI.Internal/ManagementList/FilterProviders/FilterRuleToDisplayNameConverter.cs b/src/Microsoft.Management.UI.Internal/ManagementList/FilterProviders/FilterRuleToDisplayNameConverter.cs index aaca30ff321..972c19080e0 100644 --- a/src/Microsoft.Management.UI.Internal/ManagementList/FilterProviders/FilterRuleToDisplayNameConverter.cs +++ b/src/Microsoft.Management.UI.Internal/ManagementList/FilterProviders/FilterRuleToDisplayNameConverter.cs @@ -12,7 +12,6 @@ namespace Microsoft.Management.UI.Internal /// a FilterRule value to its DisplayName. /// [System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.MSInternal", "CA903:InternalNamespaceShouldNotContainPublicTypes")] - [Serializable] public class FilterRuleToDisplayNameConverter : IValueConverter { /// diff --git a/src/Microsoft.Management.UI.Internal/ManagementList/ManagementList/ManagementListStateDescriptor.cs b/src/Microsoft.Management.UI.Internal/ManagementList/ManagementList/ManagementListStateDescriptor.cs index 668118a5b7f..bbfd3d8603c 100644 --- a/src/Microsoft.Management.UI.Internal/ManagementList/ManagementList/ManagementListStateDescriptor.cs +++ b/src/Microsoft.Management.UI.Internal/ManagementList/ManagementList/ManagementListStateDescriptor.cs @@ -16,7 +16,6 @@ namespace Microsoft.Management.UI.Internal /// Allows the state of the ManagementList to be saved and restored. /// [SuppressMessage("Microsoft.MSInternal", "CA903:InternalNamespaceShouldNotContainPublicTypes")] - [Serializable] public class ManagementListStateDescriptor : StateDescriptor { #region Fields diff --git a/src/powershell-win-core/powershell-win-core.csproj b/src/powershell-win-core/powershell-win-core.csproj index 73c55497c5b..5368518dd3c 100644 --- a/src/powershell-win-core/powershell-win-core.csproj +++ b/src/powershell-win-core/powershell-win-core.csproj @@ -13,7 +13,6 @@ ..\..\assets\pwsh.manifest Windows 8.0 - true From 434f880bcdf6cfe8ab7ccc2dbdca4f606d027762 Mon Sep 17 00:00:00 2001 From: PowerShell Team Bot <69177312+pwshBot@users.noreply.github.com> Date: Wed, 3 Sep 2025 14:32:16 -0700 Subject: [PATCH 08/16] [release/v7.5] Add Codeql Suppressions (#25972) Co-authored-by: Anam Navied Co-authored-by: Travis Plunk --- .../commands/management/Process.cs | 1 + .../utility/WebCmdlet/Common/WebRequestPSCmdlet.Common.cs | 1 + .../engine/NativeCommandProcessor.cs | 1 + .../engine/remoting/common/RunspaceConnectionInfo.cs | 1 + .../namespaces/FileSystemProvider.cs | 1 + 5 files changed, 5 insertions(+) diff --git a/src/Microsoft.PowerShell.Commands.Management/commands/management/Process.cs b/src/Microsoft.PowerShell.Commands.Management/commands/management/Process.cs index 4ca7d9aaa5b..a1f9dbf1e0f 100644 --- a/src/Microsoft.PowerShell.Commands.Management/commands/management/Process.cs +++ b/src/Microsoft.PowerShell.Commands.Management/commands/management/Process.cs @@ -1904,6 +1904,7 @@ protected override void BeginProcessing() } catch (CommandNotFoundException) { + // codeql[cs/microsoft/command-line-injection-shell-execution] - This is expected Poweshell behavior where user inputted paths are supported for the context of this method. The user assumes trust for the file path they are specifying and the process is on the user's system except for remoting in which case restricted remoting security guidelines should be used. startInfo.FileName = FilePath; #if UNIX // Arguments are passed incorrectly to the executable used for ShellExecute and not to filename https://github.com/dotnet/corefx/issues/30718 diff --git a/src/Microsoft.PowerShell.Commands.Utility/commands/utility/WebCmdlet/Common/WebRequestPSCmdlet.Common.cs b/src/Microsoft.PowerShell.Commands.Utility/commands/utility/WebCmdlet/Common/WebRequestPSCmdlet.Common.cs index 810b54a8391..d7c0931c786 100644 --- a/src/Microsoft.PowerShell.Commands.Utility/commands/utility/WebCmdlet/Common/WebRequestPSCmdlet.Common.cs +++ b/src/Microsoft.PowerShell.Commands.Utility/commands/utility/WebCmdlet/Common/WebRequestPSCmdlet.Common.cs @@ -1296,6 +1296,7 @@ internal virtual HttpResponseMessage GetResponse(HttpClient client, HttpRequestM _cancelToken = new CancellationTokenSource(); try { + // codeql[cs/ssrf] - This is expected Poweshell behavior where user inputted Uri is supported for the context of this method. The user assumes trust for the Uri and invocation is done on the user's machine, not a web application. If there is concern for remoting, they should use restricted remoting. response = client.SendAsync(currentRequest, HttpCompletionOption.ResponseHeadersRead, _cancelToken.Token).GetAwaiter().GetResult(); } catch (TaskCanceledException ex) diff --git a/src/System.Management.Automation/engine/NativeCommandProcessor.cs b/src/System.Management.Automation/engine/NativeCommandProcessor.cs index 371e1ff00ff..43113d07425 100644 --- a/src/System.Management.Automation/engine/NativeCommandProcessor.cs +++ b/src/System.Management.Automation/engine/NativeCommandProcessor.cs @@ -1396,6 +1396,7 @@ private ProcessStartInfo GetProcessStartInfo( { var startInfo = new ProcessStartInfo { + // codeql[cs/microsoft/command-line-injection-shell-execution] - This is expected Poweshell behavior where user inputted paths are supported for the context of this method. The user assumes trust for the file path specified on the user's system to retrieve process info for, and in the case of remoting, restricted remoting security guidelines should be used. FileName = this.Path }; diff --git a/src/System.Management.Automation/engine/remoting/common/RunspaceConnectionInfo.cs b/src/System.Management.Automation/engine/remoting/common/RunspaceConnectionInfo.cs index 9c221f01dbb..d18eb249cb7 100644 --- a/src/System.Management.Automation/engine/remoting/common/RunspaceConnectionInfo.cs +++ b/src/System.Management.Automation/engine/remoting/common/RunspaceConnectionInfo.cs @@ -2230,6 +2230,7 @@ internal int StartSSHProcess( // linux|macos: // Subsystem powershell /usr/local/bin/pwsh -SSHServerMode -NoLogo -NoProfile + // codeql[cs/microsoft/command-line-injection-shell-execution] - This is expected Poweshell behavior where user inputted paths are supported for the context of this method. The user assumes trust for the file path specified, so any file executed in the runspace would be in the user's local system/process or a system they have access to in which case restricted remoting security guidelines should be used. System.Diagnostics.ProcessStartInfo startInfo = new System.Diagnostics.ProcessStartInfo(filePath); // pass "-i identity_file" command line argument to ssh if KeyFilePath is set diff --git a/src/System.Management.Automation/namespaces/FileSystemProvider.cs b/src/System.Management.Automation/namespaces/FileSystemProvider.cs index aefa2d499b6..dee701296e4 100644 --- a/src/System.Management.Automation/namespaces/FileSystemProvider.cs +++ b/src/System.Management.Automation/namespaces/FileSystemProvider.cs @@ -1325,6 +1325,7 @@ protected override void InvokeDefaultAction(string path) if (ShouldProcess(resource, action)) { var invokeProcess = new System.Diagnostics.Process(); + // codeql[cs/microsoft/command-line-injection-shell-execution] - This is expected Poweshell behavior where user inputted paths are supported for the context of this method. The user assumes trust for the file path they are specifying. If there is concern for remoting, restricted remoting guidelines should be used. invokeProcess.StartInfo.FileName = path; #if UNIX bool useShellExecute = false; From 0091d13c5b8f4541dfd70da25a5300cfc0fa8194 Mon Sep 17 00:00:00 2001 From: PowerShell Team Bot <69177312+pwshBot@users.noreply.github.com> Date: Wed, 3 Sep 2025 14:53:57 -0700 Subject: [PATCH 09/16] [release/v7.5] Add build to vPack Pipeline (#25975) Co-authored-by: Justin Chung <124807742+jshigetomi@users.noreply.github.com> Co-authored-by: Travis Plunk --- .pipelines/PowerShell-vPack-Official.yml | 285 +++++++++++++++------- .pipelines/templates/obp-file-signing.yml | 25 +- tools/packaging/packaging.psm1 | 18 +- 3 files changed, 230 insertions(+), 98 deletions(-) diff --git a/.pipelines/PowerShell-vPack-Official.yml b/.pipelines/PowerShell-vPack-Official.yml index 05a8fefbb0f..5783785cfd6 100644 --- a/.pipelines/PowerShell-vPack-Official.yml +++ b/.pipelines/PowerShell-vPack-Official.yml @@ -1,32 +1,30 @@ trigger: none parameters: # parameters are shown up in ADO UI in a build queue time +- name: OfficialBuild + type: boolean + default: true - name: 'createVPack' displayName: 'Create and Submit VPack' type: boolean default: true -- name: 'debug' - displayName: 'Enable debug output' - type: boolean - default: false -- name: 'architecture' +- name: vPackName type: string - displayName: 'Select the vpack architecture:' + displayName: 'VPack Name:' + default: 'PowerShell' values: - - x64 - - x86 - - arm64 - default: x64 -- name: 'VPackPublishOverride' - type: string - displayName: 'VPack Publish Override Version (can leave blank):' - default: ' ' + - PowerShell + - PowerShellDoNotUse - name: 'ReleaseTagVar' type: string displayName: 'Release Tag Var:' default: 'fromBranch' +- name: 'debug' + displayName: 'Enable debug output' + type: boolean + default: false -name: vPack_${{ parameters.architecture }}_$(date:yyMM).$(date:dd)$(rev:rrr) +name: vPack_$(Build.SourceBranchName)_Prod.${{ parameters.OfficialBuild }}_Create.${{ parameters.createVPack }}_Name.${{ parameters.vPackName}}_$(date:yyyyMMdd).$(rev:rr) variables: - name: CDP_DEFINITION_BUILD_COUNT @@ -51,6 +49,12 @@ variables: value: ${{ parameters.ReleaseTagVar }} - group: Azure Blob variable group - group: certificate_logical_to_actual # used within signing task + - name: templateFile + value: ${{ iif ( parameters.OfficialBuild, 'v2/Microsoft.Official.yml@templates', 'v2/Microsoft.NonOfficial.yml@templates' ) }} + - group: DotNetPrivateBuildAccess + - group: certificate_logical_to_actual +# We shouldn't be using PATs anymore +# - group: mscodehub-feed-read-general resources: repositories: @@ -59,17 +63,8 @@ resources: name: OneBranch.Pipelines/GovernedTemplates ref: refs/heads/main - pipelines: - - pipeline: PSPackagesOfficial - source: 'PowerShell-Packages-Official' - trigger: - branches: - include: - - master - - releases/* - extends: - template: v2/Microsoft.Official.yml@templates + template: ${{ variables.templateFile }} parameters: platform: name: 'windows_undocked' # windows undocked @@ -99,35 +94,116 @@ extends: enabled: false tsaOptionsFile: .config/tsaoptions.json stages: - - stage: main + - stage: BuildStage jobs: - - job: main + - job: BuildJob pool: type: windows + strategy: + matrix: + x86: + architecture: x86 + + x64: + architecture: x64 + + arm64: + architecture: arm64 + variables: + ArtifactPlatform: 'windows' + ob_artifactBaseName: drop_build_$(architecture) ob_outputDirectory: '$(BUILD.SOURCESDIRECTORY)\out' ob_createvpack_enabled: ${{ parameters.createVPack }} - ob_createvpack_packagename: 'PowerShell.${{ parameters.architecture }}' - ob_createvpack_description: PowerShell ${{ parameters.architecture }} $(version) ob_createvpack_owneralias: tplunk - ob_createvpack_versionAs: string - ob_createvpack_version: '$(version)' + ob_createvpack_versionAs: parts ob_createvpack_propsFile: true ob_createvpack_verbose: true + ob_createvpack_packagename: '${{ parameters.vPackName }}.$(architecture)' + ob_createvpack_description: PowerShell $(architecture) $(version) + # I think the variables reload after we transition back to the host so this works. 🤷‍♂️ + ob_createvpack_majorVer: $(pwshMajorVersion) + ob_createvpack_minorVer: $(pwshMinorVersion) + ob_createvpack_patchVer: $(pwshPatchVersion) + ${{ if ne(variables['pwshPrereleaseVersion'], '') }}: + ob_createvpack_prereleaseVer: $(pwshPrereleaseVersion) + ${{ else }}: + ob_createvpack_prereleaseVer: $(Build.SourceVersion) steps: + - checkout: self + displayName: Checkout source code - during restore + clean: true + path: s + env: + ob_restore_phase: true + - template: .pipelines/templates/SetVersionVariables.yml@self parameters: ReleaseTagVar: $(ReleaseTagVar) CreateJson: yes UseJson: no + - pwsh: | + $version = '$(Version)' + Write-Verbose -Verbose "Version: $version" + if(!$version) { + throw "Version is not set." + } + + $mainVersionParts = $version -split '-' + + Write-Verbose -Verbose "mainVersionParts: $($mainVersionParts[0]) ; $($mainVersionParts[1])" + $versionParts = $mainVersionParts[0] -split '[.]'; + $major = $versionParts[0] + $minor = $versionParts[1] + $patch = $versionParts[2] + + $previewPart = $mainVersionParts[1] + Write-Verbose -Verbose "previewPart: $previewPart" + + Write-Host "major: $major; minor: $minor; patch: $patch;" + + $vstsCommandString = "vso[task.setvariable variable=pwshMajorVersion]$major" + Write-Host ("sending " + $vstsCommandString) + Write-Host "##$vstsCommandString" + + $vstsCommandString = "vso[task.setvariable variable=pwshMinorVersion]$minor" + Write-Host ("sending " + $vstsCommandString) + Write-Host "##$vstsCommandString" + + $vstsCommandString = "vso[task.setvariable variable=pwshPatchVersion]$patch" + Write-Host ("sending " + $vstsCommandString) + Write-Host "##$vstsCommandString" + if($previewPart) { + $vstsCommandString = "vso[task.setvariable variable=pwshPrereleaseVersion]$previewPart" + } else { + Write-Verbose -Verbose "No prerelease part found in version string." + } + displayName: Set ob_createvpack_*Ver + env: + ob_restore_phase: true + + # Validate pwsh*Version variables + - pwsh: | + $variables = @("pwshMajorVersion", "pwshMinorVersion", "pwshPatchVersion") + foreach ($var in $variables) { + if (-not (get-item "Env:\$var" -ErrorAction SilentlyContinue).value) { + throw "Required variable '`$env:$var' is not set." + } + } + displayName: Validate pwsh*Version variables + env: + ob_restore_phase: true + - pwsh: | if($env:RELEASETAGVAR -match '-') { throw "Don't release a preview build without coordinating with Windows Engineering Build Tools Team" } displayName: Stop any preview release + env: + ob_restore_phase: true - task: UseDotNet@2 displayName: 'Use .NET Core sdk' @@ -136,88 +212,115 @@ extends: version: 3.1.x installationPath: $(Agent.ToolsDirectory)/dotnet + ### BUILD ### + + - template: /.pipelines/templates/insert-nuget-config-azfeed.yml@self + parameters: + repoRoot: $(repoRoot) + + - task: CodeQL3000Init@0 # Add CodeQL Init task right before your 'Build' step. + env: + ob_restore_phase: true # Set ob_restore_phase to run this step before '🔒 Setup Signing' step. + inputs: + Enabled: true + AnalyzeInPipeline: false # Do not upload results + Language: csharp + + - task: UseDotNet@2 + displayName: 'Install .NET based on global.json' + inputs: + useGlobalJson: true + workingDirectory: $(repoRoot) + env: + ob_restore_phase: true + - pwsh: | - $packageArtifactName = 'drop_windows_package_package_win_${{ parameters.architecture }}' - $vstsCommandString = "vso[task.setvariable variable=PackageArtifactName]$packageArtifactName" - Write-Host "sending " + $vstsCommandString + # Need to set PowerShellRoot variable for obp-file-signing template + $vstsCommandString = "vso[task.setvariable variable=PowerShellRoot]$(repoRoot)" + Write-Host ("sending " + $vstsCommandString) Write-Host "##$vstsCommandString" - $packageArtifactPath = '$(Pipeline.Workspace)\PSPackagesOfficial' - $vstsCommandString = "vso[task.setvariable variable=PackageArtifactPath]$packageArtifactPath" - Write-Host "sending " + $vstsCommandString + $Architecture = '$(Architecture)' + $runtime = switch ($Architecture) + { + "x64" { "win7-x64" } + "x86" { "win7-x86" } + "arm64" { "win-arm64" } + } + + $params = @{} + if ($env:BuildConfiguration -eq 'minSize') { + $params['ForMinimalSize'] = $true + } + + $vstsCommandString = "vso[task.setvariable variable=Runtime]$runtime" + Write-Host ("sending " + $vstsCommandString) Write-Host "##$vstsCommandString" - displayName: 'Set package artifact variables' - - download: PSPackagesOfficial - artifact: $(PackageArtifactName) - displayName: Download package + Write-Verbose -Message "Building PowerShell with Runtime: $runtime for '$env:BuildConfiguration' configuration" + Import-Module -Name $(repoRoot)/build.psm1 -Force + $buildWithSymbolsPath = New-Item -ItemType Directory -Path "$(Pipeline.Workspace)/Symbols_$Architecture" -Force - - pwsh: 'Get-ChildItem $(PackageArtifactPath)\* -recurse | Select-Object -ExpandProperty Name' - displayName: 'Capture Artifact Listing' + Start-PSBootstrap -Scenario Package + $null = New-Item -ItemType Directory -Path $buildWithSymbolsPath -Force -Verbose - - pwsh: | - $message = @() - $packages = Get-ChildItem $(PackageArtifactPath)\* -recurse -include *.zip, *.msi - - if($packages.count -eq 0) {throw "No packages found in $(PackageArtifactPath)"} - - $packages | ForEach-Object { - if($_.Name -notmatch 'PowerShell-\d+\.\d+\.\d+\-([a-z]*.\d+\-)?win\-(fxdependent|x64|arm64|x86|fxdependentWinDesktop)\.(msi|zip){1}') - { - $messageInstance = "$($_.Name) is not a valid package name" - $message += $messageInstance - Write-Warning $messageInstance - } + $ReleaseTagParam = @{} + + if ($env:RELEASETAGVAR) { + $ReleaseTagParam['ReleaseTag'] = $env:RELEASETAGVAR } - if($message.count -gt 0){throw ($message | out-string)} - displayName: 'Validate Zip and MSI Package Names' + Start-PSBuild -Runtime $runtime -Configuration Release -Output $buildWithSymbolsPath -Clean -PSModuleRestore @params @ReleaseTagParam - - pwsh: | - Get-ChildItem $(PackageArtifactPath)\* -recurse -include *.zip | ForEach-Object { - if($_.Name -match 'PowerShell-\d+\.\d+\.\d+\-([a-z]*.\d+\-)?win\-(${{ parameters.architecture }})\.(zip){1}') - { - Expand-Archive -Path $_.FullName -DestinationPath $(ob_outputDirectory) - } - } - displayName: 'Extract Zip to ob_outputDirectory' + $refFolderPath = Join-Path $buildWithSymbolsPath 'ref' + Write-Verbose -Verbose "refFolderPath: $refFolderPath" + $outputPath = Join-Path '$(ob_outputDirectory)' 'psoptions' + $null = New-Item -ItemType Directory -Path $outputPath -Force + $psOptPath = "$outputPath/psoptions.json" + Save-PSOptions -PSOptionsPath $psOptPath + + Write-Verbose -Verbose "Completed building PowerShell for '$env:BuildConfiguration' configuration" + displayName: Build Windows Universal - $(Architecture) -$(BuildConfiguration) Symbols folder + env: + __DOTNET_RUNTIME_FEED_KEY: $(RUNTIME_SOURCEFEED_KEY) + ob_restore_phase: true # Set ob_restore_phase to run this step before '🔒 Setup Signing' step. + + - task: CodeQL3000Finalize@0 # Add CodeQL Finalize task right after your 'Build' step. + env: + ob_restore_phase: true # Set ob_restore_phase to run this step before '🔒 Setup Signing' step. + + - task: ms.vss-governance-buildtask.governance-build-task-component-detection.ComponentGovernanceComponentDetection@0 + displayName: 'Component Detection' + inputs: + sourceScanPath: '$(repoRoot)\src' + ob_restore_phase: true + + - template: /.pipelines/templates/obp-file-signing.yml@self + parameters: + binPath: '$(Pipeline.Workspace)/Symbols_$(Architecture)' + SigningProfile: $(windows_build_tools_cert_id) + OfficialBuild: false + vPackScenario: true + + ### END OF BUILD ### - pwsh: | - Write-Verbose "VPack Version: $(ob_createvpack_version)" -Verbose - Get-ChildItem -Path $(ob_outputDirectory)\* -Recurse - Get-Content $(ob_outputdirectory)\preview.json -ErrorAction SilentlyContinue | Write-Host + Get-ChildItem env:/ob_createvpack_*Ver + Get-ChildItem -Path "$(Pipeline.Workspace)\Symbols_$(Architecture)\*" -Recurse + Get-Content "$(Pipeline.Workspace)\PowerShell\preview.json" -ErrorAction SilentlyContinue | Write-Host displayName: Debug Output Directory and Version condition: succeededOrFailed() - pwsh: | - Write-Host "Using VPackPublishOverride variable" - $vpackVersion = '${{ parameters.VPackPublishOverride }}' - $vstsCommandString = "vso[task.setvariable variable=ob_createvpack_version]$vpackVersion" - Write-Host "sending " + $vstsCommandString - Write-Host "##$vstsCommandString" - condition: ne('${{ parameters.VPackPublishOverride }}', ' ') - displayName: 'Set ob_createvpack_version with VPackPublishOverride' - - - pwsh: | - Get-ChildItem -Path env: + Get-ChildItem -Path env: | Out-String -width 9999 -Stream | write-Verbose -Verbose displayName: Capture Environment condition: succeededOrFailed() - pwsh: | - Write-Verbose "VPack Version: $(ob_createvpack_version)" -Verbose - $vpackFiles = Get-ChildItem -Path $(ob_outputDirectory)\* -Recurse + $vpackFiles = Get-ChildItem -Path "$(Pipeline.Workspace)\Symbols_$(Architecture)\*" -Recurse if($vpackFiles.Count -eq 0) { - throw "No files found in $(ob_outputDirectory)" + throw "No files found in $(Pipeline.Workspace)\Symbols_$(Architecture)" } $vpackFiles displayName: Debug Output Directory and Version condition: succeededOrFailed() - - - task: onebranch.pipeline.signing@1 - displayName: 'Onebranch Signing' - inputs: - command: 'sign' - signing_environment: 'azure-ado' - cp_code: $(windows_build_tools_cert_id) - files_to_sign: '**/*.exe;**/System.Management.Automation.dll' - search_root: $(ob_outputDirectory) diff --git a/.pipelines/templates/obp-file-signing.yml b/.pipelines/templates/obp-file-signing.yml index ba761633b29..7c6ce7c6375 100644 --- a/.pipelines/templates/obp-file-signing.yml +++ b/.pipelines/templates/obp-file-signing.yml @@ -1,6 +1,9 @@ parameters: binPath: '$(ob_outputDirectory)' globalTool: 'false' + SigningProfile: 'external_distribution' + OfficialBuild: true + vPackScenario: false steps: - pwsh: | @@ -80,7 +83,7 @@ steps: displayName: Sign 1st party files inputs: command: 'sign' - signing_profile: external_distribution + signing_profile: ${{ parameters.SigningProfile }} files_to_sign: '**\*.psd1;**\*.psm1;**\*.ps1xml;**\*.ps1;**\*.dll;**\*.exe;**\pwsh' search_root: $(Pipeline.Workspace)/toBeSigned @@ -95,12 +98,15 @@ steps: $BuildPath = (Get-Item '${{ parameters.binPath }}').FullName Write-Verbose -Verbose -Message "BuildPath: $BuildPath" + $officialBuild = [System.Convert]::ToBoolean('${{ parameters.OfficialBuild }}') ## copy all files to be signed to build folder - Update-PSSignedBuildFolder -BuildPath $BuildPath -SignedFilesPath '$(Pipeline.Workspace)/toBeSigned' + Update-PSSignedBuildFolder -BuildPath $BuildPath -SignedFilesPath '$(Pipeline.Workspace)/toBeSigned' -OfficialBuild $officialBuild $dlls = Get-ChildItem $BuildPath/*.dll, $BuildPath/*.exe -Recurse $signatures = $dlls | Get-AuthenticodeSignature - $missingSignatures = $signatures | Where-Object { $_.status -eq 'notsigned' -or $_.SignerCertificate.Issuer -notmatch '^CN=Microsoft.*'}| select-object -ExpandProperty Path + $officialIssuerPattern = '^CN=(Microsoft Code Signing PCA|Microsoft Root Certificate Authority|Microsoft Corporation).*' + $testCert = '^CN=(Microsoft|TestAzureEngBuildCodeSign).*' + $missingSignatures = $signatures | Where-Object { $_.status -eq 'notsigned' -or $_.SignerCertificate.Issuer -notmatch $testCert -or $_.SignerCertificate.Issuer -notmatch $officialIssuerPattern} | select-object -ExpandProperty Path Write-Verbose -verbose "to be signed:`r`n $($missingSignatures | Out-String)" @@ -137,11 +143,20 @@ steps: displayName: Capture ThirdParty Signed files - pwsh: | + $officialBuild = [System.Convert]::ToBoolean('${{ parameters.OfficialBuild }}') + $vPackScenario = [System.Convert]::ToBoolean('${{ parameters.vPackScenario }}') Import-Module '$(PowerShellRoot)/build.psm1' -Force Import-Module '$(PowerShellRoot)/tools/packaging' -Force $isGlobalTool = '${{ parameters.globalTool }}' -eq 'true' - if (-not $isGlobalTool) { + if ($vPackScenario) { + Write-Verbose -Verbose -Message "vPackScenario is true, copying to $(ob_outputDirectory)" + $pathForUpload = New-Item -ItemType Directory -Path '$(ob_outputDirectory)' -Force + Write-Verbose -Verbose -Message "pathForUpload: $pathForUpload" + Copy-Item -Path '${{ parameters.binPath }}\*' -Destination $pathForUpload -Recurse -Force -Verbose + Write-Verbose -Verbose -Message "Files copied to $pathForUpload" + } + elseif (-not $isGlobalTool) { $pathForUpload = New-Item -ItemType Directory -Path '$(ob_outputDirectory)/Signed-$(Runtime)' -Force Write-Verbose -Verbose -Message "pathForUpload: $pathForUpload" Copy-Item -Path '${{ parameters.binPath }}\*' -Destination $pathForUpload -Recurse -Force -Verbose @@ -153,7 +168,7 @@ steps: Write-Verbose "Copying third party signed files to the build folder" $thirdPartySignedFilesPath = (Get-Item '$(Pipeline.Workspace)/thirdPartyToBeSigned').FullName - Update-PSSignedBuildFolder -BuildPath $pathForUpload -SignedFilesPath $thirdPartySignedFilesPath + Update-PSSignedBuildFolder -BuildPath $pathForUpload -SignedFilesPath $thirdPartySignedFilesPath -OfficialBuild $officialBuild displayName: 'Copy signed files for upload' diff --git a/tools/packaging/packaging.psm1 b/tools/packaging/packaging.psm1 index 782a4ecadc8..560a985283b 100644 --- a/tools/packaging/packaging.psm1 +++ b/tools/packaging/packaging.psm1 @@ -887,7 +887,8 @@ function Update-PSSignedBuildFolder [string]$BuildPath, [Parameter(Mandatory)] [string]$SignedFilesPath, - [string[]] $RemoveFilter = ('*.pdb', '*.zip', '*.r2rmap') + [string[]] $RemoveFilter = ('*.pdb', '*.zip', '*.r2rmap'), + [bool]$OfficialBuild = $true ) $BuildPathNormalized = (Get-Item $BuildPath).FullName @@ -943,8 +944,21 @@ function Update-PSSignedBuildFolder if ($IsWindows) { $signature = Get-AuthenticodeSignature -FilePath $signedFilePath - if ($signature.Status -ne 'Valid') { + + if ($signature.Status -ne 'Valid' -and $OfficialBuild) { + Write-Host "Certificate Issuer: $($signature.SignerCertificate.Issuer)" + Write-Host "Certificate Subject: $($signature.SignerCertificate.Subject)" Write-Error "Invalid signature for $signedFilePath" + } elseif ($OfficialBuild -eq $false) { + if ($signature.Status -eq 'NotSigned') { + Write-Warning "File is not signed: $signedFilePath" + } elseif ($signature.SignerCertificate.Issuer -notmatch '^CN=(Microsoft|TestAzureEngBuildCodeSign|Windows Internal Build Tools).*') { + Write-Warning "File signed with test certificate: $signedFilePath" + Write-Host "Certificate Issuer: $($signature.SignerCertificate.Issuer)" + Write-Host "Certificate Subject: $($signature.SignerCertificate.Subject)" + } else { + Write-Verbose -Verbose "File properly signed: $signedFilePath" + } } } else From cbfecf7ae155fa310ee78fd1ff42f0fcf1d13a71 Mon Sep 17 00:00:00 2001 From: Travis Plunk Date: Fri, 5 Sep 2025 11:11:07 -0700 Subject: [PATCH 10/16] [release/v7.5] Update container images to use mcr.microsoft.com for Linux and Azure Linux (#25986) --- .../PowerShell-Coordinated_Packages-Official.yml | 6 ++++-- .pipelines/PowerShell-Packages-Official.yml | 3 +-- .pipelines/PowerShell-Release-Official-Azure.yml | 3 +-- .pipelines/PowerShell-Release-Official.yml | 6 +++--- .pipelines/apiscan-gen-notice.yml | 2 +- .pipelines/templates/linux.yml | 1 + .pipelines/templates/mac.yml | 1 + .pipelines/templates/windows-hosted-build.yml | 2 ++ build.psm1 | 12 +++++++----- 9 files changed, 21 insertions(+), 15 deletions(-) diff --git a/.pipelines/PowerShell-Coordinated_Packages-Official.yml b/.pipelines/PowerShell-Coordinated_Packages-Official.yml index 8de89b0c508..73c6e4f39b8 100644 --- a/.pipelines/PowerShell-Coordinated_Packages-Official.yml +++ b/.pipelines/PowerShell-Coordinated_Packages-Official.yml @@ -65,7 +65,7 @@ variables: - name: __DOTNET_RUNTIME_FEED value: ${{ parameters.InternalSDKBlobURL }} - name: LinuxContainerImage - value: onebranch.azurecr.io/linux/ubuntu-2004:latest + value: mcr.microsoft.com/onebranch/azurelinux/build:3.0 - name: WindowsContainerImage value: onebranch.azurecr.io/windows/ltsc2019/vse2022:latest - name: CDP_DEFINITION_BUILD_COUNT @@ -86,6 +86,8 @@ variables: # Disable BinSkim at job level to override NonOfficial template defaults - name: ob_sdl_binskim_enabled value: false + - name: ps_official_build + value: ${{ parameters.OfficialBuild }} extends: template: ${{ variables.templateFile }} @@ -130,7 +132,7 @@ extends: - job: SetVars displayName: Set Variables pool: - type: windows + type: linux variables: - name: ob_outputDirectory diff --git a/.pipelines/PowerShell-Packages-Official.yml b/.pipelines/PowerShell-Packages-Official.yml index f0d428bf1d6..b756b6f8c36 100644 --- a/.pipelines/PowerShell-Packages-Official.yml +++ b/.pipelines/PowerShell-Packages-Official.yml @@ -56,7 +56,7 @@ variables: - name: WindowsContainerImage value: 'onebranch.azurecr.io/windows/ltsc2022/vse2022:latest' # Docker image which is used to build the project - name: LinuxContainerImage - value: mcr.microsoft.com/onebranch/cbl-mariner/build:2.0 + value: mcr.microsoft.com/onebranch/azurelinux/build:3.0 - group: mscodehub-feed-read-general - group: mscodehub-feed-read-akv - name: branchCounterKey @@ -66,7 +66,6 @@ variables: - group: MSIXSigningProfile - name: templateFile value: ${{ iif ( parameters.OfficialBuild, 'v2/OneBranch.Official.CrossPlat.yml@onebranchTemplates', 'v2/OneBranch.NonOfficial.CrossPlat.yml@onebranchTemplates' ) }} - resources: pipelines: diff --git a/.pipelines/PowerShell-Release-Official-Azure.yml b/.pipelines/PowerShell-Release-Official-Azure.yml index 8e144f1ee55..1f210ac6745 100644 --- a/.pipelines/PowerShell-Release-Official-Azure.yml +++ b/.pipelines/PowerShell-Release-Official-Azure.yml @@ -47,11 +47,10 @@ variables: - name: WindowsContainerImage value: 'onebranch.azurecr.io/windows/ltsc2022/vse2022:latest' - name: LinuxContainerImage - value: mcr.microsoft.com/onebranch/cbl-mariner/build:2.0 + value: mcr.microsoft.com/onebranch/azurelinux/build:3.0 - group: PoolNames - name: templateFile value: ${{ iif ( parameters.OfficialBuild, 'v2/OneBranch.Official.CrossPlat.yml@onebranchTemplates', 'v2/OneBranch.NonOfficial.CrossPlat.yml@onebranchTemplates' ) }} - resources: repositories: diff --git a/.pipelines/PowerShell-Release-Official.yml b/.pipelines/PowerShell-Release-Official.yml index 9d543eae3a9..0c943871462 100644 --- a/.pipelines/PowerShell-Release-Official.yml +++ b/.pipelines/PowerShell-Release-Official.yml @@ -57,7 +57,7 @@ variables: - name: WindowsContainerImage value: 'onebranch.azurecr.io/windows/ltsc2022/vse2022:latest' - name: LinuxContainerImage - value: mcr.microsoft.com/onebranch/cbl-mariner/build:2.0 + value: mcr.microsoft.com/onebranch/azurelinux/build:3.0 - name: ReleaseTagVar value: ${{ parameters.ReleaseTagVar }} - group: PoolNames @@ -284,7 +284,7 @@ extends: - stage: PublishGitHubReleaseAndNuget displayName: Publish GitHub and Nuget Release - dependsOn: + dependsOn: - setReleaseTagAndChangelog - UpdateChangeLog variables: @@ -404,7 +404,7 @@ extends: - stage: ChangesToMaster displayName: Ensure changes are in GH master - dependsOn: + dependsOn: - PublishPMC jobs: - template: /.pipelines/templates/approvalJob.yml@self diff --git a/.pipelines/apiscan-gen-notice.yml b/.pipelines/apiscan-gen-notice.yml index 1507b9345bd..af122fb2365 100644 --- a/.pipelines/apiscan-gen-notice.yml +++ b/.pipelines/apiscan-gen-notice.yml @@ -26,7 +26,7 @@ variables: - group: 'ComponentGovernance' - group: 'PoolNames' - name: LinuxContainerImage - value: onebranch.azurecr.io/linux/ubuntu-2004:latest + value: mcr.microsoft.com/onebranch/azurelinux/build:3.0 - name: WindowsContainerImage value: onebranch.azurecr.io/windows/ltsc2022/vse2022:latest - ${{ if eq(parameters['FORCE_CODEQL'],'true') }}: diff --git a/.pipelines/templates/linux.yml b/.pipelines/templates/linux.yml index d6026dc5336..ccfac3fbc8e 100644 --- a/.pipelines/templates/linux.yml +++ b/.pipelines/templates/linux.yml @@ -199,5 +199,6 @@ jobs: - template: /.pipelines/templates/obp-file-signing.yml@self parameters: binPath: $(DropRootPath) + OfficialBuild: $(ps_official_build) - template: /.pipelines/templates/step/finalize.yml@self diff --git a/.pipelines/templates/mac.yml b/.pipelines/templates/mac.yml index 310c5695979..b08becedc22 100644 --- a/.pipelines/templates/mac.yml +++ b/.pipelines/templates/mac.yml @@ -148,5 +148,6 @@ jobs: - template: /.pipelines/templates/obp-file-signing.yml@self parameters: binPath: $(DropRootPath) + OfficialBuild: $(ps_official_build) - template: /.pipelines/templates/step/finalize.yml@self diff --git a/.pipelines/templates/windows-hosted-build.yml b/.pipelines/templates/windows-hosted-build.yml index 8f8273f4ec8..1f732a0145c 100644 --- a/.pipelines/templates/windows-hosted-build.yml +++ b/.pipelines/templates/windows-hosted-build.yml @@ -204,6 +204,7 @@ jobs: - template: /.pipelines/templates/obp-file-signing.yml@self parameters: binPath: '$(Pipeline.Workspace)/Symbols_$(Architecture)' + OfficialBuild: $(ps_official_build) ## first we sign all the files in the bin folder - ${{ if eq(variables['Architecture'], 'fxdependent') }}: @@ -211,6 +212,7 @@ jobs: parameters: binPath: '$(GlobalToolArtifactPath)/publish/PowerShell.Windows.x64/release' globalTool: 'true' + OfficialBuild: $(ps_official_build) - pwsh: | Get-ChildItem '$(GlobalToolArtifactPath)/obj/PowerShell.Windows.x64/release' diff --git a/build.psm1 b/build.psm1 index ffaf7b02f9d..21bf0f332ab 100644 --- a/build.psm1 +++ b/build.psm1 @@ -193,7 +193,7 @@ function Get-EnvironmentInformation $environment += @{'IsRedHatFamily' = $environment.IsCentOS -or $environment.IsFedora -or $environment.IsRedHat} $environment += @{'IsSUSEFamily' = $environment.IsSLES -or $environment.IsOpenSUSE} $environment += @{'IsAlpine' = $LinuxInfo.ID -match 'alpine'} - $environment += @{'IsMariner' = $LinuxInfo.ID -match 'mariner'} + $environment += @{'IsMariner' = $LinuxInfo.ID -match 'mariner' -or $LinuxInfo.ID -match 'azurelinux'} # Workaround for temporary LD_LIBRARY_PATH hack for Fedora 24 # https://github.com/PowerShell/PowerShell/issues/2511 @@ -353,8 +353,8 @@ function Start-PSBuild { $PSModuleRestore = $true } - if ($Runtime -eq "linux-arm" -and $environment.IsLinux -and -not $environment.IsUbuntu) { - throw "Cross compiling for linux-arm is only supported on Ubuntu environment" + if ($Runtime -eq "linux-arm" -and $environment.IsLinux -and -not $environment.IsUbuntu -and -not $environment.IsMariner) { + throw "Cross compiling for linux-arm is only supported on AzureLinux/Ubuntu environment" } if ("win-arm","win-arm64" -contains $Runtime -and -not $environment.IsWindows) { @@ -2196,6 +2196,8 @@ function Get-RedHatPackageManager { "yum install -y -q" } elseif ($environment.IsFedora -or (Get-Command -Name dnf -CommandType Application -ErrorAction SilentlyContinue)) { "dnf install -y -q" + } elseif ($environment.IsMariner -or (Get-Command -Name Test-DscConfiguration -CommandType Application -ErrorAction SilentlyContinue)) { + "tdnf install -y -q" } else { throw "Error determining package manager for this distribution." } @@ -2267,8 +2269,8 @@ function Start-PSBootstrap { # Note that when it is null, Invoke-Expression (but not &) must be used to interpolate properly $sudo = if (!$NoSudo) { "sudo" } - if ($BuildLinuxArm -and $environment.IsLinux -and -not $environment.IsUbuntu) { - Write-Error "Cross compiling for linux-arm is only supported on Ubuntu environment" + if ($BuildLinuxArm -and $environment.IsLinux -and -not $environment.IsUbuntu -and -not $environment.IsMariner) { + Write-Error "Cross compiling for linux-arm is only supported on AzureLinux/Ubuntu environment" return } From 30814e81966ca7ebb9b5904fd54188ab6cf838e4 Mon Sep 17 00:00:00 2001 From: PowerShell Team Bot <69177312+pwshBot@users.noreply.github.com> Date: Fri, 5 Sep 2025 13:26:32 -0700 Subject: [PATCH 11/16] [release/v7.5] Make logical template name consistent between pipelines (#25991) Co-authored-by: Travis Plunk --- .pipelines/MSIXBundle-vPack-Official.yml | 4 ++-- .pipelines/PowerShell-Coordinated_Packages-Official.yml | 2 +- .pipelines/PowerShell-Packages-Official.yml | 4 ++-- .pipelines/PowerShell-Release-Official-Azure.yml | 4 ++-- .pipelines/PowerShell-Release-Official.yml | 4 ++-- .pipelines/PowerShell-vPack-Official.yml | 4 ++-- 6 files changed, 11 insertions(+), 11 deletions(-) diff --git a/.pipelines/MSIXBundle-vPack-Official.yml b/.pipelines/MSIXBundle-vPack-Official.yml index ef96f63f045..8e175c5a6bb 100644 --- a/.pipelines/MSIXBundle-vPack-Official.yml +++ b/.pipelines/MSIXBundle-vPack-Official.yml @@ -29,7 +29,7 @@ variables: resources: repositories: - - repository: templates + - repository: onebranchTemplates type: git name: OneBranch.Pipelines/GovernedTemplates ref: refs/heads/main @@ -44,7 +44,7 @@ resources: - releases/* extends: - template: v2/Microsoft.Official.yml@templates + template: v2/Microsoft.Official.yml@onebranchTemplates parameters: platform: name: 'windows_undocked' # windows undocked diff --git a/.pipelines/PowerShell-Coordinated_Packages-Official.yml b/.pipelines/PowerShell-Coordinated_Packages-Official.yml index 73c6e4f39b8..b309791d77d 100644 --- a/.pipelines/PowerShell-Coordinated_Packages-Official.yml +++ b/.pipelines/PowerShell-Coordinated_Packages-Official.yml @@ -1,4 +1,3 @@ -name: bins-$(BUILD.SOURCEBRANCHNAME)-$(Build.BuildId) trigger: none parameters: @@ -30,6 +29,7 @@ parameters: type: boolean default: false +name: bins-$(BUILD.SOURCEBRANCHNAME)-prod.${{ parameters.OfficialBuild }}-$(Build.BuildId) resources: repositories: diff --git a/.pipelines/PowerShell-Packages-Official.yml b/.pipelines/PowerShell-Packages-Official.yml index b756b6f8c36..333d73276b8 100644 --- a/.pipelines/PowerShell-Packages-Official.yml +++ b/.pipelines/PowerShell-Packages-Official.yml @@ -28,7 +28,7 @@ parameters: # parameters are shown up in ADO UI in a build queue time type: boolean default: false -name: pkgs-$(BUILD.SOURCEBRANCHNAME)-$(Build.BuildId) +name: pkgs-$(BUILD.SOURCEBRANCHNAME)-prod.${{ parameters.OfficialBuild }}-$(Build.BuildId) variables: - name: CDP_DEFINITION_BUILD_COUNT @@ -78,7 +78,7 @@ resources: - releases/* repositories: - - repository: templates + - repository: onebranchTemplates type: git name: OneBranch.Pipelines/GovernedTemplates ref: refs/heads/main diff --git a/.pipelines/PowerShell-Release-Official-Azure.yml b/.pipelines/PowerShell-Release-Official-Azure.yml index 1f210ac6745..f4c41143b5f 100644 --- a/.pipelines/PowerShell-Release-Official-Azure.yml +++ b/.pipelines/PowerShell-Release-Official-Azure.yml @@ -17,7 +17,7 @@ parameters: # parameters are shown up in ADO UI in a build queue time type: boolean default: false -name: ev2-$(BUILD.SOURCEBRANCHNAME)-$(Build.BuildId) +name: ev2-$(BUILD.SOURCEBRANCHNAME)-prod.${{ parameters.OfficialBuild }}-$(Build.BuildId) variables: - name: CDP_DEFINITION_BUILD_COUNT @@ -54,7 +54,7 @@ variables: resources: repositories: - - repository: templates + - repository: onebranchTemplates type: git name: OneBranch.Pipelines/GovernedTemplates ref: refs/heads/main diff --git a/.pipelines/PowerShell-Release-Official.yml b/.pipelines/PowerShell-Release-Official.yml index 0c943871462..974b8f328c8 100644 --- a/.pipelines/PowerShell-Release-Official.yml +++ b/.pipelines/PowerShell-Release-Official.yml @@ -29,7 +29,7 @@ parameters: # parameters are shown up in ADO UI in a build queue time type: boolean default: false -name: release-$(BUILD.SOURCEBRANCHNAME)-$(Build.BuildId) +name: release-$(BUILD.SOURCEBRANCHNAME)-prod.${{ parameters.OfficialBuild }}-$(Build.BuildId) variables: - name: CDP_DEFINITION_BUILD_COUNT @@ -71,7 +71,7 @@ variables: resources: repositories: - - repository: templates + - repository: onebranchTemplates type: git name: OneBranch.Pipelines/GovernedTemplates ref: refs/heads/main diff --git a/.pipelines/PowerShell-vPack-Official.yml b/.pipelines/PowerShell-vPack-Official.yml index 5783785cfd6..f110ff0366a 100644 --- a/.pipelines/PowerShell-vPack-Official.yml +++ b/.pipelines/PowerShell-vPack-Official.yml @@ -50,7 +50,7 @@ variables: - group: Azure Blob variable group - group: certificate_logical_to_actual # used within signing task - name: templateFile - value: ${{ iif ( parameters.OfficialBuild, 'v2/Microsoft.Official.yml@templates', 'v2/Microsoft.NonOfficial.yml@templates' ) }} + value: ${{ iif ( parameters.OfficialBuild, 'v2/Microsoft.Official.yml@onebranchTemplates', 'v2/Microsoft.NonOfficial.yml@onebranchTemplates' ) }} - group: DotNetPrivateBuildAccess - group: certificate_logical_to_actual # We shouldn't be using PATs anymore @@ -58,7 +58,7 @@ variables: resources: repositories: - - repository: templates + - repository: onebranchTemplates type: git name: OneBranch.Pipelines/GovernedTemplates ref: refs/heads/main From efdb4a61859cf3381c107a8a0473db32dab9a48f Mon Sep 17 00:00:00 2001 From: Travis Plunk Date: Fri, 5 Sep 2025 14:09:47 -0700 Subject: [PATCH 12/16] Update third-party library versions in ThirdPartyNotices.txt to 9.0.8 (#25995) --- ThirdPartyNotices.txt | 102 +++++++++++++++++++++--------------------- 1 file changed, 51 insertions(+), 51 deletions(-) diff --git a/ThirdPartyNotices.txt b/ThirdPartyNotices.txt index c97524423aa..ce31689d1f0 100644 --- a/ThirdPartyNotices.txt +++ b/ThirdPartyNotices.txt @@ -284,7 +284,7 @@ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLI --------------------------------------------------------- -Microsoft.Extensions.ObjectPool 8.0.17 - MIT +Microsoft.Extensions.ObjectPool 8.0.19 - MIT Copyright Jorn Zaefferer @@ -374,7 +374,7 @@ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLI --------------------------------------------------------- -Microsoft.Win32.Registry.AccessControl 9.0.6 - MIT +Microsoft.Win32.Registry.AccessControl 9.0.8 - MIT Copyright (c) 2021 @@ -464,7 +464,7 @@ SOFTWARE. --------------------------------------------------------- -Microsoft.Win32.SystemEvents 9.0.6 - MIT +Microsoft.Win32.SystemEvents 9.0.8 - MIT Copyright (c) 2021 @@ -554,7 +554,7 @@ SOFTWARE. --------------------------------------------------------- -Microsoft.Windows.Compatibility 9.0.6 - MIT +Microsoft.Windows.Compatibility 9.0.8 - MIT (c) Microsoft Corporation @@ -607,7 +607,7 @@ CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. --------------------------------------------------------- -runtime.android-arm.runtime.native.System.IO.Ports 9.0.6 - MIT +runtime.android-arm.runtime.native.System.IO.Ports 9.0.8 - MIT Copyright (c) 2021 @@ -697,7 +697,7 @@ SOFTWARE. --------------------------------------------------------- -runtime.android-arm64.runtime.native.System.IO.Ports 9.0.6 - MIT +runtime.android-arm64.runtime.native.System.IO.Ports 9.0.8 - MIT Copyright (c) 2021 @@ -787,7 +787,7 @@ SOFTWARE. --------------------------------------------------------- -runtime.android-x64.runtime.native.System.IO.Ports 9.0.6 - MIT +runtime.android-x64.runtime.native.System.IO.Ports 9.0.8 - MIT Copyright (c) 2021 @@ -877,7 +877,7 @@ SOFTWARE. --------------------------------------------------------- -runtime.android-x86.runtime.native.System.IO.Ports 9.0.6 - MIT +runtime.android-x86.runtime.native.System.IO.Ports 9.0.8 - MIT Copyright (c) 2021 @@ -967,7 +967,7 @@ SOFTWARE. --------------------------------------------------------- -runtime.linux-arm.runtime.native.System.IO.Ports 9.0.6 - MIT +runtime.linux-arm.runtime.native.System.IO.Ports 9.0.8 - MIT Copyright (c) 2021 @@ -1057,7 +1057,7 @@ SOFTWARE. --------------------------------------------------------- -runtime.linux-arm64.runtime.native.System.IO.Ports 9.0.6 - MIT +runtime.linux-arm64.runtime.native.System.IO.Ports 9.0.8 - MIT Copyright (c) 2021 @@ -1147,7 +1147,7 @@ SOFTWARE. --------------------------------------------------------- -runtime.linux-bionic-arm64.runtime.native.System.IO.Ports 9.0.6 - MIT +runtime.linux-bionic-arm64.runtime.native.System.IO.Ports 9.0.8 - MIT Copyright (c) 2021 @@ -1237,7 +1237,7 @@ SOFTWARE. --------------------------------------------------------- -runtime.linux-bionic-x64.runtime.native.System.IO.Ports 9.0.6 - MIT +runtime.linux-bionic-x64.runtime.native.System.IO.Ports 9.0.8 - MIT Copyright (c) 2021 @@ -1327,7 +1327,7 @@ SOFTWARE. --------------------------------------------------------- -runtime.linux-musl-arm.runtime.native.System.IO.Ports 9.0.6 - MIT +runtime.linux-musl-arm.runtime.native.System.IO.Ports 9.0.8 - MIT Copyright (c) 2021 @@ -1417,7 +1417,7 @@ SOFTWARE. --------------------------------------------------------- -runtime.linux-musl-arm64.runtime.native.System.IO.Ports 9.0.6 - MIT +runtime.linux-musl-arm64.runtime.native.System.IO.Ports 9.0.8 - MIT Copyright (c) 2021 @@ -1507,7 +1507,7 @@ SOFTWARE. --------------------------------------------------------- -runtime.linux-musl-x64.runtime.native.System.IO.Ports 9.0.6 - MIT +runtime.linux-musl-x64.runtime.native.System.IO.Ports 9.0.8 - MIT Copyright (c) 2021 @@ -1597,7 +1597,7 @@ SOFTWARE. --------------------------------------------------------- -runtime.linux-x64.runtime.native.System.IO.Ports 9.0.6 - MIT +runtime.linux-x64.runtime.native.System.IO.Ports 9.0.8 - MIT Copyright (c) 2021 @@ -1687,7 +1687,7 @@ SOFTWARE. --------------------------------------------------------- -runtime.maccatalyst-arm64.runtime.native.System.IO.Ports 9.0.6 - MIT +runtime.maccatalyst-arm64.runtime.native.System.IO.Ports 9.0.8 - MIT Copyright (c) 2021 @@ -1777,7 +1777,7 @@ SOFTWARE. --------------------------------------------------------- -runtime.maccatalyst-x64.runtime.native.System.IO.Ports 9.0.6 - MIT +runtime.maccatalyst-x64.runtime.native.System.IO.Ports 9.0.8 - MIT Copyright (c) 2021 @@ -1912,7 +1912,7 @@ SOFTWARE. --------------------------------------------------------- -runtime.native.System.IO.Ports 9.0.6 - MIT +runtime.native.System.IO.Ports 9.0.8 - MIT Copyright (c) 2021 @@ -2002,7 +2002,7 @@ SOFTWARE. --------------------------------------------------------- -runtime.osx-arm64.runtime.native.System.IO.Ports 9.0.6 - MIT +runtime.osx-arm64.runtime.native.System.IO.Ports 9.0.8 - MIT Copyright (c) 2021 @@ -2092,7 +2092,7 @@ SOFTWARE. --------------------------------------------------------- -runtime.osx-x64.runtime.native.System.IO.Ports 9.0.6 - MIT +runtime.osx-x64.runtime.native.System.IO.Ports 9.0.8 - MIT Copyright (c) 2021 @@ -2182,7 +2182,7 @@ SOFTWARE. --------------------------------------------------------- -System.CodeDom 9.0.6 - MIT +System.CodeDom 9.0.8 - MIT Copyright (c) 2021 @@ -2358,7 +2358,7 @@ SOFTWARE. --------------------------------------------------------- -System.ComponentModel.Composition 9.0.6 - MIT +System.ComponentModel.Composition 9.0.8 - MIT Copyright (c) 2021 @@ -2448,7 +2448,7 @@ SOFTWARE. --------------------------------------------------------- -System.ComponentModel.Composition.Registration 9.0.6 - MIT +System.ComponentModel.Composition.Registration 9.0.8 - MIT Copyright (c) 2021 @@ -2538,7 +2538,7 @@ SOFTWARE. --------------------------------------------------------- -System.Configuration.ConfigurationManager 9.0.6 - MIT +System.Configuration.ConfigurationManager 9.0.8 - MIT Copyright (c) 2021 @@ -2628,7 +2628,7 @@ SOFTWARE. --------------------------------------------------------- -System.Data.Odbc 9.0.6 - MIT +System.Data.Odbc 9.0.8 - MIT Copyright (c) 2021 @@ -2718,7 +2718,7 @@ SOFTWARE. --------------------------------------------------------- -System.Data.OleDb 9.0.6 - MIT +System.Data.OleDb 9.0.8 - MIT Copyright (c) 2021 @@ -2827,7 +2827,7 @@ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLI --------------------------------------------------------- -System.Diagnostics.DiagnosticSource 9.0.6 - MIT +System.Diagnostics.DiagnosticSource 9.0.8 - MIT Copyright (c) 2021 @@ -2917,7 +2917,7 @@ SOFTWARE. --------------------------------------------------------- -System.Diagnostics.EventLog 9.0.6 - MIT +System.Diagnostics.EventLog 9.0.8 - MIT Copyright (c) 2021 @@ -3007,7 +3007,7 @@ SOFTWARE. --------------------------------------------------------- -System.Diagnostics.PerformanceCounter 9.0.6 - MIT +System.Diagnostics.PerformanceCounter 9.0.8 - MIT Copyright (c) 2021 @@ -3097,7 +3097,7 @@ SOFTWARE. --------------------------------------------------------- -System.DirectoryServices 9.0.6 - MIT +System.DirectoryServices 9.0.8 - MIT Copyright (c) 2021 @@ -3187,7 +3187,7 @@ SOFTWARE. --------------------------------------------------------- -System.DirectoryServices.AccountManagement 9.0.6 - MIT +System.DirectoryServices.AccountManagement 9.0.8 - MIT Copyright (c) 2021 @@ -3277,7 +3277,7 @@ SOFTWARE. --------------------------------------------------------- -System.DirectoryServices.Protocols 9.0.6 - MIT +System.DirectoryServices.Protocols 9.0.8 - MIT Copyright (c) 2021 @@ -3367,7 +3367,7 @@ SOFTWARE. --------------------------------------------------------- -System.Drawing.Common 9.0.6 - MIT +System.Drawing.Common 9.0.8 - MIT (c) Microsoft Corporation @@ -3402,7 +3402,7 @@ SOFTWARE. --------------------------------------------------------- -System.IO.Packaging 9.0.6 - MIT +System.IO.Packaging 9.0.8 - MIT Copyright (c) 2021 @@ -3492,7 +3492,7 @@ SOFTWARE. --------------------------------------------------------- -System.IO.Ports 9.0.6 - MIT +System.IO.Ports 9.0.8 - MIT Copyright (c) 2021 @@ -3582,7 +3582,7 @@ SOFTWARE. --------------------------------------------------------- -System.Management 9.0.6 - MIT +System.Management 9.0.8 - MIT Copyright (c) 2021 @@ -3672,7 +3672,7 @@ SOFTWARE. --------------------------------------------------------- -System.Net.Http.WinHttpHandler 9.0.6 - MIT +System.Net.Http.WinHttpHandler 9.0.8 - MIT Copyright (c) 2021 @@ -3847,7 +3847,7 @@ SOFTWARE. --------------------------------------------------------- -System.Reflection.Context 9.0.6 - MIT +System.Reflection.Context 9.0.8 - MIT Copyright (c) 2021 @@ -4077,7 +4077,7 @@ SOFTWARE. --------------------------------------------------------- -System.Runtime.Caching 9.0.6 - MIT +System.Runtime.Caching 9.0.8 - MIT Copyright (c) 2021 @@ -4242,7 +4242,7 @@ SOFTWARE. --------------------------------------------------------- -System.Security.Cryptography.Pkcs 9.0.6 - MIT +System.Security.Cryptography.Pkcs 9.0.8 - MIT Copyright (c) 2021 @@ -4332,7 +4332,7 @@ SOFTWARE. --------------------------------------------------------- -System.Security.Cryptography.ProtectedData 9.0.6 - MIT +System.Security.Cryptography.ProtectedData 9.0.8 - MIT Copyright (c) 2021 @@ -4422,7 +4422,7 @@ SOFTWARE. --------------------------------------------------------- -System.Security.Cryptography.Xml 9.0.6 - MIT +System.Security.Cryptography.Xml 9.0.8 - MIT Copyright (c) 2021 @@ -4512,7 +4512,7 @@ SOFTWARE. --------------------------------------------------------- -System.Security.Permissions 9.0.6 - MIT +System.Security.Permissions 9.0.8 - MIT Copyright (c) 2021 @@ -4851,7 +4851,7 @@ SOFTWARE. --------------------------------------------------------- -System.ServiceModel.Syndication 9.0.6 - MIT +System.ServiceModel.Syndication 9.0.8 - MIT Copyright (c) 2021 @@ -4941,7 +4941,7 @@ SOFTWARE. --------------------------------------------------------- -System.ServiceProcess.ServiceController 9.0.6 - MIT +System.ServiceProcess.ServiceController 9.0.8 - MIT Copyright (c) 2021 @@ -5031,7 +5031,7 @@ SOFTWARE. --------------------------------------------------------- -System.Speech 9.0.6 - MIT +System.Speech 9.0.8 - MIT Copyright (c) 2021 @@ -5121,7 +5121,7 @@ SOFTWARE. --------------------------------------------------------- -System.Text.Encoding.CodePages 9.0.6 - MIT +System.Text.Encoding.CodePages 9.0.8 - MIT Copyright (c) 2021 @@ -5211,7 +5211,7 @@ SOFTWARE. --------------------------------------------------------- -System.Text.Encodings.Web 9.0.6 - MIT +System.Text.Encodings.Web 9.0.8 - MIT Copyright (c) 2021 @@ -5301,7 +5301,7 @@ SOFTWARE. --------------------------------------------------------- -System.Threading.AccessControl 9.0.6 - MIT +System.Threading.AccessControl 9.0.8 - MIT Copyright (c) 2021 @@ -5427,7 +5427,7 @@ SOFTWARE. --------------------------------------------------------- -System.Windows.Extensions 9.0.6 - MIT +System.Windows.Extensions 9.0.8 - MIT Copyright (c) 2021 From bcd6bc473f257c09f1d9c226b83a433c83af174b Mon Sep 17 00:00:00 2001 From: "Travis Plunk (HE/HIM)" Date: Tue, 2 Sep 2025 16:58:56 -0700 Subject: [PATCH 13/16] Fix race condition in RemoteHyperVSocket --- .github/workflows/linux-ci.yml | 1 + .github/workflows/macos-ci.yml | 1 + .github/workflows/windows-ci.yml | 1 + build.psm1 | 27 +- .../host/msh/CommandLineParameterParser.cs | 131 ++- .../host/msh/ConsoleHost.cs | 49 +- .../CommandLineParameterParserStrings.resx | 3 + .../resources/ConsoleHostStrings.resx | 6 + .../common/RemoteSessionHyperVSocket.cs | 800 ++++++++++++++---- .../fanin/OutOfProcTransportManager.cs | 24 +- .../server/OutOfProcServerMediator.cs | 28 + .../resources/RemotingErrorIdStrings.resx | 6 + test/xUnit/csharp/test_CommandLineParser.cs | 22 + test/xUnit/csharp/test_RemoteHyperV.cs | 661 +++++++++++++++ 14 files changed, 1563 insertions(+), 197 deletions(-) create mode 100644 test/xUnit/csharp/test_RemoteHyperV.cs diff --git a/.github/workflows/linux-ci.yml b/.github/workflows/linux-ci.yml index a7523f430cf..2bf61ca3e48 100644 --- a/.github/workflows/linux-ci.yml +++ b/.github/workflows/linux-ci.yml @@ -21,6 +21,7 @@ on: - master - release/** - github-mirror + - "*-feature" # Path filters for PRs need to go into the changes job concurrency: diff --git a/.github/workflows/macos-ci.yml b/.github/workflows/macos-ci.yml index 9184dc088f0..e0a85042053 100644 --- a/.github/workflows/macos-ci.yml +++ b/.github/workflows/macos-ci.yml @@ -19,6 +19,7 @@ on: - master - release/** - github-mirror + - "*-feature" # Path filters for PRs need to go into the changes job concurrency: diff --git a/.github/workflows/windows-ci.yml b/.github/workflows/windows-ci.yml index 1e955c78be6..2e392987cb0 100644 --- a/.github/workflows/windows-ci.yml +++ b/.github/workflows/windows-ci.yml @@ -18,6 +18,7 @@ on: - master - release/** - github-mirror + - "*-feature" # Path filters for PRs need to go into the changes job diff --git a/build.psm1 b/build.psm1 index 21bf0f332ab..fe7eff67b2f 100644 --- a/build.psm1 +++ b/build.psm1 @@ -1973,7 +1973,9 @@ function Test-PSPesterResults function Start-PSxUnit { [CmdletBinding()]param( - [string] $xUnitTestResultsFile = "xUnitResults.xml" + [string] $xUnitTestResultsFile = "xUnitResults.xml", + [switch] $DebugLogging, + [string] $Filter ) # Add .NET CLI tools to PATH @@ -2031,9 +2033,28 @@ function Start-PSxUnit { # We run the xUnit tests sequentially to avoid race conditions caused by manipulating the config.json file. # xUnit tests run in parallel by default. To make them run sequentially, we need to define the 'xunit.runner.json' file. - dotnet test --configuration $Options.configuration --test-adapter-path:. "--logger:xunit;LogFilePath=$xUnitTestResultsFile" + $extraParams = @() + if($Filter) { + $extraParams += @( + '--filter' + $Filter + ) + } + + if($DebugLogging) { + $extraParams += @( + "--logger:console;verbosity=detailed" + ) + } else { + $extraParams += @( + "--logger:xunit;LogFilePath=$xUnitTestResultsFile" + ) + } + dotnet test @extraParams --configuration $Options.configuration --test-adapter-path:. - Publish-TestResults -Path $xUnitTestResultsFile -Type 'XUnit' -Title 'Xunit Sequential' + if(!$DebugLogging){ + Publish-TestResults -Path $xUnitTestResultsFile -Type 'XUnit' -Title 'Xunit Sequential' + } } finally { $env:DOTNET_ROOT = $originalDOTNET_ROOT diff --git a/src/Microsoft.PowerShell.ConsoleHost/host/msh/CommandLineParameterParser.cs b/src/Microsoft.PowerShell.ConsoleHost/host/msh/CommandLineParameterParser.cs index 50d2bd77d0f..bafaa9cabdf 100644 --- a/src/Microsoft.PowerShell.ConsoleHost/host/msh/CommandLineParameterParser.cs +++ b/src/Microsoft.PowerShell.ConsoleHost/host/msh/CommandLineParameterParser.cs @@ -203,35 +203,35 @@ internal static int MaxNameLength() [Flags] internal enum ParameterBitmap : long { - Command = 0x00000001, // -Command | -c - ConfigurationName = 0x00000002, // -ConfigurationName | -config - CustomPipeName = 0x00000004, // -CustomPipeName - EncodedCommand = 0x00000008, // -EncodedCommand | -e | -ec - EncodedArgument = 0x00000010, // -EncodedArgument - ExecutionPolicy = 0x00000020, // -ExecutionPolicy | -ex | -ep - File = 0x00000040, // -File | -f - Help = 0x00000080, // -Help, -?, /? - InputFormat = 0x00000100, // -InputFormat | -inp | -if - Interactive = 0x00000200, // -Interactive | -i - Login = 0x00000400, // -Login | -l - MTA = 0x00000800, // -MTA - NoExit = 0x00001000, // -NoExit | -noe - NoLogo = 0x00002000, // -NoLogo | -nol - NonInteractive = 0x00004000, // -NonInteractive | -noni - NoProfile = 0x00008000, // -NoProfile | -nop - OutputFormat = 0x00010000, // -OutputFormat | -o | -of - SettingsFile = 0x00020000, // -SettingsFile | -settings - SSHServerMode = 0x00040000, // -SSHServerMode | -sshs - SocketServerMode = 0x00080000, // -SocketServerMode | -sockets - ServerMode = 0x00100000, // -ServerMode | -server - NamedPipeServerMode = 0x00200000, // -NamedPipeServerMode | -namedpipes - STA = 0x00400000, // -STA - Version = 0x00800000, // -Version | -v - WindowStyle = 0x01000000, // -WindowStyle | -w - WorkingDirectory = 0x02000000, // -WorkingDirectory | -wd - ConfigurationFile = 0x04000000, // -ConfigurationFile - NoProfileLoadTime = 0x08000000, // -NoProfileLoadTime - CommandWithArgs = 0x10000000, // -CommandWithArgs | -cwa + Command = 0x0000000000000001, // -Command | -c + ConfigurationName = 0x0000000000000002, // -ConfigurationName | -config + CustomPipeName = 0x0000000000000004, // -CustomPipeName + EncodedCommand = 0x0000000000000008, // -EncodedCommand | -e | -ec + EncodedArgument = 0x0000000000000010, // -EncodedArgument + ExecutionPolicy = 0x0000000000000020, // -ExecutionPolicy | -ex | -ep + File = 0x0000000000000040, // -File | -f + Help = 0x0000000000000080, // -Help, -?, /? + InputFormat = 0x0000000000000100, // -InputFormat | -inp | -if + Interactive = 0x0000000000000200, // -Interactive | -i + Login = 0x0000000000000400, // -Login | -l + MTA = 0x0000000000000800, // -MTA + NoExit = 0x0000000000001000, // -NoExit | -noe + NoLogo = 0x0000000000002000, // -NoLogo | -nol + NonInteractive = 0x0000000000004000, // -NonInteractive | -noni + NoProfile = 0x0000000000008000, // -NoProfile | -nop + OutputFormat = 0x0000000000010000, // -OutputFormat | -o | -of + SettingsFile = 0x0000000000020000, // -SettingsFile | -settings + SSHServerMode = 0x0000000000040000, // -SSHServerMode | -sshs + SocketServerMode = 0x0000000000080000, // -SocketServerMode | -sockets + ServerMode = 0x0000000000100000, // -ServerMode | -server + NamedPipeServerMode = 0x0000000000200000, // -NamedPipeServerMode | -namedpipes + STA = 0x0000000000400000, // -STA + Version = 0x0000000000800000, // -Version | -v + WindowStyle = 0x0000000001000000, // -WindowStyle | -w + WorkingDirectory = 0x0000000002000000, // -WorkingDirectory | -wd + ConfigurationFile = 0x0000000004000000, // -ConfigurationFile + NoProfileLoadTime = 0x0000000008000000, // -NoProfileLoadTime + CommandWithArgs = 0x0000000010000000, // -CommandWithArgs | -cwa // Enum values for specified ExecutionPolicy EPUnrestricted = 0x0000000100000000, // ExecutionPolicy unrestricted EPRemoteSigned = 0x0000000200000000, // ExecutionPolicy remote signed @@ -241,6 +241,8 @@ internal enum ParameterBitmap : long EPBypass = 0x0000002000000000, // ExecutionPolicy bypass EPUndefined = 0x0000004000000000, // ExecutionPolicy undefined EPIncorrect = 0x0000008000000000, // ExecutionPolicy incorrect + // V2 Socket Server Mode + V2SocketServerMode = 0x0000100000000000, // -V2SocketServerMode | -v2so } internal ParameterBitmap ParametersUsed = 0; @@ -597,6 +599,33 @@ internal bool RemoveWorkingDirectoryTrailingCharacter return _removeWorkingDirectoryTrailingCharacter; } } + + internal DateTimeOffset? UTCTimestamp + { + get + { + AssertArgumentsParsed(); + return _utcTimestamp; + } + } + + internal string? Token + { + get + { + AssertArgumentsParsed(); + return _token; + } + } + + internal bool V2SocketServerMode + { + get + { + AssertArgumentsParsed(); + return _v2SocketServerMode; + } + } #endif #endregion Internal properties @@ -916,6 +945,14 @@ private void ParseHelper(string[] args) _showBanner = false; ParametersUsed |= ParameterBitmap.SocketServerMode; } +#if !UNIX + else if (MatchSwitch(switchKey, "v2socketservermode", "v2so")) + { + _v2SocketServerMode = true; + _showBanner = false; + ParametersUsed |= ParameterBitmap.V2SocketServerMode; + } +#endif else if (MatchSwitch(switchKey, "servermode", "s")) { _serverMode = true; @@ -1176,6 +1213,35 @@ private void ParseHelper(string[] args) { _removeWorkingDirectoryTrailingCharacter = true; } + else if (MatchSwitch(switchKey, "token", "to") ) + { + ++i; + if (i >= args.Length) + { + SetCommandLineError( + string.Format(CultureInfo.CurrentCulture, CommandLineParameterParserStrings.MissingMandatoryArgument, "-Token")); + break; + } + + _token = args[i]; + // Not adding anything to ParametersUsed, because it is required with V2 socket server mode + // So, we can assume it based on that bit + } + else if (MatchSwitch(switchKey, "utctimestamp", "utc") ) + { + ++i; + if (i >= args.Length) + { + SetCommandLineError( + string.Format(CultureInfo.CurrentCulture, CommandLineParameterParserStrings.MissingMandatoryArgument, "-UTCTimestamp")); + break; + } + + // Parse as iso8601UtcString + _utcTimestamp = DateTimeOffset.ParseExact(args[i], "yyyy-MM-dd'T'HH:mm:ssK", CultureInfo.InvariantCulture, DateTimeStyles.RoundtripKind); + // Not adding anything to ParametersUsed, because it is required with V2 socket server mode + // So, we can assume it based on that bit + } #endif else { @@ -1530,6 +1596,9 @@ private bool CollectArgs(string[] args, ref int i) } private bool _socketServerMode; +#if !UNIX + private bool _v2SocketServerMode; +#endif private bool _serverMode; private bool _namedPipeServerMode; private bool _sshServerMode; @@ -1562,6 +1631,10 @@ private bool CollectArgs(string[] args, ref int i) private string? _executionPolicy; private string? _settingsFile; private string? _workingDirectory; +#if !UNIX + private string? _token; + private DateTimeOffset? _utcTimestamp; +#endif #if !UNIX private ProcessWindowStyle? _windowStyle; diff --git a/src/Microsoft.PowerShell.ConsoleHost/host/msh/ConsoleHost.cs b/src/Microsoft.PowerShell.ConsoleHost/host/msh/ConsoleHost.cs index 8cc7ee00f57..aa5d532da2a 100644 --- a/src/Microsoft.PowerShell.ConsoleHost/host/msh/ConsoleHost.cs +++ b/src/Microsoft.PowerShell.ConsoleHost/host/msh/ConsoleHost.cs @@ -198,7 +198,26 @@ internal static int Start( } // Servermode parameter validation check. - if ((s_cpp.ServerMode && s_cpp.NamedPipeServerMode) || (s_cpp.ServerMode && s_cpp.SocketServerMode) || (s_cpp.NamedPipeServerMode && s_cpp.SocketServerMode)) + int serverModeCount = 0; + if (s_cpp.ServerMode) + { + serverModeCount++; + } + if (s_cpp.NamedPipeServerMode) + { + serverModeCount++; + } + if (s_cpp.SocketServerMode) + { + serverModeCount++; + } +#if !UNIX + if (s_cpp.V2SocketServerMode) + { + serverModeCount++; + } +#endif + if (serverModeCount > 1) { s_tracer.TraceError("Conflicting server mode parameters, parameters must be used exclusively."); s_theConsoleHost?.ui.WriteErrorLine(ConsoleHostStrings.ConflictingServerModeParameters); @@ -242,6 +261,34 @@ internal static int Start( configurationName: s_cpp.ConfigurationName); exitCode = 0; } +#if !UNIX + else if (s_cpp.V2SocketServerMode) + { + if (s_cpp.Token == null) + { + s_tracer.TraceError("Token is required for V2SocketServerMode."); + s_theConsoleHost?.ui.WriteErrorLine(string.Format(CultureInfo.CurrentCulture, ConsoleHostStrings.MissingMandatoryParameter, "-Token", "-V2SocketServerMode")); + return ExitCodeBadCommandLineParameter; + } + + if (s_cpp.UTCTimestamp == null) + { + s_tracer.TraceError("UTCTimestamp is required for V2SocketServerMode."); + s_theConsoleHost?.ui.WriteErrorLine(string.Format(CultureInfo.CurrentCulture, ConsoleHostStrings.MissingMandatoryParameter, "-UTCTimestamp", "-v2socketservermode")); + return ExitCodeBadCommandLineParameter; + } + + ApplicationInsightsTelemetry.SendPSCoreStartupTelemetry("V2SocketServerMode", s_cpp.ParametersUsedAsDouble); + ProfileOptimization.StartProfile("StartupProfileData-V2SocketServerMode"); + HyperVSocketMediator.Run( + initialCommand: s_cpp.InitialCommand, + configurationName: s_cpp.ConfigurationName, + token: s_cpp.Token, + tokenCreationTime: s_cpp.UTCTimestamp.Value + ); + exitCode = 0; + } +#endif else if (s_cpp.SocketServerMode) { ApplicationInsightsTelemetry.SendPSCoreStartupTelemetry("SocketServerMode", s_cpp.ParametersUsedAsDouble); diff --git a/src/Microsoft.PowerShell.ConsoleHost/resources/CommandLineParameterParserStrings.resx b/src/Microsoft.PowerShell.ConsoleHost/resources/CommandLineParameterParserStrings.resx index 34bb696c33c..33445ceebd2 100644 --- a/src/Microsoft.PowerShell.ConsoleHost/resources/CommandLineParameterParserStrings.resx +++ b/src/Microsoft.PowerShell.ConsoleHost/resources/CommandLineParameterParserStrings.resx @@ -225,4 +225,7 @@ Valid formats are: Invalid ExecutionPolicy value '{0}'. + + An argument is required to be supplied to the '{0}' parameter. + diff --git a/src/Microsoft.PowerShell.ConsoleHost/resources/ConsoleHostStrings.resx b/src/Microsoft.PowerShell.ConsoleHost/resources/ConsoleHostStrings.resx index ce124ec084c..9bc06e0d42f 100644 --- a/src/Microsoft.PowerShell.ConsoleHost/resources/ConsoleHostStrings.resx +++ b/src/Microsoft.PowerShell.ConsoleHost/resources/ConsoleHostStrings.resx @@ -182,4 +182,10 @@ The current session does not support debugging; execution will continue. Run as Administrator + + PushRunspace can only push a remote runspace. + + + The '{0}' parameter is mandatory and must be specified when using the '{1}' parameter. + diff --git a/src/System.Management.Automation/engine/remoting/common/RemoteSessionHyperVSocket.cs b/src/System.Management.Automation/engine/remoting/common/RemoteSessionHyperVSocket.cs index 7fae8118310..a9de12c3931 100644 --- a/src/System.Management.Automation/engine/remoting/common/RemoteSessionHyperVSocket.cs +++ b/src/System.Management.Automation/engine/remoting/common/RemoteSessionHyperVSocket.cs @@ -7,8 +7,10 @@ using System.Net.Sockets; using System.Text; using System.Threading; +using System.Buffers; using Dbg = System.Diagnostics.Debug; +using SMA = System.Management.Automation; namespace System.Management.Automation.Remoting { @@ -140,6 +142,10 @@ internal sealed class RemoteSessionHyperVSocketServer : IDisposable private readonly object _syncObject; private readonly PowerShellTraceSource _tracer = PowerShellTraceSourceFactory.GetTraceSource(); + // This is to prevent persistent replay attacks. + // it is not meant to ensure all replay attacks are impossible. + private const int MAX_TOKEN_LIFE_MINUTES = 10; + #endregion #region Properties @@ -175,64 +181,74 @@ internal sealed class RemoteSessionHyperVSocketServer : IDisposable public RemoteSessionHyperVSocketServer(bool LoopbackMode) { - // TODO: uncomment below code when .NET supports Hyper-V socket duplication - /* - NamedPipeClientStream clientPipeStream; - byte[] buffer = new byte[1000]; - int bytesRead; - */ _syncObject = new object(); Exception ex = null; try { - // TODO: uncomment below code when .NET supports Hyper-V socket duplication - /* - if (!LoopbackMode) - { - // - // Create named pipe client. - // - using (clientPipeStream = new NamedPipeClientStream(".", - "PS_VMSession", - PipeDirection.InOut, - PipeOptions.None, - TokenImpersonationLevel.None)) - { - // - // Connect to named pipe server. - // - clientPipeStream.Connect(10*1000); - - // - // Read LPWSAPROTOCOL_INFO. - // - bytesRead = clientPipeStream.Read(buffer, 0, 1000); - } - } + Guid serviceId = new Guid("a5201c21-2770-4c11-a68e-f182edb29220"); // HV_GUID_VM_SESSION_SERVICE_ID_2 + Guid loopbackId = new Guid("e0e16197-dd56-4a10-9195-5ee7a155a838"); // HV_GUID_LOOPBACK + Guid parentId = new Guid("a42e7cda-d03f-480c-9cc2-a4de20abb878"); // HV_GUID_PARENT + Guid vmId = LoopbackMode ? loopbackId : parentId; + HyperVSocketEndPoint endpoint = new HyperVSocketEndPoint(HyperVSocketEndPoint.AF_HYPERV, vmId, serviceId); + + Socket listenSocket = new Socket(endpoint.AddressFamily, SocketType.Stream, (System.Net.Sockets.ProtocolType)1); + listenSocket.Bind(endpoint); + + listenSocket.Listen(1); + HyperVSocket = listenSocket.Accept(); + + Stream = new NetworkStream(HyperVSocket, true); + + // Create reader/writer streams. + TextReader = new StreamReader(Stream); + TextWriter = new StreamWriter(Stream); + TextWriter.AutoFlush = true; // - // Create duplicate socket. + // listenSocket is not closed when it goes out of scope here. Sometimes it is + // closed later in this thread, while other times it is not closed at all. This will + // cause problem when we set up a second PowerShell Direct session. Let's + // explicitly close listenSocket here for safe. // - byte[] protocolInfo = new byte[bytesRead]; - Array.Copy(buffer, protocolInfo, bytesRead); + if (listenSocket != null) + { + try { listenSocket.Dispose(); } + catch (ObjectDisposedException) { } + } + } + catch (Exception e) + { + ex = e; + } - SocketInformation sockInfo = new SocketInformation(); - sockInfo.ProtocolInformation = protocolInfo; - sockInfo.Options = SocketInformationOptions.Connected; + if (ex != null) + { + Dbg.Fail("Unexpected error in RemoteSessionHyperVSocketServer."); - socket = new Socket(sockInfo); - if (socket == null) - { - Dbg.Assert(false, "Unexpected error in RemoteSessionHyperVSocketServer."); + // Unexpected error. + string errorMessage = !string.IsNullOrEmpty(ex.Message) ? ex.Message : string.Empty; + _tracer.WriteMessage("RemoteSessionHyperVSocketServer", "RemoteSessionHyperVSocketServer", Guid.Empty, + "Unexpected error in constructor: {0}", errorMessage); - tracer.WriteMessage("RemoteSessionHyperVSocketServer", "RemoteSessionHyperVSocketServer", Guid.Empty, - "Unexpected error in constructor: {0}", "socket duplication failure"); - } - */ + throw new PSInvalidOperationException( + PSRemotingErrorInvariants.FormatResourceString(RemotingErrorIdStrings.RemoteSessionHyperVSocketServerConstructorFailure), + ex, + nameof(PSRemotingErrorId.RemoteSessionHyperVSocketServerConstructorFailure), + ErrorCategory.InvalidOperation, + null); + } + } + + public RemoteSessionHyperVSocketServer(bool LoopbackMode, string token, DateTimeOffset tokenCreationTime) + { + _syncObject = new object(); - // TODO: remove below 6 lines of code when .NET supports Hyper-V socket duplication + Exception ex = null; + + try + { Guid serviceId = new Guid("a5201c21-2770-4c11-a68e-f182edb29220"); // HV_GUID_VM_SESSION_SERVICE_ID_2 HyperVSocketEndPoint endpoint = new HyperVSocketEndPoint(HyperVSocketEndPoint.AF_HYPERV, Guid.Empty, serviceId); @@ -242,6 +258,31 @@ public RemoteSessionHyperVSocketServer(bool LoopbackMode) listenSocket.Listen(1); HyperVSocket = listenSocket.Accept(); + TimeSpan timeout = TimeSpan.FromMinutes(MAX_TOKEN_LIFE_MINUTES); + DateTimeOffset timeoutExpiry = tokenCreationTime.Add(timeout); + DateTimeOffset now = DateTimeOffset.UtcNow; + + // Calculate remaining time and create cancellation token + TimeSpan remainingTime = timeoutExpiry - now; + + // Check if the token has already expired + if (remainingTime <= TimeSpan.Zero) + { + throw new PSDirectException( + PSRemotingErrorInvariants.FormatResourceString(RemotingErrorIdStrings.InvalidCredential, "Token has expired")); + } + + // Set socket timeout for receive operations to prevent indefinite blocking + int timeoutMs = (int)remainingTime.TotalMilliseconds; + HyperVSocket.ReceiveTimeout = timeoutMs; + HyperVSocket.SendTimeout = timeoutMs; + + // Create a cancellation token that will be cancelled when the timeout expires + using var cancellationTokenSource = new CancellationTokenSource(remainingTime); + CancellationToken cancellationToken = cancellationTokenSource.Token; + + ValidateToken(HyperVSocket, token, cancellationToken); + Stream = new NetworkStream(HyperVSocket, true); // Create reader/writer streams. @@ -257,8 +298,13 @@ public RemoteSessionHyperVSocketServer(bool LoopbackMode) // if (listenSocket != null) { - try { listenSocket.Dispose(); } - catch (ObjectDisposedException) { } + try + { + listenSocket.Dispose(); + } + catch (ObjectDisposedException) + { + } } } catch (Exception e) @@ -272,8 +318,12 @@ public RemoteSessionHyperVSocketServer(bool LoopbackMode) // Unexpected error. string errorMessage = !string.IsNullOrEmpty(ex.Message) ? ex.Message : string.Empty; - _tracer.WriteMessage("RemoteSessionHyperVSocketServer", "RemoteSessionHyperVSocketServer", Guid.Empty, - "Unexpected error in constructor: {0}", errorMessage); + _tracer.WriteMessage( + "RemoteSessionHyperVSocketServer", + "RemoteSessionHyperVSocketServer", + Guid.Empty, + "Unexpected error in constructor: {0}", + errorMessage); throw new PSInvalidOperationException( PSRemotingErrorInvariants.FormatResourceString(RemotingErrorIdStrings.RemoteSessionHyperVSocketServerConstructorFailure), @@ -283,7 +333,6 @@ public RemoteSessionHyperVSocketServer(bool LoopbackMode) null); } } - #endregion #region IDisposable @@ -333,6 +382,79 @@ public void Dispose() } #endregion + + /// + /// Validates the token received from the client over the HyperVSocket. + /// Throws PSDirectException if the token is invalid or not received in time. + /// + /// The connected HyperVSocket. + /// The expected token string. + /// Cancellation token for timeout handling. + internal static void ValidateToken(Socket socket, string token, CancellationToken cancellationToken = default) + { + // Check for cancellation before starting validation + cancellationToken.ThrowIfCancellationRequested(); + + // We should move to this pattern and + // in the tests I found I needed to get a bigger buffer than the token length + // and test length of the received data similar to this pattern. + string responseString = RemoteSessionHyperVSocketClient.ReceiveResponse(socket, RemoteSessionHyperVSocketClient.VERSION_REQUEST.Length + 4); + if (string.IsNullOrEmpty(responseString) || responseString.Length != RemoteSessionHyperVSocketClient.VERSION_REQUEST.Length) + { + socket.Send("FAIL"u8); + throw new PSDirectException( + PSRemotingErrorInvariants.FormatResourceString(RemotingErrorIdStrings.HyperVInvalidResponse, "Client", "Version Request: " + responseString)); + } + + cancellationToken.ThrowIfCancellationRequested(); + + socket.Send(Encoding.UTF8.GetBytes(RemoteSessionHyperVSocketClient.CLIENT_VERSION)); + responseString = RemoteSessionHyperVSocketClient.ReceiveResponse(socket, RemoteSessionHyperVSocketClient.CLIENT_VERSION.Length + 4); + + // In the future we may need to handle different versions, differently. + // For now, we are just checking that we exchanged versions correctly. + if (string.IsNullOrEmpty(responseString) || !responseString.StartsWith(RemoteSessionHyperVSocketClient.VERSION_PREFIX, StringComparison.Ordinal)) + { + socket.Send("FAIL"u8); + throw new PSDirectException( + PSRemotingErrorInvariants.FormatResourceString(RemotingErrorIdStrings.HyperVInvalidResponse, "Client", "Version Response: " + responseString)); + } + + cancellationToken.ThrowIfCancellationRequested(); + + socket.Send("PASS"u8); + + // The client should send the token in the format TOKEN + // the token should be up to 256 bits, which is less than 50 characters. + // I'll double that to 100 characters to be safe, plus the "TOKEN " prefix. + // So we expect a response of length 6 + 100 = 106 characters. + responseString = RemoteSessionHyperVSocketClient.ReceiveResponse(socket, 110); + + cancellationToken.ThrowIfCancellationRequested(); + + if (string.IsNullOrEmpty(responseString) || !responseString.StartsWith("TOKEN ", StringComparison.Ordinal)) + { + socket.Send("FAIL"u8); + // If the response is not in the expected format, we throw an exception. + // This is a failure to authenticate the client. + // don't send this response for risk of information disclosure. + throw new PSDirectException( + PSRemotingErrorInvariants.FormatResourceString(RemotingErrorIdStrings.HyperVInvalidResponse, "Client", "Token Response")); + } + + // Extract the token from the response. + string responseToken = responseString.Substring(6).Trim(); + + if (!string.Equals(responseToken, token, StringComparison.Ordinal)) + { + socket.Send("FAIL"u8); + throw new PSDirectException( + PSRemotingErrorInvariants.FormatResourceString(RemotingErrorIdStrings.InvalidCredential)); + } + + // Acknowledge the token is valid with "PASS". + socket.Send("PASS"u8); + } } internal sealed class RemoteSessionHyperVSocketClient : IDisposable @@ -340,7 +462,15 @@ internal sealed class RemoteSessionHyperVSocketClient : IDisposable #region Members private readonly object _syncObject; - private readonly PowerShellTraceSource _tracer = PowerShellTraceSourceFactory.GetTraceSource(); + + #region tracer + /// + /// An instance of the PSTraceSource class used for trace output. + /// + [SMA.TraceSource("RemoteSessionHyperVSocketClient", "Class that has PowerShell Direct Client implementation")] + private static readonly PSTraceSource s_tracer = PSTraceSource.GetTracer("RemoteSessionHyperVSocketClient", "Class that has PowerShell Direct Client implementation"); + + #endregion tracer private static readonly ManualResetEvent s_connectDone = new ManualResetEvent(false); @@ -354,6 +484,14 @@ internal sealed class RemoteSessionHyperVSocketClient : IDisposable #endregion + #region version constants + + internal const string VERSION_REQUEST = "VERSION"; + internal const string CLIENT_VERSION = "VERSION_2"; + internal const string VERSION_PREFIX = "VERSION_"; + + #endregion + #region Properties /// @@ -364,7 +502,7 @@ internal sealed class RemoteSessionHyperVSocketClient : IDisposable /// /// Returns the Hyper-V socket object. /// - public Socket HyperVSocket { get; } + public Socket HyperVSocket { get; private set; } /// /// Returns the network stream object. @@ -381,6 +519,37 @@ internal sealed class RemoteSessionHyperVSocketClient : IDisposable /// public StreamWriter TextWriter { get; private set; } + /// + /// True if the client is a Hyper-V container. + /// + public bool IsContainer { get; } + + /// + /// True if the client is using backwards compatible mode. + /// This is used to determine if the client should use + /// the backwards compatible or not. + /// In modern mode, the vmicvmsession service will + /// hand off the socket to the PowerShell process + /// inside the VM automatically. + /// In backwards compatible mode, the vmicvmsession + /// service create a new socket to the PowerShell process + /// inside the VM. + /// + public bool UseBackwardsCompatibleMode { get; private set; } + + /// + /// The authentication token used for the session. + /// This token is provided by the broker and provided to the server to authenticate the server session. + /// This protocol uses two connections: + /// 1. The first is to the broker or vmicvmsession service to exchange credentials and configuration. + /// The broker will respond with an authentication token. The broker also launches a PowerShell + /// server process with the authentication token. + /// 2. The second is to the server process, that was launched by the broker, + /// inside the VM, which uses the authentication token to verify that the client is the same client + /// that connected to the broker. + /// + public string AuthenticationToken { get; private set; } + /// /// Returns true if object is currently disposed. /// @@ -393,7 +562,9 @@ internal sealed class RemoteSessionHyperVSocketClient : IDisposable internal RemoteSessionHyperVSocketClient( Guid vmId, bool isFirstConnection, - bool isContainer = false) + bool useBackwardsCompatibleMode = false, + bool isContainer = false, + string authenticationToken = null) { Guid serviceId; @@ -412,28 +583,16 @@ internal RemoteSessionHyperVSocketClient( EndPoint = new HyperVSocketEndPoint(HyperVSocketEndPoint.AF_HYPERV, vmId, serviceId); - HyperVSocket = new Socket(EndPoint.AddressFamily, SocketType.Stream, (System.Net.Sockets.ProtocolType)1); + IsContainer = isContainer; - // - // We need to call SetSocketOption() in order to set up Hyper-V socket connection between container host and Hyper-V container. - // Here is the scenario: the Hyper-V container is inside a utility vm, which is inside the container host - // - if (isContainer) - { - var value = new byte[sizeof(uint)]; - value[0] = 1; + UseBackwardsCompatibleMode = useBackwardsCompatibleMode; - try - { - HyperVSocket.SetSocketOption((System.Net.Sockets.SocketOptionLevel)HV_PROTOCOL_RAW, - (System.Net.Sockets.SocketOptionName)HVSOCKET_CONTAINER_PASSTHRU, - (byte[])value); - } - catch - { - throw new PSDirectException( - PSRemotingErrorInvariants.FormatResourceString(RemotingErrorIdStrings.RemoteSessionHyperVSocketClientConstructorSetSocketOptionFailure)); - } + if (!isFirstConnection && !useBackwardsCompatibleMode && !string.IsNullOrEmpty(authenticationToken)) + { + // If this is not the first connection and we are using backwards compatible mode, + // we should not set the authentication token here. + // The authentication token will be set during the Connect method. + AuthenticationToken = authenticationToken; } } @@ -489,6 +648,81 @@ public void Dispose() #region Public Methods + private void ShutdownSocket() + { + if (HyperVSocket != null) + { + // Ensure the socket is disposed properly. + try + { + s_tracer.WriteLine("ShutdownSocket: Disposing of the HyperVSocket."); + HyperVSocket.Dispose(); + } + catch (Exception ex) + { + s_tracer.WriteLine("ShutdownSocket: Exception while disposing the socket: {0}", ex.Message); + } + } + + // Dispose of the existing stream if it exists. + if (Stream != null) + { + try + { + Stream.Dispose(); + } + catch (Exception ex) + { + s_tracer.WriteLine("ShutdownSocket: Exception while disposing the stream: {0}", ex.Message); + } + } + } + + /// + /// Recreates the HyperVSocket and connects it to the endpoint, updating the Stream if successful. + /// + private bool ConnectSocket() + { + HyperVSocket = new Socket(EndPoint.AddressFamily, SocketType.Stream, (System.Net.Sockets.ProtocolType)1); + + // + // We need to call SetSocketOption() in order to set up Hyper-V socket connection between container host and Hyper-V container. + // Here is the scenario: the Hyper-V container is inside a utility vm, which is inside the container host + // + if (IsContainer) + { + var value = new byte[sizeof(uint)]; + value[0] = 1; + + try + { + HyperVSocket.SetSocketOption( + (System.Net.Sockets.SocketOptionLevel)HV_PROTOCOL_RAW, + (System.Net.Sockets.SocketOptionName)HVSOCKET_CONTAINER_PASSTHRU, + value); + } + catch + { + throw new PSDirectException( + PSRemotingErrorInvariants.FormatResourceString(RemotingErrorIdStrings.RemoteSessionHyperVSocketClientConstructorSetSocketOptionFailure)); + } + } + + s_tracer.WriteLine("Connect: Client connecting, to {0}; isContainer: {1}.", EndPoint.ServiceId.ToString(), IsContainer); + HyperVSocket.Connect(EndPoint); + + // Check if the socket is connected. + // If it is connected, create a NetworkStream. + if (HyperVSocket.Connected) + { + s_tracer.WriteLine("Connect: Client connected, to {0}; isContainer: {1}.", EndPoint.ServiceId.ToString(), IsContainer); + Stream = new NetworkStream(HyperVSocket, true); + return true; + } + + return false; + } + /// /// Connect to Hyper-V socket server. This is a blocking call until a /// connection occurs or the timeout time has elapsed. @@ -516,100 +750,51 @@ public bool Connect( } } - HyperVSocket.Connect(EndPoint); - - if (HyperVSocket.Connected) + if (ConnectSocket()) { - _tracer.WriteMessage("RemoteSessionHyperVSocketClient", "Connect", Guid.Empty, - "Client connected."); - - Stream = new NetworkStream(HyperVSocket, true); - if (isFirstConnection) { - if (string.IsNullOrEmpty(networkCredential.Domain)) + var exchangeResult = ExchangeCredentialsAndConfiguration(networkCredential, configurationName, HyperVSocket, this.UseBackwardsCompatibleMode); + if (!exchangeResult.success) { - networkCredential.Domain = "localhost"; - } + // We will not block here for a container because a container does not have a broker. + if (IsRequirePsDirectAuthenticationEnabled(@"SOFTWARE\\Microsoft\\PowerShell", Microsoft.Win32.RegistryHive.LocalMachine)) + { + s_tracer.WriteLine("ExchangeCredentialsAndConfiguration: RequirePsDirectAuthentication is enabled, requiring latest transport version."); + throw new PSDirectException( + PSRemotingErrorInvariants.FormatResourceString(RemotingErrorIdStrings.HyperVNegotiationFailed)); + } - bool emptyPassword = string.IsNullOrEmpty(networkCredential.Password); - bool emptyConfiguration = string.IsNullOrEmpty(configurationName); - - byte[] domain = Encoding.Unicode.GetBytes(networkCredential.Domain); - byte[] userName = Encoding.Unicode.GetBytes(networkCredential.UserName); - byte[] password = Encoding.Unicode.GetBytes(networkCredential.Password); - byte[] response = new byte[4]; // either "PASS" or "FAIL" - string responseString; - - // - // Send credential to VM so that PowerShell process inside VM can be - // created under the correct security context. - // - HyperVSocket.Send(domain); - HyperVSocket.Receive(response); - - HyperVSocket.Send(userName); - HyperVSocket.Receive(response); - - // - // We cannot simply send password because if it is empty, - // the vmicvmsession service in VM will block in recv method. - // - if (emptyPassword) - { - HyperVSocket.Send("EMPTYPW"u8); - HyperVSocket.Receive(response); - responseString = Encoding.ASCII.GetString(response); - } - else - { - HyperVSocket.Send("NONEMPTYPW"u8); - HyperVSocket.Receive(response); + this.UseBackwardsCompatibleMode = true; + s_tracer.WriteLine("ExchangeCredentialsAndConfiguration: Using backwards compatible mode."); - HyperVSocket.Send(password); - HyperVSocket.Receive(response); - responseString = Encoding.ASCII.GetString(response); + // If the first connection fails in modern mode, fall back to backwards compatible mode. + ShutdownSocket(); // will terminate the broker + ConnectSocket(); // restart the broker + exchangeResult = ExchangeCredentialsAndConfiguration(networkCredential, configurationName, HyperVSocket, this.UseBackwardsCompatibleMode); + if (!exchangeResult.success) + { + s_tracer.WriteLine("ExchangeCredentialsAndConfiguration: Failed to exchange credentials and configuration in backwards compatible mode."); + throw new PSDirectException( + PSRemotingErrorInvariants.FormatResourceString(RemotingErrorIdStrings.HyperVInvalidResponse, "Broker", "Credential")); + } } - - // - // There are 3 cases for the responseString received above. - // - "FAIL": credential is invalid - // - "PASS": credential is valid, but PowerShell Direct in VM does not support configuration (Server 2016 TP4 and before) - // - "CONF": credential is valid, and PowerShell Direct in VM supports configuration (Server 2016 TP5 and later) - // - - // - // Credential is invalid. - // - if (string.Equals(responseString, "FAIL", StringComparison.Ordinal)) + else { - HyperVSocket.Send(response); - - throw new PSDirectException( - PSRemotingErrorInvariants.FormatResourceString(RemotingErrorIdStrings.InvalidCredential)); + this.AuthenticationToken = exchangeResult.authenticationToken; } + } - // - // If PowerShell Direct in VM supports configuration, send configuration name. - // - if (string.Equals(responseString, "CONF", StringComparison.Ordinal)) + if (!isFirstConnection) + { + if (!this.UseBackwardsCompatibleMode) { - if (emptyConfiguration) - { - HyperVSocket.Send("EMPTYCF"u8); - } - else - { - HyperVSocket.Send("NONEMPTYCF"u8); - HyperVSocket.Receive(response); - - byte[] configName = Encoding.Unicode.GetBytes(configurationName); - HyperVSocket.Send(configName); - } + s_tracer.WriteLine("Connect-Server: Performing transport version and token exchange for Hyper-V socket. isFirstConnection: {0}, UseBackwardsCompatibleMode: {1}", isFirstConnection, this.UseBackwardsCompatibleMode); + RemoteSessionHyperVSocketClient.PerformTransportVersionAndTokenExchange(HyperVSocket, this.AuthenticationToken); } else { - HyperVSocket.Send(response); + s_tracer.WriteLine("Connect-Server: Skipping transport version and token exchange for backwards compatible mode."); } } @@ -621,8 +806,7 @@ public bool Connect( } else { - _tracer.WriteMessage("RemoteSessionHyperVSocketClient", "Connect", Guid.Empty, - "Client unable to connect."); + s_tracer.WriteLine("Connect: Client unable to connect."); result = false; } @@ -630,12 +814,318 @@ public bool Connect( return result; } + /// + /// Performs the transport version and token exchange sequence for the Hyper-V socket connection. + /// Throws PSDirectException on failure. + /// + /// The socket to use for communication. + /// The authentication token to send. + public static void PerformTransportVersionAndTokenExchange(Socket socket, string authenticationToken) + { + if (string.IsNullOrEmpty(authenticationToken)) + { + s_tracer.WriteLine("PerformTransportVersionAndTokenExchange: Authentication token is null or empty. Aborting transport version and token exchange."); + throw new PSDirectException( + PSRemotingErrorInvariants.FormatResourceString(RemotingErrorIdStrings.InvalidCredential)); + } + + socket.Send(Encoding.UTF8.GetBytes(VERSION_REQUEST)); + string responseStr = ReceiveResponse(socket, 16); + + // Check if the response starts with the expected version prefix. + // We will rely on the broker to determine if the two can communicate. + // At least, for now. + if (!responseStr.StartsWith(VERSION_PREFIX, StringComparison.Ordinal)) + { + s_tracer.WriteLine("PerformTransportVersionAndTokenExchange: Server responded with an invalid response of {0}. Notifying the transport manager to downgrade if allowed.", responseStr); + throw new PSDirectException( + PSRemotingErrorInvariants.FormatResourceString(RemotingErrorIdStrings.HyperVInvalidResponse, "Server", "TransportVersion")); + } + + socket.Send(Encoding.UTF8.GetBytes(CLIENT_VERSION)); + string response = ReceiveResponse(socket, 4); // either "PASS" or "FAIL" + + if (!string.Equals(response, "PASS", StringComparison.Ordinal)) + { + s_tracer.WriteLine( + "PerformTransportVersionAndTokenExchange: Transport version negotiation with server failed. Response: {0}", response); + throw new PSDirectException( + PSRemotingErrorInvariants.FormatResourceString(RemotingErrorIdStrings.HyperVInvalidResponse, "Server", "TransportVersion")); + } + + byte[] tokenBytes = Encoding.UTF8.GetBytes("TOKEN " + authenticationToken); + socket.Send(tokenBytes); + + // This is the opportunity for the server to tell the client to go away. + string tokenResponse = ReceiveResponse(socket, 256); // either "PASS" or "FAIL", but get a little more buffer to allow for better error in the future + if (!string.Equals(tokenResponse, "PASS", StringComparison.Ordinal)) + { + s_tracer.WriteLine( + "PerformTransportVersionAndTokenExchange: Server Authentication Token exchange failed. Response: {0}", tokenResponse); + throw new PSDirectException( + PSRemotingErrorInvariants.FormatResourceString(RemotingErrorIdStrings.InvalidCredential)); + } + } + + /// + /// Checks if the registry key RequirePsDirectAuthentication is set to 1. + /// Returns true if fallback should be aborted. + /// Uses the 64-bit registry view on 64-bit systems to ensure consistent behavior regardless of process architecture. + /// On 32-bit systems, uses the default registry view since there is no WOW64 redirection. + /// + internal static bool IsRequirePsDirectAuthenticationEnabled(string keyPath, Microsoft.Win32.RegistryHive registryHive) + { + const string regValueName = "RequirePsDirectAuthentication"; + + try + { + Microsoft.Win32.RegistryView registryView = Environment.Is64BitOperatingSystem + ? Microsoft.Win32.RegistryView.Registry64 + : Microsoft.Win32.RegistryView.Default; + + using (Microsoft.Win32.RegistryKey baseKey = Microsoft.Win32.RegistryKey.OpenBaseKey( + registryHive, + registryView)) + { + using (Microsoft.Win32.RegistryKey key = baseKey.OpenSubKey(keyPath)) + { + if (key != null) + { + var value = key.GetValue(regValueName); + if (value is int intValue && intValue != 0) + { + return true; + } + } + + return false; + } + } + } + catch (Exception regEx) + { + s_tracer.WriteLine("IsRequirePsDirectAuthenticationEnabled: Exception while checking registry key: {0}", regEx.Message); + return false; // If we cannot read the registry, assume the feature is not enabled. + } + } + + /// + /// Handles credential and configuration exchange with the VM for the first connection. + /// + public static (bool success, string authenticationToken) ExchangeCredentialsAndConfiguration(NetworkCredential networkCredential, string configurationName, Socket HyperVSocket, bool useBackwardsCompatibleMode) + { + // Encoding for the Hyper-V socket communication + // To send the domain, username, password, and configuration name, use UTF-16 (Encoding.Unicode) + // All other sends use UTF-8 (Encoding.UTF8) + // Receiving uses ASCII encoding + // NOT CONFUSING AT ALL + + if (!useBackwardsCompatibleMode) + { + HyperVSocket.Send(Encoding.UTF8.GetBytes(VERSION_REQUEST)); + // vmicvmsession service in VM will respond with "VERSION_2" or newer + // Version 1 protocol will respond with "PASS" or "FAIL" + // Receive the response and check for VERSION_2 or newer + string responseStr = ReceiveResponse(HyperVSocket, 16); + if (!responseStr.StartsWith(VERSION_PREFIX, StringComparison.Ordinal)) + { + s_tracer.WriteLine("When asking for version the server responded with an invalid response of {0}.", responseStr); + s_tracer.WriteLine("Session is invalid, continuing session with a fake user to close the session with the broker for stability."); + // If not the new protocol, finish the conversation + // Send a fake user + // Use ? <> that are illegal in user names so no one can create the user + string probeUserName = "?"; // must be less than or equal to 20 characters for Windows Server 2016 + s_tracer.WriteLine("probeUserName (static): length: {0}", probeUserName.Length); + SendUserData(probeUserName, HyperVSocket); + responseStr = ReceiveResponse(HyperVSocket, 4); // either "PASS" or "FAIL" + s_tracer.WriteLine("When sending user {0}.", responseStr); + + // Send that the password is empty + HyperVSocket.Send("EMPTYPW"u8); + responseStr = ReceiveResponse(HyperVSocket, 4); // either "CONF", "PASS" or "FAIL" + s_tracer.WriteLine("When sending EMPTYPW: {0}.", responseStr); // server responds with FAIL so we respond with FAIL and the conversation is done + HyperVSocket.Send("FAIL"u8); + + s_tracer.WriteLine("Notifying the transport manager to downgrade if allowed."); + // end new code + return (false, null); + } + + HyperVSocket.Send(Encoding.UTF8.GetBytes(CLIENT_VERSION)); + ReceiveResponse(HyperVSocket, 4); // either "PASS" or "FAIL" + } + + if (string.IsNullOrEmpty(networkCredential.Domain)) + { + networkCredential.Domain = "localhost"; + } + + System.Security.SecureString securePassword = networkCredential.SecurePassword; + int passwordLength = securePassword.Length; + bool emptyPassword = (passwordLength <= 0); + bool emptyConfiguration = string.IsNullOrEmpty(configurationName); + + string responseString; + + // Send credential to VM so that PowerShell process inside VM can be + // created under the correct security context. + SendUserData(networkCredential.Domain, HyperVSocket); + ReceiveResponse(HyperVSocket, 4); // only "PASS" is expected + + SendUserData(networkCredential.UserName, HyperVSocket); + ReceiveResponse(HyperVSocket, 4); // only "PASS" is expected + + // We cannot simply send password because if it is empty, + // the vmicvmsession service in VM will block in recv method. + if (emptyPassword) + { + HyperVSocket.Send("EMPTYPW"u8); + responseString = ReceiveResponse(HyperVSocket, 4); // either "CONF", "PASS" or "FAIL" (note, "PASS" is not used in VERSION_2 or newer mode) + } + else + { + HyperVSocket.Send("NONEMPTYPW"u8); + ReceiveResponse(HyperVSocket, 4); // only "PASS" is expected + + // Get the password bytes from the SecureString, send them, and then zero out the byte array. + byte[] passwordBytes = Microsoft.PowerShell.SecureStringHelper.GetData(securePassword); + try + { + HyperVSocket.Send(passwordBytes); + } + finally + { + // Zero out the byte array for security + Array.Clear(passwordBytes); + } + + responseString = ReceiveResponse(HyperVSocket, 4); // either "CONF", "PASS" or "FAIL" (note, "PASS" is not used in VERSION_2 or newer mode) + } + + // Check for invalid response from server + if (!string.Equals(responseString, "FAIL", StringComparison.Ordinal) && + !string.Equals(responseString, "PASS", StringComparison.Ordinal) && + !string.Equals(responseString, "CONF", StringComparison.Ordinal)) + { + s_tracer.WriteLine("ExchangeCredentialsAndConfiguration: Server responded with an invalid response of {0} for credentials.", responseString); + throw new PSDirectException( + PSRemotingErrorInvariants.FormatResourceString(RemotingErrorIdStrings.HyperVInvalidResponse, "Broker", "Credential")); + } + + // Credential is invalid. + if (string.Equals(responseString, "FAIL", StringComparison.Ordinal)) + { + HyperVSocket.Send("FAIL"u8); + // should we be doing this? Disabling the test for now + // HyperVSocket.Shutdown(SocketShutdown.Both); + s_tracer.WriteLine("ExchangeCredentialsAndConfiguration: Server responded with FAIL for credentials."); + throw new PSDirectException( + PSRemotingErrorInvariants.FormatResourceString(RemotingErrorIdStrings.InvalidCredential)); + } + + // If PowerShell Direct in VM supports configuration, send configuration name. + if (string.Equals(responseString, "CONF", StringComparison.Ordinal)) + { + if (emptyConfiguration) + { + HyperVSocket.Send("EMPTYCF"u8); + } + else + { + HyperVSocket.Send("NONEMPTYCF"u8); + ReceiveResponse(HyperVSocket, 4); // only "PASS" is expected + + SendUserData(configurationName, HyperVSocket); + } + } + else + { + HyperVSocket.Send("PASS"u8); + } + + if (!useBackwardsCompatibleMode) + { + // Receive the token from the server + // Getting 1024 bytes because it is well above the expected token size + // The expected size at the time of writing this would be about 50 based64 characters, + // plus the 6 characters for the "TOKEN " prefix. + // The 50 character size is designed to last 10 years of cryptographic changes. + // Since the broker completely controls the cryptographic portion here, + // allowing a significant larger size, allows the broker to make almost arbitrary changes, + // without breaking the client. + string token = ReceiveResponse(HyperVSocket, 1024); // either "PASS" or "FAIL" + if (token == null || !token.StartsWith("TOKEN ", StringComparison.Ordinal)) + { + s_tracer.WriteLine("ExchangeCredentialsAndConfiguration: Server did not respond with a valid token. Response: {0}", token); + throw new PSDirectException( + PSRemotingErrorInvariants.FormatResourceString(RemotingErrorIdStrings.HyperVInvalidResponse, "Broker", "Token " + token)); + } + + token = token.Substring(6); // remove "TOKEN " prefix + + HyperVSocket.Send("PASS"u8); // acknowledge the token + return (true, token); + } + + return (true, null); + } + public void Close() { Stream.Dispose(); HyperVSocket.Dispose(); } + /// + /// Receives a response from the socket and decodes it. + /// + /// The socket to receive from. + /// The size of the buffer to use for receiving data. + /// The decoded response string. + internal static string ReceiveResponse(Socket socket, int bufferSize) + { + System.Buffers.ArrayPool pool = System.Buffers.ArrayPool.Shared; + byte[] responseBuffer = pool.Rent(bufferSize); + int bytesReceived = 0; + try + { + bytesReceived = socket.Receive(responseBuffer); + if (bytesReceived == 0) + { + return null; + } + + string response = Encoding.ASCII.GetString(responseBuffer, 0, bytesReceived); + + // Handle null terminators and log if found + if (response.EndsWith('\0')) + { + int originalLength = response.Length; + response = response.TrimEnd('\0'); + // Cannot log actual response, because we don't know if it is sensitive + s_tracer.WriteLine( + "ReceiveResponse: Removed null terminator(s). Original length: {0}, New length: {1}", + originalLength, + response.Length); + } + + return response; + } + finally + { + pool.Return(responseBuffer); + } + } + + /// + /// Sends user data (domain, username, etc.) over the HyperVSocket using Unicode encoding. + /// + private static void SendUserData(string data, Socket socket) + { + // this encodes the data in UTF-16 (Unicode) + byte[] buffer = Encoding.Unicode.GetBytes(data); + socket.Send(buffer); + } #endregion } } diff --git a/src/System.Management.Automation/engine/remoting/fanin/OutOfProcTransportManager.cs b/src/System.Management.Automation/engine/remoting/fanin/OutOfProcTransportManager.cs index 96a8b833885..d9532c8691a 100644 --- a/src/System.Management.Automation/engine/remoting/fanin/OutOfProcTransportManager.cs +++ b/src/System.Management.Automation/engine/remoting/fanin/OutOfProcTransportManager.cs @@ -1014,7 +1014,7 @@ internal void OnCloseTimeOutTimerElapsed(object source) } #endregion - + #region Protected Methods /// @@ -1544,8 +1544,9 @@ internal VMHyperVSocketClientSessionTransportManager( /// public override void CreateAsync() { - _client = new RemoteSessionHyperVSocketClient(_vmGuid, true); - if (!_client.Connect(_networkCredential, _configurationName, true)) + // isFirstConnection: true - specifies to use VM_SESSION_SERVICE_ID socket. + _client = new RemoteSessionHyperVSocketClient(_vmGuid, useBackwardsCompatibleMode: false, isFirstConnection: true); + if (!_client.Connect(_networkCredential, _configurationName, isFirstConnection: true)) { _client.Dispose(); throw new PSInvalidOperationException( @@ -1555,11 +1556,14 @@ public override void CreateAsync() ErrorCategory.InvalidOperation, null); } + bool useBackwardsCompatibleMode = _client.UseBackwardsCompatibleMode; + string token = _client.AuthenticationToken; - // TODO: remove below 3 lines when Hyper-V socket duplication is supported in .NET framework. _client.Dispose(); - _client = new RemoteSessionHyperVSocketClient(_vmGuid, false); - if (!_client.Connect(_networkCredential, _configurationName, false)) + + // isFirstConnection: false - specifies to use the SESSION_SERVICE_ID_2 socket. + _client = new RemoteSessionHyperVSocketClient(_vmGuid, useBackwardsCompatibleMode: useBackwardsCompatibleMode, isFirstConnection: false, authenticationToken: token); + if (!_client.Connect(_networkCredential, _configurationName, isFirstConnection: false)) { _client.Dispose(); throw new PSInvalidOperationException( @@ -1617,7 +1621,9 @@ internal ContainerHyperVSocketClientSessionTransportManager( /// public override void CreateAsync() { - _client = new RemoteSessionHyperVSocketClient(_targetGuid, false, true); + // Container scenario is not working. + // When we fix it we need to setup the token in ContainerConnectionInfo and use it here. + _client = new RemoteSessionHyperVSocketClient(_targetGuid, isFirstConnection: false, useBackwardsCompatibleMode: false, isContainer: true); if (!_client.Connect(null, string.Empty, false)) { _client.Dispose(); @@ -1716,7 +1722,7 @@ public override void CreateAsync() // Start connection timeout timer if requested. // Timer callback occurs only once after timeout time. _connectionTimer = new Timer( - callback: (_) => + callback: (_) => { if (_connectionEstablished) { @@ -2505,7 +2511,7 @@ internal OutOfProcessServerSessionTransportManager(OutOfProcessTextWriter outWri _stdErrWriter = errWriter; _cmdTransportManagers = new Dictionary(); - this.WSManTransportErrorOccured += (object sender, TransportErrorOccuredEventArgs e) => + this.WSManTransportErrorOccured += (object sender, TransportErrorOccuredEventArgs e) => { string msg = e.Exception.TransportMessage ?? e.Exception.InnerException?.Message ?? string.Empty; _stdErrWriter.WriteLine(StringUtil.Format(RemotingErrorIdStrings.RemoteTransportError, msg)); diff --git a/src/System.Management.Automation/engine/remoting/server/OutOfProcServerMediator.cs b/src/System.Management.Automation/engine/remoting/server/OutOfProcServerMediator.cs index 14b0240858b..6c794e21b24 100644 --- a/src/System.Management.Automation/engine/remoting/server/OutOfProcServerMediator.cs +++ b/src/System.Management.Automation/engine/remoting/server/OutOfProcServerMediator.cs @@ -635,6 +635,16 @@ private HyperVSocketMediator() originalStdErr = new HyperVSocketErrorTextWriter(_hypervSocketServer.TextWriter); } + private HyperVSocketMediator(string token, + DateTimeOffset tokenCreationTime) + : base(false) + { + _hypervSocketServer = new RemoteSessionHyperVSocketServer(false, token: token, tokenCreationTime: tokenCreationTime); + + originalStdIn = _hypervSocketServer.TextReader; + originalStdOut = new OutOfProcessTextWriter(_hypervSocketServer.TextWriter); + originalStdErr = new HyperVSocketErrorTextWriter(_hypervSocketServer.TextWriter); + } #endregion #region Static Methods @@ -656,6 +666,24 @@ internal static void Run( configurationFile: null); } + internal static void Run( + string initialCommand, + string configurationName, + string token, + DateTimeOffset tokenCreationTime) + { + lock (SyncObject) + { + s_instance = new HyperVSocketMediator(token, tokenCreationTime); + } + + s_instance.Start( + initialCommand: initialCommand, + cryptoHelper: new PSRemotingCryptoHelperServer(), + workingDirectory: null, + configurationName: configurationName, + configurationFile: null); + } #endregion } diff --git a/src/System.Management.Automation/resources/RemotingErrorIdStrings.resx b/src/System.Management.Automation/resources/RemotingErrorIdStrings.resx index da56deb4598..05f12b0c29b 100644 --- a/src/System.Management.Automation/resources/RemotingErrorIdStrings.resx +++ b/src/System.Management.Automation/resources/RemotingErrorIdStrings.resx @@ -1723,4 +1723,10 @@ SSH client process terminated before connection could be established. Failed to get Hyper-V VM State. The value was of the type {0} but was expected to be Microsoft.HyperV.PowerShell.VMState or System.String. + + Hyper-V {0} sent an invalid {1} response during the connection negotiation. + + + Negotiating a secure connection to Hyper-V failed. Make sure the Host and Guest are updated with all relevant Microsoft Updates. + diff --git a/test/xUnit/csharp/test_CommandLineParser.cs b/test/xUnit/csharp/test_CommandLineParser.cs index 5025584d6ac..01f572d230d 100644 --- a/test/xUnit/csharp/test_CommandLineParser.cs +++ b/test/xUnit/csharp/test_CommandLineParser.cs @@ -48,6 +48,9 @@ public static void TestDefaults() Assert.False(cpp.ShowVersion); Assert.False(cpp.SkipProfiles); Assert.False(cpp.SocketServerMode); +#if !UNIX + Assert.False(cpp.V2SocketServerMode); +#endif Assert.False(cpp.SSHServerMode); if (Platform.IsWindows) { @@ -336,6 +339,25 @@ public static void TestParameter_SocketServerMode(params string[] commandLine) Assert.Null(cpp.ErrorMessage); } +#if !UNIX + [Theory] + [InlineData("-v2socketservermode", "-token", "natoheusatoehusnatoeu", "-utctimestamp", "2023-10-01T12:00:00Z")] + [InlineData("-v2so", "-token", "asentuhasoneuthsaoe", "-utctimestamp", "2025-06-09T12:00:00Z")] + public static void TestParameter_V2SocketServerMode(params string[] commandLine) + { + var cpp = new CommandLineParameterParser(); + + cpp.Parse(commandLine); + + Assert.False(cpp.AbortStartup); + Assert.True(cpp.NoExit); + Assert.False(cpp.ShowShortHelp); + Assert.False(cpp.ShowBanner); + Assert.True(cpp.V2SocketServerMode); + Assert.Null(cpp.ErrorMessage); + } +#endif + [Theory] [InlineData("-servermode")] [InlineData("-s")] diff --git a/test/xUnit/csharp/test_RemoteHyperV.cs b/test/xUnit/csharp/test_RemoteHyperV.cs new file mode 100644 index 00000000000..f694f6894df --- /dev/null +++ b/test/xUnit/csharp/test_RemoteHyperV.cs @@ -0,0 +1,661 @@ +// Copyright (c) Microsoft Corporation. +// Licensed under the MIT License. + +using System; +using System.Collections.Generic; +using System.Management.Automation.Language; +using System.Management.Automation.Subsystem; +using System.Management.Automation.Subsystem.Prediction; +using System.Threading; +using System.Net; +using System.Net.Sockets; +using System.Text; +using System.Reflection; +using System.Threading.Tasks; +using Xunit; +using Xunit.Abstractions; + +namespace PSTests.Sequential +{ + public class RemoteHyperVTests + { + private static ITestOutputHelper _output; + private static TimeSpan timeout = TimeSpan.FromSeconds(15); + + public RemoteHyperVTests(ITestOutputHelper output) + { + if (!System.Management.Automation.Platform.IsWindows) + { + throw new SkipException("RemoteHyperVTests are only supported on Windows."); + } + + _output = output; + } + + // Helper method to connect with retries + private static void ConnectWithRetry(Socket client, IPAddress address, int port, ITestOutputHelper output, int maxRetries = 10) + { + int retryDelayMs = 500; + int attempt = 0; + bool connected = false; + while (attempt < maxRetries && !connected) + { + try + { + client.Connect(address, port); + connected = true; + } + catch (SocketException) + { + attempt++; + if (attempt < maxRetries) + { + output?.WriteLine($"Connect attempt {attempt} failed, retrying in {retryDelayMs}ms..."); + Thread.Sleep(retryDelayMs); + retryDelayMs *= 2; + } + else + { + output?.WriteLine($"Failed to connect after {maxRetries} attempts. This is most likely an intermittent failure due to environmental issues."); + throw; + } + } + } + } + + private static void StartHandshakeServer( + string name, + int port, + IEnumerable<(string message, + Encoding encoding)> expectedClientSends, + IEnumerable<(string message, Encoding encoding)> serverResponses, + bool verifyConnectionClosed, + CancellationToken cancellationToken, + bool sendFirst = false) + { + var expectedMessages = new Queue<(string message, byte[] bytes, Encoding encoding)>(); + foreach (var item in expectedClientSends) + { + var itemBytes = item.encoding.GetBytes(item.message); + expectedMessages.Enqueue((message: item.message, bytes: itemBytes, encoding: item.encoding)); + } + + var serverResponseBytes = new Queue(); + foreach (var item in serverResponses) + { + serverResponseBytes.Enqueue(item.encoding.GetBytes(item.message)); + } + + StartHandshakeServer(name, port, expectedMessages, serverResponseBytes, verifyConnectionClosed, cancellationToken, sendFirst); + } + + private static void StartHandshakeServer(string name, int port, Queue<(string message, byte[] bytes, Encoding encoding)> expectedClientSends, Queue serverResponses, bool verifyConnectionClosed, CancellationToken cancellationToken, bool sendFirst = false) + { + var buffer = new byte[1024]; + var listener = new TcpListener(IPAddress.Loopback, port); + listener.Start(); + try + { + using (var client = listener.AcceptSocket()) + { + if (sendFirst) + { + // Send the first message from the serverResponses queue + if (serverResponses.Count > 0) + { + var resp = serverResponses.Dequeue(); + client.Send(resp, resp.Length, SocketFlags.None); + _output.WriteLine($"Mock {name} - sent response: " + Encoding.ASCII.GetString(resp)); + } + } + + while (expectedClientSends.Count > 0) + { + client.ReceiveTimeout = 2 * 1000; // 2 seconds timeout for receiving data + cancellationToken.ThrowIfCancellationRequested(); + var expectedMessage = expectedClientSends.Dequeue(); + var expected = expectedMessage.bytes; + Array.Clear(buffer, 0, buffer.Length); + int received = client.Receive(buffer); + // Optionally validate received data matches expected + string expectedString = expectedMessage.message; + string bufferString = expectedMessage.encoding.GetString(buffer, 0, received); + string alternativeEncodedString = string.Empty; + if (expectedMessage.encoding == Encoding.Unicode) + { + alternativeEncodedString = Encoding.UTF8.GetString(buffer, 0, received); + } + else if (expectedMessage.encoding == Encoding.UTF8) + { + alternativeEncodedString = Encoding.Unicode.GetString(buffer, 0, received); + } + + if (received != expected.Length) + { + string errorMessage = $"Mock {name} - Expected {expected.Length} bytes, but received {received} bytes: `{bufferString}`(alt encoding: `{alternativeEncodedString}`); expected: {expectedString}"; + _output.WriteLine(errorMessage); + throw new Exception(errorMessage); + } + if (!string.Equals(bufferString, expectedString, StringComparison.OrdinalIgnoreCase)) + { + string errorMessage = $"Mock {name} - Expected `{expectedString}`; length {expected.Length}, but received; length {received}; `{bufferString}`(alt encoding: `{alternativeEncodedString}`) instead."; + _output.WriteLine(errorMessage); + throw new Exception(errorMessage); + } + _output.WriteLine($"Mock {name} - received expected message: " + expectedString); + if (serverResponses.Count > 0) + { + var resp = serverResponses.Dequeue(); + client.Send(resp, resp.Length, SocketFlags.None); + _output.WriteLine($"Mock {name} - sent response: " + Encoding.ASCII.GetString(resp)); + } + } + + if (verifyConnectionClosed) + { + _output.WriteLine($"Mock {name} - verifying client connection is closed."); + // Wait for the client to close the connection synchronously (no timeout) + try + { + while (true) + { + int bytesRead = client.Receive(buffer, SocketFlags.None); + if (bytesRead == 0) + { + break; + } + + // If we receive any data, log and throw (assume UTF8 encoding) + string unexpectedData = Encoding.UTF8.GetString(buffer, 0, bytesRead); + _output.WriteLine($"Mock {name} - received unexpected data after handshake: {unexpectedData}"); + throw new Exception($"Mock {name} - received unexpected data after handshake: {unexpectedData}"); + } + _output.WriteLine($"Mock {name} - client closed the connection."); + } + catch (SocketException ex) + { + _output.WriteLine($"Mock {name} - socket exception while waiting for client close: {ex.Message} {ex.GetType().FullName}"); + } + catch (ObjectDisposedException) + { + // Socket already closed + } + } + } + + _output.WriteLine($"Mock {name} - on port {port} completed successfully."); + } + finally + { + listener.Stop(); + } + } + + // Helper function to create a random 4-character ASCII response + private static string CreateRandomAsciiResponse() + { + var rand = new Random(); + // Randomly return either "PASS" or "FAIL" + return rand.Next(0, 2) == 0 ? "PASS" : "FAIL"; + } + + // Helper method to create test data + private static (List<(string, Encoding)> expectedClientSends, List<(string, Encoding)> serverResponses) CreateHandshakeTestData(NetworkCredential cred) + { + var expectedClientSends = new List<(string message, Encoding encoding)> + { + (message: cred.Domain, encoding: Encoding.Unicode), + (message: cred.UserName, encoding: Encoding.Unicode), + (message: "NONEMPTYPW", encoding: Encoding.ASCII), + (message: cred.Password, encoding: Encoding.Unicode) + }; + + var serverResponses = new List<(string message, Encoding encoding)> + { + (message: CreateRandomAsciiResponse(), encoding: Encoding.ASCII), // Response to domain + (message: CreateRandomAsciiResponse(), encoding: Encoding.ASCII), // Response to username + (message: CreateRandomAsciiResponse(), encoding: Encoding.ASCII) // Response to non-empty password + }; + + return (expectedClientSends, serverResponses); + } + + private static List<(string message, Encoding encoding)> CreateVersionNegotiationClientSends() + { + return new List<(string message, Encoding encoding)> + { + (message: "VERSION", encoding: Encoding.UTF8), + (message: "VERSION_2", encoding: Encoding.UTF8), + }; + } + + private static List<(string, Encoding)> CreateV2Sends(NetworkCredential cred, string configurationName) + { + var sends = CreateVersionNegotiationClientSends(); + var password = cred.Password; + var emptyPassword = string.IsNullOrEmpty(password); + + sends.AddRange(new List<(string message, Encoding encoding)> + { + (message: cred.Domain, encoding: Encoding.Unicode), + (message: cred.UserName, encoding: Encoding.Unicode) + }); + + if (!emptyPassword) + { + sends.AddRange(new List<(string message, Encoding encoding)> + { + (message: "NONEMPTYPW", encoding: Encoding.UTF8), + (message: cred.Password, encoding: Encoding.Unicode) + }); + } + else + { + sends.Add((message: "EMPTYPW", encoding: Encoding.UTF8)); // Empty password and we don't expect a response + } + + if (!string.IsNullOrEmpty(configurationName)) + { + sends.Add((message: "NONEMPTYCF", encoding: Encoding.UTF8)); + sends.Add((message: configurationName, encoding: Encoding.Unicode)); // Configuration string and we don't expect a response + } + else + { + sends.Add((message: "EMPTYCF", encoding: Encoding.UTF8)); // Configuration string and we don't expect a response + } + + sends.Add((message: "PASS", encoding: Encoding.ASCII)); // Response to TOKEN + + return sends; + } + + private static List<(string, Encoding)> CreateV2Responses(string version = "VERSION_2", bool emptyConfig = false, string token = "FakeToken0+/=", bool emptyPassword = false) + { + var responses = new List<(string message, Encoding encoding)> + { + (message: version, encoding: Encoding.ASCII), // Response to VERSION + (message: "PASS", encoding: Encoding.ASCII), // Response to VERSION_2 + (message: "PASS", encoding: Encoding.ASCII), // Response to domain + (message: "PASS", encoding: Encoding.ASCII), // Response to username + }; + + if (!emptyPassword) + { + responses.Add((message: "PASS", encoding: Encoding.ASCII)); // Response to non-empty password + } + + responses.Add((message: "CONF", encoding: Encoding.ASCII)); // Response to configuration + + if (!emptyConfig) + { + responses.Add((message: "PASS", encoding: Encoding.ASCII)); // Response to non-empty configuration + } + responses.Add((message: "TOKEN " + token, encoding: Encoding.ASCII)); // Response to with a token than uses each class of character in base 64 encoding + + return responses; + } + + // Helper method to create test data + private static (List<(string, Encoding)> expectedClientSends, List<(string, Encoding)> serverResponses) + CreateHandshakeTestDataV2(NetworkCredential cred, string version, string configurationName, string token) + { + bool emptyConfig = string.IsNullOrEmpty(configurationName); + bool emptyPassword = string.IsNullOrEmpty(cred.Password); + return (CreateV2Sends(cred, configurationName), CreateV2Responses(version, emptyConfig, token, emptyPassword)); + } + + // Helper method to create test data + private static (List<(string, Encoding)> expectedClientSends, List<(string, Encoding)> serverResponses) CreateHandshakeTestDataForFallback(NetworkCredential cred) + { + var expectedClientSends = new List<(string message, Encoding encoding)> + { + (message: "VERSION", encoding: Encoding.UTF8), + (message: @"?", encoding: Encoding.Unicode), + (message: "EMPTYPW", encoding: Encoding.UTF8), // Response to domain + (message: "FAIL", encoding: Encoding.UTF8), // Response to domain + }; + + List<(string message, Encoding encoding)> serverResponses = new List<(string message, Encoding encoding)> + { + (message: "PASS", encoding: Encoding.ASCII), // Response to VERSION but v1 server expects domain so it says "PASS" + (message: "PASS", encoding: Encoding.ASCII), // Response to username + (message: "FAIL", encoding: Encoding.ASCII) // Response to EMPTYPW + }; + + return (expectedClientSends, serverResponses); + } + + // Helper to create a password with at least one non-ASCII Unicode character + public static string CreateRandomUnicodePassword(string prefix) + { + var rand = new Random(); + var asciiPart = new char[6 + prefix.Length]; + // Copy prefix into asciiPart + Array.Copy(prefix.ToCharArray(), 0, asciiPart, 0, prefix.Length); + for (int i = prefix.Length; i < asciiPart.Length; i++) + { + asciiPart[i] = (char)rand.Next(33, 127); // ASCII printable + } + // Add a random Unicode character outside ASCII range (e.g., U+0100 to U+017F) + char unicodeChar = (char)rand.Next(0x0100, 0x017F); + // Insert the unicode character at a random position + int insertPos = rand.Next(0, asciiPart.Length + 1); + var passwordChars = new List(asciiPart); + passwordChars.Insert(insertPos, unicodeChar); + return new string(passwordChars.ToArray()); + } + + public static NetworkCredential CreateTestCredential() + { + return new NetworkCredential(CreateRandomUnicodePassword("username"), CreateRandomUnicodePassword("password"), CreateRandomUnicodePassword("domain")); + } + + [SkippableFact] + public async Task PerformCredentialAndConfigurationHandshake_V1_Pass() + { + // Arrange + int port = 50000 + (int)(DateTime.Now.Ticks % 10000); + var cred = CreateTestCredential(); + string configurationName = CreateRandomUnicodePassword("config"); + + var (expectedClientSends, serverResponses) = CreateHandshakeTestData(cred); + expectedClientSends.Add(("PASS", Encoding.ASCII)); + serverResponses.Add(("PASS", Encoding.ASCII)); + + using var cts = new CancellationTokenSource(TimeSpan.FromMinutes(1)); + var serverTask = Task.Run(() => StartHandshakeServer("Broker", port, expectedClientSends, serverResponses, verifyConnectionClosed: false, cts.Token), cts.Token); + + using (var client = new Socket(AddressFamily.InterNetwork, SocketType.Stream, ProtocolType.Tcp)) + { + ConnectWithRetry(client, IPAddress.Loopback, port, _output); + var exchangeResult = System.Management.Automation.Remoting.RemoteSessionHyperVSocketClient.ExchangeCredentialsAndConfiguration(cred, configurationName, client, true); + var result = exchangeResult.success; + _output.WriteLine($"Exchange result: {result}, Token: {exchangeResult.authenticationToken}"); + System.Threading.Thread.Sleep(100); // Allow time for server to process + Assert.True(result, $"Expected Exchange to pass"); + } + + await serverTask; + } + + [SkippableTheory] + [InlineData("VERSION_2", "configurationname1", "FakeTokenaaaaaaaaaAAAAAAAAAAAAAAAAAAAAAA0FakeTokenaaaaaaaaaAAAAAAAAAAAAAAAAAAAAAA0+/==")] // a fake base64 token about 512 bits long (double the size when this was spec'ed) + [InlineData("VERSION_10", null, "FakeTokenaaaaaaaaaAAAAAAAAAAAAAAAAAAAAAA0+/=")] // a fake base64 token about 256 bits Long (the size when this was spec'ed) + public async Task PerformCredentialAndConfigurationHandshake_V2_Pass(string versionResponse, string configurationName, string token) + { + // Arrange + int port = 50000 + (int)(DateTime.Now.Ticks % 10000); + var cred = CreateTestCredential(); + + var (expectedClientSends, serverResponses) = CreateHandshakeTestDataV2(cred, versionResponse, configurationName, token); + + using var cts = new CancellationTokenSource(TimeSpan.FromMinutes(1)); + var serverTask = Task.Run(() => StartHandshakeServer("Broker", port, expectedClientSends, serverResponses, verifyConnectionClosed: true, cts.Token), cts.Token); + + using (var client = new Socket(AddressFamily.InterNetwork, SocketType.Stream, ProtocolType.Tcp)) + { + client.Connect(IPAddress.Loopback, port); + var exchangeResult = System.Management.Automation.Remoting.RemoteSessionHyperVSocketClient.ExchangeCredentialsAndConfiguration(cred, configurationName, client, false); + var result = exchangeResult.success; + System.Threading.Thread.Sleep(100); // Allow time for server to process + Assert.True(result, $"Expected Exchange to pass for version response '{versionResponse}'"); + Assert.Equal(token, exchangeResult.authenticationToken); + } + + await serverTask; + } + + [SkippableFact] + public async Task PerformCredentialAndConfigurationHandshake_V1_Fallback() + { + // Arrange + int port = 50000 + (int)(DateTime.Now.Ticks % 10000); + var cred = CreateTestCredential(); + string configurationName = CreateRandomUnicodePassword("config"); + + var (expectedClientSends, serverResponses) = CreateHandshakeTestDataForFallback(cred); + + using var cts = new CancellationTokenSource(TimeSpan.FromMinutes(1)); + var serverTask = Task.Run(() => StartHandshakeServer("Broker", port, expectedClientSends, serverResponses, verifyConnectionClosed: false, cts.Token), cts.Token); + + bool isFallback = false; + using (var client = new Socket(AddressFamily.InterNetwork, SocketType.Stream, ProtocolType.Tcp)) + { + _output.WriteLine("Starting handshake with V2 protocol."); + client.Connect(IPAddress.Loopback, port); + var exchangeResult = System.Management.Automation.Remoting.RemoteSessionHyperVSocketClient.ExchangeCredentialsAndConfiguration(cred, configurationName, client, false); + isFallback = !exchangeResult.success; + + System.Threading.Thread.Sleep(100); // Allow time for server to process + _output.WriteLine("Handshake indicated fallback to V1."); + Assert.True(isFallback, "Expected fallback to V1."); + } + _output.WriteLine("Handshake completed successfully with fallback to V1."); + + await serverTask; + } + + [SkippableFact] + public async Task PerformCredentialAndConfigurationHandshake_V2_InvalidResponse() + { + // Arrange + int port = 51000 + (int)(DateTime.Now.Ticks % 10000); + var cred = CreateTestCredential(); + + var (expectedClientSends, serverResponses) = CreateHandshakeTestData(cred); + //expectedClientSends.Add("FAI1"); + serverResponses.Add(("FAI1", Encoding.ASCII)); + + using var cts = new CancellationTokenSource(TimeSpan.FromSeconds(30)); + + //cts.Token.Register(() => throw new OperationCanceledException("Test timed out.")); + + var serverTask = Task.Run(() => StartHandshakeServer("Broker", port, expectedClientSends, serverResponses, verifyConnectionClosed: false, cts.Token), cts.Token); + + using (var client = new Socket(AddressFamily.InterNetwork, SocketType.Stream, ProtocolType.Tcp)) + { + _output.WriteLine("connecting on port " + port); + ConnectWithRetry(client, IPAddress.Loopback, port, _output); + + var ex = Record.Exception(() => System.Management.Automation.Remoting.RemoteSessionHyperVSocketClient.ExchangeCredentialsAndConfiguration(cred, "config", client, true)); + + try + { + await serverTask; + } + catch (AggregateException exAgg) + { + Assert.Null(exAgg.Flatten().InnerExceptions[1].Message); + } + cts.Token.ThrowIfCancellationRequested(); + + Assert.NotNull(ex); + Assert.NotNull(ex.Message); + Assert.Contains("Hyper-V Broker sent an invalid Credential response", ex.Message); + } + } + + [SkippableFact] + public async Task PerformCredentialAndConfigurationHandshake_V1_Fail() + { + // Arrange + int port = 51000 + (int)(DateTime.Now.Ticks % 10000); + var cred = CreateTestCredential(); + + var (expectedClientSends, serverResponses) = CreateHandshakeTestData(cred); + expectedClientSends.Add(("FAIL", Encoding.ASCII)); + serverResponses.Add(("FAIL", Encoding.ASCII)); + + using var cts = new CancellationTokenSource(TimeSpan.FromSeconds(15)); + + // This scenario does not close the connection in a timely manner, so we set verifyConnectionClosed to false + var serverTask = Task.Run(() => StartHandshakeServer("Broker", port, expectedClientSends, serverResponses, verifyConnectionClosed: false, cts.Token), cts.Token); + + using (var client = new Socket(AddressFamily.InterNetwork, SocketType.Stream, ProtocolType.Tcp)) + { + client.Connect(IPAddress.Loopback, port); + + var ex = Record.Exception(() => System.Management.Automation.Remoting.RemoteSessionHyperVSocketClient.ExchangeCredentialsAndConfiguration(cred, "config", client, true)); + + try + { + await serverTask; + } + catch (AggregateException exAgg) + { + Assert.Null(exAgg.Flatten().InnerExceptions[1].Message); + } + + cts.Token.ThrowIfCancellationRequested(); + + Assert.NotNull(ex); + Assert.NotNull(ex.Message); + Assert.Contains("The credential is invalid.", ex.Message); + } + } + + [SkippableTheory] + [InlineData("VERSION_2", "FakeTokenaaaaaaaaaAAAAAAAAAAAAAAAAAAAAAA0FakeTokenaaaaaaaaaAAAAAAAAAAAAAAAAAAAAAA0+/==")] // a fake base64 token about 512 bits long (double the size when this was spec'ed) + [InlineData("VERSION_10", "FakeTokenaaaaaaaaaAAAAAAAAAAAAAAAAAAAAAA0+/=")] // a fake base64 token about 256 bits Long (the size when this was spec'ed) + public async Task PerformTransportVersionAndTokenExchange_Pass(string version, string token) + { + // Arrange + int port = 50000 + (int)(DateTime.Now.Ticks % 10000); + var cred = CreateTestCredential(); + + var expectedClientSends = CreateVersionNegotiationClientSends(); + expectedClientSends.Add((message: "TOKEN " + token, encoding: Encoding.ASCII)); + + var serverResponses = new List<(string message, Encoding encoding)>{ + (message: version, encoding: Encoding.ASCII), // Response to VERSION + (message: "PASS", encoding: Encoding.ASCII), // Response to VERSION_2 + (message: "PASS", encoding: Encoding.ASCII) // Response to token + }; + + using var cts = new CancellationTokenSource(TimeSpan.FromMinutes(1)); + var serverTask = Task.Run(() => StartHandshakeServer("Server", port, expectedClientSends, serverResponses, verifyConnectionClosed: true, cts.Token), cts.Token); + + using (var client = new Socket(AddressFamily.InterNetwork, SocketType.Stream, ProtocolType.Tcp)) + { + ConnectWithRetry(client, IPAddress.Loopback, port, _output); + System.Management.Automation.Remoting.RemoteSessionHyperVSocketClient.PerformTransportVersionAndTokenExchange(client, token); + System.Threading.Thread.Sleep(100); // Allow time for server to process + } + + await serverTask; + } + + [SkippableTheory] + [InlineData(1, true)] + [InlineData(2, true)] + [InlineData(0, false)] + [InlineData(null, false)] + [System.Runtime.Versioning.SupportedOSPlatform("windows")] + public void IsRequirePsDirectAuthenticationEnabled(int? regValue, bool expected) + { + const string testKeyPath = @"SOFTWARE\Microsoft\TestRequirePsDirectAuthentication"; + const string valueName = "RequirePsDirectAuthentication"; + if (!System.Management.Automation.Platform.IsWindows) + { + throw new SkipException("RemoteHyperVTests are only supported on Windows."); + } + + // Clean up any previous test key + var regHive = Microsoft.Win32.RegistryHive.CurrentUser; + var baseKey = Microsoft.Win32.RegistryKey.OpenBaseKey(regHive, Microsoft.Win32.RegistryView.Registry64); + baseKey.DeleteSubKeyTree(testKeyPath, false); + + bool? result = null; + + // Create the test key + using (var key = baseKey.CreateSubKey(testKeyPath)) + { + if (regValue.HasValue) + { + key.SetValue(valueName, regValue.Value, Microsoft.Win32.RegistryValueKind.DWord); + } + else + { + // Ensure the value does not exist + key.DeleteValue(valueName, false); + } + + result = System.Management.Automation.Remoting.RemoteSessionHyperVSocketClient.IsRequirePsDirectAuthenticationEnabled(testKeyPath, regHive); + } + + Assert.True(result.HasValue, "IsRequirePsDirectAuthenticationEnabled should return a value."); + Assert.True(expected == result.Value, + $"Expected IsRequirePsDirectAuthenticationEnabled to return {expected} when registry value is {(regValue.HasValue ? regValue.ToString() : "not set")}."); + + return; + } + + [SkippableTheory] + [InlineData("testToken", "testToken")] + [InlineData("testToken\0", "testToken")] + public async Task ValidatePassesWhenTokensMatch(string token, string expectedToken) + { + int port = 50000 + (int)(DateTime.Now.Ticks % 10000); + + var expectedClientSends = new List<(string message, Encoding encoding)>{ + (message: "VERSION", encoding: Encoding.ASCII), // Response to VERSION + (message: "VERSION_2", encoding: Encoding.ASCII), // Response to VERSION_2 + (message: $"TOKEN {token}", encoding: Encoding.ASCII) + }; + + var serverResponses = new List<(string message, Encoding encoding)>{ + (message: "VERSION_2", encoding: Encoding.ASCII), // Response to VERSION_2 + (message: "PASS", encoding: Encoding.ASCII), // Response to VERSION_2 + (message: "PASS", encoding: Encoding.ASCII) // Response to token + }; + + using var cts = new CancellationTokenSource(TimeSpan.FromMinutes(1)); + var serverTask = Task.Run(() => StartHandshakeServer("Client", port, serverResponses, expectedClientSends, verifyConnectionClosed: true, cts.Token, sendFirst: true), cts.Token); + + using (var client = new Socket(AddressFamily.InterNetwork, SocketType.Stream, ProtocolType.Tcp)) + { + ConnectWithRetry(client, IPAddress.Loopback, port, _output); + System.Management.Automation.Remoting.RemoteSessionHyperVSocketServer.ValidateToken(client, expectedToken); + System.Threading.Thread.Sleep(100); // Allow time for server to process + } + + await serverTask; + } + + [SkippableTheory] + [InlineData("abc", "xyz")] + [InlineData("abc", "abcdef")] + [InlineData("abcdef", "abc")] + [InlineData("abc\0def", "abc")] + public async Task ValidateFailsWhenTokensMismatch(string token, string expectedToken) + { + int port = 50000 + (int)(DateTime.Now.Ticks % 10000); + + var expectedClientSends = new List<(string message, Encoding encoding)>{ + (message: "VERSION", encoding: Encoding.ASCII), // Initial request + (message: "VERSION_2", encoding: Encoding.ASCII), // Response to VERSION_2 + (message: $"TOKEN {token}", encoding: Encoding.ASCII) + }; + + var serverResponses = new List<(string message, Encoding encoding)>{ + (message: "VERSION_2", encoding: Encoding.ASCII), // Response to VERSION + (message: "PASS", encoding: Encoding.ASCII), // Response to VERSION_2 + (message: "FAIL", encoding: Encoding.ASCII) // Response to token + }; + + using var cts = new CancellationTokenSource(TimeSpan.FromMinutes(1)); + var serverTask = Task.Run(() => StartHandshakeServer("Client", port, serverResponses, expectedClientSends, verifyConnectionClosed: true, cts.Token, sendFirst: true), cts.Token); + + using (var client = new Socket(AddressFamily.InterNetwork, SocketType.Stream, ProtocolType.Tcp)) + { + ConnectWithRetry(client, IPAddress.Loopback, port, _output); + var exception = Assert.Throws( + () => System.Management.Automation.Remoting.RemoteSessionHyperVSocketServer.ValidateToken(client, expectedToken)); + System.Threading.Thread.Sleep(100); // Allow time for server to process + Assert.Contains("The credential is invalid.", exception.Message); + } + + await serverTask; + } + } +} From 37086d441c9704d2fe66ce6dc0d5057e9e9e5668 Mon Sep 17 00:00:00 2001 From: "Travis Plunk (HE/HIM)" Date: Fri, 5 Sep 2025 17:50:40 -0700 Subject: [PATCH 14/16] Add LinuxHost Network configuration to PowerShell Packages pipeline --- .pipelines/PowerShell-Packages-Official.yml | 2 ++ 1 file changed, 2 insertions(+) diff --git a/.pipelines/PowerShell-Packages-Official.yml b/.pipelines/PowerShell-Packages-Official.yml index 333d73276b8..552e7d10a68 100644 --- a/.pipelines/PowerShell-Packages-Official.yml +++ b/.pipelines/PowerShell-Packages-Official.yml @@ -92,6 +92,8 @@ extends: WindowsHostVersion: Version: 2022 Network: KS3 + LinuxHostVersion: + Network: KS3 linuxEsrpSigning: true incrementalSDLBinaryAnalysis: true globalSdl: From 82c9317ad186500d446b8cf3da4170c798ada485 Mon Sep 17 00:00:00 2001 From: "Travis Plunk (HE/HIM)" Date: Mon, 8 Sep 2025 12:00:32 -0700 Subject: [PATCH 15/16] Fix variable reference for release environment in pipeline --- .pipelines/PowerShell-Release-Official.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.pipelines/PowerShell-Release-Official.yml b/.pipelines/PowerShell-Release-Official.yml index 974b8f328c8..986f803361b 100644 --- a/.pipelines/PowerShell-Release-Official.yml +++ b/.pipelines/PowerShell-Release-Official.yml @@ -288,7 +288,7 @@ extends: - setReleaseTagAndChangelog - UpdateChangeLog variables: - ob_release_environment: ${{ parameters.releaseEnvironment }} + ob_release_environment: ${{ variables.releaseEnvironment }} jobs: - template: /.pipelines/templates/release-githubNuget.yml@self parameters: From b72c7ab1238c2d95b5c9004bca8399b8b3ca88ac Mon Sep 17 00:00:00 2001 From: PowerShell Team Bot <69177312+pwshBot@users.noreply.github.com> Date: Mon, 8 Sep 2025 12:53:26 -0700 Subject: [PATCH 16/16] [release/v7.5] Add v7.5.3 Changelog (#26015) Co-authored-by: Justin Chung <124807742+jshigetomi@users.noreply.github.com> --- CHANGELOG/7.5.md | 43 ++++++++++++++++++++++++++++++++++++++++++- 1 file changed, 42 insertions(+), 1 deletion(-) diff --git a/CHANGELOG/7.5.md b/CHANGELOG/7.5.md index d2071727790..e231ce6b0e2 100644 --- a/CHANGELOG/7.5.md +++ b/CHANGELOG/7.5.md @@ -1,5 +1,47 @@ # 7.5 Changelog +## [7.5.3] + +### General Cmdlet Updates and Fixes + +- Fix `Out-GridView` by replacing the use of obsolete `BinaryFormatter` with custom implementation. (#25559) +- Remove `OnDeserialized` and `Serializable` attributes from `Microsoft.Management.UI.Internal` project (#25831) +- Make the interface `IDeepCloneable` internal (#25830) + +### Tools + +- Add CodeQL suppressions (#25972) + +### Tests + +- Fix updatable help test for new content (#25944) + +### Build and Packaging Improvements + +
+ + + +

Update to .NET SDK 9.0.304

+ +
+ +
    +
  • Make logical template name consistent between pipelines (#25991)
  • +
  • Update container images to use mcr.microsoft.com for Linux and Azure Linux (#25986)
  • +
  • Add build to vPack Pipeline (#25975)
  • +
  • Remove AsyncSDL from Pipelines Toggle Official/NonOfficial Runs (#25964)
  • +
  • Update branch for release (#25942)
  • +
+ +
+ +### Documentation and Help Content + +- Fix typo in CHANGELOG for script filename suggestion (#25963) + +[7.5.3]: https://github.com/PowerShell/PowerShell/compare/v7.5.2...v7.5.3 + ## [7.5.2] - 2025-06-24 ### Engine Updates and Fixes @@ -41,7 +83,6 @@ [7.5.2]: https://github.com/PowerShell/PowerShell/compare/v7.5.1...v7.5.2 - ## [7.5.1] ### Engine Updates and Fixes