public void Ctor_WithUnicodeRanges() { // Arrange HtmlEncoder encoder = new HtmlEncoder(UnicodeRanges.Latin1Supplement, UnicodeRanges.MiscellaneousSymbols); // Act & assert Assert.Equal("a", encoder.HtmlEncode("a")); Assert.Equal("\u00E9", encoder.HtmlEncode("\u00E9" /* LATIN SMALL LETTER E WITH ACUTE */)); Assert.Equal("\u2601", encoder.HtmlEncode("\u2601" /* CLOUD */)); }
public void Ctor_WithNoParameters_DefaultsToBasicLatin() { // Arrange HtmlEncoder encoder = new HtmlEncoder(); // Act & assert Assert.Equal("a", encoder.HtmlEncode("a")); Assert.Equal("é", encoder.HtmlEncode("\u00E9" /* LATIN SMALL LETTER E WITH ACUTE */)); Assert.Equal("☁", encoder.HtmlEncode("\u2601" /* CLOUD */)); }
public void HtmlEncode_NullInput_Throws() { // Arrange HtmlEncoder encoder = new HtmlEncoder(); Assert.Throws <ArgumentNullException>(() => { encoder.HtmlEncode(null); }); }
public void HtmlEncode_EmptyStringInput_ReturnsEmptyString() { // Arrange HtmlEncoder encoder = new HtmlEncoder(); // Act & assert Assert.Equal("", encoder.HtmlEncode("")); }
public void HtmlEncode_NullInput_ReturnsNull() { // Arrange HtmlEncoder encoder = new HtmlEncoder(); // Act & assert Assert.Null(encoder.HtmlEncode(null)); }
public void HtmlEncode_InputDoesNotRequireEncoding_ReturnsOriginalStringInstance() { // Arrange HtmlEncoder encoder = new HtmlEncoder(); string input = "Hello, there!"; // Act & assert Assert.Same(input, encoder.HtmlEncode(input)); }
public void HtmlEncode_AllRangesAllowed_StillEncodesForbiddenChars_Simple(string input, string expected) { // Arrange HtmlEncoder encoder = new HtmlEncoder(UnicodeRanges.All); // Act string retVal = encoder.HtmlEncode(input); // Assert Assert.Equal(expected, retVal); }
public void HtmlEncode_StringSubstring() { // Arrange HtmlEncoder encoder = new HtmlEncoder(); var output = new StringWriter(); // Act encoder.HtmlEncode("Hello+world!", 3, 5, output); // Assert Assert.Equal("lo+wo", output.ToString()); }
public void HtmlEncode_PositiveTestCase() { // Arrange IHtmlEncoder encoder = new HtmlEncoder(UnicodeRanges.All); StringWriter writer = new StringWriter(); // Act encoder.HtmlEncode("Hello+there!", writer); // Assert Assert.Equal("Hello+there!", writer.ToString()); }
public void HtmlEncode_BadSurrogates_ReturnsUnicodeReplacementChar() { // Arrange HtmlEncoder encoder = new HtmlEncoder(UnicodeRanges.All); // allow all codepoints // "a<unpaired leading>b<unpaired trailing>c<trailing before leading>d<unpaired trailing><valid>e<high at end of string>" const string input = "a\uD800b\uDFFFc\uDFFF\uD800d\uDFFF\uD800\uDFFFe\uD800"; const string expected = "a\uFFFDb\uFFFDc\uFFFD\uFFFDd\uFFFD𐏿e\uFFFD"; // Act string retVal = encoder.HtmlEncode(input); // Assert Assert.Equal(expected, retVal); }
public void Default_EquivalentToBasicLatin() { // Arrange HtmlEncoder controlEncoder = new HtmlEncoder(UnicodeRanges.BasicLatin); HtmlEncoder testEncoder = HtmlEncoder.Default; // Act & assert for (int i = 0; i <= Char.MaxValue; i++) { if (!IsSurrogateCodePoint(i)) { string input = new String((char)i, 1); Assert.Equal(controlEncoder.HtmlEncode(input), testEncoder.HtmlEncode(input)); } } }
public void Default_EquivalentToBasicLatin() { // Arrange HtmlEncoder controlEncoder = new HtmlEncoder(UnicodeRanges.BasicLatin); HtmlEncoder testEncoder = HtmlEncoder.Default; // Act & assert for (int i = 0; i <= char.MaxValue; i++) { if (!IsSurrogateCodePoint(i)) { string input = new string((char)i, 1); Assert.Equal(controlEncoder.HtmlEncode(input), testEncoder.HtmlEncode(input)); } } }
public void Ctor_WithCodePointFilter() { // Arrange var filter = new CodePointFilter().AllowChars("ab").AllowChars('\0', '&', '\uFFFF', 'd'); HtmlEncoder encoder = new HtmlEncoder(filter); // Act & assert Assert.Equal("a", encoder.HtmlEncode("a")); Assert.Equal("b", encoder.HtmlEncode("b")); Assert.Equal("c", encoder.HtmlEncode("c")); Assert.Equal("d", encoder.HtmlEncode("d")); Assert.Equal("�", encoder.HtmlEncode("\0")); // we still always encode control chars Assert.Equal("&", encoder.HtmlEncode("&")); // we still always encode HTML-special chars Assert.Equal("", encoder.HtmlEncode("\uFFFF")); // we still always encode non-chars and other forbidden chars }
public void JavaScriptStringEncode_DoesNotOutputHtmlSensitiveCharacters() { // Per the design document, we provide additional defense-in-depth // by never emitting HTML-sensitive characters unescaped. // Arrange JavaScriptStringEncoder javaScriptStringEncoder = new JavaScriptStringEncoder(UnicodeRanges.All); HtmlEncoder htmlEncoder = new HtmlEncoder(UnicodeRanges.All); // Act & assert for (int i = 0; i <= 0x10FFFF; i++) { if (IsSurrogateCodePoint(i)) { continue; // surrogates don't matter here } string javaScriptStringEncoded = javaScriptStringEncoder.JavaScriptStringEncode(Char.ConvertFromUtf32(i)); string thenHtmlEncoded = htmlEncoder.HtmlEncode(javaScriptStringEncoded); Assert.Equal(javaScriptStringEncoded, thenHtmlEncoded); // should have contained no HTML-sensitive characters } }
public void UrlEncode_DoesNotOutputHtmlSensitiveCharacters() { // Per the design document, we provide additional defense-in-depth // by never emitting HTML-sensitive characters unescaped. // Arrange UrlEncoder urlEncoder = new UrlEncoder(UnicodeRanges.All); HtmlEncoder htmlEncoder = new HtmlEncoder(UnicodeRanges.All); // Act & assert for (int i = 0; i <= 0x10FFFF; i++) { if (IsSurrogateCodePoint(i)) { continue; // surrogates don't matter here } string urlEncoded = urlEncoder.UrlEncode(Char.ConvertFromUtf32(i)); string thenHtmlEncoded = htmlEncoder.HtmlEncode(urlEncoded); Assert.Equal(urlEncoded, thenHtmlEncoded); // should have contained no HTML-sensitive characters } }
public async Task RenderAsync_AsPartial_ExecutesLayout_ButNotViewStartPages() { // Arrange var htmlEncoder = new HtmlEncoder(); var expected = string.Join(htmlEncoder.HtmlEncode(Environment.NewLine), "layout-content", "page-content"); var page = new TestableRazorPage(v => { v.HtmlEncoder = htmlEncoder; v.Layout = LayoutPath; v.Write("page-content"); }); var layout = new TestableRazorPage(v => { v.HtmlEncoder = htmlEncoder; v.Write("layout-content" + Environment.NewLine); v.RenderBodyPublic(); }); var pageFactory = new Mock<IRazorPageFactory>(); pageFactory.Setup(p => p.CreateInstance(LayoutPath)) .Returns(layout); var viewEngine = new Mock<IRazorViewEngine>(); viewEngine.Setup(v => v.FindPage(It.IsAny<ActionContext>(), LayoutPath)) .Returns(new RazorPageResult(LayoutPath, layout)); var viewStartProvider = CreateViewStartProvider(); var view = new RazorView(viewEngine.Object, Mock.Of<IRazorPageActivator>(), viewStartProvider, page, isPartial: true); var viewContext = CreateViewContext(view); // Act await view.RenderAsync(viewContext); // Assert Mock.Get(viewStartProvider) .Verify(v => v.GetViewStartPages(It.IsAny<string>()), Times.Never()); Assert.Equal(expected, viewContext.Writer.ToString()); }
public async Task RenderAsync_WithNestedSections_ThrowsIfSectionsWereDefinedButNotRendered() { // Arrange var htmlEncoder = new HtmlEncoder(); var page = new TestableRazorPage(v => { v.HtmlEncoder = htmlEncoder; v.Layout = "~/Shared/Layout1.cshtml"; v.WriteLiteral("BodyContent"); v.DefineSection("foo", async writer => { await writer.WriteLineAsync("foo-content"); }); }); var nestedLayout = new TestableRazorPage(v => { v.HtmlEncoder = htmlEncoder; v.Layout = "~/Shared/Layout2.cshtml"; v.Write("NestedLayout" + Environment.NewLine); v.RenderBodyPublic(); v.DefineSection("foo", async writer => { await writer.WriteLineAsync(htmlEncoder.HtmlEncode(v.RenderSection("foo").ToString())); }); }); var baseLayout = new TestableRazorPage(v => { v.HtmlEncoder = htmlEncoder; v.Write("BaseLayout" + Environment.NewLine); v.RenderBodyPublic(); }); var viewEngine = new Mock<IRazorViewEngine>(); viewEngine.Setup(p => p.FindPage(It.IsAny<ActionContext>(), "~/Shared/Layout1.cshtml")) .Returns(new RazorPageResult("~/Shared/Layout1.cshtml", nestedLayout)); viewEngine.Setup(p => p.FindPage(It.IsAny<ActionContext>(), "~/Shared/Layout2.cshtml")) .Returns(new RazorPageResult("~/Shared/Layout2.cshtml", baseLayout)); var view = new RazorView(viewEngine.Object, Mock.Of<IRazorPageActivator>(), CreateViewStartProvider(), page, isPartial: false); var viewContext = CreateViewContext(view); // Act and Assert var ex = await Assert.ThrowsAsync<InvalidOperationException>(() => view.RenderAsync(viewContext)); Assert.Equal("The following sections have been defined but have not been rendered: 'foo'.", ex.Message); }
public async Task FlushAsync_DoesNotThrowWhenInvokedInsideOfASection() { // Arrange var htmlEncoder = new HtmlEncoder(); var expected = "layout-1" + htmlEncoder.HtmlEncode(Environment.NewLine) + "section-content-1" + Environment.NewLine + "section-content-2"; var page = new TestableRazorPage(v => { v.HtmlEncoder = htmlEncoder; v.Layout = "layout-1"; v.DefineSection("foo", async _ => { v.WriteLiteral("section-content-1" + Environment.NewLine); await v.FlushAsync(); v.WriteLiteral("section-content-2"); }); }); var layout1 = new TestableRazorPage(v => { v.HtmlEncoder = htmlEncoder; v.Write("layout-1" + Environment.NewLine); v.RenderBodyPublic(); v.Write(v.RenderSection("foo")); }); var viewEngine = new Mock<IRazorViewEngine>(); viewEngine.Setup(p => p.FindPage(It.IsAny<ActionContext>(), "layout-1")) .Returns(new RazorPageResult("layout-1", layout1)); var view = new RazorView(viewEngine.Object, Mock.Of<IRazorPageActivator>(), CreateViewStartProvider(), page, isPartial: false); var viewContext = CreateViewContext(view); // Act await view.RenderAsync(viewContext); // Assert Assert.Equal(expected, viewContext.Writer.ToString()); }
public async Task RenderAsync_ExecutesNestedLayoutsWithNestedSections() { // Arrange var htmlEncoder = new HtmlEncoder(); var htmlEncodedNewLine = htmlEncoder.HtmlEncode(Environment.NewLine); var expected = "BaseLayout" + htmlEncodedNewLine + "NestedLayout" + htmlEncodedNewLine + "BodyContent" + "foo-content" + Environment.NewLine + Environment.NewLine; var page = new TestableRazorPage(v => { v.HtmlEncoder = htmlEncoder; v.Layout = "~/Shared/Layout1.cshtml"; v.WriteLiteral("BodyContent"); v.DefineSection("foo", async writer => { await writer.WriteLineAsync("foo-content"); }); }); var nestedLayout = new TestableRazorPage(v => { v.HtmlEncoder = htmlEncoder; v.Layout = "~/Shared/Layout2.cshtml"; v.Write("NestedLayout" + Environment.NewLine); v.RenderBodyPublic(); v.DefineSection("foo", async writer => { await writer.WriteLineAsync(htmlEncoder.HtmlEncode(v.RenderSection("foo").ToString())); }); }); var baseLayout = new TestableRazorPage(v => { v.HtmlEncoder = htmlEncoder; v.Write("BaseLayout" + Environment.NewLine); v.RenderBodyPublic(); v.Write(v.RenderSection("foo")); }); var viewEngine = new Mock<IRazorViewEngine>(); viewEngine.Setup(p => p.FindPage(It.IsAny<ActionContext>(), "~/Shared/Layout1.cshtml")) .Returns(new RazorPageResult("~/Shared/Layout1.cshtml", nestedLayout)); viewEngine.Setup(p => p.FindPage(It.IsAny<ActionContext>(), "~/Shared/Layout2.cshtml")) .Returns(new RazorPageResult("~/Shared/Layout2.cshtml", baseLayout)); var view = new RazorView(viewEngine.Object, Mock.Of<IRazorPageActivator>(), CreateViewStartProvider(), page, isPartial: false); var viewContext = CreateViewContext(view); // Act await view.RenderAsync(viewContext); // Assert Assert.Equal(expected, viewContext.Writer.ToString()); }
public void HtmlEncode_NullInput_Throws() { // Arrange HtmlEncoder encoder = new HtmlEncoder(); Assert.Throws<ArgumentNullException>(() => { encoder.HtmlEncode(null); }); }
public void HtmlEncode_AllRangesAllowed_StillEncodesForbiddenChars_Extended() { // Arrange HtmlEncoder encoder = new HtmlEncoder(UnicodeRanges.All); // Act & assert - BMP chars for (int i = 0; i <= 0xFFFF; i++) { string input = new String((char)i, 1); string expected; if (IsSurrogateCodePoint(i)) { expected = "\uFFFD"; // unpaired surrogate -> Unicode replacement char } else { if (input == "<") { expected = "<"; } else if (input == ">") { expected = ">"; } else if (input == "&") { expected = "&"; } else if (input == "\"") { expected = """; } else { bool mustEncode = false; if (i == '\'' || i == '+') { mustEncode = true; // apostrophe, plus } else if (i <= 0x001F || (0x007F <= i && i <= 0x9F)) { mustEncode = true; // control char } else if (!UnicodeHelpers.IsCharacterDefined((char)i)) { mustEncode = true; // undefined (or otherwise disallowed) char } if (mustEncode) { expected = String.Format(CultureInfo.InvariantCulture, "&#x{0:X};", i); } else { expected = input; // no encoding } } } string retVal = encoder.HtmlEncode(input); Assert.Equal(expected, retVal); } // Act & assert - astral chars for (int i = 0x10000; i <= 0x10FFFF; i++) { string input = Char.ConvertFromUtf32(i); string expected = String.Format(CultureInfo.InvariantCulture, "&#x{0:X};", i); string retVal = encoder.HtmlEncode(input); Assert.Equal(expected, retVal); } }
public void HtmlEncode_AllRangesAllowed_StillEncodesForbiddenChars_Extended() { // Arrange HtmlEncoder encoder = new HtmlEncoder(UnicodeRanges.All); // Act & assert - BMP chars for (int i = 0; i <= 0xFFFF; i++) { string input = new string((char)i, 1); string expected; if (IsSurrogateCodePoint(i)) { expected = "\uFFFD"; // unpaired surrogate -> Unicode replacement char } else { if (input == "<") { expected = "<"; } else if (input == ">") { expected = ">"; } else if (input == "&") { expected = "&"; } else if (input == "\"") { expected = """; } else { bool mustEncode = false; if (i == '\'' || i == '+') { mustEncode = true; // apostrophe, plus } else if (i <= 0x001F || (0x007F <= i && i <= 0x9F)) { mustEncode = true; // control char } else if (!UnicodeHelpers.IsCharacterDefined((char)i)) { mustEncode = true; // undefined (or otherwise disallowed) char } if (mustEncode) { expected = string.Format(CultureInfo.InvariantCulture, "&#x{0:X};", i); } else { expected = input; // no encoding } } } string retVal = encoder.HtmlEncode(input); Assert.Equal(expected, retVal); } // Act & assert - astral chars for (int i = 0x10000; i <= 0x10FFFF; i++) { string input = char.ConvertFromUtf32(i); string expected = string.Format(CultureInfo.InvariantCulture, "&#x{0:X};", i); string retVal = encoder.HtmlEncode(input); Assert.Equal(expected, retVal); } }
public async Task RenderAsync_ExecutesLayoutPages() { // Arrange var htmlEncoder = new HtmlEncoder(); var htmlEncodedNewLine = htmlEncoder.HtmlEncode(Environment.NewLine); var expected = "layout-content" + htmlEncodedNewLine + "head-content" + htmlEncodedNewLine + "body-content" + htmlEncodedNewLine + "foot-content"; var page = new TestableRazorPage(v => { v.HtmlEncoder = htmlEncoder; v.WriteLiteral("body-content"); v.Layout = LayoutPath; v.DefineSection("head", async writer => { await writer.WriteAsync("head-content"); }); v.DefineSection("foot", async writer => { await writer.WriteAsync("foot-content"); }); }); var layout = new TestableRazorPage(v => { v.HtmlEncoder = htmlEncoder; v.Write("layout-content" + Environment.NewLine); v.Write(v.RenderSection("head")); v.Write(Environment.NewLine); v.RenderBodyPublic(); v.Write(Environment.NewLine); v.Write(v.RenderSection("foot")); }); var activator = new Mock<IRazorPageActivator>(); activator.Setup(a => a.Activate(page, It.IsAny<ViewContext>())) .Verifiable(); activator.Setup(a => a.Activate(layout, It.IsAny<ViewContext>())) .Verifiable(); var viewEngine = new Mock<IRazorViewEngine>(); var view = new RazorView(viewEngine.Object, activator.Object, CreateViewStartProvider(), page, isPartial: false); var viewContext = CreateViewContext(view); viewEngine.Setup(p => p.FindPage(viewContext, LayoutPath)) .Returns(new RazorPageResult(LayoutPath, layout)) .Verifiable(); // Act await view.RenderAsync(viewContext); // Assert // Verify the activator was invoked for the primary page and layout page. activator.Verify(); Assert.Equal(expected, viewContext.Writer.ToString()); viewEngine.Verify(); }