When you use SanitizeDocument the 'context' parameter of DoPostProcess and RemoveComments is set to be the HTML document itself. The post processing require the context to be an IElement which isn't the case for AngleSharps HtmlDocument. Changed signatures of methods in post processing to use an INode instead. This allows the PostProcessNode event to be called when using SanitizeDocument.
3177 lines
174 KiB
C#
3177 lines
174 KiB
C#
using AngleSharp;
|
||
using AngleSharp.Css.Dom;
|
||
using AngleSharp.Dom;
|
||
using AngleSharp.Html.Dom;
|
||
using AngleSharp.Html.Parser;
|
||
using System;
|
||
using System.Collections.Generic;
|
||
using System.IO;
|
||
using System.Linq;
|
||
using System.Reflection;
|
||
using System.Text;
|
||
using System.Text.RegularExpressions;
|
||
using System.Threading;
|
||
using Xunit;
|
||
|
||
// Tests based on tests from http://roadkill.codeplex.com/
|
||
|
||
// To create unit tests in this class reference is taken from
|
||
// https://www.owasp.org/index.php/XSS_(Cross_Site_Scripting)_Prevention_Cheat_Sheet#RULE_.232_-_Attribute_Escape_Before_Inserting_Untrusted_Data_into_HTML_Common_Attributes
|
||
// and http://ha.ckers.org/xss.html
|
||
|
||
// disable XML comments warnings
|
||
#pragma warning disable 1591
|
||
|
||
namespace Ganss.XSS.Tests
|
||
{
|
||
public class HtmlSanitizerFixture
|
||
{
|
||
public HtmlSanitizer Sanitizer { get; set; } = new HtmlSanitizer();
|
||
}
|
||
|
||
/// <summary>
|
||
/// Tests for <see cref="HtmlSanitizer"/>.
|
||
/// </summary>
|
||
public class HtmlSanitizerTests: IClassFixture<HtmlSanitizerFixture>
|
||
{
|
||
public HtmlSanitizer Sanitizer { get; set; }
|
||
|
||
public HtmlSanitizerTests(HtmlSanitizerFixture fixture)
|
||
{
|
||
Encoding.RegisterProvider(CodePagesEncodingProvider.Instance);
|
||
Sanitizer = fixture.Sanitizer;
|
||
}
|
||
|
||
/// <summary>
|
||
/// A test for Xss locator
|
||
/// </summary>
|
||
[Fact]
|
||
public void XSSLocatorTest()
|
||
{
|
||
// Arrange
|
||
var sanitizer = Sanitizer;
|
||
|
||
// Act
|
||
string htmlFragment = "<a href=\"'';!--\"<XSS>=&{()}\">";
|
||
string actual = sanitizer.Sanitize(htmlFragment);
|
||
|
||
// Assert
|
||
string expected = @"<a href=""'';!--"">=&{()}""></a>";
|
||
Assert.Equal(expected, actual, ignoreCase: true);
|
||
}
|
||
|
||
/// <summary>
|
||
/// A test for Image Xss vector
|
||
/// Example <!-- <IMG SRC="javascript:alert('XSS');"> -->
|
||
/// </summary>
|
||
[Fact]
|
||
public void ImageXSS1Test()
|
||
{
|
||
// Arrange
|
||
var sanitizer = Sanitizer;
|
||
|
||
|
||
// Action
|
||
string htmlFragment = "<IMG SRC=\"javascript:alert('XSS');\">";
|
||
string actual = sanitizer.Sanitize(htmlFragment);
|
||
|
||
// Assert
|
||
string expected = "<img>";
|
||
Assert.Equal(expected, actual, ignoreCase: true);
|
||
}
|
||
|
||
/// <summary>
|
||
/// A test for Image Xss vector without quotes and semicolon.
|
||
/// Example <!-- <IMG SRC=javascript:alert('XSS')> -->
|
||
/// </summary>
|
||
[Fact]
|
||
public void ImageXSS2Test()
|
||
{
|
||
// Arrange
|
||
var sanitizer = Sanitizer;
|
||
|
||
|
||
// Act
|
||
string htmlFragment = "<IMG SRC=javascript:alert('XSS')>";
|
||
string actual = sanitizer.Sanitize(htmlFragment);
|
||
|
||
// Assert
|
||
string expected = "<IMG>";
|
||
Assert.Equal(expected, actual, ignoreCase: true);
|
||
}
|
||
|
||
/// <summary>
|
||
/// A test for Image xss vector with case insensitive.
|
||
/// Example <!-- <IMG SRC=JaVaScRiPt:alert('XSS')> -->
|
||
/// </summary>
|
||
[Fact]
|
||
public void ImageCaseInsensitiveXSSTest()
|
||
{
|
||
// Arrange
|
||
var sanitizer = Sanitizer;
|
||
|
||
|
||
// Act
|
||
string htmlFragment = "<IMG SRC=JaVaScRiPt:alert('XSS')>";
|
||
string actual = sanitizer.Sanitize(htmlFragment);
|
||
|
||
// Assert
|
||
string expected = "<IMG>";
|
||
Assert.Equal(expected, actual, ignoreCase: true);
|
||
}
|
||
|
||
/// <summary>
|
||
/// A test for Image Xss vector with Html entities
|
||
/// Example <!-- <IMG SRC=javascript:alert("XSS")> -->
|
||
/// </summary>
|
||
[Fact]
|
||
public void ImageHtmlEntitiesXSSTest()
|
||
{
|
||
// Arrange
|
||
var sanitizer = Sanitizer;
|
||
|
||
|
||
// Act
|
||
string htmlFragment = "<IMG SRC=javascript:alert("XSS")>";
|
||
string actual = sanitizer.Sanitize(htmlFragment);
|
||
|
||
// Assert
|
||
string expected = "<IMG>";
|
||
Assert.Equal(expected, actual, ignoreCase: true);
|
||
}
|
||
|
||
/// <summary>
|
||
/// A test for Image Xss vector with grave accent
|
||
/// Example <!-- <IMG SRC=`javascript:alert("RSnake says, 'XSS'")`> -->
|
||
/// </summary>
|
||
[Fact]
|
||
public void ImageGraveAccentXSSTest()
|
||
{
|
||
// Arrange
|
||
var sanitizer = Sanitizer;
|
||
|
||
|
||
// Act
|
||
string htmlFragment = "<IMG SRC=`javascript:alert(\"RSnake says, 'XSS'\")`>";
|
||
string actual = sanitizer.Sanitize(htmlFragment);
|
||
|
||
// Assert
|
||
string expected = "<img>";
|
||
Assert.Equal(expected, actual, ignoreCase: true);
|
||
}
|
||
|
||
/// <summary>
|
||
/// A test for Image Xss vector with malformed
|
||
/// Example <!-- <IMG \"\"\"><SCRIPT>alert(\"XSS\")</SCRIPT>\"> -->
|
||
/// </summary>
|
||
[Fact]
|
||
public void ImageMalformedXSSTest()
|
||
{
|
||
// Arrange
|
||
var sanitizer = Sanitizer;
|
||
|
||
|
||
// Act
|
||
string htmlFragment = "<IMG \"\"\"><SCRIPT>alert(\"XSS\")</SCRIPT>\">";
|
||
string actual = sanitizer.Sanitize(htmlFragment);
|
||
|
||
// Assert
|
||
string expected = "<img>\">";
|
||
Assert.Equal(expected, actual, ignoreCase: true);
|
||
}
|
||
|
||
/// <summary>
|
||
/// A test for Image Xss vector with ImageFromCharCode
|
||
/// Example <!-- <IMG SRC=javascript:alert(String.fromCharCode(88,83,83))> -->
|
||
/// </summary>
|
||
[Fact]
|
||
public void ImageFromCharCodeXSSTest()
|
||
{
|
||
// Arrange
|
||
var sanitizer = Sanitizer;
|
||
|
||
|
||
// Act
|
||
string htmlFragment = "<IMG SRC=javascript:alert(String.fromCharCode(88,83,83))>";
|
||
string actual = sanitizer.Sanitize(htmlFragment);
|
||
|
||
// Assert
|
||
string expected = "<img>";
|
||
Assert.Equal(expected, actual, ignoreCase: true);
|
||
}
|
||
|
||
/// <summary>
|
||
/// A test for Image Xss vector with UTF-8 Unicode
|
||
/// Example <!-- <IMG SRC=javascript:alert('XSS')> -->
|
||
/// </summary>
|
||
[Fact]
|
||
public void ImageUTF8UnicodeXSSTest()
|
||
{
|
||
// Arrange
|
||
var sanitizer = Sanitizer;
|
||
|
||
|
||
// Act
|
||
string htmlFragment = "<IMG SRC=javascript:alert('XSS')>";
|
||
string actual = sanitizer.Sanitize(htmlFragment);
|
||
|
||
// Assert
|
||
string expected = "<img>";
|
||
Assert.Equal(expected, actual, ignoreCase: true);
|
||
}
|
||
|
||
/// <summary>
|
||
/// A test for Image Xss vector with Long UTF-8 Unicode
|
||
/// Example <!-- <IMG SRC=javascript:alert('XSS')> -->
|
||
/// </summary>
|
||
[Fact]
|
||
public void ImageLongUTF8UnicodeXSSTest()
|
||
{
|
||
// Arrange
|
||
var sanitizer = Sanitizer;
|
||
|
||
|
||
// Act
|
||
string htmlFragment = "<IMG SRC=javascript:alert('XSS')>";
|
||
string actual = sanitizer.Sanitize(htmlFragment);
|
||
|
||
// Assert
|
||
string expected = "<img>";
|
||
Assert.Equal(expected, actual, ignoreCase: true);
|
||
}
|
||
|
||
/// <summary>
|
||
/// A test for Image Xss vector with Hex encoding without semicolon
|
||
/// Example <!-- <IMG SRC=javascript:alert('XSS')> -->
|
||
/// </summary>
|
||
[Fact]
|
||
public void ImageHexEncodeXSSTest()
|
||
{
|
||
// Arrange
|
||
var sanitizer = Sanitizer;
|
||
|
||
|
||
// Act
|
||
string htmlFragment = "<IMG SRC=javascript:alert('XSS')>";
|
||
string actual = sanitizer.Sanitize(htmlFragment);
|
||
|
||
// Assert
|
||
string expected = "<img>";
|
||
Assert.Equal(expected, actual, ignoreCase: true);
|
||
}
|
||
|
||
/// <summary>
|
||
/// A test for Image Xss vector with embedded tab
|
||
/// Example <!-- <IMG SRC=\"jav ascript:alert('XSS');\"> -->
|
||
/// </summary>
|
||
[Fact]
|
||
public void ImageEmbeddedTabXSSTest()
|
||
{
|
||
// Arrange
|
||
var sanitizer = Sanitizer;
|
||
|
||
|
||
// Act
|
||
string htmlFragment = "<IMG SRC=\"jav ascript:alert('XSS');\">";
|
||
string actual = sanitizer.Sanitize(htmlFragment);
|
||
|
||
// Assert
|
||
string expected = "<img>";
|
||
Assert.Equal(expected, actual, ignoreCase: true);
|
||
}
|
||
|
||
/// <summary>
|
||
/// A test for Image Xss vector with embedded encoded tab
|
||
/// Example <!-- <IMG SRC="jav	ascript:alert('XSS');"> -->
|
||
/// </summary>
|
||
[Fact]
|
||
public void ImageEmbeddedEncodedTabXSSTest()
|
||
{
|
||
// Arrange
|
||
var sanitizer = Sanitizer;
|
||
|
||
|
||
// Act
|
||
string htmlFragment = "<IMG SRC=\"jav	ascript:alert('XSS');\">";
|
||
string actual = sanitizer.Sanitize(htmlFragment);
|
||
|
||
// Assert
|
||
string expected = "<img>";
|
||
Assert.Equal(expected, actual, ignoreCase: true);
|
||
}
|
||
|
||
/// <summary>
|
||
/// A test for Image Xss vector with embedded new line
|
||
/// Example <!-- <IMG SRC="jav
ascript:alert('XSS');"> -->
|
||
/// </summary>
|
||
[Fact]
|
||
public void ImageEmbeddedNewLineXSSTest()
|
||
{
|
||
// Arrange
|
||
var sanitizer = Sanitizer;
|
||
|
||
|
||
// Act
|
||
string htmlFragment = "<IMG SRC=\"jav
ascript:alert('XSS');\">";
|
||
string actual = sanitizer.Sanitize(htmlFragment);
|
||
|
||
// Assert
|
||
string expected = "<img>";
|
||
Assert.Equal(expected, actual, ignoreCase: true);
|
||
}
|
||
|
||
/// <summary>
|
||
/// A test for Image Xss vector with embedded carriage return
|
||
/// Example <!-- <IMG SRC=\"jav
ascript:alert('XSS');\"> -->
|
||
/// </summary>
|
||
[Fact]
|
||
public void ImageEmbeddedCarriageReturnXSSTest()
|
||
{
|
||
// Arrange
|
||
var sanitizer = Sanitizer;
|
||
|
||
|
||
// Act
|
||
string htmlFragment = "<IMG SRC=\"jav
ascript:alert('XSS');\">";
|
||
string actual = sanitizer.Sanitize(htmlFragment);
|
||
|
||
// Assert
|
||
string expected = "<img>";
|
||
Assert.Equal(expected, actual, ignoreCase: true);
|
||
}
|
||
|
||
/// <summary>
|
||
/// A test for Image Xss vector with Multiline using ASCII carriage return
|
||
/// Example <!-- <IMG
|
||
/// SRC
|
||
/// =
|
||
/// "
|
||
/// j
|
||
/// a
|
||
/// v
|
||
/// a
|
||
/// s
|
||
/// c
|
||
/// r
|
||
/// i
|
||
/// p
|
||
/// t
|
||
/// :
|
||
/// a
|
||
/// l
|
||
/// e
|
||
/// r
|
||
/// t
|
||
/// (
|
||
/// '
|
||
/// X
|
||
/// S
|
||
/// S
|
||
/// '
|
||
/// )
|
||
/// "
|
||
///> -->
|
||
/// </summary>
|
||
[Fact]
|
||
public void ImageMultilineInjectedXSSTest()
|
||
{
|
||
// Arrange
|
||
var sanitizer = Sanitizer;
|
||
|
||
|
||
// Act
|
||
string htmlFragment = @"<IMG
|
||
SRC
|
||
=
|
||
""
|
||
j
|
||
a
|
||
v
|
||
a
|
||
s
|
||
c
|
||
r
|
||
i
|
||
p
|
||
t
|
||
:
|
||
a
|
||
l
|
||
e
|
||
r
|
||
t
|
||
(
|
||
'
|
||
X
|
||
S
|
||
S
|
||
'
|
||
)
|
||
""
|
||
>
|
||
";
|
||
|
||
string actual = sanitizer.Sanitize(htmlFragment);
|
||
|
||
// Assert
|
||
string expected = "<img>\n";
|
||
Assert.Equal(expected, actual, ignoreCase: true);
|
||
}
|
||
|
||
/// <summary>
|
||
/// A test for Image Xss vector with Null breaks up Javascript directive
|
||
/// Example <!-- perl -e 'print "<IMG SRC=java\0script:alert(\"XSS\")>";' > out -->
|
||
/// </summary>
|
||
[Fact]
|
||
public void ImageNullBreaksUpXSSTest1()
|
||
{
|
||
// Arrange
|
||
var sanitizer = Sanitizer;
|
||
|
||
|
||
// Act
|
||
string htmlFragment = "<IMG SRC=java\0script:alert(\"XSS\")>";
|
||
string actual = sanitizer.Sanitize(htmlFragment);
|
||
|
||
// Assert
|
||
string expected = "<img>";
|
||
Assert.Equal(expected, actual, ignoreCase: true);
|
||
}
|
||
|
||
/// <summary>
|
||
/// A test for Image Xss vector with Null breaks up cross site scripting vector
|
||
/// Example <!-- <image src=" perl -e 'print "<SCR\0IPT>alert(\"XSS\")</SCR\0IPT>";' > out "> -->
|
||
/// </summary>
|
||
[Fact]
|
||
public void ImageNullBreaksUpXSSTest2()
|
||
{
|
||
// Arrange
|
||
var sanitizer = Sanitizer;
|
||
|
||
|
||
// Act
|
||
string htmlFragment = "<SCR\0IPT>alert(\"XSS\")</SCR\0IPT>";
|
||
string actual = sanitizer.Sanitize(htmlFragment);
|
||
|
||
// Assert
|
||
string expected = "";
|
||
Assert.Equal(expected, actual, ignoreCase: true);
|
||
}
|
||
|
||
/// <summary>
|
||
/// A test for Image Xss vector with spaces and Meta characters
|
||
/// Example <!-- <IMG SRC="  javascript:alert('XSS');"> -->
|
||
/// </summary>
|
||
[Fact]
|
||
public void ImageSpaceAndMetaCharXSSTest()
|
||
{
|
||
// Arrange
|
||
var sanitizer = Sanitizer;
|
||
|
||
|
||
// Act
|
||
string htmlFragment = "<IMG SRC=\"  javascript:alert('XSS');\">";
|
||
string actual = sanitizer.Sanitize(htmlFragment);
|
||
|
||
// Assert
|
||
string expected = "<img>";
|
||
Assert.Equal(expected, actual, ignoreCase: true);
|
||
}
|
||
|
||
/// <summary>
|
||
/// A test for Image Xss vector with half open html
|
||
/// Example <!-- <IMG SRC="javascript:alert('XSS')" -->
|
||
/// </summary>
|
||
[Fact]
|
||
public void ImageHalfOpenHtmlXSSTest()
|
||
{
|
||
// Arrange
|
||
var sanitizer = Sanitizer;
|
||
|
||
|
||
// Act
|
||
string htmlFragment = "<IMG SRC=\"javascript:alert('XSS')\"";
|
||
string actual = sanitizer.Sanitize(htmlFragment);
|
||
|
||
// Assert
|
||
string expected = "";
|
||
Assert.Equal(expected, actual, ignoreCase: true);
|
||
}
|
||
|
||
/// <summary>
|
||
/// A test for Image Xss vector with double open angle bracket
|
||
/// Example <!-- <image src=http://ha.ckers.org/scriptlet.html < -->
|
||
/// </summary>
|
||
[Fact]
|
||
public void ImageDoubleOpenAngleBracketXSSTest()
|
||
{
|
||
// Arrange
|
||
var sanitizer = Sanitizer;
|
||
|
||
// Act
|
||
string htmlFragment = "<image src=http://ha.ckers.org/scriptlet.html <";
|
||
string actual = sanitizer.Sanitize(htmlFragment);
|
||
|
||
// Assert
|
||
string expected = "";
|
||
Assert.Equal(expected, actual, ignoreCase: true);
|
||
}
|
||
|
||
/// <summary>
|
||
/// A test for Dic Xss vector with Javascript escaping
|
||
/// Example <!-- <div style="\";alert('XSS');//"> -->
|
||
/// </summary>
|
||
[Fact]
|
||
public void DivJavascriptEscapingXSSTest()
|
||
{
|
||
// Arrange
|
||
var sanitizer = Sanitizer;
|
||
|
||
|
||
// Act
|
||
string htmlFragment = "<div style=\"\";alert('XSS');//\">";
|
||
string actual = sanitizer.Sanitize(htmlFragment);
|
||
|
||
// Assert
|
||
string expected = "<div style=\"\"></div>";
|
||
Assert.Equal(expected, actual, ignoreCase: true);
|
||
}
|
||
|
||
/// <summary>
|
||
/// A test for Image Xss vector with input image
|
||
/// Example <!-- <INPUT TYPE="IMAGE" SRC="javascript:alert('XSS');"> -->
|
||
/// </summary>
|
||
[Fact]
|
||
public void ImageInputXSSTest()
|
||
{
|
||
// Arrange
|
||
var sanitizer = Sanitizer;
|
||
|
||
|
||
// Act
|
||
string htmlFragment = "<INPUT TYPE=\"IMAGE\" SRC=\"javascript:alert('XSS');\">";
|
||
string actual = sanitizer.Sanitize(htmlFragment);
|
||
|
||
// Assert
|
||
string expected = "<input type=\"image\">";
|
||
Assert.Equal(expected, actual, ignoreCase: true);
|
||
}
|
||
|
||
/// <summary>
|
||
/// A test for Image Xss vector with Dynsrc
|
||
/// Example <!-- <IMG DYNSRC="javascript:alert('XSS')"> -->
|
||
/// </summary>
|
||
[Fact]
|
||
public void ImageDynsrcXSSTest()
|
||
{
|
||
// Arrange
|
||
var sanitizer = Sanitizer;
|
||
|
||
|
||
// Act
|
||
string htmlFragment = "<IMG DYNSRC=\"javascript:alert('XSS')\">";
|
||
string actual = sanitizer.Sanitize(htmlFragment);
|
||
|
||
// Assert
|
||
string expected = "<img>";
|
||
Assert.Equal(expected, actual, ignoreCase: true);
|
||
}
|
||
|
||
/// <summary>
|
||
/// A test for Image Xss vector with Lowsrc
|
||
/// Example <!-- <IMG LOWSRC="javascript:alert('XSS')"> -->
|
||
/// </summary>
|
||
[Fact]
|
||
public void ImageLowsrcXSSTest()
|
||
{
|
||
// Arrange
|
||
var sanitizer = Sanitizer;
|
||
|
||
|
||
// Act
|
||
string htmlFragment = "<IMG LOWSRC=\"javascript:alert('XSS')\">";
|
||
string actual = sanitizer.Sanitize(htmlFragment);
|
||
|
||
// Assert
|
||
string expected = "<img>";
|
||
Assert.Equal(expected, actual, ignoreCase: true);
|
||
}
|
||
|
||
/// <summary>
|
||
/// A test for Xss vector with BGSound
|
||
/// Example <!-- <BGSOUND SRC="javascript:alert('XSS');"> -->
|
||
/// </summary>
|
||
[Fact]
|
||
public void BGSoundXSSTest()
|
||
{
|
||
// Arrange
|
||
var sanitizer = Sanitizer;
|
||
|
||
|
||
// Act
|
||
string htmlFragment = "<BGSOUND SRC=\"javascript:alert('XSS');\">";
|
||
string actual = sanitizer.Sanitize(htmlFragment);
|
||
|
||
// Assert
|
||
string expected = "";
|
||
Assert.Equal(expected, actual, ignoreCase: true);
|
||
}
|
||
|
||
/// <summary>
|
||
/// A test for BR with Javascript Include
|
||
/// Example <!-- <BR SIZE="&{alert('XSS')}"> -->
|
||
/// </summary>
|
||
[Fact]
|
||
public void BRJavascriptIncludeXSSTest()
|
||
{
|
||
// Arrange
|
||
var sanitizer = Sanitizer;
|
||
|
||
|
||
// Act
|
||
string htmlFragment = "<BR SIZE=\"&{alert('XSS')}\">";
|
||
string actual = sanitizer.Sanitize(htmlFragment);
|
||
|
||
// Assert
|
||
string expected = "<BR>";
|
||
Assert.Equal(expected, actual, ignoreCase: true);
|
||
}
|
||
|
||
/// <summary>
|
||
/// A test for P with url in style
|
||
/// Example <!-- <p STYLE="behavior: url(www.ha.ckers.org);"> -->
|
||
/// </summary>
|
||
[Fact]
|
||
public void PWithUrlInStyleXSSTest()
|
||
{
|
||
// Arrange
|
||
var sanitizer = Sanitizer;
|
||
|
||
|
||
// Act
|
||
string htmlFragment = "<p STYLE=\"behavior: url(www.ha.ckers.org);\">";
|
||
string actual = sanitizer.Sanitize(htmlFragment);
|
||
|
||
// Assert
|
||
// intentionally keep it failing to get notice when reviewing unit tests so can disucss
|
||
string expected = "<p></p>";
|
||
Assert.Equal(expected, actual, ignoreCase: true);
|
||
}
|
||
|
||
/// <summary>
|
||
/// A test for Image with vbscript
|
||
/// Example <!-- <IMG SRC='vbscript:msgbox("XSS")'> -->
|
||
/// </summary>
|
||
[Fact]
|
||
public void ImageWithVBScriptXSSTest()
|
||
{
|
||
// Arrange
|
||
var sanitizer = Sanitizer;
|
||
|
||
|
||
// Act
|
||
string htmlFragment = "<IMG SRC='vbscript:msgbox(\"XSS\")'>";
|
||
string actual = sanitizer.Sanitize(htmlFragment);
|
||
|
||
// Assert
|
||
string expected = "<img>";
|
||
Assert.Equal(expected, actual, ignoreCase: true);
|
||
}
|
||
|
||
/// <summary>
|
||
/// A test for Image with Mocha
|
||
/// Example <!-- <IMG SRC="mocha:[code]"> -->
|
||
/// </summary>
|
||
[Fact]
|
||
public void ImageWithMochaXSSTest()
|
||
{
|
||
// Arrange
|
||
var sanitizer = Sanitizer;
|
||
|
||
|
||
// Act
|
||
string htmlFragment = "<IMG SRC=\"mocha:[code]\">";
|
||
string actual = sanitizer.Sanitize(htmlFragment);
|
||
|
||
// Assert
|
||
string expected = "<img>";
|
||
Assert.Equal(expected, actual, ignoreCase: true);
|
||
}
|
||
|
||
/// <summary>
|
||
/// A test for Image with Livescript
|
||
/// Example <!-- <IMG SRC="Livescript:[code]"> -->
|
||
/// </summary>
|
||
[Fact]
|
||
public void ImageWithLivescriptXSSTest()
|
||
{
|
||
// Arrange
|
||
var sanitizer = Sanitizer;
|
||
|
||
|
||
// Act
|
||
string htmlFragment = "<IMG SRC=\"Livescript:[code]\">";
|
||
string actual = sanitizer.Sanitize(htmlFragment);
|
||
|
||
// Assert
|
||
string expected = "<img>";
|
||
Assert.Equal(expected, actual, ignoreCase: true);
|
||
}
|
||
|
||
/// <summary>
|
||
/// A test for Iframe
|
||
/// Example <!-- <IFRAME SRC="javascript:alert('XSS');"></IFRAME> -->
|
||
/// </summary>
|
||
[Fact]
|
||
public void IframeXSSTest()
|
||
{
|
||
// Arrange
|
||
var sanitizer = Sanitizer;
|
||
|
||
|
||
// Act
|
||
string htmlFragment = "<IFRAME SRC=\"javascript:alert('XSS');\"></IFRAME>";
|
||
string actual = sanitizer.Sanitize(htmlFragment);
|
||
|
||
// Assert
|
||
string expected = "";
|
||
Assert.Equal(expected, actual, ignoreCase: true);
|
||
}
|
||
|
||
/// <summary>
|
||
/// A test for Frame
|
||
/// Example <!-- <FRAMESET><FRAME SRC="javascript:alert('XSS');"></FRAMESET> -->
|
||
/// </summary>
|
||
[Fact]
|
||
public void FrameXSSTest()
|
||
{
|
||
// Arrange
|
||
var sanitizer = Sanitizer;
|
||
|
||
|
||
// Act
|
||
string htmlFragment = "<FRAMESET><FRAME SRC=\"javascript:alert('XSS');\"></FRAMESET>";
|
||
string actual = sanitizer.Sanitize(htmlFragment);
|
||
|
||
// Assert
|
||
string expected = "";
|
||
Assert.Equal(expected, actual, ignoreCase: true);
|
||
}
|
||
|
||
/// <summary>
|
||
/// A test for Table
|
||
/// Example <!-- <TABLE BACKGROUND="javascript:alert('XSS')"> -->
|
||
/// </summary>
|
||
[Fact]
|
||
public void TableXSSTest()
|
||
{
|
||
// Arrange
|
||
var sanitizer = Sanitizer;
|
||
|
||
|
||
// Act
|
||
string htmlFragment = "<TABLE BACKGROUND=\"javascript:alert('XSS')\">";
|
||
string actual = sanitizer.Sanitize(htmlFragment);
|
||
|
||
// Assert
|
||
string expected = "<table></table>";
|
||
Assert.Equal(expected, actual, ignoreCase: true);
|
||
}
|
||
|
||
/// <summary>
|
||
/// A test for TD
|
||
/// Example <!-- <TABLE><TD BACKGROUND="javascript:alert('XSS')"> -->
|
||
/// </summary>
|
||
[Fact]
|
||
public void TDXSSTest()
|
||
{
|
||
// Arrange
|
||
var sanitizer = Sanitizer;
|
||
|
||
|
||
// Act
|
||
string htmlFragment = "<TABLE><TD BACKGROUND=\"javascript:alert('XSS')\">";
|
||
string actual = sanitizer.Sanitize(htmlFragment);
|
||
|
||
// Assert
|
||
string expected = "<table><tbody><tr><td></td></tr></tbody></table>";
|
||
Assert.Equal(expected, actual, ignoreCase: true);
|
||
}
|
||
|
||
/// <summary>
|
||
/// A test for Div Background Image
|
||
/// Example <!-- <DIV STYLE="background-image: url(javascript:alert('XSS'))"> -->
|
||
/// </summary>
|
||
[Fact]
|
||
public void DivBackgroundImageXSSTest()
|
||
{
|
||
// Arrange
|
||
var sanitizer = Sanitizer;
|
||
|
||
|
||
// Act
|
||
string htmlFragment = "<DIV STYLE=\"background-image: url(javascript:alert('XSS'))\">";
|
||
string actual = sanitizer.Sanitize(htmlFragment);
|
||
|
||
// Assert
|
||
string expected = "<div></div>";
|
||
Assert.Equal(expected, actual, ignoreCase: true);
|
||
}
|
||
|
||
/// <summary>
|
||
/// A test for Div Background Image with unicoded XSS
|
||
/// Example <!-- <DIV STYLE="background-image:\0075\0072\006C\0028'\006a\0061\0076\0061\0073\0063\0072\0069\0070\0074\003a\0061\006c\0065\0072\0074\0028.1027\0058.1053\0053\0027\0029'\0029"> -->
|
||
/// </summary>
|
||
[Fact]
|
||
public void DivBackgroundImageWithUnicodedXSSTest()
|
||
{
|
||
// Arrange
|
||
var sanitizer = Sanitizer;
|
||
|
||
|
||
// Act
|
||
string htmlFragment = @"<DIV STYLE=""background-image:\0075\0072\006C\0028'\006a\0061\0076\0061\0073\0063\0072\0069\0070\0074\003a\0061\006c\0065\0072\0074\0028\0027\0058\0053\0053\0027\0029'\0029"">";
|
||
string actual = sanitizer.Sanitize(htmlFragment);
|
||
|
||
// Assert
|
||
string expected = "<div></div>";
|
||
Assert.Equal(expected, actual, ignoreCase: true);
|
||
}
|
||
|
||
/// <summary>
|
||
/// A test for Div Background Image with extra characters
|
||
/// Example <!-- <DIV STYLE="background-image: url(javascript:alert('XSS'))"> -->
|
||
/// </summary>
|
||
[Fact]
|
||
public void DivBackgroundImageWithExtraCharactersXSSTest()
|
||
{
|
||
// Arrange
|
||
var sanitizer = Sanitizer;
|
||
|
||
|
||
// Act
|
||
string htmlFragment = "<DIV STYLE=\"background-image: url(javascript:alert('XSS'))\">";
|
||
string actual = sanitizer.Sanitize(htmlFragment);
|
||
|
||
// Assert
|
||
string expected = "<div></div>";
|
||
Assert.Equal(expected, actual, ignoreCase: true);
|
||
}
|
||
|
||
/// <summary>
|
||
/// A test for DIV expression
|
||
/// Example <!-- <DIV STYLE="width: expression(alert('XSS'));"> -->
|
||
/// </summary>
|
||
[Fact]
|
||
public void DivExpressionXSSTest()
|
||
{
|
||
// Arrange
|
||
var sanitizer = Sanitizer;
|
||
|
||
|
||
// Act
|
||
string htmlFragment = "<DIV STYLE=\"width: expression(alert('XSS'));\">";
|
||
string actual = sanitizer.Sanitize(htmlFragment);
|
||
|
||
// Assert
|
||
string expected = "<div></div>";
|
||
Assert.Equal(expected, actual, ignoreCase: true);
|
||
}
|
||
|
||
/// <summary>
|
||
/// A test for Image with break up expression
|
||
/// Example <!-- <IMG STYLE="xss:expr/*XSS*/ession(alert('XSS'))"> -->
|
||
/// </summary>
|
||
[Fact]
|
||
public void ImageStyleExpressionXSSTest()
|
||
{
|
||
// Arrange
|
||
var sanitizer = Sanitizer;
|
||
|
||
|
||
// Act
|
||
string htmlFragment = "<IMG STYLE=\"xss:expr/*XSS*/ession(alert('XSS'))\">";
|
||
string actual = sanitizer.Sanitize(htmlFragment);
|
||
|
||
// Assert
|
||
string expected = "<img>";
|
||
Assert.Equal(expected, actual, ignoreCase: true);
|
||
}
|
||
|
||
/// <summary>
|
||
/// A test for AnchorTag with break up expression
|
||
/// Example <!-- exp/*<A STYLE='no\xss:noxss("*//*");xss:ex/*XSS*//*/*/pression(alert("XSS"))'> -->
|
||
/// </summary>
|
||
[Fact]
|
||
public void AnchorTagStyleExpressionXSSTest()
|
||
{
|
||
// Arrange
|
||
var sanitizer = Sanitizer;
|
||
|
||
|
||
// Act
|
||
string htmlFragment = "exp/*<A STYLE='no\\xss:noxss(\"*//*\");xss:ex/*XSS*//*/*/pression(alert(\"XSS\"))'>";
|
||
string actual = sanitizer.Sanitize(htmlFragment);
|
||
|
||
// Assert
|
||
string expected = "exp/*<a></a>";
|
||
Assert.Equal(expected, actual, ignoreCase: true);
|
||
}
|
||
|
||
/// <summary>
|
||
/// A test for BaseTag
|
||
/// Example <!-- <BASE HREF="javascript:alert('XSS');//"> -->
|
||
/// </summary>
|
||
[Fact]
|
||
public void BaseTagXSSTest()
|
||
{
|
||
// Arrange
|
||
var sanitizer = Sanitizer;
|
||
|
||
|
||
// Act
|
||
string htmlFragment = "<BASE HREF=\"javascript:alert('XSS');//\">";
|
||
string actual = sanitizer.Sanitize(htmlFragment);
|
||
|
||
// Assert
|
||
string expected = "";
|
||
Assert.Equal(expected, actual, ignoreCase: true);
|
||
}
|
||
|
||
/// <summary>
|
||
/// A test for EMBEDTag
|
||
/// Example <!-- <EMBED SRC="http://ha.ckers.org/xss.swf" AllowScriptAccess="always"></EMBED> -->
|
||
/// </summary>
|
||
[Fact]
|
||
public void EmbedTagXSSTest()
|
||
{
|
||
// Arrange
|
||
var sanitizer = Sanitizer;
|
||
|
||
|
||
// Act
|
||
string htmlFragment = "<EMBED SRC=\"http://ha.ckers.org/xss.swf\" AllowScriptAccess=\"always\"></EMBED>";
|
||
string actual = sanitizer.Sanitize(htmlFragment);
|
||
|
||
// Assert
|
||
string expected = "";
|
||
Assert.Equal(expected, actual, ignoreCase: true);
|
||
}
|
||
|
||
/// <summary>
|
||
/// A test for EMBEDSVG
|
||
/// Example <!-- <EMBED SRC="data:image/svg+xml;base64,PHN2ZyB4bWxuczpzdmc9Imh0dH A6Ly93d3cudzMub3JnLzIwMDAvc3ZnIiB4bWxucz0iaHR0cDovL3d3dy53My5vcmcv MjAwMC9zdmciIHhtbG5zOnhsaW5rPSJodHRwOi8vd3d3LnczLm9yZy8xOTk5L3hs aW5rIiB2ZXJzaW9uPSIxLjAiIHg9IjAiIHk9IjAiIHdpZHRoPSIxOTQiIGhlaWdodD0iMjAw IiBpZD0ieHNzIj48c2NyaXB0IHR5cGU9InRleHQvZWNtYXNjcmlwdCI+YWxlcnQoIlh TUyIpOzwvc2NyaXB0Pjwvc3ZnPg==" type="image/svg+xml" AllowScriptAccess="always"></EMBED> -->
|
||
/// </summary>
|
||
[Fact]
|
||
public void EmbedSVGXSSTest()
|
||
{
|
||
// Arrange
|
||
var sanitizer = Sanitizer;
|
||
|
||
|
||
// Act
|
||
string htmlFragment = "<EMBED SRC=\"data:image/svg+xml;base64,PHN2ZyB4bWxuczpzdmc9Imh0dH A6Ly93d3cudzMub3JnLzIwMDAvc3ZnIiB4bWxucz0iaHR0cDovL3d3dy53My5vcmcv MjAwMC9zdmciIHhtbG5zOnhsaW5rPSJodHRwOi8vd3d3LnczLm9yZy8xOTk5L3hs aW5rIiB2ZXJzaW9uPSIxLjAiIHg9IjAiIHk9IjAiIHdpZHRoPSIxOTQiIGhlaWdodD0iMjAw IiBpZD0ieHNzIj48c2NyaXB0IHR5cGU9InRleHQvZWNtYXNjcmlwdCI+YWxlcnQoIlh TUyIpOzwvc2NyaXB0Pjwvc3ZnPg==\" type=\"image/svg+xml\" AllowScriptAccess=\"always\"></EMBED>";
|
||
string actual = sanitizer.Sanitize(htmlFragment);
|
||
|
||
// Assert
|
||
string expected = "";
|
||
Assert.Equal(expected, actual, ignoreCase: true);
|
||
}
|
||
|
||
/// <summary>
|
||
/// A test for XML namespace
|
||
/// Example <!-- <HTML xmlns:xss> <?import namespace="xss" implementation="http://ha.ckers.org/xss.htc"> <xss:xss>XSS</xss:xss></HTML> -->
|
||
/// </summary>
|
||
[Fact]
|
||
public void XmlNamespaceXSSTest()
|
||
{
|
||
// Arrange
|
||
var sanitizer = Sanitizer;
|
||
|
||
|
||
// Act
|
||
string htmlFragment = "<HTML xmlns:xss><?import namespace=\"xss\" implementation=\"http://ha.ckers.org/xss.htc\"><xss:xss>XSS</xss:xss></HTML>";
|
||
string actual = sanitizer.Sanitize(htmlFragment);
|
||
|
||
// Assert
|
||
string expected = "";
|
||
Assert.Equal(expected, actual, ignoreCase: true);
|
||
}
|
||
|
||
/// <summary>
|
||
/// A test for XML with CData
|
||
/// Example <!-- <XML ID=I><X><C><![CDATA[<IMG SRC="javas]]><![CDATA[cript:alert('XSS');">]]></C></X></xml><SPAN DATASRC=#I DATAFLD=C DATAFORMATAS=HTML></SPAN> -->
|
||
/// </summary>
|
||
[Fact]
|
||
public void XmlWithCDataXSSTest()
|
||
{
|
||
// Arrange
|
||
var sanitizer = Sanitizer;
|
||
|
||
|
||
// Act
|
||
string htmlFragment = "<XML ID=I><X><C><![CDATA[<IMG SRC=\"javas]]><![CDATA[cript:alert('XSS');\">]]></C></X></xml><SPAN DATASRC=#I DATAFLD=C DATAFORMATAS=HTML></SPAN>";
|
||
string actual = sanitizer.Sanitize(htmlFragment);
|
||
|
||
// Assert
|
||
string expected = "<SPAN></SPAN>";
|
||
Assert.Equal(expected, actual, ignoreCase: true);
|
||
}
|
||
|
||
/// <summary>
|
||
/// A test for XML with Comment obfuscation
|
||
/// </summary>
|
||
[Fact]
|
||
public void XmlWithCommentObfuscationXSSTest()
|
||
{
|
||
// Arrange
|
||
var sanitizer = Sanitizer;
|
||
|
||
|
||
// Act
|
||
string htmlFragment = "<XML ID=\"xss\"><I><B><IMG SRC=\"javas<!-- -->cript:alert('XSS')\"></B></I></XML><SPAN DATASRC=\"#xss\" DATAFLD=\"B\" DATAFORMATAS=\"HTML\"></SPAN>";
|
||
string actual = sanitizer.Sanitize(htmlFragment);
|
||
|
||
// Assert
|
||
string expected = "<SPAN></SPAN>";
|
||
Assert.Equal(expected, actual, ignoreCase: true);
|
||
}
|
||
|
||
/// <summary>
|
||
/// A test for XML with Embedded script
|
||
/// Example <!-- <XML SRC="xsstest.xml" ID=I></XML><SPAN DATASRC=#I DATAFLD=C DATAFORMATAS=HTML></SPAN> -->
|
||
/// </summary>
|
||
[Fact]
|
||
public void XmlWithEmbeddedScriptXSSTest()
|
||
{
|
||
// Arrange
|
||
var sanitizer = Sanitizer;
|
||
|
||
|
||
// Act
|
||
string htmlFragment = "<XML SRC=\"xsstest.xml\" ID=I></XML><SPAN DATASRC=#I DATAFLD=C DATAFORMATAS=HTML></SPAN>";
|
||
string actual = sanitizer.Sanitize(htmlFragment);
|
||
|
||
// Assert
|
||
string expected = "<SPAN></SPAN>";
|
||
Assert.Equal(expected, actual, ignoreCase: true);
|
||
}
|
||
|
||
/// <summary>
|
||
/// A test for Html + Time
|
||
/// Example <!-- <HTML><BODY><?xml:namespace prefix="t" ns="urn:schemas-microsoft-com:time"><?import namespace="t" implementation="#default#time2"><t:set attributeName="innerHTML" to="XSS<SCRIPT DEFER>alert("XSS")</SCRIPT>"></BODY></HTML> -->
|
||
/// </summary>
|
||
[Fact]
|
||
public void HtmlPlusTimeXSSTest()
|
||
{
|
||
// Arrange
|
||
var sanitizer = Sanitizer;
|
||
|
||
|
||
// Act
|
||
string htmlFragment = "<HTML><BODY><?xml:namespace prefix=\"t\" ns=\"urn:schemas-microsoft-com:time\"><?import namespace=\"t\" implementation=\"#default#time2\"><t:set attributeName=\"innerHTML\" to=\"XSS<SCRIPT DEFER>alert("XSS")</SCRIPT>\"></BODY></HTML>";
|
||
string actual = sanitizer.Sanitize(htmlFragment);
|
||
|
||
// Assert
|
||
string expected = "";
|
||
Assert.Equal(expected, actual, ignoreCase: true);
|
||
}
|
||
|
||
/// <summary>
|
||
/// A test for AnchorTag with javascript link location
|
||
/// Example <!-- <A HREF="javascript:document.location='http://www.google.com/'">XSS</A> -->
|
||
/// </summary>
|
||
[Fact]
|
||
public void AnchorTagJavascriptLinkLocationXSSTest()
|
||
{
|
||
// Arrange
|
||
var sanitizer = Sanitizer;
|
||
|
||
|
||
// Act
|
||
string htmlFragment = "<A HREF=\"javascript:document.location='http://www.google.com/'\">XSS</A>";
|
||
string actual = sanitizer.Sanitize(htmlFragment);
|
||
|
||
// Assert
|
||
string expected = "<a>XSS</a>";
|
||
Assert.Equal(expected, actual, ignoreCase: true);
|
||
}
|
||
|
||
/// <summary>
|
||
/// A test for Div with no filter evasion
|
||
/// Example <!-- <Div style="background-color: http://www.codeplex.com?url=<SCRIPT SRC=http://ha.ckers.org/xss.js></SCRIPT>"> -->
|
||
/// </summary>
|
||
[Fact]
|
||
public void DivNoFilterEvasionXSSTest()
|
||
{
|
||
// Arrange
|
||
var sanitizer = Sanitizer;
|
||
|
||
|
||
// Act
|
||
string htmlFragment = "<Div style=\"background-color: http://www.codeplex.com?url=<SCRIPT SRC=http://ha.ckers.org/xss.js></SCRIPT>\">";
|
||
string actual = sanitizer.Sanitize(htmlFragment);
|
||
|
||
// Assert
|
||
string expected = "<div></div>";
|
||
Assert.Equal(expected, actual, ignoreCase: true);
|
||
}
|
||
|
||
/// <summary>
|
||
/// A test for Div with style expression and no filter evasion
|
||
/// Example <!-- <Div style="background-color: expression(<SCRIPT SRC=http://ha.ckers.org/xss.js></SCRIPT>)"> -->
|
||
/// </summary>
|
||
[Fact]
|
||
public void DivStyleExpressionNoFilterEvasionXSSTest()
|
||
{
|
||
// Arrange
|
||
var sanitizer = Sanitizer;
|
||
|
||
|
||
// Act
|
||
string htmlFragment = "<Div style=\"background-color: expression(<SCRIPT SRC=http://ha.ckers.org/xss.js></SCRIPT>)\">";
|
||
string actual = sanitizer.Sanitize(htmlFragment);
|
||
|
||
// Assert
|
||
string expected = "<div></div>";
|
||
Assert.Equal(expected, actual, ignoreCase: true);
|
||
}
|
||
|
||
/// <summary>
|
||
/// A test for AnchorTag with non alpha non digit xss
|
||
/// Example <!-- <A HREF="http://www.codeplex.com?url=<SCRIPT/XSS SRC="http://ha.ckers.org/xss.js"></SCRIPT>">XSS</A> -->
|
||
/// </summary>
|
||
[Fact]
|
||
public void AnchorTagNonAlphaNonDigitXSSTest()
|
||
{
|
||
// Arrange
|
||
var sanitizer = Sanitizer;
|
||
|
||
|
||
// Act
|
||
string htmlFragment = "<A HREF=\"http://www.codeplex.com?url=<SCRIPT/XSS SRC=\"http://ha.ckers.org/xss.js\"></SCRIPT>\">XSS</A>";
|
||
string actual = sanitizer.Sanitize(htmlFragment);
|
||
|
||
// Assert
|
||
string expected = "<a href=\"http://www.codeplex.com?url=<SCRIPT/XSS SRC=\">\">XSS</a>";
|
||
Assert.Equal(expected, actual, ignoreCase: true);
|
||
}
|
||
|
||
/// <summary>
|
||
/// A test for Div with non alpha non digit xss
|
||
/// Example <!-- <Div style="background-color: http://www.codeplex.com?url=<SCRIPT/XSS SRC=http://ha.ckers.org/xss.js></SCRIPT>"> -->
|
||
/// </summary>
|
||
[Fact]
|
||
public void DivNonAlphaNonDigitXSSTest()
|
||
{
|
||
// Arrange
|
||
var sanitizer = Sanitizer;
|
||
|
||
|
||
// Act
|
||
string htmlFragment = "<Div style=\"background-color: http://www.codeplex.com?url=<SCRIPT/XSS SRC=\"http://ha.ckers.org/xss.js\"></SCRIPT>\">";
|
||
string actual = sanitizer.Sanitize(htmlFragment);
|
||
|
||
// Assert
|
||
string expected = "<div>\"></div>";
|
||
Assert.Equal(expected, actual, ignoreCase: true);
|
||
}
|
||
|
||
/// <summary>
|
||
/// A test for Div with style expression and non alpha non digit xss
|
||
/// Example <!-- <Div style="background-color: expression(<SCRIPT/XSS SRC="http://ha.ckers.org/xss.js"></SCRIPT>)"> -->
|
||
/// </summary>
|
||
[Fact]
|
||
public void DivStyleExpressionNonAlphaNonDigitXSSTest()
|
||
{
|
||
// Arrange
|
||
var sanitizer = Sanitizer;
|
||
|
||
|
||
// Act
|
||
string htmlFragment = "<Div style=\"background-color: expression(<SCRIPT/XSS SRC=\"http://ha.ckers.org/xss.js\"></SCRIPT>)\">";
|
||
string actual = sanitizer.Sanitize(htmlFragment);
|
||
|
||
// Assert
|
||
string expected = "<div>)\"></div>";
|
||
Assert.Equal(expected, actual, ignoreCase: true);
|
||
}
|
||
|
||
/// <summary>
|
||
/// A test for Div with non alpha non digit part 3 xss
|
||
/// Example <!-- <Div style="background-color: http://www.codeplex.com?url=<SCRIPT/SRC=http://ha.ckers.org/xss.js></SCRIPT>"> -->
|
||
/// </summary>
|
||
[Fact]
|
||
public void DivNonAlphaNonDigit3XSSTest()
|
||
{
|
||
// Arrange
|
||
var sanitizer = Sanitizer;
|
||
|
||
|
||
// Act
|
||
string htmlFragment = "<Div style=\"background-color: http://www.codeplex.com?url=<SCRIPT/SRC=\"http://ha.ckers.org/xss.js\"></SCRIPT>\">";
|
||
string actual = sanitizer.Sanitize(htmlFragment);
|
||
|
||
// Assert
|
||
string expected = "<div>\"></div>";
|
||
Assert.Equal(expected, actual, ignoreCase: true);
|
||
}
|
||
|
||
/// <summary>
|
||
/// A test for Div with style expression and non alpha non digit part 3 xss
|
||
/// Example <!-- <Div style="background-color: expression(<SCRIPT/SRC="http://ha.ckers.org/xss.js"></SCRIPT>)"> -->
|
||
/// </summary>
|
||
[Fact]
|
||
public void DivStyleExpressionNonAlphaNonDigit3XSSTest()
|
||
{
|
||
// Arrange
|
||
var sanitizer = Sanitizer;
|
||
|
||
|
||
// Act
|
||
string htmlFragment = "<Div style=\"background-color: expression(<SCRIPT/SRC=\"http://ha.ckers.org/xss.js\"></SCRIPT>)\">";
|
||
string actual = sanitizer.Sanitize(htmlFragment);
|
||
|
||
// Assert
|
||
string expected = "<div>)\"></div>";
|
||
Assert.Equal(expected, actual, ignoreCase: true);
|
||
}
|
||
|
||
/// <summary>
|
||
/// A test for AnchorTag with Extraneous open brackets xss
|
||
/// Example <!-- <A HREF="http://www.codeplex.com?url=<<SCRIPT>alert("XSS");//<</SCRIPT>">XSS</A> -->
|
||
/// </summary>
|
||
[Fact]
|
||
public void AnchorTagExtraneousOpenBracketsXSSTest()
|
||
{
|
||
// Arrange
|
||
var sanitizer = Sanitizer;
|
||
|
||
|
||
// Act
|
||
string htmlFragment = "<A HREF=\"http://www.codeplex.com?url=<<SCRIPT>alert(\"XSS\");//<</SCRIPT>\">XSS</A>";
|
||
string actual = sanitizer.Sanitize(htmlFragment);
|
||
|
||
// Assert
|
||
string expected = "<a href=\"http://www.codeplex.com?url=<<SCRIPT>alert(\">\">XSS</a>";
|
||
Assert.Equal(expected, actual, ignoreCase: true);
|
||
}
|
||
|
||
/// <summary>
|
||
/// A test for Div with Extraneous open brackets xss
|
||
/// Example <!-- <Div style="background-color: http://www.codeplex.com?url=<<SCRIPT>alert("XSS");//<</SCRIPT>"> -->
|
||
/// </summary>
|
||
[Fact]
|
||
public void DivExtraneousOpenBracketsXSSTest()
|
||
{
|
||
// Arrange
|
||
var sanitizer = Sanitizer;
|
||
|
||
|
||
// Act
|
||
string htmlFragment = "<Div style=\"background-color: http://www.codeplex.com?url=<<SCRIPT>alert(\"XSS\");//<</SCRIPT>\">";
|
||
string actual = sanitizer.Sanitize(htmlFragment);
|
||
|
||
// Assert
|
||
string expected = "<div>\"></div>";
|
||
Assert.Equal(expected, actual, ignoreCase: true);
|
||
}
|
||
|
||
/// <summary>
|
||
/// A test for Div with style expression and Extraneous open brackets xss
|
||
/// Example <!-- <Div style="background-color: expression(<<SCRIPT>alert("XSS");//<</SCRIPT>)"> -->
|
||
/// </summary>
|
||
[Fact]
|
||
public void DivStyleExpressionExtraneousOpenBracketsXSSTest()
|
||
{
|
||
// Arrange
|
||
var sanitizer = Sanitizer;
|
||
|
||
// Act
|
||
string htmlFragment = "<Div style=\"background-color: expression(<<SCRIPT>alert(\"XSS\");//<</SCRIPT>)\">";
|
||
string actual = sanitizer.Sanitize(htmlFragment);
|
||
|
||
// Assert
|
||
string expected = "<div>)\"></div>";
|
||
Assert.Equal(expected, actual, ignoreCase: true);
|
||
}
|
||
|
||
/// <summary>
|
||
/// A test for Div with No closing script tags xss
|
||
/// Example <!-- <Div style="background-color: http://www.codeplex.com?url=<SCRIPT SRC=http://ha.ckers.org/xss.js?<B>"> -->
|
||
/// </summary>
|
||
[Fact]
|
||
public void DivNoClosingScriptTagsXSSTest()
|
||
{
|
||
// Arrange
|
||
var sanitizer = Sanitizer;
|
||
|
||
|
||
// Act
|
||
string htmlFragment = "<Div style=\"background-color: http://www.codeplex.com?url=<SCRIPT SRC=http://ha.ckers.org/xss.js?<B>\">";
|
||
string actual = sanitizer.Sanitize(htmlFragment);
|
||
|
||
// Assert
|
||
string expected = "<div></div>";
|
||
Assert.Equal(expected, actual, ignoreCase: true);
|
||
}
|
||
|
||
/// <summary>
|
||
/// A test for Div with style expression and No closing script tags xss
|
||
/// Example <!-- <Div style="background-color: expression(<SCRIPT SRC=http://ha.ckers.org/xss.js?<B>)"> -->
|
||
/// </summary>
|
||
[Fact]
|
||
public void DivStyleExpressionNoClosingScriptTagsXSSTest()
|
||
{
|
||
// Arrange
|
||
var sanitizer = Sanitizer;
|
||
|
||
|
||
// Act
|
||
string htmlFragment = "<Div style=\"background-color: expression(<SCRIPT SRC=http://ha.ckers.org/xss.js?<B>)\">";
|
||
string actual = sanitizer.Sanitize(htmlFragment);
|
||
|
||
// Assert
|
||
string expected = "<div></div>";
|
||
Assert.Equal(expected, actual, ignoreCase: true);
|
||
}
|
||
|
||
/// <summary>
|
||
/// A test for AnchorTag with Protocol resolution in script tags xss
|
||
/// Example <!-- <A HREF="http://www.codeplex.com?url=<SCRIPT SRC=//ha.ckers.org/.j>">XSS</A> -->
|
||
/// </summary>
|
||
[Fact]
|
||
public void AnchorTagProtocolResolutionScriptXSSTest()
|
||
{
|
||
// Arrange
|
||
var sanitizer = Sanitizer;
|
||
|
||
|
||
// Act
|
||
string htmlFragment = "<A HREF=\"http://www.codeplex.com?url=<SCRIPT SRC=//ha.ckers.org/.j>\">XSS</A>";
|
||
string actual = sanitizer.Sanitize(htmlFragment);
|
||
|
||
// Assert
|
||
string expected = "<a href=\"http://www.codeplex.com?url=<SCRIPT SRC=//ha.ckers.org/.j>\">XSS</a>";
|
||
Assert.Equal(expected, actual, ignoreCase: true);
|
||
}
|
||
|
||
/// <summary>
|
||
/// A test for Div with Protocol resolution in script tags xss
|
||
/// Example <!-- <Div style="background-color: http://www.codeplex.com?url=<SCRIPT SRC=//ha.ckers.org/.j>"> -->
|
||
/// </summary>
|
||
[Fact]
|
||
public void DivProtocolResolutionScriptXSSTest()
|
||
{
|
||
// Arrange
|
||
var sanitizer = Sanitizer;
|
||
|
||
|
||
// Act
|
||
string htmlFragment = "<Div style=\"background-color: http://www.codeplex.com?url=<SCRIPT SRC=//ha.ckers.org/.j>\">";
|
||
string actual = sanitizer.Sanitize(htmlFragment);
|
||
|
||
// Assert
|
||
string expected = "<div></div>";
|
||
Assert.Equal(expected, actual, ignoreCase: true);
|
||
}
|
||
|
||
/// <summary>
|
||
/// A test for Div with style expression and Protocol resolution in script tags xss
|
||
/// Example <!-- <Div style="background-color: expression(<SCRIPT SRC=//ha.ckers.org/.j>)"> -->
|
||
/// </summary>
|
||
[Fact]
|
||
public void DivStyleExpressionProtocolResolutionScriptXSSTest()
|
||
{
|
||
// Arrange
|
||
var sanitizer = Sanitizer;
|
||
|
||
|
||
// Act
|
||
string htmlFragment = "<Div style=\"background-color: expression(<SCRIPT SRC=//ha.ckers.org/.j>)\">";
|
||
string actual = sanitizer.Sanitize(htmlFragment);
|
||
|
||
// Assert
|
||
string expected = "<div></div>";
|
||
Assert.Equal(expected, actual, ignoreCase: true);
|
||
}
|
||
|
||
/// <summary>
|
||
/// A test for AnchorTag with no single quotes or double quotes or semicolons xss
|
||
/// Example <!-- <A HREF="http://www.codeplex.com?url=<SCRIPT>a=/XSS/alert(a.source)</SCRIPT>">XSS</A> -->
|
||
/// </summary>
|
||
[Fact]
|
||
public void AnchorTagNoQuotesXSSTest()
|
||
{
|
||
// Arrange
|
||
var sanitizer = Sanitizer;
|
||
|
||
|
||
// Act
|
||
string htmlFragment = "<A HREF=\"http://www.codeplex.com?url=<SCRIPT>a=/XSS/alert(a.source)</SCRIPT>\">XSS</A>";
|
||
string actual = sanitizer.Sanitize(htmlFragment);
|
||
|
||
// Assert
|
||
string expected = "<a href=\"http://www.codeplex.com?url=<SCRIPT>a=/XSS/alert(a.source)</SCRIPT>\">XSS</a>";
|
||
Assert.Equal(expected, actual, ignoreCase: true);
|
||
}
|
||
|
||
/// <summary>
|
||
/// A test for Div with no single quotes or double quotes or semicolons xss
|
||
/// Example <!-- <Div style="background-color: http://www.codeplex.com?url=<SCRIPT>a=/XSS/alert(a.source)</SCRIPT>"> -->
|
||
/// </summary>
|
||
[Fact]
|
||
public void DivNoQuotesXSSTest()
|
||
{
|
||
// Arrange
|
||
var sanitizer = Sanitizer;
|
||
|
||
|
||
// Act
|
||
string htmlFragment = "<Div style=\"background-color: http://www.codeplex.com?url=<SCRIPT>a=/XSS/alert(a.source)</SCRIPT>\">";
|
||
string actual = sanitizer.Sanitize(htmlFragment);
|
||
|
||
// Assert
|
||
string expected = "<div></div>";
|
||
Assert.Equal(expected, actual, ignoreCase: true);
|
||
}
|
||
|
||
/// <summary>
|
||
/// A test for Div with style expression and no single quotes or double quotes or semicolons xss
|
||
/// Example <!-- <Div style="background-color: expression(<SCRIPT>a=/XSS/alert(a.source)</SCRIPT>)"> -->
|
||
/// </summary>
|
||
[Fact]
|
||
public void DivStyleExpressionNoQuotesXSSTest()
|
||
{
|
||
// Arrange
|
||
var sanitizer = Sanitizer;
|
||
|
||
|
||
// Act
|
||
string htmlFragment = "<Div style=\"background-color: expression(<SCRIPT>a=/XSS/alert(a.source)</SCRIPT>)\">";
|
||
string actual = sanitizer.Sanitize(htmlFragment);
|
||
|
||
// Assert
|
||
string expected = "<div></div>";
|
||
Assert.Equal(expected, actual, ignoreCase: true);
|
||
}
|
||
|
||
/// <summary>
|
||
/// A test for AnchorTag with US-ASCII encoding xss
|
||
/// Example <!-- <A HREF="http://www.codeplex.com?url=¼script¾alert(¢XSS¢)¼/script¾">XSS</A> -->
|
||
/// </summary>
|
||
[Fact]
|
||
public void AnchorTagUSASCIIEncodingXSSTest()
|
||
{
|
||
// Arrange
|
||
var sanitizer = Sanitizer;
|
||
|
||
|
||
// Act
|
||
string htmlFragment = "<A HREF=\"http://www.codeplex.com?url=¼script¾alert(¢XSS¢)¼/script¾\">XSS</A>";
|
||
string actual = sanitizer.Sanitize(htmlFragment);
|
||
|
||
// Assert
|
||
string expected = "<a href=\"http://www.codeplex.com?url=¼script¾alert(¢XSS¢)¼/script¾\">XSS</a>";
|
||
Assert.Equal(expected, actual, ignoreCase: true);
|
||
}
|
||
|
||
/// <summary>
|
||
/// A test for AnchorTag with Downlevel-Hidden block xss
|
||
/// </summary>
|
||
[Fact]
|
||
public void AnchorTagDownlevelHiddenBlockXSSTest()
|
||
{
|
||
// Arrange
|
||
var sanitizer = Sanitizer;
|
||
|
||
|
||
// Act
|
||
string htmlFragment = "<A HREF=\"http://www.codeplex.com?url=<!--[if gte IE 4]><SCRIPT>alert('XSS');</SCRIPT><![endif]-->\">XSS</A>";
|
||
string actual = sanitizer.Sanitize(htmlFragment);
|
||
|
||
// Assert
|
||
string expected = "<a href=\"http://www.codeplex.com?url=<!--[if gte IE 4]><SCRIPT>alert('XSS');</SCRIPT><![endif]-->\">XSS</a>";
|
||
|
||
try
|
||
{
|
||
Assert.Equal(expected, actual, ignoreCase: true);
|
||
}
|
||
catch (Exception)
|
||
{
|
||
|
||
//in .net 3.5 there is a bug with URI, and so this test would otherwise fail on .net 3.5 in Appveyor / nunit:
|
||
//http://help.appveyor.com/discussions/problems/1625-nunit-not-picking-up-net-framework-version
|
||
//http://stackoverflow.com/questions/27019061/forcing-nunit-console-runner-to-use-clr-4-5
|
||
string expectedNet35 = @"<a href=""http://www.codeplex.com/?url=%3C!--%5Bif%20gte%20IE%204%5D%3E%3CSCRIPT%3Ealert('XSS');%3C/SCRIPT%3E%3C!%5Bendif%5D--%3E"">XSS</a>";
|
||
|
||
|
||
Assert.Equal(expectedNet35, actual, ignoreCase: true);
|
||
}
|
||
}
|
||
|
||
/// <summary>
|
||
/// A test for Div with Downlevel-Hidden block xss
|
||
/// </summary>
|
||
[Fact]
|
||
public void DivDownlevelHiddenBlockXSSTest()
|
||
{
|
||
// Arrange
|
||
var sanitizer = Sanitizer;
|
||
|
||
|
||
// Act
|
||
string htmlFragment = "<Div style=\"background-color: http://www.codeplex.com?url=<!--[if gte IE 4]><SCRIPT>alert('XSS');</SCRIPT><![endif]-->\">";
|
||
string actual = sanitizer.Sanitize(htmlFragment);
|
||
|
||
// Assert
|
||
string expected = @"<div></div>";
|
||
Assert.Equal(expected, actual, ignoreCase: true);
|
||
}
|
||
|
||
/// <summary>
|
||
/// A test for AnchorTag with Html Quotes Encapsulation 1 xss
|
||
/// Example <!-- <A HREF="http://www.codeplex.com?url=<SCRIPT a=">" SRC="http://ha.ckers.org/xss.js"></SCRIPT>">XSS</A> -->
|
||
/// </summary>
|
||
[Fact]
|
||
public void AnchorTagHtmlQuotesEncapsulation1XSSTest()
|
||
{
|
||
// Arrange
|
||
var sanitizer = Sanitizer;
|
||
|
||
|
||
// Act
|
||
string htmlFragment = "<A HREF=\"http://www.codeplex.com?url=<SCRIPT a=\">\" SRC=\"http://ha.ckers.org/xss.js\"></SCRIPT>\">XSS</A>";
|
||
string actual = sanitizer.Sanitize(htmlFragment);
|
||
|
||
// Assert
|
||
string expected = "<a href=\"http://www.codeplex.com?url=<SCRIPT a=\">\" SRC=\"http://ha.ckers.org/xss.js\">\">XSS</a>";
|
||
Assert.Equal(expected, actual, ignoreCase: true);
|
||
}
|
||
|
||
/// <summary>
|
||
/// A test for Div with Html Quotes Encapsulation 1 xss
|
||
/// Example <!-- <Div style="background-color: http://www.codeplex.com?url=<SCRIPT a=">" SRC="http://ha.ckers.org/xss.js"></SCRIPT>"> -->
|
||
/// </summary>
|
||
[Fact]
|
||
public void DivHtmlQuotesEncapsulation1XSSTest()
|
||
{
|
||
// Arrange
|
||
var sanitizer = Sanitizer;
|
||
|
||
|
||
// Act
|
||
string htmlFragment = "<Div style=\"background-color: http://www.codeplex.com?url=<SCRIPT a=\">\" SRC=\"http://ha.ckers.org/xss.js\"></SCRIPT>\">";
|
||
string actual = sanitizer.Sanitize(htmlFragment);
|
||
|
||
// Assert
|
||
string expected = "<div>\" SRC=\"http://ha.ckers.org/xss.js\">\"></div>";
|
||
Assert.Equal(expected, actual, ignoreCase: true);
|
||
}
|
||
|
||
/// <summary>
|
||
/// A test for various legal fragments
|
||
/// </summary>
|
||
[Fact]
|
||
public void LegalTest()
|
||
{
|
||
// Arrange
|
||
var sanitizer = Sanitizer;
|
||
|
||
// Act
|
||
string htmlFragment = "<div style=\"background-color: test\">";
|
||
string actual = sanitizer.Sanitize(htmlFragment);
|
||
|
||
// Assert
|
||
string expected = "<div></div>";
|
||
Assert.Equal(expected, actual, ignoreCase: true);
|
||
}
|
||
|
||
/// <summary>
|
||
/// More tests for legal fragments.
|
||
/// </summary>
|
||
[Fact]
|
||
public void MoreLegalTest()
|
||
{
|
||
// Arrange
|
||
var sanitizer = Sanitizer;
|
||
|
||
// Act
|
||
string htmlFragment = "<div style=\"background-color: test;\">Test<img src=\"http://www.example.com/test.gif\" style=\"background-image: url(http://www.example.com/bg.jpg); margin: 10px\"></div>";
|
||
string actual = sanitizer.Sanitize(htmlFragment);
|
||
|
||
// Assert
|
||
string expected = "<div>Test<img src=\"http://www.example.com/test.gif\" style=\"background-image: url("http://www.example.com/bg.jpg"); margin: 10px\"></div>";
|
||
Assert.Equal(expected, actual, ignoreCase: true);
|
||
}
|
||
|
||
/// <summary>
|
||
/// Misc tests.
|
||
/// </summary>
|
||
[Fact]
|
||
public void MiscTest()
|
||
{
|
||
var sanitizer = Sanitizer;
|
||
|
||
var html = @"<SCRIPT/SRC=""http://ha.ckers.org/xss.js""></SCRIPT>";
|
||
var actual = sanitizer.Sanitize(html);
|
||
var expected = "";
|
||
Assert.Equal(expected, actual, ignoreCase: true);
|
||
|
||
html = @"<DIV STYLE=""padding: 1px; mar/*xss*/gin: ex/*XSS*/pression(alert('xss')); background-image:\0075\0072\006C\0028\0022\006a\0061\0076\0061\0073\0063\0072\0069\0070\0074\003a\0061\006c\0065\0072\0074\0028\0027\0058\0053\0053\0027\0029\0022\0029"">";
|
||
actual = sanitizer.Sanitize(html);
|
||
expected = @"<div style=""padding: 1px""></div>";
|
||
Assert.Equal(expected, actual, ignoreCase: true);
|
||
|
||
html = @"<!--[if gte IE 4]><SCRIPT>alert('XSS');</SCRIPT><![endif]--><!-- Comment -->";
|
||
actual = sanitizer.Sanitize(html);
|
||
expected = "";
|
||
Assert.Equal(expected, actual, ignoreCase: true);
|
||
|
||
html = @"<STYLE>@im\port'\ja\vasc\ript:alert(""XSS"")';</STYLE>";
|
||
actual = sanitizer.Sanitize(html);
|
||
expected = "";
|
||
Assert.Equal(expected, actual, ignoreCase: true);
|
||
|
||
html = @"<div onload!#$%&()*~+-_.,:;?@[/|\]^`=alert(""XSS"")>";
|
||
actual = sanitizer.Sanitize(html);
|
||
expected = "<div></div>";
|
||
Assert.Equal(expected, actual, ignoreCase: true);
|
||
|
||
html = @"<SCRIPT/XSS SRC=""http://ha.ckers.org/xss.js""></SCRIPT>";
|
||
actual = sanitizer.Sanitize(html);
|
||
expected = "";
|
||
Assert.Equal(expected, actual, ignoreCase: true);
|
||
|
||
html = "<IMG SRC=javascript:alert(\"XSS\")>\"";
|
||
actual = sanitizer.Sanitize(html);
|
||
expected = "<img>\"";
|
||
Assert.Equal(expected, actual, ignoreCase: true);
|
||
|
||
html = "<IMG SRC=java\0script:alert(\"XSS\")>\"";
|
||
actual = sanitizer.Sanitize(html);
|
||
expected = "<img>\"";
|
||
Assert.Equal(expected, actual, ignoreCase: true);
|
||
|
||
html = @"<IMG SRC=""jav
ascript:alert('XSS');"">";
|
||
actual = sanitizer.Sanitize(html);
|
||
expected = "<img>";
|
||
Assert.Equal(expected, actual, ignoreCase: true);
|
||
|
||
html = @"<IMG SRC=""jav
ascript:alert('XSS');"">";
|
||
actual = sanitizer.Sanitize(html);
|
||
expected = "<img>";
|
||
Assert.Equal(expected, actual, ignoreCase: true);
|
||
|
||
html = @"<IMG SRC=""jav	ascript:alert('XSS');"">";
|
||
actual = sanitizer.Sanitize(html);
|
||
expected = "<img>";
|
||
Assert.Equal(expected, actual, ignoreCase: true);
|
||
|
||
html = @"<div style=""background-color: red""><sCRipt>hallo</scripT></div><a href=""#"">Test</a>";
|
||
actual = sanitizer.Sanitize(html);
|
||
expected = @"<div style=""background-color: rgba(255, 0, 0, 1)""></div><a href=""#"">Test</a>";
|
||
Assert.Equal(expected, actual, ignoreCase: true);
|
||
|
||
html = @"<IMG SRC=""jav ascript:alert('XSS');"">";
|
||
actual = sanitizer.Sanitize(html);
|
||
expected = "<img>";
|
||
Assert.Equal(expected, actual, ignoreCase: true);
|
||
|
||
html = @"<IMG SRC=""  javascript:alert('XSS');"">";
|
||
actual = sanitizer.Sanitize(html);
|
||
expected = "<img>";
|
||
Assert.Equal(expected, actual, ignoreCase: true);
|
||
|
||
html = @"<IMG SRC=javascript:alert('XSS')>";
|
||
actual = sanitizer.Sanitize(html);
|
||
expected = "<img>";
|
||
Assert.Equal(expected, actual, ignoreCase: true);
|
||
|
||
html = @"<IMG SRC=javascript:alert('XSS')>";
|
||
actual = sanitizer.Sanitize(html);
|
||
expected = "<img>";
|
||
Assert.Equal(expected, actual, ignoreCase: true);
|
||
|
||
html = @"<IMG SRC=javascript:alert('XSS')>";
|
||
actual = sanitizer.Sanitize(html);
|
||
expected = "<img>";
|
||
Assert.Equal(expected, actual, ignoreCase: true);
|
||
|
||
html = "<script>alert('xss')</script><div onload=\"alert('xss')\" style=\"background-color: red\">Test<img src=\"test.gif\" style=\"background-image: url(javascript:alert('xss')); margin: 10px\"></div>";
|
||
actual = sanitizer.Sanitize(html, "http://www.example.com");
|
||
expected = @"<div style=""background-color: rgba(255, 0, 0, 1)"">Test<img src=""http://www.example.com/test.gif"" style=""margin: 10px""></div>";
|
||
Assert.Equal(expected, actual, ignoreCase: true);
|
||
}
|
||
|
||
/// <summary>
|
||
/// Tests disallowed tags.
|
||
/// </summary>
|
||
[Fact]
|
||
public void DisallowedTagTest()
|
||
{
|
||
var sanitizer = Sanitizer;
|
||
|
||
var html = @"<bla>Hallo</bla>";
|
||
var actual = sanitizer.Sanitize(html);
|
||
var expected = "";
|
||
Assert.Equal(expected, actual, ignoreCase: true);
|
||
}
|
||
|
||
/// <summary>
|
||
/// Tests disallowed HTML attributes.
|
||
/// </summary>
|
||
[Fact]
|
||
public void DisallowedAttributeTest()
|
||
{
|
||
var sanitizer = Sanitizer;
|
||
|
||
var html = @"<div bla=""test"">Test</div>";
|
||
var actual = sanitizer.Sanitize(html);
|
||
var expected = @"<div>Test</div>";
|
||
Assert.Equal(expected, actual, ignoreCase: true);
|
||
}
|
||
|
||
/// <summary>
|
||
/// Tests sanitization of attributes that contain a URL.
|
||
/// </summary>
|
||
[Fact]
|
||
public void UrlAttributeTest()
|
||
{
|
||
var sanitizer = Sanitizer;
|
||
|
||
var html = @"<a href=""mailto:test@example.com"">test</a>";
|
||
var actual = sanitizer.Sanitize(html);
|
||
var expected = @"<a>test</a>";
|
||
Assert.Equal(expected, actual, ignoreCase: true);
|
||
|
||
html = @"<a href=""http:xxx"">test</a>";
|
||
actual = sanitizer.Sanitize(html);
|
||
expected = @"<a href=""http:xxx"">test</a>";
|
||
Assert.Equal(expected, actual, ignoreCase: true);
|
||
|
||
html = @"<a href=""folder/file.jpg"">test</a>";
|
||
actual = sanitizer.Sanitize(html, @"http://www.example.com");
|
||
expected = @"<a href=""http://www.example.com/folder/file.jpg"">test</a>";
|
||
Assert.Equal(expected, actual, ignoreCase: true);
|
||
}
|
||
|
||
/// <summary>
|
||
/// Tests disallowed css properties.
|
||
/// </summary>
|
||
[Fact]
|
||
public void DisallowedStyleTest()
|
||
{
|
||
var sanitizer = Sanitizer;
|
||
|
||
var html = @"<div style=""margin: 8px; bla: 1px"">test</div>";
|
||
var actual = sanitizer.Sanitize(html);
|
||
var expected = @"<div style=""margin: 8px"">test</div>";
|
||
Assert.Equal(expected, actual, ignoreCase: true);
|
||
}
|
||
|
||
/// <summary>
|
||
/// Tests sanitization of URLs that are contained in CSS property values.
|
||
/// </summary>
|
||
[Fact]
|
||
public void UrlStyleTest()
|
||
{
|
||
var sanitizer = Sanitizer;
|
||
|
||
var html = @"<div style=""padding: 10px; background-image: url(mailto:test@example.com)""></div>";
|
||
var actual = sanitizer.Sanitize(html);
|
||
var expected = @"<div style=""padding: 10px""></div>";
|
||
Assert.Equal(expected, actual, ignoreCase: true);
|
||
|
||
html = @"<div style=""padding: 10px; background-image: url(folder/file.jpg)""></div>";
|
||
actual = sanitizer.Sanitize(html, @"http://www.example.com");
|
||
expected = @"<div style=""padding: 10px; background-image: url("http://www.example.com/folder/file.jpg")""></div>";
|
||
Assert.Equal(expected, actual, ignoreCase: true);
|
||
}
|
||
|
||
// test below from http://genshi.edgewall.org/
|
||
|
||
[Fact]
|
||
public void SanitizeUnchangedTest()
|
||
{
|
||
var sanitizer = Sanitizer;
|
||
var html = @"<a href=""#"">fo<br />o</a>";
|
||
Assert.Equal(@"<a href=""#"">fo<br>o</a>", sanitizer.Sanitize(html), ignoreCase: true);
|
||
|
||
html = @"<a href=""#with:colon"">foo</a>";
|
||
Assert.Equal(html, sanitizer.Sanitize(html), ignoreCase: true);
|
||
}
|
||
|
||
[Fact]
|
||
public void SanitizeEscapeTextTest()
|
||
{
|
||
var sanitizer = Sanitizer;
|
||
var html = @"<a href=""#"">fo&</a>";
|
||
Assert.Equal(@"<a href=""#"">fo&</a>", sanitizer.Sanitize(html), ignoreCase: true);
|
||
|
||
html = @"<a href=""#""><foo></a>";
|
||
Assert.Equal(@"<a href=""#""><foo></a>", sanitizer.Sanitize(html), ignoreCase: true);
|
||
}
|
||
|
||
[Fact]
|
||
public void SanitizeEntityrefTextTest()
|
||
{
|
||
var sanitizer = Sanitizer;
|
||
var html = @"<a href=""#"">foö</a>";
|
||
Assert.Equal(@"<a href=""#"">foö</a>", sanitizer.Sanitize(html), ignoreCase: true);
|
||
}
|
||
|
||
[Fact]
|
||
public void SanitizeEscapeAttrTest()
|
||
{
|
||
var sanitizer = Sanitizer;
|
||
var html = @"<div title=""<foo>""></div>";
|
||
Assert.Equal(@"<div title=""<foo>""></div>", sanitizer.Sanitize(html), ignoreCase: true);
|
||
}
|
||
|
||
[Fact]
|
||
public void SanitizeCloseEmptyTagTest()
|
||
{
|
||
var sanitizer = Sanitizer;
|
||
var html = @"<a href=""#"">fo<br>o</a>";
|
||
Assert.Equal(@"<a href=""#"">fo<br>o</a>", sanitizer.Sanitize(html), ignoreCase: true);
|
||
}
|
||
|
||
[Fact]
|
||
public void SanitizeInvalidEntityTest()
|
||
{
|
||
var sanitizer = Sanitizer;
|
||
var html = @"&junk;";
|
||
Assert.Equal(@"&junk;", sanitizer.Sanitize(html), ignoreCase: true);
|
||
}
|
||
|
||
[Fact]
|
||
public void SanitizeRemoveScriptElemTest()
|
||
{
|
||
var sanitizer = Sanitizer;
|
||
var html = @"<script>alert(""Foo"")</script>";
|
||
Assert.Equal(@"", sanitizer.Sanitize(html), ignoreCase: true);
|
||
html = @"<SCRIPT SRC=""http://example.com/""></SCRIPT>";
|
||
Assert.Equal(@"", sanitizer.Sanitize(html), ignoreCase: true);
|
||
}
|
||
|
||
[Fact]
|
||
public void SanitizeRemoveOnclickAttrTest()
|
||
{
|
||
var sanitizer = Sanitizer;
|
||
var html = @"<div onclick=\'alert(""foo"")\' />";
|
||
Assert.Equal(@"<div></div>", sanitizer.Sanitize(html), ignoreCase: true);
|
||
}
|
||
|
||
[Fact]
|
||
public void SanitizeRemoveCommentsTest()
|
||
{
|
||
var sanitizer = Sanitizer;
|
||
var html = @"<div><!-- conditional comment crap --></div>";
|
||
Assert.Equal(@"<div></div>", sanitizer.Sanitize(html), ignoreCase: true);
|
||
}
|
||
|
||
[Fact]
|
||
public void SanitizeRemoveStyleScriptsTest()
|
||
{
|
||
var sanitizer = Sanitizer;
|
||
// Inline style with url() using javascript: scheme
|
||
var html = @"<DIV STYLE='background-image: url(javascript:alert(""foo""))'>";
|
||
Assert.Equal(@"<div></div>", sanitizer.Sanitize(html), ignoreCase: true);
|
||
// Inline style with url() using javascript: scheme, using control char
|
||
html = @"<DIV STYLE='background-image: url(javascript:alert(""foo""))'>";
|
||
Assert.Equal(@"<div></div>", sanitizer.Sanitize(html), ignoreCase: true);
|
||
// Inline style with url() using javascript: scheme, in quotes
|
||
html = @"<DIV STYLE='background-image: url(""javascript:alert(foo)"")'>";
|
||
Assert.Equal(@"<div></div>", sanitizer.Sanitize(html), ignoreCase: true);
|
||
// IE expressions in CSS not allowed
|
||
html = @"<DIV STYLE='width: expression(alert(""foo""));'>";
|
||
Assert.Equal(@"<div></div>", sanitizer.Sanitize(html), ignoreCase: true);
|
||
html = @"<DIV STYLE='width: e/**/xpression(alert(""foo""));'>";
|
||
Assert.Equal(@"<div></div>", sanitizer.Sanitize(html), ignoreCase: true);
|
||
html = @"<DIV STYLE='background-image: url(javascript:alert(""foo""));color: #fff'>";
|
||
Assert.Equal(@"<div style=""color: rgba(255, 255, 255, 1)""></div>", sanitizer.Sanitize(html), ignoreCase: true);
|
||
|
||
// Inline style with url() using javascript: scheme, using unicode
|
||
// escapes
|
||
html = @"<DIV STYLE='background-image: \75rl(javascript:alert(""foo""))'>";
|
||
Assert.Equal(@"<div></div>", sanitizer.Sanitize(html), ignoreCase: true);
|
||
html = @"<DIV STYLE='background-image: \000075rl(javascript:alert(""foo""))'>";
|
||
Assert.Equal(@"<div></div>", sanitizer.Sanitize(html), ignoreCase: true);
|
||
html = @"<DIV STYLE='background-image: \75 rl(javascript:alert(""foo""))'>";
|
||
Assert.Equal(@"<div></div>", sanitizer.Sanitize(html), ignoreCase: true);
|
||
html = @"<DIV STYLE='background-image: \000075 rl(javascript:alert(""foo""))'>";
|
||
Assert.Equal(@"<div></div>", sanitizer.Sanitize(html), ignoreCase: true);
|
||
html = @"<DIV STYLE='background-image: \000075
|
||
rl(javascript:alert(""foo""))'>";
|
||
Assert.Equal(@"<div></div>", sanitizer.Sanitize(html), ignoreCase: true);
|
||
}
|
||
|
||
[Fact]
|
||
public void SanitizeRemoveStylePhishingTest()
|
||
{
|
||
var sanitizer = Sanitizer;
|
||
// The position property is not allowed
|
||
var html = @"<div style=""position:absolute;top:0""></div>";
|
||
Assert.Equal(@"<div style=""top: 0""></div>", sanitizer.Sanitize(html), ignoreCase: true);
|
||
// Normal margins get passed through
|
||
html = @"<div style=""margin:10px 20px""></div>";
|
||
Assert.Equal(@"<div style=""margin: 10px 20px""></div>", sanitizer.Sanitize(html), ignoreCase: true);
|
||
}
|
||
|
||
[Fact]
|
||
public void SanitizeRemoveSrcJavascriptTest()
|
||
{
|
||
var sanitizer = Sanitizer;
|
||
var html = @"<img src=\'javascript:alert(""foo"")\'>";
|
||
Assert.Equal(@"<img>", sanitizer.Sanitize(html), ignoreCase: true);
|
||
// Case-insensitive protocol matching
|
||
html = @"<IMG SRC=\'JaVaScRiPt:alert(""foo"")\'>";
|
||
Assert.Equal(@"<img>", sanitizer.Sanitize(html), ignoreCase: true);
|
||
// Grave accents (not parsed)
|
||
// Protocol encoded using UTF-8 numeric entities
|
||
html = @"<IMG SRC=\'javascript:alert(""foo"")\'>";
|
||
Assert.Equal(@"<img>", sanitizer.Sanitize(html), ignoreCase: true);
|
||
// Protocol encoded using UTF-8 numeric entities without a semicolon
|
||
// (which is allowed because the max number of digits is used)
|
||
html = @"<IMG SRC=\'javascript:alert(""foo"")\'>";
|
||
Assert.Equal(@"<img>", sanitizer.Sanitize(html), ignoreCase: true);
|
||
// Protocol encoded using UTF-8 numeric hex entities without a semicolon
|
||
// (which is allowed because the max number of digits is used)
|
||
html = @"<IMG SRC=\'javascript:alert(""foo"")\'>";
|
||
Assert.Equal(@"<img>", sanitizer.Sanitize(html), ignoreCase: true);
|
||
// Embedded tab character in protocol
|
||
html = @"<IMG SRC=\'jav\tascript:alert(""foo"");\'>";
|
||
Assert.Equal(@"<img>", sanitizer.Sanitize(html), ignoreCase: true);
|
||
// Embedded tab character in protocol, but encoded this time
|
||
html = @"<IMG SRC=\'jav	ascript:alert(""foo"");\'>";
|
||
Assert.Equal(@"<img>", sanitizer.Sanitize(html), ignoreCase: true);
|
||
}
|
||
|
||
[Fact]
|
||
public void SanitizeExpressionTest()
|
||
{
|
||
var sanitizer = Sanitizer;
|
||
var html = @"<div style=""top:expression(alert())"">XSS</div>";
|
||
Assert.Equal(@"<div>XSS</div>", sanitizer.Sanitize(html), ignoreCase: true);
|
||
}
|
||
|
||
[Fact]
|
||
public void CapitalExpressionTest()
|
||
{
|
||
var sanitizer = Sanitizer;
|
||
var html = @"<div style=""top:EXPRESSION(alert())"">XSS</div>";
|
||
Assert.Equal(@"<div>XSS</div>", sanitizer.Sanitize(html), ignoreCase: true);
|
||
}
|
||
|
||
[Fact]
|
||
public void SanitizeUrlWithJavascriptTest()
|
||
{
|
||
var sanitizer = Sanitizer;
|
||
var html = @"<div style=""background-image:url(javascript:alert())"">XSS</div>";
|
||
Assert.Equal(@"<div>XSS</div>", sanitizer.Sanitize(html), ignoreCase: true);
|
||
}
|
||
|
||
[Fact]
|
||
public void SanitizeCapitalUrlWithJavascriptTest()
|
||
{
|
||
var sanitizer = Sanitizer;
|
||
var html = @"<div style=""background-image:URL(javascript:alert())"">XSS</div>";
|
||
Assert.Equal(@"<div>XSS</div>", sanitizer.Sanitize(html), ignoreCase: true);
|
||
}
|
||
|
||
[Fact]
|
||
public void SanitizeUnicodeEscapesTest()
|
||
{
|
||
var sanitizer = Sanitizer;
|
||
var html = @"<div style=""top:exp\72 ess\000069 on(alert())"">XSS</div>";
|
||
Assert.Equal(@"<div>XSS</div>", sanitizer.Sanitize(html), ignoreCase: true);
|
||
}
|
||
|
||
[Fact]
|
||
public void SanitizeBackslashWithoutHexTest()
|
||
{
|
||
var sanitizer = Sanitizer;
|
||
var html = @"<div style=""top:e\xp\ression(alert())"">XSS</div>";
|
||
Assert.Equal(@"<div>XSS</div>", sanitizer.Sanitize(html), ignoreCase: true);
|
||
html = @"<div style=""top:e\\xp\\ression(alert())"">XSS</div>";
|
||
Assert.Equal(@"<div>XSS</div>", sanitizer.Sanitize(html), ignoreCase: true);
|
||
}
|
||
|
||
[Fact]
|
||
public void SanitizeUnsafePropsTest()
|
||
{
|
||
var sanitizer = Sanitizer;
|
||
var html = @"<div style=""POSITION:RELATIVE"">XSS</div>";
|
||
Assert.Equal(@"<div>XSS</div>", sanitizer.Sanitize(html), ignoreCase: true);
|
||
|
||
html = @"<div style=""behavior:url(test.htc)"">XSS</div>";
|
||
Assert.Equal(@"<div>XSS</div>", sanitizer.Sanitize(html), ignoreCase: true);
|
||
|
||
html = @"<div style=""-ms-behavior:url(test.htc) url(#obj)"">XSS</div>";
|
||
Assert.Equal(@"<div>XSS</div>", sanitizer.Sanitize(html), ignoreCase: true);
|
||
|
||
html = @"<div style=""-o-link:'javascript:alert(1)';-o-link-source:current"">XSS</div>";
|
||
Assert.Equal(@"<div>XSS</div>", sanitizer.Sanitize(html), ignoreCase: true);
|
||
|
||
html = @"<div style=""-moz-binding:url(xss.xbl)"">XSS</div>";
|
||
Assert.Equal(@"<div>XSS</div>", sanitizer.Sanitize(html), ignoreCase: true);
|
||
}
|
||
|
||
[Fact]
|
||
public void SanitizeCssHackTest()
|
||
{
|
||
var sanitizer = Sanitizer;
|
||
var html = @"<div style=""*position:static"">XSS</div>";
|
||
Assert.Equal(@"<div>XSS</div>", sanitizer.Sanitize(html), ignoreCase: true);
|
||
}
|
||
|
||
[Fact]
|
||
public void SanitizePropertyNameTest()
|
||
{
|
||
var sanitizer = Sanitizer;
|
||
var html = @"<div style=""display:none;border-left-color:red;userDefined:1;-moz-user-selct:-moz-all"">prop</div>";
|
||
Assert.Equal(@"<div style=""display: none; border-left-color: rgba(255, 0, 0, 1)"">prop</div>", sanitizer.Sanitize(html), ignoreCase: true);
|
||
}
|
||
|
||
[Fact]
|
||
public void SanitizeUnicodeExpressionTest()
|
||
{
|
||
var sanitizer = Sanitizer;
|
||
// Fullwidth small letters
|
||
var html = @"<div style=""top:expression(alert())"">XSS</div>";
|
||
Assert.Equal(@"<div>XSS</div>", sanitizer.Sanitize(html), ignoreCase: true);
|
||
// Fullwidth capital letters
|
||
html = @"<div style=""top:EXPRESSION(alert())"">XSS</div>";
|
||
Assert.Equal(@"<div>XSS</div>", sanitizer.Sanitize(html), ignoreCase: true);
|
||
// IPA extensions
|
||
html = @"<div style=""top:expʀessɪoɴ(alert())"">XSS</div>";
|
||
Assert.Equal(@"<div>XSS</div>", sanitizer.Sanitize(html), ignoreCase: true);
|
||
}
|
||
|
||
[Fact]
|
||
public void SanitizeUnicodeUrlTest()
|
||
{
|
||
var sanitizer = Sanitizer;
|
||
// IPA extensions
|
||
var html = @"<div style=""background-image:uʀʟ(javascript:alert())"">XSS</div>";
|
||
Assert.Equal(@"<div>XSS</div>", sanitizer.Sanitize(html), ignoreCase: true);
|
||
}
|
||
|
||
[Fact]
|
||
public void RemovingTagEventTest()
|
||
{
|
||
var sanitizer = new HtmlSanitizer();
|
||
sanitizer.RemovingTag += (s, e) => e.Cancel = e.Tag.NodeName == "BLINK";
|
||
var html = @"<div><script></script><blink>Test</blink></div>";
|
||
Assert.Equal(@"<div><blink>Test</blink></div>", sanitizer.Sanitize(html), ignoreCase: true);
|
||
}
|
||
|
||
[Fact]
|
||
public void RemovingAttributeEventTest()
|
||
{
|
||
var sanitizer = new HtmlSanitizer();
|
||
sanitizer.RemovingAttribute += (s, e) => e.Cancel = e.Attribute.Name == "onclick";
|
||
var html = @"<div alt=""alt"" onclick=""test"" onload=""test""></div>";
|
||
Assert.Equal(@"<div alt=""alt"" onclick=""test""></div>", sanitizer.Sanitize(html), ignoreCase: true);
|
||
}
|
||
|
||
[Fact]
|
||
public void RemovingAttributeEventTagTest()
|
||
{
|
||
var sanitizer = new HtmlSanitizer();
|
||
sanitizer.RemovingAttribute += (s, e) => Assert.IsAssignableFrom<IHtmlDivElement>(e.Tag);
|
||
var html = @"<div alt=""alt"" onclick=""test"" onload=""test""></div>";
|
||
sanitizer.Sanitize(html);
|
||
}
|
||
|
||
[Fact]
|
||
public void RemovingStyleEventTest()
|
||
{
|
||
var sanitizer = new HtmlSanitizer();
|
||
sanitizer.RemovingStyle += (s, e) => e.Cancel = e.Style.Name == "column-count";
|
||
var html = @"<div style=""top: 1px; column-count: 3;""></div>";
|
||
Assert.Equal(@"<div style=""top: 1px; column-count: 3""></div>", sanitizer.Sanitize(html), ignoreCase: true);
|
||
}
|
||
|
||
[Fact]
|
||
public void RemovingStyleEventTagTest()
|
||
{
|
||
var sanitizer = new HtmlSanitizer();
|
||
sanitizer.RemovingStyle += (s, e) => Assert.IsAssignableFrom<IHtmlDivElement>(e.Tag);
|
||
var html = @"<div style=""background: 0; test: xyz; bad: bad;""></div>";
|
||
sanitizer.Sanitize(html);
|
||
}
|
||
|
||
[Fact]
|
||
public void ProtocolRelativeTest()
|
||
{
|
||
var sanitizer = Sanitizer;
|
||
var html = @"<a href=""//www.example.com/test"">Test</a>";
|
||
Assert.Equal(@"<a href=""//www.example.com/test"">Test</a>", sanitizer.Sanitize(html), ignoreCase: true);
|
||
Assert.Equal(@"<a href=""https://www.example.com/test"">Test</a>", sanitizer.Sanitize(html, baseUrl: @"https://www.xyz.com/123"), ignoreCase: true);
|
||
}
|
||
|
||
[Fact]
|
||
public void JavaScriptIncludeAndAngleBracketsTest()
|
||
{
|
||
// Arrange
|
||
var sanitizer = Sanitizer;
|
||
|
||
// Act
|
||
string htmlFragment = "<BR SIZE=\"&{alert('XSS>')}\">";
|
||
string actual = sanitizer.Sanitize(htmlFragment);
|
||
|
||
// Assert
|
||
string expected = "<BR>";
|
||
Assert.Equal(expected, actual, ignoreCase: true);
|
||
}
|
||
|
||
[Fact]
|
||
public void AllowDataAttributesTest()
|
||
{
|
||
var sanitizer = new HtmlSanitizer()
|
||
{
|
||
AllowDataAttributes = true
|
||
};
|
||
var html = @"<div data-test1=""value x""></div>";
|
||
Assert.Equal(html, sanitizer.Sanitize(html), ignoreCase: true);
|
||
}
|
||
|
||
[Fact]
|
||
public void AllowDataAttributesCaseTest()
|
||
{
|
||
var sanitizer = new HtmlSanitizer()
|
||
{
|
||
AllowDataAttributes = true
|
||
};
|
||
var html = @"<div DAta-test1=""value x""></div>";
|
||
Assert.Equal(html, sanitizer.Sanitize(html), ignoreCase: true);
|
||
}
|
||
|
||
[Fact]
|
||
public void AllowDataAttributesOffTest()
|
||
{
|
||
var sanitizer = new HtmlSanitizer()
|
||
{
|
||
AllowDataAttributes = false
|
||
};
|
||
var html = @"<div data-test1=""value x""></div>";
|
||
Assert.Equal(@"<div></div>", sanitizer.Sanitize(html), ignoreCase: true);
|
||
}
|
||
|
||
[Fact]
|
||
public void SanitizeNonClosedTagTest()
|
||
{
|
||
var sanitizer = Sanitizer;
|
||
var html = @"<div>Hallo <p><b>Bold<br>Ballo";
|
||
Assert.Equal(@"<div>Hallo <p><b>Bold<br>Ballo</b></p></div>", sanitizer.Sanitize(html), ignoreCase: true);
|
||
}
|
||
|
||
[Fact]
|
||
public void PostProcessNodeTest()
|
||
{
|
||
var sanitizer = new HtmlSanitizer();
|
||
sanitizer.PostProcessNode += (s, e) =>
|
||
{
|
||
if (e.Node is IHtmlElement el)
|
||
{
|
||
el.ClassList.Add("test");
|
||
var b = e.Document.CreateElement("b");
|
||
b.TextContent = "Test";
|
||
el.AppendChild(b);
|
||
}
|
||
};
|
||
var html = @"<div>Hallo</div>";
|
||
var sanitized = sanitizer.Sanitize(html);
|
||
Assert.Equal(@"<div class=""test"">Hallo<b>Test</b></div>", sanitized, ignoreCase: true);
|
||
}
|
||
|
||
[Fact]
|
||
public void PostProcessNodeTestUsingDocument()
|
||
{
|
||
var sanitizer = new HtmlSanitizer();
|
||
sanitizer.PostProcessNode += (s, e) =>
|
||
{
|
||
if (e.Node is IHtmlDivElement el)
|
||
{
|
||
el.ClassList.Add("test");
|
||
var b = e.Document.CreateElement("b");
|
||
b.TextContent = "Test";
|
||
el.AppendChild(b);
|
||
}
|
||
};
|
||
var html = @"<html><head></head><body><div>Hallo</div></body></html>";
|
||
var sanitized = sanitizer.SanitizeDocument(html);
|
||
Assert.Equal(@"<html><head></head><body><div class=""test"">Hallo<b>Test</b></div></body></html>", sanitized, ignoreCase: true);
|
||
}
|
||
|
||
[Fact]
|
||
public void PostProcessDomTest()
|
||
{
|
||
var sanitizer = new HtmlSanitizer();
|
||
sanitizer.PostProcessDom += (s, e) =>
|
||
{
|
||
var p = e.Document.CreateElement("p");
|
||
p.TextContent = "World";
|
||
e.Document.Body.AppendChild(p);
|
||
};
|
||
|
||
var html = @"<div>Hallo</div>";
|
||
var sanitized = sanitizer.Sanitize(html);
|
||
Assert.Equal(@"<div>Hallo</div><p>World</p>", sanitized, ignoreCase: true);
|
||
}
|
||
|
||
[Fact]
|
||
public void AutoLinkTest()
|
||
{
|
||
var sanitizer = new HtmlSanitizer();
|
||
sanitizer.PostProcessNode += (s, e) =>
|
||
{
|
||
if (e.Node is IText text)
|
||
{
|
||
var autolinked = Regex.Replace(text.NodeValue, @"https?://[^\s]+[^\s!?.:;,]+", m => $@"<a href=""{m.Value}"">{m.Value}</a>", RegexOptions.IgnoreCase);
|
||
if (autolinked != text.NodeValue)
|
||
{
|
||
var f = new HtmlParser().ParseDocument(autolinked);
|
||
foreach (var node in f.Body.ChildNodes)
|
||
e.ReplacementNodes.Add(node);
|
||
}
|
||
}
|
||
};
|
||
var html = @"<div>Click here: http://example.com/.</div>";
|
||
Assert.Equal(@"<div>Click here: <a href=""http://example.com/"">http://example.com/</a>.</div>", sanitizer.Sanitize(html), ignoreCase: true);
|
||
Assert.Equal(@"Check out <a href=""https://www.google.com"">https://www.google.com</a>.", sanitizer.Sanitize("Check out https://www.google.com."), ignoreCase: true);
|
||
}
|
||
|
||
[Fact]
|
||
public void RussianTextTest()
|
||
{
|
||
// Arrange
|
||
var s = Sanitizer;
|
||
|
||
// Act
|
||
var htmlFragment = "Тест";
|
||
var actual = s.Sanitize(htmlFragment, "");
|
||
|
||
// Assert
|
||
var expected = htmlFragment;
|
||
Assert.Equal(expected, actual, ignoreCase: true);
|
||
}
|
||
|
||
[Fact]
|
||
public void DisallowCssPropertyValueTest()
|
||
{
|
||
// Arrange
|
||
var s = new HtmlSanitizer { DisallowCssPropertyValue = new Regex(@"^rgba\(0.*") };
|
||
|
||
// Act
|
||
var htmlFragment = @"<div style=""color: rgba(0, 0, 0, 1); background-color: rgba(255, 255, 255, 1)"">Test</div>";
|
||
var actual = s.Sanitize(htmlFragment);
|
||
|
||
// Assert
|
||
var expected = @"<div style=""background-color: rgba(255, 255, 255, 1)"">Test</div>";
|
||
Assert.Equal(expected, actual, ignoreCase: true);
|
||
}
|
||
|
||
[Fact]
|
||
public void CssKeyTest()
|
||
{
|
||
// Arrange
|
||
var s = Sanitizer;
|
||
|
||
// Act
|
||
var htmlFragment = @"<div style=""\000062ackground-image: URL(http://www.example.com/bg.jpg)"">Test</div>";
|
||
var actual = s.Sanitize(htmlFragment);
|
||
|
||
// Assert
|
||
var expected = @"<div style=""background-image: url("http://www.example.com/bg.jpg")"">Test</div>";
|
||
Assert.Equal(expected, actual, ignoreCase: true);
|
||
}
|
||
|
||
[Fact]
|
||
public void InvalidBaseUrlTest()
|
||
{
|
||
// Arrange
|
||
var s = Sanitizer;
|
||
|
||
// Act
|
||
var htmlFragment = @"<div style=""color: rgba(0, 0, 0, 1); background-image: URL(x/y/bg.jpg)"">Test</div>";
|
||
var actual = s.Sanitize(htmlFragment, "hallo");
|
||
|
||
// Assert
|
||
var expected = @"<div style=""color: rgba(0, 0, 0, 1)"">Test</div>";
|
||
Assert.Equal(expected, actual, ignoreCase: true);
|
||
}
|
||
|
||
[Fact]
|
||
public void XhtmlTest()
|
||
{
|
||
// Arrange
|
||
var s = Sanitizer;
|
||
|
||
// Act
|
||
var htmlFragment = @"<div><img src=""xyz""><br></div>";
|
||
var actual = s.Sanitize(htmlFragment, "", AngleSharp.Xml.XmlMarkupFormatter.Instance);
|
||
|
||
// Assert
|
||
var expected = @"<div><img src=""xyz"" /><br /></div>";
|
||
Assert.Equal(expected, actual, ignoreCase: true);
|
||
}
|
||
|
||
[Fact]
|
||
public void MultipleRecipientsTest()
|
||
{
|
||
// https://github.com/mganss/HtmlSanitizer/issues/41
|
||
|
||
// Arrange
|
||
var s = new HtmlSanitizer();
|
||
s.AllowedSchemes.Add("mailto");
|
||
|
||
// Act
|
||
var htmlFragment = @"<a href=""mailto:bonnie@example.com,clyde@example.com"">Bang Bang</a>";
|
||
var actual = s.Sanitize(htmlFragment);
|
||
|
||
// Assert
|
||
var expected = htmlFragment;
|
||
Assert.Equal(expected, actual, ignoreCase: true);
|
||
}
|
||
|
||
[Fact]
|
||
public void QuotedBackgroundImageTest()
|
||
{
|
||
// https://github.com/mganss/HtmlSanitizer/issues/44
|
||
|
||
// Arrange
|
||
var s = Sanitizer;
|
||
|
||
// Act
|
||
var htmlFragment = "<div style=\"background-image: url('some/random/url.img')\"></div>";
|
||
var actual = s.Sanitize(htmlFragment);
|
||
|
||
// Assert
|
||
var expected = "<div style=\"background-image: url("some/random/url.img")\"></div>";
|
||
Assert.Equal(expected, actual, ignoreCase: true);
|
||
}
|
||
|
||
[Fact]
|
||
public void QuotedBackgroundImageFromIE9()
|
||
{
|
||
// Arrange
|
||
var s = Sanitizer;
|
||
|
||
// Act
|
||
var htmlFragment = "<span style='background-image: url(\"/api/users/defaultAvatar\");'></span>";
|
||
var actual = s.Sanitize(htmlFragment);
|
||
|
||
// Assert
|
||
var expected = "<span style=\"background-image: url("/api/users/defaultAvatar")\"></span>";
|
||
Assert.Equal(expected, actual, ignoreCase: true);
|
||
}
|
||
|
||
[Fact]
|
||
public void RemoveEventForNotAllowedTag()
|
||
{
|
||
var allowedTags = new[] {"a"};
|
||
RemoveReason? actual = null;
|
||
|
||
var s = new HtmlSanitizer(allowedTags);
|
||
s.RemovingTag += (sender, args) =>
|
||
{
|
||
actual = args.Reason;
|
||
};
|
||
|
||
s.Sanitize("<span>just any content</span>");
|
||
|
||
Assert.Equal(RemoveReason.NotAllowedTag, actual);
|
||
}
|
||
|
||
[Fact]
|
||
public void RemoveEventForNotAllowedAttribute()
|
||
{
|
||
var allowedTags = new[] { "a" };
|
||
var allowedAttributes = new[] {"id"};
|
||
RemoveReason? actual = null;
|
||
|
||
var s = new HtmlSanitizer(allowedTags: allowedTags, allowedAttributes: allowedAttributes);
|
||
s.RemovingAttribute += (sender, args) =>
|
||
{
|
||
actual = args.Reason;
|
||
};
|
||
|
||
s.Sanitize("<a href=\"http://www.example.com\">just any content</a>");
|
||
|
||
Assert.Equal(RemoveReason.NotAllowedAttribute, actual);
|
||
}
|
||
|
||
[Fact]
|
||
public void RemoveEventForNotAllowedStyle()
|
||
{
|
||
var allowedTags = new[] { "a" };
|
||
var allowedAttributes = new[] { "style" };
|
||
var allowedStyles = new[] { "margin" };
|
||
RemoveReason? actual = null;
|
||
|
||
var s = new HtmlSanitizer(allowedTags: allowedTags, allowedAttributes: allowedAttributes, allowedCssProperties: allowedStyles);
|
||
s.RemovingStyle += (sender, args) =>
|
||
{
|
||
actual = args.Reason;
|
||
};
|
||
|
||
s.Sanitize("<a style=\"padding:5px\">just any content</a>");
|
||
|
||
Assert.Equal(RemoveReason.NotAllowedStyle, actual);
|
||
}
|
||
|
||
[Fact]
|
||
public void RemoveEventForNotAllowedValueAtAttribute()
|
||
{
|
||
var allowedTags = new[] { "a" };
|
||
var allowedAttributes = new[] { "id" };
|
||
RemoveReason? actual = null;
|
||
|
||
var s = new HtmlSanitizer(allowedTags: allowedTags, allowedAttributes: allowedAttributes);
|
||
s.RemovingAttribute += (sender, args) =>
|
||
{
|
||
actual = args.Reason;
|
||
};
|
||
|
||
s.Sanitize("<a id=\"anyId&{\">just any content</a>");
|
||
|
||
Assert.Equal(RemoveReason.NotAllowedValue, actual);
|
||
}
|
||
|
||
[Fact]
|
||
public void RemoveEventForNotAllowedValueAtStyle()
|
||
{
|
||
var allowedTags = new[] { "a" };
|
||
var allowedAttributes = new[] { "style" };
|
||
var allowedStyles = new[] { "margin-top" };
|
||
RemoveReason? actual = null;
|
||
|
||
var s = new HtmlSanitizer(allowedTags: allowedTags, allowedAttributes: allowedAttributes, allowedCssProperties: allowedStyles)
|
||
{
|
||
DisallowCssPropertyValue = new Regex(@"\d+.*")
|
||
};
|
||
s.RemovingStyle += (sender, args) =>
|
||
{
|
||
actual = args.Reason;
|
||
};
|
||
|
||
s.Sanitize("<a style=\"margin-top:17px\">just any content</a>");
|
||
|
||
Assert.Equal(RemoveReason.NotAllowedValue, actual);
|
||
}
|
||
|
||
[Fact]
|
||
public void RemoveEventForNotAllowedUrlAtUriAttribute()
|
||
{
|
||
var allowedTags = new[] { "a" };
|
||
var allowedAttributes = new[] { "href" };
|
||
var uriAttributes = new[] { "href" };
|
||
RemoveReason? actual = null;
|
||
|
||
var s = new HtmlSanitizer(allowedTags: allowedTags, allowedAttributes: allowedAttributes, uriAttributes: uriAttributes);
|
||
s.RemovingAttribute += (sender, args) =>
|
||
{
|
||
actual = args.Reason;
|
||
};
|
||
|
||
s.Sanitize("<a href=\"javascript:(alert('xss'))\">just any content</a>");
|
||
|
||
Assert.Equal(RemoveReason.NotAllowedUrlValue, actual);
|
||
}
|
||
|
||
[Fact]
|
||
public void RemoveEventForNotAllowedUrlAtStyle()
|
||
{
|
||
var allowedTags = new[] { "a" };
|
||
var allowedAttributes = new[] { "style" };
|
||
var allowedStyles = new[] { "background-image" };
|
||
RemoveReason? actual = null;
|
||
|
||
var s = new HtmlSanitizer(allowedTags: allowedTags, allowedAttributes: allowedAttributes, allowedCssProperties: allowedStyles);
|
||
s.RemovingStyle += (sender, args) =>
|
||
{
|
||
actual = args.Reason;
|
||
};
|
||
|
||
var h = s.Sanitize("<a style=\"background-image:url(javascript:alert('xss'))\">just any content</a>");
|
||
|
||
Assert.Equal(RemoveReason.NotAllowedUrlValue, actual);
|
||
}
|
||
|
||
[Fact]
|
||
public void RemoveEventForNotAllowedTag_ScriptTag()
|
||
{
|
||
RemoveReason? actual = null;
|
||
var s = new HtmlSanitizer();
|
||
s.RemovingTag += (sender, args) =>
|
||
{
|
||
actual = args.Reason;
|
||
};
|
||
s.Sanitize("<script>alert('Hello world!')</script>");
|
||
Assert.Equal(RemoveReason.NotAllowedTag, actual);
|
||
}
|
||
|
||
[Fact]
|
||
public void RemoveEventForNotAllowedTag_StyleTag()
|
||
{
|
||
RemoveReason? actual = null;
|
||
var s = new HtmlSanitizer();
|
||
s.RemovingTag += (sender, args) =>
|
||
{
|
||
actual = args.Reason;
|
||
};
|
||
s.Sanitize("<style> body {background-color:lightgrey;}</style>");
|
||
Assert.Equal(RemoveReason.NotAllowedTag, actual);
|
||
}
|
||
|
||
[Fact]
|
||
public void RemoveEventForNotAllowedTag_ScriptTagAndSpan()
|
||
{
|
||
RemoveReason? actual = null;
|
||
var s = new HtmlSanitizer();
|
||
s.RemovingTag += (sender, args) =>
|
||
{
|
||
actual = args.Reason;
|
||
};
|
||
s.Sanitize("<span>Hi</span><script>alert('Hello world!')</script>");
|
||
Assert.Equal(RemoveReason.NotAllowedTag, actual);
|
||
}
|
||
|
||
[Fact]
|
||
public void RemoveEventForNotAllowedCssClass()
|
||
{
|
||
RemoveReason? reason = null;
|
||
string removedClass = null;
|
||
|
||
var s = new HtmlSanitizer(allowedAttributes: new[] { "class" }, allowedCssClasses: new[] { "good" });
|
||
s.RemovingCssClass += (sender, args) =>
|
||
{
|
||
reason = args.Reason;
|
||
removedClass = args.CssClass;
|
||
};
|
||
|
||
s.Sanitize(@"<div class=""good bad"">Test</div>");
|
||
|
||
Assert.Equal("bad", removedClass);
|
||
Assert.Equal(RemoveReason.NotAllowedCssClass, reason);
|
||
}
|
||
|
||
[Fact]
|
||
public void RemoveEventForEmptyClassAttributeAfterClassRemoval()
|
||
{
|
||
RemoveReason? reason = null;
|
||
string attributeName = null;
|
||
|
||
var s = new HtmlSanitizer(allowedAttributes: new[] { "class" }, allowedCssClasses: new[] { "other" });
|
||
s.RemovingAttribute += (sender, args) =>
|
||
{
|
||
attributeName = args.Attribute.Name;
|
||
reason = args.Reason;
|
||
};
|
||
|
||
s.Sanitize(@"<div class=""good bad"">Test</div>");
|
||
|
||
Assert.Equal("class", attributeName);
|
||
Assert.Equal(RemoveReason.ClassAttributeEmpty, reason);
|
||
}
|
||
|
||
[Fact]
|
||
public void DocumentTest()
|
||
{
|
||
var s = new HtmlSanitizer();
|
||
s.AllowedTags.Add("title");
|
||
var html = "<html><head><title>Test</title></head><body><div>Test</div></body></html>";
|
||
|
||
var actual = s.SanitizeDocument(html);
|
||
|
||
Assert.Equal(html, actual);
|
||
}
|
||
|
||
[Fact]
|
||
public void DocumentFromFragmentTest()
|
||
{
|
||
var s = Sanitizer;
|
||
var html = "<div>Test</div>";
|
||
|
||
var actual = s.SanitizeDocument(html);
|
||
|
||
Assert.Equal("<html><head></head><body><div>Test</div></body></html>", actual);
|
||
}
|
||
|
||
[Fact]
|
||
public void FragmentFromDocumentTest()
|
||
{
|
||
var s = Sanitizer;
|
||
var html = "<html><head><title>Test</title></head><body><div>Test</div></body></html>";
|
||
|
||
var actual = s.Sanitize(html);
|
||
|
||
Assert.Equal("<div>Test</div>", actual);
|
||
}
|
||
|
||
[Fact]
|
||
public void StyleTagTest()
|
||
{
|
||
var s = new HtmlSanitizer();
|
||
s.AllowedTags.Add("style");
|
||
var html = "<html><head><style>body { background-color: rgba(255, 255, 255, 1); hallo-ballo: xyz }</style></head><body><div>Test</div></body></html>";
|
||
|
||
var actual = s.SanitizeDocument(html);
|
||
|
||
Assert.Equal("<html><head><style>body { background-color: rgba(255, 255, 255, 1) }</style></head><body><div>Test</div></body></html>", actual);
|
||
}
|
||
|
||
[Fact]
|
||
public void StyleAtTest()
|
||
{
|
||
var s = new HtmlSanitizer();
|
||
s.AllowedTags.Add("style");
|
||
s.AllowedAtRules.Add(AngleSharp.Css.Dom.CssRuleType.Media);
|
||
s.AllowedAtRules.Add(AngleSharp.Css.Dom.CssRuleType.Keyframes);
|
||
s.AllowedAtRules.Add(AngleSharp.Css.Dom.CssRuleType.Keyframe);
|
||
s.AllowedAtRules.Add(AngleSharp.Css.Dom.CssRuleType.Page);
|
||
var html = @"<html><head><style>
|
||
@charset ""UTF-8"";
|
||
@import url(evil.css);
|
||
@namespace url(http://www.w3.org/1999/xhtml);
|
||
@namespace svg url(http://www.w3.org/2000/svg);
|
||
@media (min-width: 100px) {
|
||
div { color: rgba(0, 0, 0, 1); }
|
||
@font-face { font-family: test }
|
||
}
|
||
@supports (--foo: green) {
|
||
body {
|
||
color: green;
|
||
}
|
||
@media (min-width: 200px) {
|
||
body { color: red; }
|
||
}
|
||
}
|
||
@document url(http://www.w3.org/),
|
||
url-prefix(http://www.w3.org/Style/),
|
||
domain(mozilla.org),
|
||
regexp(""https:.* "")
|
||
{
|
||
body {
|
||
color: purple;
|
||
background: yellow;
|
||
}
|
||
}
|
||
@page { size:8.5in 11in; margin-top: 2cm }
|
||
@font-face {
|
||
font-family: ""Bitstream Vera Serif Bold""
|
||
src: url(""https://mdn.mozillademos.org/files/2468/VeraSeBd.ttf"");
|
||
color: rgba(0, 0, 0, 1);
|
||
}
|
||
@keyframes identifier {
|
||
0% { top: 0; }
|
||
50% { top: 30px; left: 20px; }
|
||
50% { top: 10px; }
|
||
100% { top: 0; background-image: url('javascript:alert(xss)') }
|
||
}
|
||
@viewport {
|
||
min-width: 640px;
|
||
max-width: 800px;
|
||
}
|
||
@counter-style winners-list {
|
||
system: fixed;
|
||
symbols: url(gold-medal.svg) url(silver-medal.svg) url(bronze-medal.svg);
|
||
suffix: "" "";
|
||
}
|
||
@font-feature-values Font One { /* How to activate nice-style in Font One */
|
||
@styleset {
|
||
nice-style: 12;
|
||
}
|
||
}
|
||
</style></head></html>";
|
||
|
||
var actual = s.SanitizeDocument(html);
|
||
|
||
Assert.Equal(@"<html><head><style>@namespace url(""http://www.w3.org/1999/xhtml"");
|
||
@namespace svg url(""http://www.w3.org/2000/svg"");
|
||
@media (min-width: 100px) { div { color: rgba(0, 0, 0, 1) } }
|
||
@page { margin-top: 2cm }
|
||
@keyframes identifier { 0% { top: 0 } 50% { top: 30px; left: 20px } 50% { top: 10px } 100% { top: 0 } }</style></head><body></body></html>".Replace("\r\n", "\n"),
|
||
actual);
|
||
}
|
||
|
||
[Fact]
|
||
public void DataTest()
|
||
{
|
||
// https://github.com/mganss/HtmlSanitizer/issues/66
|
||
|
||
var sanitizer = new HtmlSanitizer()
|
||
{
|
||
AllowDataAttributes = true
|
||
};
|
||
sanitizer.AllowedSchemes.Add("data");
|
||
var html = @" <p>
|
||
<img src=""data:image/jpeg;base64,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"">
|
||
</p>
|
||
".Replace("\r\n", "\n");
|
||
|
||
var actual = sanitizer.Sanitize(html);
|
||
|
||
Assert.Equal(html, actual);
|
||
}
|
||
|
||
[Fact]
|
||
public void UriHashTest()
|
||
{
|
||
var s = Sanitizer;
|
||
var html = @"<a href=""http://domain.com/index.html?test=#value#"">test</a>";
|
||
|
||
var actual = s.Sanitize(html);
|
||
|
||
Assert.Equal(html, actual);
|
||
}
|
||
|
||
[Fact]
|
||
public void FragmentTest()
|
||
{
|
||
var s = Sanitizer;
|
||
var html = @"<script>alert('test');</script><p>Test</p>";
|
||
|
||
var actual = s.Sanitize(html);
|
||
|
||
Assert.Equal("<p>Test</p>", actual);
|
||
}
|
||
|
||
[Fact]
|
||
public void OpenTagFragmentTest()
|
||
{
|
||
// https://github.com/mganss/HtmlSanitizer/issues/75
|
||
|
||
var s = Sanitizer;
|
||
var html = "<p>abc<script>xyz</p>";
|
||
|
||
var actual = s.Sanitize(html);
|
||
|
||
Assert.Equal("<p>abc</p>", actual);
|
||
}
|
||
|
||
[Fact]
|
||
public void NullStyleTest()
|
||
{
|
||
// https://github.com/mganss/HtmlSanitizer/issues/81
|
||
|
||
var s = new HtmlSanitizer { HtmlParserFactory = () => new HtmlParser(new HtmlParserOptions(), BrowsingContext.New(new Configuration())) };
|
||
var html = @"<p style=""t"">xyz</p>";
|
||
|
||
var actual = s.Sanitize(html);
|
||
|
||
Assert.Equal("<p>xyz</p>", actual);
|
||
}
|
||
|
||
[Fact]
|
||
public void EscapeEntityInAttributeValueTest()
|
||
{
|
||
// https://github.com/mganss/HtmlSanitizer/issues/84
|
||
|
||
var s = new HtmlSanitizer { HtmlParserFactory = () => new HtmlParser(new HtmlParserOptions(), BrowsingContext.New(new Configuration())) };
|
||
var html = @"<input type=""text"" name=""my_name"" value=""<insert name>"">";
|
||
|
||
var actual = s.Sanitize(html);
|
||
|
||
Assert.Equal(@"<input type=""text"" name=""my_name"" value=""<insert name>"">", actual);
|
||
}
|
||
|
||
[Fact]
|
||
public void FontFaceTest()
|
||
{
|
||
// https://github.com/mganss/HtmlSanitizer/issues/80
|
||
|
||
var s = new HtmlSanitizer()
|
||
{
|
||
AllowDataAttributes = true
|
||
};
|
||
s.AllowedAtRules.Add(CssRuleType.FontFace);
|
||
|
||
s.AllowedTags.Add("style");
|
||
|
||
s.AllowedCssProperties.Add("src");
|
||
s.AllowedCssProperties.Add("font-family");
|
||
|
||
var html = @"<html><head><style>@font-face { font-family: FrutigerLTStd; src: url(""https://example.com/FrutigerLTStd-Light.otf"") format(""opentype"") }</style></head><body></body></html>";
|
||
var actual = s.SanitizeDocument(html);
|
||
|
||
Assert.Equal(html, actual);
|
||
}
|
||
|
||
public static IEnumerable<T> Shuffle<T>(IEnumerable<T> source, Random rng)
|
||
{
|
||
T[] elements = source.ToArray();
|
||
for (int i = elements.Length - 1; i >= 0; i--)
|
||
{
|
||
// Swap element "i" with a random earlier element it (or itself)
|
||
// ... except we don't really need to swap it fully, as we can
|
||
// return it immediately, and afterwards it's irrelevant.
|
||
int swapIndex = rng.Next(i + 1);
|
||
yield return elements[swapIndex];
|
||
elements[swapIndex] = elements[i];
|
||
}
|
||
}
|
||
|
||
[Fact]
|
||
public void ThreadTest()
|
||
{
|
||
const int numThreads = 16;
|
||
const int numRuns = 1000;
|
||
var random = new Random(615322944);
|
||
|
||
for (int i = 0; i < numRuns; i++)
|
||
{
|
||
var allGo = new ManualResetEvent(false);
|
||
Exception firstException = null;
|
||
var failures = 0;
|
||
var fixture = new HtmlSanitizerFixture();
|
||
var tests = new HtmlSanitizerTests(fixture);
|
||
var waiting = numThreads;
|
||
var methods = typeof(HtmlSanitizerTests).GetTypeInfo().GetMethods()
|
||
.Where(m => m.GetCustomAttributes(typeof(Xunit.FactAttribute), false).Any())
|
||
.Where(m => m.Name != "ThreadTest");
|
||
var threads = Shuffle(methods, random)
|
||
.Take(numThreads)
|
||
.Select(m => new Thread(() =>
|
||
{
|
||
try
|
||
{
|
||
if (Interlocked.Decrement(ref waiting) == 0) allGo.Set();
|
||
m.Invoke(tests, null);
|
||
}
|
||
catch (Exception ex)
|
||
{
|
||
Interlocked.CompareExchange(ref firstException, ex, null);
|
||
Interlocked.Increment(ref failures);
|
||
}
|
||
})).ToList();
|
||
|
||
foreach (var thread in threads)
|
||
thread.Start();
|
||
foreach (var thread in threads)
|
||
thread.Join();
|
||
|
||
Assert.Null(firstException);
|
||
Assert.Equal(0, failures);
|
||
}
|
||
}
|
||
|
||
[Fact]
|
||
public void AllowAllClassesByDefaultTest()
|
||
{
|
||
var sanitizer = new HtmlSanitizer(allowedAttributes: new[] { "class" });
|
||
|
||
var html = @"<div class=""good bad"">Test</div>";
|
||
var actual = sanitizer.Sanitize(html);
|
||
|
||
Assert.Equal(@"<div class=""good bad"">Test</div>", actual);
|
||
}
|
||
|
||
[Fact]
|
||
public void AllowClassesTest()
|
||
{
|
||
var sanitizer = new HtmlSanitizer(allowedAttributes: new[] { "class" }, allowedCssClasses: new[] { "good" });
|
||
|
||
var html = @"<div class=""good bad"">Test</div>";
|
||
var actual = sanitizer.Sanitize(html);
|
||
|
||
Assert.Equal(@"<div class=""good"">Test</div>", actual);
|
||
}
|
||
|
||
[Fact]
|
||
public void AllowClassesUsingEventTest()
|
||
{
|
||
var sanitizer = new HtmlSanitizer();
|
||
sanitizer.RemovingAttribute += (s, e) =>
|
||
{
|
||
if (e.Attribute.Name == "class")
|
||
{
|
||
e.Tag.ClassList.Remove(e.Tag.ClassList.Except(new[] { "good", "oktoo" }, StringComparer.OrdinalIgnoreCase).ToArray());
|
||
e.Cancel = e.Tag.ClassList.Any();
|
||
}
|
||
};
|
||
|
||
var html = @"<div class=""good bad"">Test</div>";
|
||
var actual = sanitizer.Sanitize(html);
|
||
|
||
Assert.Equal(@"<div class=""good"">Test</div>", actual);
|
||
}
|
||
|
||
[Fact]
|
||
public void RemoveClassAttributeIfNoAllowedClassesTest()
|
||
{
|
||
// Empty array for allowed classes = no classes allowed
|
||
var sanitizer = new HtmlSanitizer(allowedAttributes: new[] { "class" }, allowedCssClasses: new string[0]);
|
||
|
||
var html = @"<div class=""good bad"">Test</div>";
|
||
var actual = sanitizer.Sanitize(html);
|
||
|
||
Assert.Equal(@"<div>Test</div>", actual);
|
||
}
|
||
|
||
[Fact]
|
||
public void RemoveClassAttributeIfEmptyTest()
|
||
{
|
||
var sanitizer = new HtmlSanitizer(allowedAttributes: new[] { "class" }, allowedCssClasses: new[] { "other" });
|
||
|
||
var html = @"<div class=""good bad"">Test</div>";
|
||
var actual = sanitizer.Sanitize(html);
|
||
|
||
Assert.Equal(@"<div>Test</div>", actual);
|
||
}
|
||
|
||
[Fact]
|
||
public void TextTest()
|
||
{
|
||
var sanitizer = new HtmlSanitizer();
|
||
sanitizer.AllowedTags.Remove("div");
|
||
sanitizer.RemovingTag += (s, e) =>
|
||
{
|
||
if (e.Tag.HasChildNodes)
|
||
{
|
||
e.Tag.Replace(e.Tag.ChildNodes.ToArray());
|
||
e.Cancel = true;
|
||
}
|
||
};
|
||
|
||
var html = @"Test1 <div>Test2 <script>Test3</script> <b>Test4</b></div>";
|
||
var actual = sanitizer.Sanitize(html);
|
||
|
||
Assert.Equal("Test1 Test2 Test3 <b>Test4</b>", actual);
|
||
}
|
||
|
||
[Fact]
|
||
public void KeepChildNodesTest()
|
||
{
|
||
var sanitizer = new HtmlSanitizer { KeepChildNodes = true };
|
||
sanitizer.AllowedTags.Remove("div");
|
||
|
||
var html = @"Test1 <div>Test2 <script>Test3</script> <b>Test4</b></div>";
|
||
var actual = sanitizer.Sanitize(html);
|
||
|
||
Assert.Equal("Test1 Test2 Test3 <b>Test4</b>", actual);
|
||
}
|
||
|
||
[Fact]
|
||
public void NormalizeTest()
|
||
{
|
||
var sanitizer = new HtmlSanitizer();
|
||
sanitizer.PostProcessNode += (s, e) =>
|
||
{
|
||
Assert.Single(e.Document.Body.ChildNodes);
|
||
var text = e.Node as IText;
|
||
Assert.NotNull(text);
|
||
Assert.Equal("Test1Test2", text.NodeValue);
|
||
};
|
||
|
||
var html = @"Test1<script>test();</script>Test2<!-- comment -->";
|
||
|
||
var actual = sanitizer.Sanitize(html);
|
||
|
||
Assert.Equal("Test1Test2", actual);
|
||
}
|
||
|
||
[Fact]
|
||
public void RemovingCommentTest()
|
||
{
|
||
var sanitizer = new HtmlSanitizer();
|
||
sanitizer.RemovingComment += (s, e) => e.Cancel = e.Comment.TextContent.Contains("good comment");
|
||
|
||
var html = @"<!-- bad comment --><!-- good comment -->";
|
||
|
||
var actual = sanitizer.Sanitize(html);
|
||
|
||
Assert.Equal("<!-- good comment -->", actual);
|
||
}
|
||
|
||
[Fact]
|
||
public void TrailingSlashTest()
|
||
{
|
||
var sanitizer = new HtmlSanitizer();
|
||
sanitizer.AllowedSchemes.Add("resources");
|
||
|
||
var html = "<IMG src=\"resources://ais_w20_h20_33ba129c.png\">";
|
||
|
||
var actual = sanitizer.Sanitize(html);
|
||
|
||
Assert.Equal(html, actual, ignoreCase: true);
|
||
}
|
||
|
||
[Fact]
|
||
public void FileUrlTest()
|
||
{
|
||
var sanitizer = new HtmlSanitizer();
|
||
sanitizer.AllowedSchemes.Add("file");
|
||
|
||
var html = @"<a href=""file:///C:/exampleㄓ.txt"">test</a>";
|
||
|
||
var actual = sanitizer.Sanitize(html);
|
||
|
||
Assert.Equal(html, actual);
|
||
}
|
||
|
||
[Fact]
|
||
public void SvgTest()
|
||
{
|
||
// https://github.com/mganss/HtmlSanitizer/issues/119
|
||
|
||
var sanitizer = new HtmlSanitizer();
|
||
sanitizer.AllowedTags.Add("svg");
|
||
|
||
var html = @"<svg onchange='alert(1)'>123</svg>";
|
||
|
||
var actual = sanitizer.Sanitize(html);
|
||
|
||
Assert.Equal("<svg>123</svg>", actual);
|
||
}
|
||
|
||
[Fact]
|
||
public void SquareBracketTest()
|
||
{
|
||
// https://github.com/mganss/HtmlSanitizer/issues/137
|
||
|
||
var sanitizer = new HtmlSanitizer();
|
||
sanitizer.AllowedAttributes.Add("[minutes]");
|
||
|
||
var html = @"<div [minutes]=""2"">123</div>";
|
||
|
||
var actual = sanitizer.Sanitize(html);
|
||
|
||
Assert.Equal(html, actual);
|
||
}
|
||
|
||
[Fact]
|
||
public void FilterUrlTest()
|
||
{
|
||
// https://github.com/mganss/HtmlSanitizer/issues/156
|
||
|
||
var sanitizer = new HtmlSanitizer();
|
||
sanitizer.FilterUrl += (s, e) => e.SanitizedUrl = "https://www.example.com/test.png";
|
||
|
||
var html = @"<img src=""http://www.example.com/"">";
|
||
|
||
var actual = sanitizer.Sanitize(html);
|
||
|
||
Assert.Equal(@"<img src=""https://www.example.com/test.png"">", actual);
|
||
}
|
||
|
||
|
||
[Fact]
|
||
public void EncodingTest()
|
||
{
|
||
// https://github.com/mganss/HtmlSanitizer/issues/158
|
||
|
||
var sanitizer = new HtmlSanitizer();
|
||
sanitizer.AllowedTags.Add("meta");
|
||
sanitizer.AllowedAttributes.Add("http-equiv");
|
||
sanitizer.AllowedAttributes.Add("content");
|
||
|
||
var html = @"<html><head><meta http-equiv=""Content-Type"" content=""text/html; charset=iso-8859-1""></head><body>kopieën</body></html>";
|
||
|
||
using (var stream = new MemoryStream(Encoding.GetEncoding("iso-8859-1").GetBytes(html)))
|
||
{
|
||
var actual = sanitizer.SanitizeDocument(stream);
|
||
|
||
Assert.Equal(html, actual);
|
||
}
|
||
}
|
||
|
||
[Fact]
|
||
public void RemovingFramesetShouldTriggerEventTest()
|
||
{
|
||
// https://github.com/mganss/HtmlSanitizer/issues/163
|
||
|
||
var sanitizer = new HtmlSanitizer();
|
||
bool anyNodeRemoved = false;
|
||
sanitizer.RemovingTag += (s, e) => anyNodeRemoved = true;
|
||
var html = @"<html><frameset><frame src=""javascript:alert(1)""></frame></frameset></html>";
|
||
var actual = sanitizer.SanitizeDocument(html);
|
||
Assert.True(anyNodeRemoved);
|
||
Assert.Equal("<html><head></head></html>", actual);
|
||
}
|
||
|
||
[Fact]
|
||
public void HtmlDocumentTest()
|
||
{
|
||
// https://github.com/mganss/HtmlSanitizer/issues/164
|
||
|
||
var sanitizer = new HtmlSanitizer();
|
||
var html = @"<html onmousemove=""alert(document.location)""><head></head><body></body></html>";
|
||
|
||
var actual = sanitizer.SanitizeDocument(html);
|
||
|
||
Assert.Equal("<html><head></head><body></body></html>", actual);
|
||
}
|
||
}
|
||
}
|
||
|
||
#pragma warning restore 1591
|