From f8fb77052fd2a89b6ad018f50c5a89c4c3e33e62 Mon Sep 17 00:00:00 2001
From: "Joel Sallow (/u/ta11ow)" <32407840+vexx32@users.noreply.github.com>
Date: Tue, 24 Mar 2020 13:51:55 -0400
Subject: [PATCH] Fix detection regex in web cmdlets (#12099)
---
.../BasicHtmlWebResponseObject.Common.cs | 2 +-
.../WebCmdlets.Tests.ps1 | 44 +++++++++++++++++++
.../WebListener/Controllers/DosController.cs | 17 ++++---
3 files changed, 56 insertions(+), 7 deletions(-)
diff --git a/src/Microsoft.PowerShell.Commands.Utility/commands/utility/WebCmdlet/Common/BasicHtmlWebResponseObject.Common.cs b/src/Microsoft.PowerShell.Commands.Utility/commands/utility/WebCmdlet/Common/BasicHtmlWebResponseObject.Common.cs
index 54950f841..fd4c4b412 100644
--- a/src/Microsoft.PowerShell.Commands.Utility/commands/utility/WebCmdlet/Common/BasicHtmlWebResponseObject.Common.cs
+++ b/src/Microsoft.PowerShell.Commands.Utility/commands/utility/WebCmdlet/Common/BasicHtmlWebResponseObject.Common.cs
@@ -232,7 +232,7 @@ namespace Microsoft.PowerShell.Commands
if (s_imageRegex == null)
{
- s_imageRegex = new Regex(@"]*>",
+ s_imageRegex = new Regex(@"]*?>",
RegexOptions.Singleline | RegexOptions.IgnoreCase | RegexOptions.Compiled);
}
}
diff --git a/test/powershell/Modules/Microsoft.PowerShell.Utility/WebCmdlets.Tests.ps1 b/test/powershell/Modules/Microsoft.PowerShell.Utility/WebCmdlets.Tests.ps1
index ba1c5e43e..d588674d6 100644
--- a/test/powershell/Modules/Microsoft.PowerShell.Utility/WebCmdlets.Tests.ps1
+++ b/test/powershell/Modules/Microsoft.PowerShell.Utility/WebCmdlets.Tests.ps1
@@ -1900,7 +1900,51 @@ Describe "Invoke-WebRequest tests" -Tags "Feature", "RequireAdminOnWindows" {
}
}
+ Context "Regex Parsing" {
+
+ It 'correctly parses an image with id, class, and src attributes' {
+ $dosUri = Get-WebListenerUrl -Test 'Dos' -query @{
+ dosType = 'img-attribute'
+ }
+
+ $response = Invoke-WebRequest -Uri $dosUri
+ $response.Images | Should -Not -BeNullOrEmpty
+ }
+ }
+
Context "Denial of service" -Tag 'DOS' {
+ It "Image Parsing" {
+ Set-ItResult -Pending -Because "The pathological regex runs fast due to https://github.com/dotnet/runtime/issues/33399. Fixed in .NET 5 preview.2"
+ $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(']*>')
+
+ [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
+ # in some cases we will be running in a Docker container with modest resources
+ $pathologicalRatio | Should -BeGreaterThan 5
+ }
+
It "Charset Parsing" {
$dosUri = Get-WebListenerUrl -Test 'Dos' -query @{
dosType='charset'
diff --git a/test/tools/WebListener/Controllers/DosController.cs b/test/tools/WebListener/Controllers/DosController.cs
index 7966ab39b..864bb3aa8 100644
--- a/test/tools/WebListener/Controllers/DosController.cs
+++ b/test/tools/WebListener/Controllers/DosController.cs
@@ -29,33 +29,38 @@ namespace mvc.Controllers
}
StringValues dosLengths;
- Int32 dosLength =1;
+ Int32 dosLength = 1;
if (Request.Query.TryGetValue("dosLength", out dosLengths))
{
Int32.TryParse(dosLengths.FirstOrDefault(), out dosLength);
}
string body = string.Empty;
- switch(dosType)
+ switch (dosType)
{
case "img":
contentType = "text/html; charset=utf8";
body = "";
+ break;
case "charset":
contentType = "text/html; charset=melon";
body = "
{
- var httpContext = (HttpContext) state;
- httpContext.Response.ContentType = contentType;
- return Task.FromResult(0);
+ var httpContext = (HttpContext)state;
+ httpContext.Response.ContentType = contentType;
+ return Task.FromResult(0);
}, HttpContext);
Response.ContentLength = Encoding.UTF8.GetBytes(body).Length;