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 70f03f5ddef..c0b237fb256 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 @@ -107,6 +107,11 @@ public abstract class WebRequestPSCmdlet : PSCmdlet /// internal int _maximumFollowRelLink = int.MaxValue; + /// + /// Maximum number of Redirects to follow. + /// + internal int _maximumRedirection; + /// /// Parse Rel Links. /// @@ -549,6 +554,8 @@ protected override void ProcessRecord() WriteVerbose(reqVerboseMsg); + _maximumRedirection = WebSession.MaximumRedirection; + using HttpResponseMessage response = GetResponse(client, request, handleRedirect); string contentType = ContentHelper.GetContentType(response); @@ -1230,7 +1237,7 @@ internal virtual HttpResponseMessage GetResponse(HttpClient client, HttpRequestM response = client.SendAsync(currentRequest, HttpCompletionOption.ResponseHeadersRead, _cancelToken.Token).GetAwaiter().GetResult(); if (handleRedirect - && WebSession.MaximumRedirection is not 0 + && _maximumRedirection is not 0 && IsRedirectCode(response.StatusCode) && response.Headers.Location is not null) { @@ -1238,9 +1245,9 @@ internal virtual HttpResponseMessage GetResponse(HttpClient client, HttpRequestM _cancelToken = null; // If explicit count was provided, reduce it for this redirection. - if (WebSession.MaximumRedirection > 0) + if (_maximumRedirection > 0) { - WebSession.MaximumRedirection--; + _maximumRedirection--; } // For selected redirects, GET must be used with the redirected Location. diff --git a/test/powershell/Modules/Microsoft.PowerShell.Utility/WebCmdlets.Tests.ps1 b/test/powershell/Modules/Microsoft.PowerShell.Utility/WebCmdlets.Tests.ps1 index 2da345fcd3a..d78f57141c6 100644 --- a/test/powershell/Modules/Microsoft.PowerShell.Utility/WebCmdlets.Tests.ps1 +++ b/test/powershell/Modules/Microsoft.PowerShell.Utility/WebCmdlets.Tests.ps1 @@ -617,7 +617,7 @@ Describe "Invoke-WebRequest tests" -Tags "Feature", "RequireAdminOnWindows" { param($proxy_address, $name, $protocol) # use external url, but with proxy the external url should not actually be called - $command = "Invoke-WebRequest -Uri ${protocol}://httpbin.org -Proxy '${protocol}://${proxy_address}' -SkipCertificateCheck" + $command = "Invoke-WebRequest -Uri ${protocol}://httpbin.org -Proxy '${protocol}://${proxy_address}'" $result = ExecuteWebCommand -command $command $command = "Invoke-WebRequest -Uri '${protocol}://${proxy_address}' -NoProxy" $expectedResult = ExecuteWebCommand -command $command @@ -969,6 +969,42 @@ Describe "Invoke-WebRequest tests" -Tags "Feature", "RequireAdminOnWindows" { $response.Content.Headers."Authorization" | Should -BeExactly "test" } + It "Validates Invoke-WebRequest with -WebSession and -PreserveAuthorizationOnRedirect doesn't change session variable on multiple redirects: " -TestCases $redirectTests { + param($redirectType) + + #[SuppressMessage("Microsoft.Security", "CS002:SecretInNextLine", Justification="Demo/doc/test secret.")] + $token = "testpassword" | ConvertTo-SecureString -AsPlainText -Force + $credential = [pscredential]::new("testuser", $token) + $certificate = Get-WebListenerClientCertificate + $headers = @{"Authorization" = "test"} + $proxy = (Get-WebListenerUrl).Authority + $uri = Get-WebListenerUrl -Test 'Redirect' -TestValue 2 -Query @{type = $redirectType} + + $session = [Microsoft.PowerShell.Commands.WebRequestSession]::new() + $session.MaximumRedirection = 2 + $session.MaximumRetryCount = 2 + $session.RetryIntervalInSeconds = 2 + $session.UseDefaultCredentials = $true + $null = Invoke-WebRequest -Uri $uri -PreserveAuthorizationOnRedirect -WebSession $session -AllowUnencryptedAuthentication -Headers $headers + $session.MaximumRedirection | Should -BeExactly 2 + $session.MaximumRetryCount | Should -BeExactly 2 + $session.RetryIntervalInSeconds | Should -BeExactly 2 + $session.UseDefaultCredentials | Should -BeExactly $true + + $session = [Microsoft.PowerShell.Commands.WebRequestSession]::new() + $session.Credentials = $credential + $session.Certificates = [System.Security.Cryptography.X509Certificates.X509CertificateCollection]::new([X509Certificate]$certificate) + $null = Invoke-WebRequest -Uri $uri -PreserveAuthorizationOnRedirect -WebSession $session -SkipCertificateCheck -Headers $headers + $session.Credentials.UserName | Should -BeExactly $credential.UserName + $session.Credentials.Password | Should -BeExactly $credential.GetNetworkCredential().Password + $session.Certificates.Thumbprint | Should -BeExactly $certificate.Thumbprint + + $session = [Microsoft.PowerShell.Commands.WebRequestSession]::new() + $session.Proxy = [System.Net.WebProxy]::new($proxy) + $null = Invoke-WebRequest -Uri $uri -PreserveAuthorizationOnRedirect -WebSession $session -Headers $headers + $session.Proxy.GetProxy($uri).Authority | Should -BeExactly $proxy + } + It "Validates Invoke-WebRequest strips the authorization header on various redirects: " -TestCases $redirectTests { param($redirectType) $uri = Get-WebListenerUrl -Test 'Redirect' -Query @{type = $redirectType} @@ -2706,6 +2742,42 @@ Describe "Invoke-RestMethod tests" -Tags "Feature", "RequireAdminOnWindows" { $response.Content.Headers."Authorization" | Should -BeExactly "test" } + It "Validates Invoke-RestMethod with -WebSession and -PreserveAuthorizationOnRedirect doesn't change session variable on multiple redirects: " -TestCases $redirectTests { + param($redirectType) + + #[SuppressMessage("Microsoft.Security", "CS002:SecretInNextLine", Justification="Demo/doc/test secret.")] + $token = "testpassword" | ConvertTo-SecureString -AsPlainText -Force + $credential = [pscredential]::new("testuser", $token) + $certificate = Get-WebListenerClientCertificate + $headers = @{"Authorization" = "test"} + $proxy = (Get-WebListenerUrl).Authority + $uri = Get-WebListenerUrl -Test 'Redirect' -TestValue 2 -Query @{type = $redirectType} + + $session = [Microsoft.PowerShell.Commands.WebRequestSession]::new() + $session.MaximumRedirection = 2 + $session.MaximumRetryCount = 2 + $session.RetryIntervalInSeconds = 2 + $session.UseDefaultCredentials = $true + $null = Invoke-RestMethod -Uri $uri -PreserveAuthorizationOnRedirect -WebSession $session -AllowUnencryptedAuthentication -Headers $headers + $session.MaximumRedirection | Should -BeExactly 2 + $session.MaximumRetryCount | Should -BeExactly 2 + $session.RetryIntervalInSeconds | Should -BeExactly 2 + $session.UseDefaultCredentials | Should -BeExactly $true + + $session = [Microsoft.PowerShell.Commands.WebRequestSession]::new() + $session.Credentials = $credential + $session.Certificates = [System.Security.Cryptography.X509Certificates.X509CertificateCollection]::new([X509Certificate]$certificate) + $null = Invoke-RestMethod -Uri $uri -PreserveAuthorizationOnRedirect -WebSession $session -SkipCertificateCheck -Headers $headers + $session.Credentials.UserName | Should -BeExactly $credential.UserName + $session.Credentials.Password | Should -BeExactly $credential.GetNetworkCredential().Password + $session.Certificates.Thumbprint | Should -BeExactly $certificate.Thumbprint + + $session = [Microsoft.PowerShell.Commands.WebRequestSession]::new() + $session.Proxy = [System.Net.WebProxy]::new($proxy) + $null = Invoke-RestMethod -Uri $uri -PreserveAuthorizationOnRedirect -WebSession $session -Headers $headers + $session.Proxy.GetProxy($uri).Authority | Should -BeExactly $proxy + } + It "Validates Invoke-RestMethod strips the authorization header on various redirects: " -TestCases $redirectTests { param($redirectType) $uri = Get-WebListenerUrl -Test 'Redirect' -Query @{type = $redirectType}