diff --git a/samples/ErrorHandlerSample/Startup.cs b/samples/ErrorHandlerSample/Startup.cs index 7ba297860a..db4f7f3e77 100644 --- a/samples/ErrorHandlerSample/Startup.cs +++ b/samples/ErrorHandlerSample/Startup.cs @@ -2,6 +2,7 @@ using Microsoft.AspNet.Builder; using Microsoft.AspNet.Diagnostics; using Microsoft.AspNet.Http; +using Microsoft.Framework.WebEncoders; namespace ErrorHandlerSample { @@ -24,7 +25,7 @@ namespace ErrorHandlerSample if (error != null) { // This error would not normally be exposed to the client - await context.Response.WriteAsync("
Error: " + System.Net.WebUtility.HtmlEncode(error.Error.Message) + "
\r\n"); + await context.Response.WriteAsync("
Error: " + HtmlEncoder.Default.HtmlEncode(error.Error.Message) + "
\r\n"); } await context.Response.WriteAsync("
Home
\r\n"); await context.Response.WriteAsync("\r\n"); diff --git a/samples/RuntimeInfoPageSample/wwwroot/placeholder.html b/samples/RuntimeInfoPageSample/wwwroot/placeholder.html new file mode 100644 index 0000000000..9b8445a4b0 --- /dev/null +++ b/samples/RuntimeInfoPageSample/wwwroot/placeholder.html @@ -0,0 +1,11 @@ + + + + + + + + + + + \ No newline at end of file diff --git a/samples/StatusCodePagesSample/Startup.cs b/samples/StatusCodePagesSample/Startup.cs index bf5ccb4668..010f9910a1 100644 --- a/samples/StatusCodePagesSample/Startup.cs +++ b/samples/StatusCodePagesSample/Startup.cs @@ -8,6 +8,7 @@ using System.Threading.Tasks; using Microsoft.AspNet.Builder; using Microsoft.AspNet.Diagnostics; using Microsoft.AspNet.Http; +using Microsoft.Framework.WebEncoders; namespace StatusCodePagesSample { @@ -61,11 +62,11 @@ namespace StatusCodePagesSample { var builder = new StringBuilder(); builder.AppendLine(""); - builder.AppendLine("An error occurred, Status Code: " + WebUtility.HtmlEncode(context.Request.Path.ToString().Substring(1)) + "
"); + builder.AppendLine("An error occurred, Status Code: " + HtmlEncoder.Default.HtmlEncode(context.Request.Path.ToString().Substring(1)) + "
"); var referrer = context.Request.Headers["referer"]; if (!string.IsNullOrEmpty(referrer)) { - builder.AppendLine("Retry " + WebUtility.HtmlEncode(referrer) + "
"); + builder.AppendLine("Retry " + WebUtility.HtmlEncode(referrer) + "
"); } builder.AppendLine(""); await context.Response.SendAsync(builder.ToString(), "text/html"); @@ -78,8 +79,8 @@ namespace StatusCodePagesSample var builder = new StringBuilder(); builder.AppendLine(""); builder.AppendLine("" + - WebUtility.HtmlEncode(context.Request.PathBase.ToString()) + "/missingpage/
"); + HtmlEncoder.Default.HtmlEncode(context.Request.PathBase.ToString()) + "/missingpage/\">" + + HtmlEncoder.Default.HtmlEncode(context.Request.PathBase.ToString()) + "/missingpage/
"); var space = string.Concat(Enumerable.Repeat(" ", 12)); builder.AppendFormat("
{0}{1}{2}
", "Status Code", space, "Status Code Pages"); diff --git a/samples/StatusCodePagesSample/wwwroot/placeholder.html b/samples/StatusCodePagesSample/wwwroot/placeholder.html new file mode 100644 index 0000000000..9b8445a4b0 --- /dev/null +++ b/samples/StatusCodePagesSample/wwwroot/placeholder.html @@ -0,0 +1,11 @@ + + + + + + + + + + + \ No newline at end of file diff --git a/src/Microsoft.AspNet.Diagnostics/Views/BaseView.cs b/src/Microsoft.AspNet.Diagnostics/Views/BaseView.cs index 883031e324..9a245bc420 100644 --- a/src/Microsoft.AspNet.Diagnostics/Views/BaseView.cs +++ b/src/Microsoft.AspNet.Diagnostics/Views/BaseView.cs @@ -5,9 +5,10 @@ using System; using System.Globalization; using System.IO; -using System.Net; +using System.Linq; using System.Threading.Tasks; using Microsoft.AspNet.Http; +using Microsoft.Framework.WebEncoders; namespace Microsoft.AspNet.Diagnostics.Views { @@ -36,6 +37,11 @@ namespace Microsoft.AspNet.Diagnostics.Views /// protected StreamWriter Output { get; private set; } + /// + /// Html encoder used to encode content. + /// + protected IHtmlEncoder HtmlEncoder { get; set; } + /// /// Execute an individual request /// @@ -46,6 +52,7 @@ namespace Microsoft.AspNet.Diagnostics.Views Request = Context.Request; Response = Context.Response; Output = new StreamWriter(Response.Body); + HtmlEncoder = context.ApplicationServices.GetHtmlEncoder(); await ExecuteAsync(); Output.Dispose(); } @@ -217,7 +224,7 @@ namespace Microsoft.AspNet.Diagnostics.Views /// The to write. protected void WriteTo(TextWriter writer, string value) { - WriteLiteralTo(writer, WebUtility.HtmlEncode(value)); + WriteLiteralTo(writer, HtmlEncoder.HtmlEncode(value)); } /// @@ -241,5 +248,20 @@ namespace Microsoft.AspNet.Diagnostics.Views writer.Write(value); } } + + protected string HtmlEncodeAndReplaceLineBreaks(string input) + { + if (string.IsNullOrEmpty(input)) + { + return string.Empty; + } + + // Split on line breaks before passing it through the encoder. + // We use the static default encoder since we can't depend on DI in the error handling logic. + return string.Join("
" + Environment.NewLine, + input.Split(new[] { "\r\n" }, StringSplitOptions.None) + .SelectMany(s => s.Split(new[] { '\r', '\n' }, StringSplitOptions.None)) + .Select(HtmlEncoder.HtmlEncode)); + } } -} +} \ No newline at end of file diff --git a/src/Microsoft.AspNet.Diagnostics/Views/CompilationErrorPage.cs b/src/Microsoft.AspNet.Diagnostics/Views/CompilationErrorPage.cs index c2647b0698..6649e04287 100644 --- a/src/Microsoft.AspNet.Diagnostics/Views/CompilationErrorPage.cs +++ b/src/Microsoft.AspNet.Diagnostics/Views/CompilationErrorPage.cs @@ -95,7 +95,7 @@ using Views #line hidden WriteLiteral(": "); #line 30 "CompilationErrorPage.cshtml" - Output.Write(WebUtility.HtmlEncode(errorDetail.Error.Message).Replace("\r\n", "
").Replace("\r", "
").Replace("\n", "
")); + Output.Write(HtmlEncodeAndReplaceLineBreaks(errorDetail.Error.Message)); #line default #line hidden @@ -158,8 +158,8 @@ using Views #line hidden WriteLiteral("
  • (tabIndex, 1381), false)); + WriteAttribute("tabindex", Tuple.Create(" tabindex=\"", 1308), Tuple.Create("\"", 1328), + Tuple.Create(Tuple.Create("", 1319), Tuple.Create(tabIndex, 1319), false)); WriteLiteral(">\r\n"); #line 45 "CompilationErrorPage.cshtml" @@ -226,8 +226,8 @@ using Views #line default #line hidden WriteLiteral(" in (frame.File, 1998), false)); + WriteAttribute("title", Tuple.Create(" title=\"", 1928), Tuple.Create("\"", 1947), + Tuple.Create(Tuple.Create("", 1936), Tuple.Create(frame.File, 1936), false)); WriteLiteral(">"); #line 58 "CompilationErrorPage.cshtml" Write(System.IO.Path.GetFileName(frame.File)); @@ -242,14 +242,8 @@ using Views #line default #line hidden - WriteLiteral(" \r\n"); -#line 62 "CompilationErrorPage.cshtml" - - -#line default -#line hidden - -#line 62 "CompilationErrorPage.cshtml" + WriteLiteral(" "); +#line 61 "CompilationErrorPage.cshtml" if (frame.Line != 0 && frame.ContextCode.Any()) { @@ -257,13 +251,13 @@ using Views #line hidden WriteLiteral("
    \r\n"); -#line 65 "CompilationErrorPage.cshtml" +#line 64 "CompilationErrorPage.cshtml" #line default #line hidden -#line 65 "CompilationErrorPage.cshtml" +#line 64 "CompilationErrorPage.cshtml" if (frame.PreContextCode != null) { @@ -271,16 +265,16 @@ using Views #line hidden WriteLiteral(" (frame.PreContextLine, 2461), false)); + WriteAttribute("start", Tuple.Create(" start=\"", 2353), Tuple.Create("\"", 2382), + Tuple.Create(Tuple.Create("", 2361), Tuple.Create(frame.PreContextLine, 2361), false)); WriteLiteral(" class=\"collapsible\">\r\n"); -#line 68 "CompilationErrorPage.cshtml" +#line 67 "CompilationErrorPage.cshtml" #line default #line hidden -#line 68 "CompilationErrorPage.cshtml" +#line 67 "CompilationErrorPage.cshtml" foreach (var line in frame.PreContextCode) { @@ -288,36 +282,36 @@ using Views #line hidden WriteLiteral("
  • "); -#line 70 "CompilationErrorPage.cshtml" +#line 69 "CompilationErrorPage.cshtml" Write(line); #line default #line hidden WriteLiteral("
  • \r\n"); -#line 71 "CompilationErrorPage.cshtml" +#line 70 "CompilationErrorPage.cshtml" } #line default #line hidden WriteLiteral(" \r\n"); -#line 73 "CompilationErrorPage.cshtml" - } +#line 72 "CompilationErrorPage.cshtml" + } #line default #line hidden - WriteLiteral("\r\n (frame.Line, 2874), false)); + WriteLiteral(" (frame.Line, 2771), false)); WriteLiteral(" class=\"highlight\">\r\n"); -#line 76 "CompilationErrorPage.cshtml" +#line 74 "CompilationErrorPage.cshtml" #line default #line hidden -#line 76 "CompilationErrorPage.cshtml" +#line 74 "CompilationErrorPage.cshtml" foreach (var line in frame.ContextCode) { @@ -325,26 +319,26 @@ using Views #line hidden WriteLiteral("
  • "); -#line 78 "CompilationErrorPage.cshtml" +#line 76 "CompilationErrorPage.cshtml" Write(line); #line default #line hidden WriteLiteral("
  • \r\n"); -#line 79 "CompilationErrorPage.cshtml" +#line 77 "CompilationErrorPage.cshtml" } #line default #line hidden - WriteLiteral(" \r\n\r\n"); -#line 82 "CompilationErrorPage.cshtml" + WriteLiteral(" \r\n"); +#line 79 "CompilationErrorPage.cshtml" #line default #line hidden -#line 82 "CompilationErrorPage.cshtml" +#line 79 "CompilationErrorPage.cshtml" if (frame.PostContextCode != null) { @@ -352,16 +346,16 @@ using Views #line hidden WriteLiteral(" (frame.Line + 1, 3360), false)); + WriteAttribute("start", Tuple.Create(" start=\'", 3211), Tuple.Create("\'", 3236), + Tuple.Create(Tuple.Create("", 3219), Tuple.Create(frame.Line + 1, 3219), false)); WriteLiteral(" class=\"collapsible\">\r\n"); -#line 85 "CompilationErrorPage.cshtml" +#line 82 "CompilationErrorPage.cshtml" #line default #line hidden -#line 85 "CompilationErrorPage.cshtml" +#line 82 "CompilationErrorPage.cshtml" foreach (var line in frame.PostContextCode) { @@ -369,41 +363,41 @@ using Views #line hidden WriteLiteral("
  • "); -#line 87 "CompilationErrorPage.cshtml" +#line 84 "CompilationErrorPage.cshtml" Write(line); #line default #line hidden WriteLiteral("
  • \r\n"); -#line 88 "CompilationErrorPage.cshtml" +#line 85 "CompilationErrorPage.cshtml" } #line default #line hidden WriteLiteral(" \r\n"); -#line 90 "CompilationErrorPage.cshtml" +#line 87 "CompilationErrorPage.cshtml" } #line default #line hidden WriteLiteral(" \r\n"); -#line 92 "CompilationErrorPage.cshtml" +#line 89 "CompilationErrorPage.cshtml" } #line default #line hidden WriteLiteral(" \r\n"); -#line 94 "CompilationErrorPage.cshtml" +#line 91 "CompilationErrorPage.cshtml" } #line default #line hidden WriteLiteral(" \r\n \r\n"); -#line 97 "CompilationErrorPage.cshtml" +#line 94 "CompilationErrorPage.cshtml" } #line default diff --git a/src/Microsoft.AspNet.Diagnostics/Views/CompilationErrorPage.cshtml b/src/Microsoft.AspNet.Diagnostics/Views/CompilationErrorPage.cshtml index be1af37dac..619778b097 100644 --- a/src/Microsoft.AspNet.Diagnostics/Views/CompilationErrorPage.cshtml +++ b/src/Microsoft.AspNet.Diagnostics/Views/CompilationErrorPage.cshtml @@ -27,7 +27,7 @@

    @Resources.ErrorPageHtml_CompilationException

    @if (Model.Options.ShowExceptionDetails) { -
    @errorDetail.Error.GetType().Name: @{ Output.Write(WebUtility.HtmlEncode(errorDetail.Error.Message).Replace("\r\n", "
    ").Replace("\r", "
    ").Replace("\n", "
    ")); }
    +
    @errorDetail.Error.GetType().Name: @{ Output.Write(HtmlEncodeAndReplaceLineBreaks(errorDetail.Error.Message)); }
    } else { diff --git a/src/Microsoft.AspNet.Diagnostics/Views/ErrorPage.cs b/src/Microsoft.AspNet.Diagnostics/Views/ErrorPage.cs index ecb486deae..2372239729 100644 --- a/src/Microsoft.AspNet.Diagnostics/Views/ErrorPage.cs +++ b/src/Microsoft.AspNet.Diagnostics/Views/ErrorPage.cs @@ -105,7 +105,7 @@ using Views #line hidden WriteLiteral(": "); #line 37 "ErrorPage.cshtml" - Output.Write(WebUtility.HtmlEncode(errorDetail.Error.Message).Replace("\r\n", "
    ").Replace("\r", "
    ").Replace("\n", "
    ")); + Output.Write(HtmlEncodeAndReplaceLineBreaks(errorDetail.Error.Message)); #line default #line hidden @@ -149,8 +149,8 @@ using Views #line default #line hidden WriteLiteral(" in (firstFrame.File, 2097), false)); + WriteAttribute("title", Tuple.Create(" title=\"", 2027), Tuple.Create("\"", 2051), + Tuple.Create(Tuple.Create("", 2035), Tuple.Create(firstFrame.File, 2035), false)); WriteLiteral(">"); #line 52 "ErrorPage.cshtml" Write(System.IO.Path.GetFileName(firstFrame.File)); @@ -398,8 +398,8 @@ using Views #line hidden WriteLiteral("
  • (tabIndex, 4359), false)); + WriteAttribute("tabindex", Tuple.Create(" tabindex=\"", 4286), Tuple.Create("\"", 4306), + Tuple.Create(Tuple.Create("", 4297), Tuple.Create(tabIndex, 4297), false)); WriteLiteral(">\r\n"); #line 113 "ErrorPage.cshtml" @@ -449,8 +449,8 @@ using Views #line default #line hidden WriteLiteral(" in (frame.File, 4803), false)); + WriteAttribute("title", Tuple.Create(" title=\"", 4733), Tuple.Create("\"", 4752), + Tuple.Create(Tuple.Create("", 4741), Tuple.Create(frame.File, 4741), false)); WriteLiteral(">"); #line 120 "ErrorPage.cshtml" Write(System.IO.Path.GetFileName(frame.File)); @@ -493,8 +493,8 @@ using Views #line hidden WriteLiteral(" (frame.PreContextLine, 5283), false)); + WriteAttribute("start", Tuple.Create(" start=\"", 5213), Tuple.Create("\"", 5242), + Tuple.Create(Tuple.Create("", 5221), Tuple.Create(frame.PreContextLine, 5221), false)); WriteLiteral(" class=\"collapsible\">\r\n"); #line 129 "ErrorPage.cshtml" @@ -530,8 +530,8 @@ using Views #line hidden WriteLiteral("\r\n (frame.Line, 5780), false)); + WriteAttribute("start", Tuple.Create(" start=\"", 5710), Tuple.Create("\"", 5729), + Tuple.Create(Tuple.Create("", 5718), Tuple.Create(frame.Line, 5718), false)); WriteLiteral(" class=\"highlight\">\r\n"); #line 137 "ErrorPage.cshtml" @@ -574,8 +574,8 @@ using Views #line hidden WriteLiteral(" (frame.Line + 1, 6326), false)); + WriteAttribute("start", Tuple.Create(" start=\'", 6256), Tuple.Create("\'", 6281), + Tuple.Create(Tuple.Create("", 6264), Tuple.Create(frame.Line + 1, 6264), false)); WriteLiteral(" class=\"collapsible\">\r\n"); #line 146 "ErrorPage.cshtml" diff --git a/src/Microsoft.AspNet.Diagnostics/Views/ErrorPage.cshtml b/src/Microsoft.AspNet.Diagnostics/Views/ErrorPage.cshtml index 7a0075ac42..eabeb0cdc8 100644 --- a/src/Microsoft.AspNet.Diagnostics/Views/ErrorPage.cshtml +++ b/src/Microsoft.AspNet.Diagnostics/Views/ErrorPage.cshtml @@ -34,7 +34,7 @@ { foreach (var errorDetail in Model.ErrorDetails) { -
    @errorDetail.Error.GetType().Name: @{ Output.Write(WebUtility.HtmlEncode(errorDetail.Error.Message).Replace("\r\n", "
    ").Replace("\r", "
    ").Replace("\n", "
    ")); }
    +
    @errorDetail.Error.GetType().Name: @{ Output.Write(HtmlEncodeAndReplaceLineBreaks(errorDetail.Error.Message)); }
    @{ StackFrame firstFrame = null; firstFrame = errorDetail.StackFrames.FirstOrDefault(); diff --git a/src/Microsoft.AspNet.Diagnostics/project.json b/src/Microsoft.AspNet.Diagnostics/project.json index bfadf7dffa..75da49084d 100644 --- a/src/Microsoft.AspNet.Diagnostics/project.json +++ b/src/Microsoft.AspNet.Diagnostics/project.json @@ -4,7 +4,8 @@ "dependencies": { "Microsoft.AspNet.Diagnostics.Interfaces": "1.0.0-*", "Microsoft.AspNet.RequestContainer": "1.0.0-*", - "Microsoft.AspNet.WebUtilities": "1.0.0-*" + "Microsoft.AspNet.WebUtilities": "1.0.0-*", + "Microsoft.Framework.WebEncoders": "1.0.0-*" }, "frameworks": { "aspnet50": {}, diff --git a/test/Microsoft.AspNet.Diagnostics.Tests/ElmMiddlewareTest.cs b/test/Microsoft.AspNet.Diagnostics.Tests/ElmMiddlewareTest.cs index 8343b54910..be8813128f 100644 --- a/test/Microsoft.AspNet.Diagnostics.Tests/ElmMiddlewareTest.cs +++ b/test/Microsoft.AspNet.Diagnostics.Tests/ElmMiddlewareTest.cs @@ -101,6 +101,9 @@ namespace Microsoft.AspNet.Diagnostics.Tests contextMock .SetupGet(c => c.Response.Body) .Returns(responseStream); + contextMock + .SetupGet(c => c.ApplicationServices) + .Returns(() => null); // Act await captureMiddleware.Invoke(contextMock.Object); diff --git a/test/Microsoft.AspNet.Diagnostics.Tests/RuntimeInfoMiddlewareTest.cs b/test/Microsoft.AspNet.Diagnostics.Tests/RuntimeInfoMiddlewareTest.cs index 8e6eb38bae..82308b0f4d 100644 --- a/test/Microsoft.AspNet.Diagnostics.Tests/RuntimeInfoMiddlewareTest.cs +++ b/test/Microsoft.AspNet.Diagnostics.Tests/RuntimeInfoMiddlewareTest.cs @@ -4,13 +4,14 @@ using System; using System.Collections.Generic; using System.IO; -using System.Linq; using System.Reflection; using System.Text; using System.Threading.Tasks; using Microsoft.AspNet.Builder; using Microsoft.AspNet.Http; +using Microsoft.Framework.DependencyInjection; using Microsoft.Framework.Runtime; +using Microsoft.Framework.WebEncoders; #if ASPNET50 using Moq; #endif @@ -121,6 +122,9 @@ namespace Microsoft.AspNet.Diagnostics.Tests contextMock .SetupGet(c => c.Response.Body) .Returns(responseStream); + contextMock + .SetupGet(c => c.ApplicationServices) + .Returns(() => null); // Act await middleware.Invoke(contextMock.Object); @@ -136,6 +140,53 @@ namespace Microsoft.AspNet.Diagnostics.Tests Assert.True(response.Contains("Path2")); } } + + [Fact] + public async void Invoke_WithMatchingPath_ReturnsInfoPage_UsingCustomHtmlEncoder() + { + // Arrange + var libraryManagerMock = new Mock(MockBehavior.Strict); + libraryManagerMock.Setup(l => l.GetLibraries()).Returns(new ILibraryInformation[] { + new FakeLibraryInformation() { Name ="LibInfo1", Version = "1.0.0-beta1", Path = "Path1" }, + }); + + RequestDelegate next = _ => + { + return Task.FromResult(null); + }; + + var middleware = new RuntimeInfoMiddleware( + next, + new RuntimeInfoPageOptions(), + libraryManagerMock.Object); + + var buffer = new byte[4096]; + using (var responseStream = new MemoryStream(buffer)) + { + var contextMock = new Mock(MockBehavior.Strict); + contextMock + .SetupGet(c => c.Request.Path) + .Returns(new PathString("/runtimeinfo")); + contextMock + .SetupGet(c => c.Response.Body) + .Returns(responseStream); + contextMock + .SetupGet(c => c.ApplicationServices) + .Returns(new ServiceCollection(). + AddInstance(new CustomHtmlEncoder()). + BuildServiceProvider()); + + // Act + await middleware.Invoke(contextMock.Object); + + // Assert + string response = Encoding.UTF8.GetString(buffer); + + Assert.True(response.Contains("[LibInfo1]")); + Assert.True(response.Contains("[1.0.0-beta1]")); + Assert.True(response.Contains("[Path1]")); + } + } #endif private class FakeLibraryInformation : ILibraryInformation @@ -170,5 +221,23 @@ namespace Microsoft.AspNet.Diagnostics.Tests } } } + + private class CustomHtmlEncoder : IHtmlEncoder + { + public string HtmlEncode(string value) + { + return "[" + value + "]"; + } + + public void HtmlEncode(string value, int startIndex, int charCount, TextWriter output) + { + throw new NotImplementedException(); + } + + public void HtmlEncode(char[] value, int startIndex, int charCount, TextWriter output) + { + throw new NotImplementedException(); + } + } } } \ No newline at end of file