Merge branch 'release/v7.0.0-preview.2' into master

# Conflicts:
#	test/nanoserver/nanoserver.tests.ps1
This commit is contained in:
Travis Plunk 2019-07-17 14:30:34 -07:00
commit 6f0dacddc1
No known key found for this signature in database
GPG key ID: B60A25843F0C2CD1
12 changed files with 481 additions and 138 deletions

View file

@ -699,6 +699,7 @@ timcurwick
timestamp
timothywlewis
-title
tokenizing
tomconte
toolchain
toolset
@ -716,6 +717,7 @@ unregister-event
unregister-packagesource
unregister-psrepository
unregister-pssessionconfiguration
untracked
un-versioned
update-formatdata
update-modulemanifest
@ -831,6 +833,7 @@ SytzeAndr
yashrajbharti
- CHANGELOG.md
aavdberg
asrosent
azkarmoulana
chucklu
Claustn
@ -843,8 +846,11 @@ jeis2497052
Jocapear
lassehastrup
markwragg
nbkalex
NeoBeum
nycjan
paalbra
robdy
SeeminglyScience
StingyJack
ThreeFive-O

View file

@ -1,5 +1,129 @@
# Changelog
## v7.0.0-preview.2 - 07/15/2019
### Breaking Changes
- Cleanup workflow - remove `PSProxyJob` (#10083) (Thanks @iSazonov!)
- Disable `Enter-PSHostProcess` cmdlet when system in lock down mode (Internal 9168)
### Engine Updates and Fixes
- Consider `DBNull.Value` and `NullString.Value` the same as `$null` when comparing with `$null` and casting to bool (#9794) (Thanks @vexx32!)
- Allow methods to be named after keywords (#9812) (Thanks @vexx32!)
- Create `JumpList` in `STA` thread as some `COM` `APIs` are strictly `STA` only to avoid sporadic `CLR` crashes (#9928) (#10057) (Thanks @bergmeister!)
- Skip `JumpList` on `NanoServer` and `IoT` (#10164)
- Display `COM` method signature with argument names (#9858) (Thanks @nbkalex!)
- Use the original precision (prior-dotnet-core-3) for double/float-to-string conversion (#9893)
- `Import-DscResource` should allow to overwrite DSC built-in resources. (#9879)
- Add ability to pass `InitialSessionState` to the `ConsoleShell.Start` (#9802) (Thanks @asrosent!)
- Have console host not enter command prompt mode when using `Read-Host -Prompt` (#9743)
- Fix use of `Start-Process http://bing.com` (#9793)
- Support negative numbers in `-split` operator (#8960) (Thanks @ece-jacob-scott!)
### General Cmdlet Updates and Fixes
- Support DSC compilation on Linux. (#9834)
- Add alias for Service `StartType` (#9940) (Thanks @NeoBeum!)
- Add `-SecurityDescriptorSddl` parameter to `Set-Service` (#8626) (Thanks @kvprasoon!)
- Fix auto-download of files when enumerating files from a `OneDrive` folder (#9895)
- Set request headers when request body is empty in Web Cmdlets (#10034) (Thanks @markekraus!)
- Fix wrong comparison in `CertificateProvider` (#9987) (Thanks @iSazonov!)
- Sync docs changes into the embedded help for `pwsh` (#9952)
- Display Duration when displaying `HistoryInfo` (#9751) (Thanks @rkeithhill!)
- Update console startup and help `url` for PowerShell docs (#9775)
- Make `UseAbbreviationExpansion` and `TempDrive` official features (#9872)
- Fix `Get-ChildItem -Path` with wildcard `char` (#9257) (Thanks @kwkam!)
### Performance
- Add another fast path to `WildcardPattern.IsMatch` for patterns that only have an asterisk in the end (#10054) (Thanks @iSazonov!)
- Move some of the creations of `WildcardPattern` in outer loop to avoid unnecessary allocation (#10053) (Thanks @iSazonov!)
- Make `Foreach-Object` 2 times faster by reducing unnecessary allocations and boxing (#10047)
- Use a static cache for `PSVersionInfo.PSVersion` to avoid casting `SemanticVersion` to `Version` every time accessing that property (#10028)
- Reduce allocations in `NavigationCmdletProvider.NormalizePath()` (#10038) (Thanks @iSazonov!)
- Add fast path for wildcard patterns that contains no wildcard characters (#10020)
- Avoid `Assembly.GetName()` in `ClrFacade.GetAssemblies(string)` to reduce allocations of `CultureInfo` objects (#10024) (Thanks @iSazonov!)
- Avoid the `int[]` and `int[,]` allocation when tokenizing line comments and matching wildcard pattern (#10009)
### Tools
- Update change log generation tool to deal with private commits (#10096)
- Update `Start-PSBuild -Clean` logic of `git clean` to ignore locked files from `VS2019` (#10071) (Thanks @bergmeister!)
- Indent fix in `markdown-link.tests.ps1` (#10049) (Thanks @RDIL!)
- `Start-PSBuild -Clean` does not remove all untracked files (#10022) (Thanks @vexx32!)
- Add module to support Pester tests for automating debugger commands (`stepInto`, `stepOut`, etc.), along with basic tests (#9825) (Thanks @KirkMunro!)
- Remove `markdownlint` tests due to security issues (#10163)
### Code Cleanup
- Cleanup `CompiledScriptBlock.cs` (#9735) (Thanks @vexx32!)
- Cleanup workflow code (#9638) (Thanks @iSazonov!)
- Use `AddOrUpdate()` instead of `Remove` then `Add` to register runspace (#10007) (Thanks @iSazonov!)
- Suppress `PossibleIncorrectUsageOfAssignmentOperator` rule violation by adding extra parenthesis (#9460) (Thanks @xtqqczze!)
- Use `AddRange` in `GetModules()` (#9975) (Thanks @iSazonov!)
- Code cleanup: use `IndexOf(char)` overload (#9722) (Thanks @iSazonov!)
- Move `consts` and methods to single `CharExtensions` class (#9992) (Thanks @iSazonov!)
- Cleanup: Use `EndsWith(char)` and `StartsWith(char)` (#9994) (Thanks @iSazonov!)
- Remove `LCIDToLocaleName` `P/Invoke` from `GetComputerInfoCommand` (#9716) (Thanks @iSazonov!)
- Cleanup Parser tests (#9792) (Thanks @vexx32!)
- Remove `EtwActivity` empty constructor and make minor style fixes (#9958) (Thanks @RDIL!)
- Fix style issues from last commits (#9937) (Thanks @iSazonov!)
- Remove dead code about `IsTransparentProxy` (#9966)
- Fix minor typos in code comments (#9917) (Thanks @RDIL!)
- Style fixes for `CimAsyncOperations` (#9945) (Thanks @RDIL!)
- Fix minor `CodeFactor` style issues in `ModuleCmdletBase` (#9915) (Thanks @RDIL!)
- Clean up the use of `SetProfileRoot` and `StartProfile` in ConsoleHost (#9931)
- Fix minor style issues come from last commits (#9640) (Thanks @iSazonov!)
- Improve whitespace for Parser tests (#9806) (Thanks @vexx32!)
- Use new `string.ConCat()` in `Process.cs` (#9720) (Thanks @iSazonov!)
- Code Cleanup: Tidy up `scriptblock.cs` (#9732) (Thanks @vexx32!)
### Tests
- Mark `Set-Service` tests with password as `Pending` (#10146)
- Fix test password generation rule to meet Windows complexity requirements (#10143)
- Add test for `New-Item -Force` (#9971) (Thanks @robdy!)
- Fix gulp versions (#9916) (Thanks @RDIL!)
- Indentation fixes in `ci.psm1` (#9947) (Thanks @RDIL!)
- Remove some `Travis-CI` references (#9919) (Thanks @RDIL!)
- Improve release testing Docker images (#9942) (Thanks @RDIL!)
- Use `yarn` to install global tools (#9904) (Thanks @RDIL!)
- Attempt to work around the zip download issue in Azure DevOps Windows CI (#9911)
- Update PowerShell SDK version for hosting tests (Internal 9185)
### Build and Packaging Improvements
- Update the target framework for reference assemblies to `netcoreapp3.0` (#9747)
- Pin version of `netDumbster` to `2.0.0.4` (#9748)
- Fix daily `CodeCoverageAndTest` build by explicitly calling `Start-PSBootStrap` (#9724)
- Split the `fxdependent` package on Windows into two packages (#10134)
- Bump `System.Data.SqlClient` (#10109)
- Bump `System.Security.AccessControl` (#10100)
- Add performance tag to change log command (Internal)
- Upgrade .Net Core 3 SDK from `preview5` to `preview6` and related out of band `Nuget` packages from `2.1` to `3.0-preview6` (#9888) (Thanks @bergmeister!)
- Add to `/etc/shells` on macOS (#10066)
- Bump `Markdig.Signed` from `0.17.0` to `0.17.1` (#10062)
- Update copyright symbol for `NuGet` packages (#9936)
- Download latest version `(6.2.0)` of `PSDesiredStateConfiguration` `nuget` package. (#9932)
- Add automated `RPM` signing to release build (#10013)
- Bump `ThreadJob` from `1.1.2` to `2.0.1` in `/src/Modules` (#10003)
- Bump `PowerShellGet` from `2.1.4` to `2.2` in /src/Modules (#9933) (#10085)
- Bump `PackageManagement` from `1.4` to `1.4.3` in `/src/Modules` (#9820) (#9918) (#10084)
- Update to use `TSAv2` (#9914)
- Bump `NJsonSchema` from `9.14.1` to `10.0.21` (#9805) (#9843) (#9854) (#9862) (#9875) (#9885) (#9954) (#10017)
- Bump `System.Net.Http.WinHttpHandler` from `4.5.3` to `4.5.4` (#9786)
- Bump `Microsoft.ApplicationInsights` from `2.9.1` to `2.10.0` (#9757)
- Increase timeout of NuGet job to workaround build timeout (#9772)
### Documentation and Help Content
- Change log `6.1.4` (#9759)
- Change log for release `6.2.1` (#9760)
- Add quick steps for adding docs to cmdlets (#9978)
- Update readme `gitter` badge (#9920) (Thanks @RDIL!)
- Update `README` and `metadata.json` for `7.0.0-preview.1` release (#9767)
## v7.0.0-preview.1 - 05/30/2019
### Breaking Changes

View file

@ -226,7 +226,7 @@ namespace Microsoft.PowerShell.Commands
if (s_imageRegex == null)
{
s_imageRegex = new Regex(@"<img\s+[^>]*>",
s_imageRegex = new Regex(@"<img\s+[^\s>]*>",
RegexOptions.Singleline | RegexOptions.IgnoreCase | RegexOptions.Compiled);
}
}

View file

@ -417,7 +417,10 @@ namespace Microsoft.PowerShell.Commands
return result;
}
private static readonly Regex s_metaexp = new Regex(@"<meta\s[.\n]*[^><]*charset\s*=\s*[""'\n]?(?<charset>[A-Za-z].[^\s""'\n<>]*)[\s""'\n>]");
private static readonly Regex s_metaexp = new Regex(
@"<meta\s.*[^.><]*charset\s*=\s*[""'\n]?(?<charset>[A-Za-z].[^\s""'\n<>]*)[\s""'\n>]",
RegexOptions.Compiled | RegexOptions.Singleline | RegexOptions.ExplicitCapture | RegexOptions.CultureInvariant | RegexOptions.IgnoreCase
);
internal static string DecodeStream(Stream stream, ref Encoding encoding)
{

View file

@ -13,6 +13,7 @@ using System.Management.Automation.Host;
using System.Management.Automation.Internal;
using System.Management.Automation.Remoting;
using System.Management.Automation.Runspaces;
using System.Management.Automation.Security;
using System.Text;
namespace Microsoft.PowerShell.Commands
@ -126,6 +127,19 @@ namespace Microsoft.PowerShell.Commands
/// </summary>
protected override void EndProcessing()
{
// Check if system is in locked down mode, in which case this cmdlet is disabled.
if (SystemPolicy.GetSystemLockdownPolicy() == SystemEnforcementMode.Enforce)
{
WriteError(
new ErrorRecord(
new PSSecurityException(RemotingErrorIdStrings.EnterPSHostProcessCmdletDisabled),
"EnterPSHostProcessCmdletDisabled",
ErrorCategory.SecurityError,
null));
return;
}
// Check for host that supports interactive remote sessions.
_interactiveHost = this.Host as IHostSupportsInteractiveSession;
if (_interactiveHost == null)

View file

@ -1684,4 +1684,7 @@ All WinRM sessions connected to PowerShell session configurations, such as Micro
<data name="PSCoreRemotingEnableWarning" xml:space="preserve">
<value>PowerShell remoting has been enabled only for PowerShell 6+ configurations and does not affect Windows PowerShell remoting configurations. Run this cmdlet in Windows PowerShell to affect all PowerShell remoting configurations.</value>
</data>
<data name="EnterPSHostProcessCmdletDisabled" xml:space="preserve">
<value>Enter-PSHostProcess cmdlet is disabled because an application control policy such as 'AppLocker' or 'Windows Defender Application Control' is in enforcement.</value>
</data>
</root>

View file

@ -18,7 +18,7 @@
<PackageReference Include="xunit" Version="2.4.0" />
<PackageReference Include="XunitXml.TestLogger" Version="2.0.0" />
<!-- The version of Microsoft.PowerShell.SDK should be the version we are releasing, so the tests use the correct SDK before publishing to NuGet.org -->
<PackageReference Include="Microsoft.PowerShell.SDK" Version="7.0.0-preview.1" />
<PackageReference Include="Microsoft.PowerShell.SDK" Version="7.0.0-preview.2" />
<PackageReference Include="Xunit.SkippableFact" Version="1.3.6" />
<PackageReference Include="xunit.runner.visualstudio" Version="2.4.0" />
</ItemGroup>

View file

@ -1164,6 +1164,31 @@ try
}
}
Describe "Enter-PSHostProcess cmdlet should be disabled on locked down systems" -Tags 'Feature','RequireAdminOnWindows' {
It "Verifies that Enter-PSHostProcess is disabled with lock down policy" {
$expectedError = $null
try
{
Invoke-LanguageModeTestingSupportCmdlet -SetLockdownMode
$ExecutionContext.SessionState.LanguageMode = "ConstrainedLanguage"
Enter-PSHostProcess -Id 5555 -ErrorAction Stop
}
catch
{
$expectedError = $_
}
finally
{
Invoke-LanguageModeTestingSupportCmdlet -RevertLockdownMode -EnableFullLanguageMode
}
$expectedError.FullyQualifiedErrorId | Should -BeExactly 'EnterPSHostProcessCmdletDisabled,Microsoft.PowerShell.Commands.EnterPSHostProcessCommand'
}
}
# End Describe blocks
}
finally

View file

@ -780,90 +780,89 @@ Describe "Invoke-WebRequest tests" -Tags "Feature", "RequireAdminOnWindows" {
$result.Output.RelationLink["next"] | Should -BeExactly "${baseUri}?maxlinks=3&linknumber=2&type=${type}"
}
#region Redirect tests
Context "Redirect" {
It "Validates Invoke-WebRequest with -PreserveAuthorizationOnRedirect preserves the authorization header on redirect: <redirectType> <redirectedMethod>" -TestCases $redirectTests {
param($redirectType, $redirectedMethod)
$uri = Get-WebListenerUrl -Test 'Redirect' -Query @{type = $redirectType}
$response = ExecuteRedirectRequest -Uri $uri -PreserveAuthorizationOnRedirect
It "Validates Invoke-WebRequest with -PreserveAuthorizationOnRedirect preserves the authorization header on redirect: <redirectType> <redirectedMethod>" -TestCases $redirectTests {
param($redirectType, $redirectedMethod)
$uri = Get-WebListenerUrl -Test 'Redirect' -Query @{type = $redirectType}
$response = ExecuteRedirectRequest -Uri $uri -PreserveAuthorizationOnRedirect
$response.Error | Should -BeNullOrEmpty
$response.Content.Headers."Authorization" | Should -BeExactly "test"
}
$response.Error | Should -BeNullOrEmpty
$response.Content.Headers."Authorization" | Should -BeExactly "test"
It "Validates Invoke-WebRequest preserves the authorization header on multiple redirects: <redirectType>" -TestCases $redirectTests {
param($redirectType)
$uri = Get-WebListenerUrl -Test 'Redirect' -TestValue 3 -Query @{type = $redirectType}
$response = ExecuteRedirectRequest -Uri $uri -PreserveAuthorizationOnRedirect
$response.Error | Should -BeNullOrEmpty
$response.Content.Headers."Authorization" | Should -BeExactly "test"
}
It "Validates Invoke-WebRequest strips the authorization header on various redirects: <redirectType>" -TestCases $redirectTests {
param($redirectType)
$uri = Get-WebListenerUrl -Test 'Redirect' -Query @{type = $redirectType}
$response = ExecuteRedirectRequest -Uri $uri
$response.Error | Should -BeNullOrEmpty
# ensure user-agent is present (i.e., no false positives )
$response.Content.Headers."User-Agent" | Should -Not -BeNullOrEmpty
# ensure Authorization header has been removed.
$response.Content.Headers."Authorization" | Should -BeNullOrEmpty
}
# NOTE: Only testing redirection of POST -> GET for unique underlying values of HttpStatusCode.
# Some names overlap in underlying value.
It "Validates Invoke-WebRequest strips the authorization header redirects and switches from POST to GET when it handles the redirect: <redirectType> <redirectedMethod>" -TestCases $redirectTests {
param($redirectType, $redirectedMethod)
$uri = Get-WebListenerUrl -Test 'Redirect' -Query @{type = $redirectType}
$response = ExecuteRedirectRequest -Uri $uri -Method 'POST'
$response.Error | Should -BeNullOrEmpty
# ensure user-agent is present (i.e., no false positives )
$response.Content.Headers."User-Agent" | Should -Not -BeNullOrEmpty
# ensure Authorization header has been removed.
$response.Content.Headers."Authorization" | Should -BeNullOrEmpty
# ensure POST was changed to GET for selected redirections and remains as POST for others.
$response.Content.Method | Should -Be $redirectedMethod
}
It "Validates Invoke-WebRequest -PreserveAuthorizationOnRedirect keeps the authorization header redirects and switches from POST to GET when it handles the redirect: <redirectType> <redirectedMethod>" -TestCases $redirectTests {
param($redirectType, $redirectedMethod)
$uri = Get-WebListenerUrl -Test 'Redirect' -Query @{type = $redirectType}
$response = ExecuteRedirectRequest -PreserveAuthorizationOnRedirect -Uri $uri -Method 'POST'
$response.Error | Should -BeNullOrEmpty
# ensure user-agent is present (i.e., no false positives )
$response.Content.Headers."User-Agent" | Should -Not -BeNullOrEmpty
# ensure Authorization header has been removed.
$response.Content.Headers."Authorization" | Should -BeExactly 'test'
# ensure POST was changed to GET for selected redirections and remains as POST for others.
$response.Content.Method | Should -Be $redirectedMethod
}
It "Validates Invoke-WebRequest handles responses without Location header for requests with Authorization header and redirect: <redirectType>" -TestCases $redirectTests {
param($redirectType, $redirectedMethod)
# Skip relative test as it is not a valid response type.
if ($redirectType -eq 'relative') { return }
# When an Authorization request header is present,
# and -PreserveAuthorizationOnRedirect is not present,
# PowerShell should throw an HTTP Response Exception
# for a redirect response which does not contain a Location response header.
# The correct redirect status code should be included in the exception.
$StatusCode = [int][System.Net.HttpStatusCode]$redirectType
$uri = Get-WebListenerUrl -Test Response -Query @{statuscode = $StatusCode}
$command = "Invoke-WebRequest -Uri '$uri' -Headers @{Authorization = 'foo'}"
$response = ExecuteWebCommand -command $command
$response.Error.Exception | Should -BeOfType 'Microsoft.PowerShell.Commands.HttpResponseException'
$response.Error.Exception.Response.StatusCode | Should -Be $StatusCode
$response.Error.Exception.Response.Headers.Location | Should -BeNullOrEmpty
}
}
It "Validates Invoke-WebRequest preserves the authorization header on multiple redirects: <redirectType>" -TestCases $redirectTests {
param($redirectType)
$uri = Get-WebListenerUrl -Test 'Redirect' -TestValue 3 -Query @{type = $redirectType}
$response = ExecuteRedirectRequest -Uri $uri -PreserveAuthorizationOnRedirect
$response.Error | Should -BeNullOrEmpty
$response.Content.Headers."Authorization" | Should -BeExactly "test"
}
It "Validates Invoke-WebRequest strips the authorization header on various redirects: <redirectType>" -TestCases $redirectTests {
param($redirectType)
$uri = Get-WebListenerUrl -Test 'Redirect' -Query @{type = $redirectType}
$response = ExecuteRedirectRequest -Uri $uri
$response.Error | Should -BeNullOrEmpty
# ensure user-agent is present (i.e., no false positives )
$response.Content.Headers."User-Agent" | Should -Not -BeNullOrEmpty
# ensure Authorization header has been removed.
$response.Content.Headers."Authorization" | Should -BeNullOrEmpty
}
# NOTE: Only testing redirection of POST -> GET for unique underlying values of HttpStatusCode.
# Some names overlap in underlying value.
It "Validates Invoke-WebRequest strips the authorization header redirects and switches from POST to GET when it handles the redirect: <redirectType> <redirectedMethod>" -TestCases $redirectTests {
param($redirectType, $redirectedMethod)
$uri = Get-WebListenerUrl -Test 'Redirect' -Query @{type = $redirectType}
$response = ExecuteRedirectRequest -Uri $uri -Method 'POST'
$response.Error | Should -BeNullOrEmpty
# ensure user-agent is present (i.e., no false positives )
$response.Content.Headers."User-Agent" | Should -Not -BeNullOrEmpty
# ensure Authorization header has been removed.
$response.Content.Headers."Authorization" | Should -BeNullOrEmpty
# ensure POST was changed to GET for selected redirections and remains as POST for others.
$response.Content.Method | Should -Be $redirectedMethod
}
It "Validates Invoke-WebRequest -PreserveAuthorizationOnRedirect keeps the authorization header redirects and switches from POST to GET when it handles the redirect: <redirectType> <redirectedMethod>" -TestCases $redirectTests {
param($redirectType, $redirectedMethod)
$uri = Get-WebListenerUrl -Test 'Redirect' -Query @{type = $redirectType}
$response = ExecuteRedirectRequest -PreserveAuthorizationOnRedirect -Uri $uri -Method 'POST'
$response.Error | Should -BeNullOrEmpty
# ensure user-agent is present (i.e., no false positives )
$response.Content.Headers."User-Agent" | Should -Not -BeNullOrEmpty
# ensure Authorization header has been removed.
$response.Content.Headers."Authorization" | Should -BeExactly 'test'
# ensure POST was changed to GET for selected redirections and remains as POST for others.
$response.Content.Method | Should -Be $redirectedMethod
}
It "Validates Invoke-WebRequest handles responses without Location header for requests with Authorization header and redirect: <redirectType>" -TestCases $redirectTests {
param($redirectType, $redirectedMethod)
# Skip relative test as it is not a valid response type.
if ($redirectType -eq 'relative') { return }
# When an Authorization request header is present,
# and -PreserveAuthorizationOnRedirect is not present,
# PowerShell should throw an HTTP Response Exception
# for a redirect response which does not contain a Location response header.
# The correct redirect status code should be included in the exception.
$StatusCode = [int][System.Net.HttpStatusCode]$redirectType
$uri = Get-WebListenerUrl -Test Response -Query @{statuscode = $StatusCode}
$command = "Invoke-WebRequest -Uri '$uri' -Headers @{Authorization = 'foo'}"
$response = ExecuteWebCommand -command $command
$response.Error.Exception | Should -BeOfType 'Microsoft.PowerShell.Commands.HttpResponseException'
$response.Error.Exception.Response.StatusCode | Should -Be $StatusCode
$response.Error.Exception.Response.Headers.Location | Should -BeNullOrEmpty
}
#endregion Redirect tests
Context "Invoke-WebRequest SkipHeaderVerification Tests" {
BeforeAll {
@ -1168,75 +1167,76 @@ Describe "Invoke-WebRequest tests" -Tags "Feature", "RequireAdminOnWindows" {
#endregion charset encoding tests
#region Content Header Inclusion
Context "Content Header" {
It "Verifies Invoke-WebRequest includes Content headers in Headers property" {
$query = @{
contenttype = 'text/plain'
body = 'OK'
}
$uri = Get-WebListenerUrl -Test 'Response' -Query $query
$command = "Invoke-WebRequest -Uri '$uri'"
$result = ExecuteWebCommand -command $command
ValidateResponse $result
It "Verifies Invoke-WebRequest includes Content headers in Headers property" {
$query = @{
contenttype = 'text/plain'
body = 'OK'
$result.Output.Headers.'Content-Type' | Should -Be 'text/plain'
$result.Output.Headers.'Content-Length' | Should -Be 2
}
$uri = Get-WebListenerUrl -Test 'Response' -Query $query
$command = "Invoke-WebRequest -Uri '$uri'"
$result = ExecuteWebCommand -command $command
ValidateResponse $result
$result.Output.Headers.'Content-Type' | Should -Be 'text/plain'
$result.Output.Headers.'Content-Length' | Should -Be 2
}
It "Verifies Invoke-WebRequest includes Content headers in RawContent property" {
$query = @{
contenttype = 'text/plain'
body = 'OK'
}
$uri = Get-WebListenerUrl -Test 'Response' -Query $query
$command = "Invoke-WebRequest -Uri '$uri'"
$result = ExecuteWebCommand -command $command
ValidateResponse $result
It "Verifies Invoke-WebRequest includes Content headers in RawContent property" {
$query = @{
contenttype = 'text/plain'
body = 'OK'
$result.Output.RawContent | Should -Match ([regex]::Escape('Content-Type: text/plain'))
$result.Output.RawContent | Should -Match ([regex]::Escape('Content-Length: 2'))
}
$uri = Get-WebListenerUrl -Test 'Response' -Query $query
$command = "Invoke-WebRequest -Uri '$uri'"
$result = ExecuteWebCommand -command $command
ValidateResponse $result
$result.Output.RawContent | Should -Match ([regex]::Escape('Content-Type: text/plain'))
$result.Output.RawContent | Should -Match ([regex]::Escape('Content-Length: 2'))
}
It "Verifies Invoke-WebRequest Supports Multiple response headers with same name" {
$query = @{
contenttype = 'text/plain'
body = 'OK'
headers = @{
'X-Fake-Header' = @('testvalue01', 'testvalue02')
} | ConvertTo-Json -Compress
}
$uri = Get-WebListenerUrl -Test 'Response' -Query $query
$command = "Invoke-WebRequest -Uri '$uri'"
$result = ExecuteWebCommand -command $command
ValidateResponse $result
It "Verifies Invoke-WebRequest Supports Multiple response headers with same name" {
$query = @{
contenttype = 'text/plain'
body = 'OK'
headers = @{
'X-Fake-Header' = @('testvalue01', 'testvalue02')
} | ConvertTo-Json -Compress
$result.Output.Headers.'X-Fake-Header'.Count | Should -Be 2
$result.Output.Headers.'X-Fake-Header'.Contains('testvalue01') | Should -BeTrue
$result.Output.Headers.'X-Fake-Header'.Contains('testvalue02') | Should -BeTrue
$result.Output.RawContent | Should -Match ([regex]::Escape('X-Fake-Header: testvalue01'))
$result.Output.RawContent | Should -Match ([regex]::Escape('X-Fake-Header: testvalue02'))
}
$uri = Get-WebListenerUrl -Test 'Response' -Query $query
$command = "Invoke-WebRequest -Uri '$uri'"
$result = ExecuteWebCommand -command $command
ValidateResponse $result
$result.Output.Headers.'X-Fake-Header'.Count | Should -Be 2
$result.Output.Headers.'X-Fake-Header'.Contains('testvalue01') | Should -BeTrue
$result.Output.Headers.'X-Fake-Header'.Contains('testvalue02') | Should -BeTrue
$result.Output.RawContent | Should -Match ([regex]::Escape('X-Fake-Header: testvalue01'))
$result.Output.RawContent | Should -Match ([regex]::Escape('X-Fake-Header: testvalue02'))
}
It "Verifies Invoke-WebRequest does not sent expect 100-continue headers by default" {
$uri = Get-WebListenerUrl -Test 'Get'
It "Verifies Invoke-WebRequest does not sent expect 100-continue headers by default" {
$uri = Get-WebListenerUrl -Test 'Get'
$response = Invoke-WebRequest -Uri $uri
$result = $response.Content | ConvertFrom-Json
$response = Invoke-WebRequest -Uri $uri
$result = $response.Content | ConvertFrom-Json
$result.headers.Expect | Should -BeNullOrEmpty
$result.method | Should -BeExactly "GET"
$result.url | Should -BeExactly $uri.ToString()
}
$result.headers.Expect | Should -BeNullOrEmpty
$result.method | Should -BeExactly "GET"
$result.url | Should -BeExactly $uri.ToString()
}
It "Verifies Invoke-WebRequest sends expect 100-continue header when defined in -Headers" {
$uri = Get-WebListenerUrl -Test 'Get'
It "Verifies Invoke-WebRequest sends expect 100-continue header when defined in -Headers" {
$uri = Get-WebListenerUrl -Test 'Get'
$response = Invoke-WebRequest -Uri $uri -Headers @{Expect = '100-continue'}
$result = $response.Content | ConvertFrom-Json
$response = Invoke-WebRequest -Uri $uri -Headers @{Expect = '100-continue'}
$result = $response.Content | ConvertFrom-Json
$result.headers.Expect | Should -BeExactly '100-continue'
$result.method | Should -BeExactly "GET"
$result.url | Should -BeExactly $uri.ToString()
$result.headers.Expect | Should -BeExactly '100-continue'
$result.method | Should -BeExactly "GET"
$result.url | Should -BeExactly $uri.ToString()
}
}
#endregion Content Header Inclusion
@ -1865,6 +1865,66 @@ Describe "Invoke-WebRequest tests" -Tags "Feature", "RequireAdminOnWindows" {
$jsonResult.SessionId | Should -BeExactly $sessionId
}
}
Context "Denial of service" -Tag 'DOS' {
It "Image Parsing" {
$dosUri = Get-WebListenerUrl -Test 'Dos' -query @{
dosType='img'
dosLength='5000'
}
$script:content = ''
[TimeSpan] $timeSpan = Measure-Command {
$response = Invoke-WebRequest -Uri $dosUri
$script:content = $response.content
$response.Images | out-null
}
$script:content | should -Not -BeNullOrEmpty
# pathological regex
$regex = [RegEx]::new('<img\s+[^>]*>')
[TimeSpan] $pathologicalTimeSpan = Measure-Command {
$regex.Match($content)
}
$pathologicalRatio = $pathologicalTimeSpan.TotalMilliseconds/$timeSpan.TotalMilliseconds
Write-Verbose "Pathological ratio: $pathologicalRatio" -Verbose
# dosLength 4,000 on my 3.5 GHz 6-Core Intel Xeon E5 macpro produced a ratio of 12
# dosLength 5,000 on my 3.5 GHz 6-Core Intel Xeon E5 macpro produced a ratio of 21
# dosLength 10,000 on my 3.5 GHz 6-Core Intel Xeon E5 macpro produced a ratio of 75
$pathologicalRatio | Should -BeGreaterThan 10
}
It "Charset Parsing" {
$dosUri = Get-WebListenerUrl -Test 'Dos' -query @{
dosType='charset'
dosLength='2850'
}
$script:content = ''
[TimeSpan] $timeSpan = Measure-Command {
$response = Invoke-WebRequest -Uri $dosUri
$script:content = $response.content
}
# Pathological regex
$regex = [RegEx]::new('<meta\s[.\n]*[^><]*charset\s*=\s*["''\n]?(?<charset>[A-Za-z].[^\s"''\n<>]*)[\s"''\n>]')
$script:content | should -Not -BeNullOrEmpty
[TimeSpan] $pathologicalTimeSpan = Measure-Command {
$regex.Match($content)
}
$pathologicalRatio = $pathologicalTimeSpan.TotalMilliseconds/$timeSpan.TotalMilliseconds
Write-Verbose "Pathological ratio: $pathologicalRatio" -Verbose
# dosLength 2,750 on my 3.5 GHz 6-Core Intel Xeon E5 macpro produced a ratio of 13
# dosLength 2,850 on my 3.5 GHz 6-Core Intel Xeon E5 macpro produced a ratio of 22
# dosLength 3,000 on my 3.5 GHz 6-Core Intel Xeon E5 macpro produced a ratio of 31
$pathologicalRatio | Should -BeGreaterThan 10
}
}
}
Describe "Invoke-RestMethod tests" -Tags "Feature", "RequireAdminOnWindows" {

View file

@ -217,6 +217,7 @@ function Get-WebListenerUrl {
'Compression',
'Delay',
'Delete',
'Dos',
'Encoding',
'Get',
'Home',

View file

@ -0,0 +1,71 @@
// Copyright (c) Microsoft Corporation. All rights reserved.
// Licensed under the MIT License.
using System;
using System.Diagnostics;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using Microsoft.AspNetCore.Mvc;
using Microsoft.AspNetCore.Http;
using Microsoft.Extensions.Primitives;
using mvc.Models;
namespace mvc.Controllers
{
public class DosController : Controller
{
public string Index()
{
string output = string.Empty;
string contentType = Constants.ApplicationJson;
Response.StatusCode = 200;
StringValues dosType;
if (Request.Query.TryGetValue("dosType", out dosType))
{
output = dosType.FirstOrDefault();
}
StringValues dosLengths;
Int32 dosLength =1;
if (Request.Query.TryGetValue("dosLength", out dosLengths))
{
Int32.TryParse(dosLengths.FirstOrDefault(), out dosLength);
}
string body = string.Empty;
switch(dosType)
{
case "img":
contentType = "text/html; charset=utf8";
body = "<img" + (new String(' ', dosLength));
break;
case "charset":
contentType = "text/html; charset=melon";
body = "<meta " + (new String('.', dosLength));
break;
default:
throw new InvalidOperationException("Invalid dosType: "+dosType);
}
// Content-Type must be applied right before it is sent to the client or MVC will overwrite.
Response.OnStarting(state =>
{
var httpContext = (HttpContext) state;
httpContext.Response.ContentType = contentType;
return Task.FromResult(0);
}, HttpContext);
Response.ContentLength = Encoding.UTF8.GetBytes(body).Length;
return body;
}
public IActionResult Error()
{
return View(new ErrorViewModel { RequestId = Activity.Current?.Id ?? HttpContext.TraceIdentifier });
}
}
}

View file

@ -235,6 +235,42 @@ Invoke-RestMethod -Uri $uri -Body $body -Method 'Delete'
}
```
### /Dos/
Returns HTML designed to create denial of service against specific RegEx Expressions
#### Image Parsing RegEx
```powershell
$uri = Get-WebListenerUrl -Test 'Dos' -query @{
dosType='img'
dosLength='5000'
}
Invoke-RestMethod -Uri $uri -Body $body -Method 'Delete'
```
Return the following followed by 5,000 spaces.
```html
<img
```
#### Charset Parsing RegEx
```powershell
$uri = Get-WebListenerUrl -Test 'Dos' -query @{
dosType='charset'
dosLength='5000'
}
Invoke-RestMethod -Uri $uri -Body $body -Method 'Delete'
```
Return the following followed by 5,000 spaces.
```html
<meta
```
### /Encoding/Utf8/
Returns page containing UTF-8 data.