diff --git a/docs/release-notes.md b/docs/release-notes.md index a599d51b6..f2bbc474a 100644 --- a/docs/release-notes.md +++ b/docs/release-notes.md @@ -1,5 +1,15 @@ # Release Notes +## v2.7.1 + +```plain +### Bugs +* #990: Fix SMTP attachment name parsing, when the name contains a space + +### Security +* #997: Fix an XSS exploit on the default error pages +``` + ## v2.7.0 ```plain diff --git a/src/Listener/PodeSmtpRequest.cs b/src/Listener/PodeSmtpRequest.cs index eb8771217..1d5e416e5 100644 --- a/src/Listener/PodeSmtpRequest.cs +++ b/src/Listener/PodeSmtpRequest.cs @@ -305,6 +305,7 @@ private Hashtable ParseHeaders(string value) var lines = value.Split(new string[] { PodeHelpers.NEW_LINE }, StringSplitOptions.None); var match = default(Match); + var previousHeader = string.Empty; foreach (var line in lines) { @@ -317,8 +318,17 @@ private Hashtable ParseHeaders(string value) match = Regex.Match(line, "^(?.*?)\\:\\s+(?.*?)$"); if (match.Success) { + previousHeader = match.Groups["name"].Value; headers.Add(match.Groups["name"].Value, match.Groups["value"].Value); } + else + { + match = Regex.Match(line, "^(?.*?)\\:\\s+"); + if (!match.Success) + { + headers[previousHeader] += line; + } + } // boundary line match = Regex.Match(line, "^\\s*boundary=\"?(?.+?)\"?$"); @@ -375,8 +385,8 @@ private void ParseBoundary() var contentDisposition = $"{headers["Content-Disposition"]}"; if (!string.IsNullOrEmpty(contentDisposition) && contentDisposition.ToLowerInvariant().Contains("attachment")) { - var match = Regex.Match(contentType, "name=\"?(?.+)\"?"); - var name = match.Groups["name"].Value; + var match = Regex.Match(contentType, "name=(?.+)"); + var name = match.Groups["name"].Value.Trim('"'); var stream = new MemoryStream(); stream.Write(bodyBytes, 0, bodyBytes.Length); diff --git a/src/Private/Responses.ps1 b/src/Private/Responses.ps1 index 0c6a40954..7e8f955ca 100644 --- a/src/Private/Responses.ps1 +++ b/src/Private/Responses.ps1 @@ -1,6 +1,6 @@ function Show-PodeErrorPage { - param ( + param( [Parameter()] [int] $Code, @@ -29,22 +29,22 @@ function Show-PodeErrorPage $ex = $null if (!(Test-PodeIsEmpty $Exception) -and $PodeContext.Server.Web.ErrorPages.ShowExceptions) { $ex = @{ - 'Message' = [System.Web.HttpUtility]::HtmlEncode($Exception.Exception.Message); - 'StackTrace' = [System.Web.HttpUtility]::HtmlEncode($Exception.ScriptStackTrace); - 'Line' = [System.Web.HttpUtility]::HtmlEncode($Exception.InvocationInfo.PositionMessage); - 'Category' = [System.Web.HttpUtility]::HtmlEncode($Exception.CategoryInfo.ToString()); + Message = [System.Web.HttpUtility]::HtmlEncode($Exception.Exception.Message) + StackTrace = [System.Web.HttpUtility]::HtmlEncode($Exception.ScriptStackTrace) + Line = [System.Web.HttpUtility]::HtmlEncode($Exception.InvocationInfo.PositionMessage) + Category = [System.Web.HttpUtility]::HtmlEncode($Exception.CategoryInfo.ToString()) } } # setup the data object for dynamic pages $data = @{ - 'Url' = (Get-PodeUrl); - 'Status' = @{ - 'Code' = $Code; - 'Description' = $Description; - }; - 'Exception' = $ex; - 'ContentType' = $errorPage.ContentType; + Url = [System.Web.HttpUtility]::HtmlEncode((Get-PodeUrl)) + Status = @{ + Code = $Code + Description = $Description + } + Exception = $ex + ContentType = $errorPage.ContentType } # write the error page to the stream