From 638bff0ac3cfdd5608b89cfa75973ae8d30bd66b Mon Sep 17 00:00:00 2001 From: KirtiRamchandani Date: Sun, 24 May 2026 12:49:22 +0530 Subject: [PATCH 1/3] Bound member invocation logging argument strings --- .../engine/runtime/Operations/MiscOps.cs | 21 +++++++++++++-- .../Logging/MemberInvocationLogging.Tests.ps1 | 27 +++++++++++++++++++ 2 files changed, 46 insertions(+), 2 deletions(-) create mode 100644 test/powershell/engine/Logging/MemberInvocationLogging.Tests.ps1 diff --git a/src/System.Management.Automation/engine/runtime/Operations/MiscOps.cs b/src/System.Management.Automation/engine/runtime/Operations/MiscOps.cs index d584666ab62..74d434ed80f 100644 --- a/src/System.Management.Automation/engine/runtime/Operations/MiscOps.cs +++ b/src/System.Management.Automation/engine/runtime/Operations/MiscOps.cs @@ -3657,6 +3657,23 @@ internal static class MemberInvocationLoggingOps ); #endif + private const int MaxLoggedArgumentStringLength = 4096; + + private static string LimitLoggedArgumentString(string value) + { + if (value is null || value.Length <= MaxLoggedArgumentStringLength) + { + return value; + } + + string originalLength = value.Length.ToString(CultureInfo.InvariantCulture); + return string.Concat( + value.AsSpan(0, MaxLoggedArgumentStringLength), + "...".AsSpan()); + } + private static string ArgumentToString(object arg) { object baseObj = PSObject.Base(arg); @@ -3669,7 +3686,7 @@ private static string ArgumentToString(object arg) // The comparisons below are ordered by the likelihood of arguments being of those types. if (baseObj is string str) { - return str; + return LimitLoggedArgumentString(str); } // Special case some types to call 'ToString' on the object. For the rest, we return its @@ -3683,7 +3700,7 @@ private static string ArgumentToString(object arg) || baseType == typeof(BigInteger) || baseType == typeof(decimal)) { - return baseObj.ToString(); + return LimitLoggedArgumentString(baseObj.ToString()); } return baseType.FullName; diff --git a/test/powershell/engine/Logging/MemberInvocationLogging.Tests.ps1 b/test/powershell/engine/Logging/MemberInvocationLogging.Tests.ps1 new file mode 100644 index 00000000000..fe93994ce28 --- /dev/null +++ b/test/powershell/engine/Logging/MemberInvocationLogging.Tests.ps1 @@ -0,0 +1,27 @@ +# Copyright (c) Microsoft Corporation. +# Licensed under the MIT License. + +Describe 'Member invocation logging' -Tags 'CI' { + BeforeAll { + $type = [psobject].Assembly.GetType('System.Management.Automation.MemberInvocationLoggingOps') + $argumentToString = $type.GetMethod( + 'ArgumentToString', + [System.Reflection.BindingFlags]'NonPublic, Static') + } + + It 'Keeps short string arguments unchanged' { + $value = 'short argument' + + $argumentToString.Invoke($null, [object[]]@($value)) | Should -BeExactly $value + } + + It 'Limits long string arguments' { + $value = 'a' * 5000 + + $result = $argumentToString.Invoke($null, [object[]]@($value)) + + $result.Length | Should -BeLessThan $value.Length + $result.StartsWith(('a' * 4096), [System.StringComparison]::Ordinal) | Should -BeTrue + $result | Should -Match '' + } +} From b659b27a81e82fa73c78f9cd4f585a888afa1c7e Mon Sep 17 00:00:00 2001 From: KirtiRamchandani Date: Sun, 24 May 2026 18:44:06 +0530 Subject: [PATCH 2/3] Cap member invocation log argument length --- .../engine/runtime/Operations/MiscOps.cs | 7 +++-- .../Logging/MemberInvocationLogging.Tests.ps1 | 29 +++++++++++++++++-- 2 files changed, 31 insertions(+), 5 deletions(-) diff --git a/src/System.Management.Automation/engine/runtime/Operations/MiscOps.cs b/src/System.Management.Automation/engine/runtime/Operations/MiscOps.cs index 74d434ed80f..39d0f31a16f 100644 --- a/src/System.Management.Automation/engine/runtime/Operations/MiscOps.cs +++ b/src/System.Management.Automation/engine/runtime/Operations/MiscOps.cs @@ -3667,11 +3667,14 @@ private static string LimitLoggedArgumentString(string value) } string originalLength = value.Length.ToString(CultureInfo.InvariantCulture); - return string.Concat( - value.AsSpan(0, MaxLoggedArgumentStringLength), + string truncationMarker = string.Concat( "...".AsSpan()); + int prefixLength = MaxLoggedArgumentStringLength - truncationMarker.Length; + return string.Concat( + value.AsSpan(0, prefixLength), + truncationMarker.AsSpan()); } private static string ArgumentToString(object arg) diff --git a/test/powershell/engine/Logging/MemberInvocationLogging.Tests.ps1 b/test/powershell/engine/Logging/MemberInvocationLogging.Tests.ps1 index fe93994ce28..0100c770ec3 100644 --- a/test/powershell/engine/Logging/MemberInvocationLogging.Tests.ps1 +++ b/test/powershell/engine/Logging/MemberInvocationLogging.Tests.ps1 @@ -7,6 +7,9 @@ Describe 'Member invocation logging' -Tags 'CI' { $argumentToString = $type.GetMethod( 'ArgumentToString', [System.Reflection.BindingFlags]'NonPublic, Static') + $maxLoggedArgumentStringLength = [int]$type.GetField( + 'MaxLoggedArgumentStringLength', + [System.Reflection.BindingFlags]'NonPublic, Static').GetRawConstantValue() } It 'Keeps short string arguments unchanged' { @@ -15,13 +18,33 @@ Describe 'Member invocation logging' -Tags 'CI' { $argumentToString.Invoke($null, [object[]]@($value)) | Should -BeExactly $value } + It 'Keeps string arguments at the maximum length unchanged' { + $value = 'a' * $maxLoggedArgumentStringLength + + $argumentToString.Invoke($null, [object[]]@($value)) | Should -BeExactly $value + } + It 'Limits long string arguments' { - $value = 'a' * 5000 + $originalLength = $maxLoggedArgumentStringLength + 904 + $value = 'a' * $originalLength + + $result = $argumentToString.Invoke($null, [object[]]@($value)) + $truncationMarker = "..." + $expectedPrefixLength = $maxLoggedArgumentStringLength - $truncationMarker.Length + + $result.Length | Should -Be $maxLoggedArgumentStringLength + $result.StartsWith(('a' * $expectedPrefixLength), [System.StringComparison]::Ordinal) | Should -BeTrue + $result | Should -Match $truncationMarker + } + + It 'Limits string arguments just over the maximum length' { + $originalLength = $maxLoggedArgumentStringLength + 1 + $value = 'a' * $originalLength $result = $argumentToString.Invoke($null, [object[]]@($value)) + $result.Length | Should -Be $maxLoggedArgumentStringLength $result.Length | Should -BeLessThan $value.Length - $result.StartsWith(('a' * 4096), [System.StringComparison]::Ordinal) | Should -BeTrue - $result | Should -Match '' + $result | Should -Match "" } } From 17ff944050dd63e5bbeba6e71f6b30851cc3f77e Mon Sep 17 00:00:00 2001 From: KirtiRamchandani Date: Sun, 24 May 2026 23:47:33 +0530 Subject: [PATCH 3/3] Address member invocation logging review feedback --- .../Logging/MemberInvocationLogging.Tests.ps1 | 19 ++++++++++++++++--- 1 file changed, 16 insertions(+), 3 deletions(-) diff --git a/test/powershell/engine/Logging/MemberInvocationLogging.Tests.ps1 b/test/powershell/engine/Logging/MemberInvocationLogging.Tests.ps1 index 0100c770ec3..5a788b160ed 100644 --- a/test/powershell/engine/Logging/MemberInvocationLogging.Tests.ps1 +++ b/test/powershell/engine/Logging/MemberInvocationLogging.Tests.ps1 @@ -6,7 +6,10 @@ Describe 'Member invocation logging' -Tags 'CI' { $type = [psobject].Assembly.GetType('System.Management.Automation.MemberInvocationLoggingOps') $argumentToString = $type.GetMethod( 'ArgumentToString', - [System.Reflection.BindingFlags]'NonPublic, Static') + [System.Reflection.BindingFlags]'NonPublic, Static', + $null, + [type[]]@([object]), + $null) $maxLoggedArgumentStringLength = [int]$type.GetField( 'MaxLoggedArgumentStringLength', [System.Reflection.BindingFlags]'NonPublic, Static').GetRawConstantValue() @@ -34,7 +37,7 @@ Describe 'Member invocation logging' -Tags 'CI' { $result.Length | Should -Be $maxLoggedArgumentStringLength $result.StartsWith(('a' * $expectedPrefixLength), [System.StringComparison]::Ordinal) | Should -BeTrue - $result | Should -Match $truncationMarker + $result.EndsWith($truncationMarker, [System.StringComparison]::Ordinal) | Should -BeTrue } It 'Limits string arguments just over the maximum length' { @@ -45,6 +48,16 @@ Describe 'Member invocation logging' -Tags 'CI' { $result.Length | Should -Be $maxLoggedArgumentStringLength $result.Length | Should -BeLessThan $value.Length - $result | Should -Match "" + $result.EndsWith("...", [System.StringComparison]::Ordinal) | Should -BeTrue + } + + It 'Limits long non-string special-cased arguments' { + $originalLength = $maxLoggedArgumentStringLength + 101 + $value = [System.Numerics.BigInteger]::Pow([System.Numerics.BigInteger]10, $originalLength - 1) + + $result = $argumentToString.Invoke($null, [object[]]@($value)) + + $result.Length | Should -Be $maxLoggedArgumentStringLength + $result.EndsWith("...", [System.StringComparison]::Ordinal) | Should -BeTrue } }