支持更多目标框架

This commit is contained in:
Jackson.Bruce
2020-03-21 23:03:08 +08:00
parent d527fe5902
commit 7784c1a45c
61 changed files with 2366 additions and 19711 deletions

View File

@@ -1,74 +0,0 @@
<Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup>
<TargetFramework>netcoreapp3.1</TargetFramework>
<RootNamespace>Ufangx.Xss</RootNamespace>
<Authors>Jackson.bruce</Authors>
<PackageProjectUrl>https://github.com/JacksonBruce/AntiXssUF</PackageProjectUrl>
<RepositoryUrl>https://github.com/JacksonBruce/AntiXssUF.git</RepositoryUrl>
<RepositoryType>git</RepositoryType>
<Description>anti xss mvc model binder policy</Description>
<PackageTags>anti xss mvc model binder policy</PackageTags>
<Company>ufangx</Company>
<Copyright>Copyright (c) 2020 Jackson.Bruce</Copyright>
<PackageReleaseNotes>https://github.com/JacksonBruce/AntiXssUF/blob/master/README.md</PackageReleaseNotes>
<Version>1.0.0-beta.0</Version>
<GeneratePackageOnBuild>true</GeneratePackageOnBuild>
</PropertyGroup>
<ItemGroup>
<Compile Remove="AntiXssUFMvcServiceCollectionExtensions.cs" />
</ItemGroup>
<ItemGroup>
<None Remove="resources\antixss-policy-Default.json" />
</ItemGroup>
<ItemGroup>
<EmbeddedResource Include="resources\antixss-policy-Default.json">
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
<ExcludeFromSingleFile>true</ExcludeFromSingleFile>
<CopyToPublishDirectory>PreserveNewest</CopyToPublishDirectory>
</EmbeddedResource>
</ItemGroup>
<ItemGroup>
<PackageReference Include="Microsoft.AspNetCore.Mvc.Abstractions" Version="2.2.0" />
<PackageReference Include="Microsoft.AspNetCore.Mvc.Core" Version="2.2.5" />
</ItemGroup>
<ItemGroup>
<ProjectReference Include="..\AntiXssUF\AntiXssUF.csproj" />
</ItemGroup>
<ItemGroup>
<None Update="resources\antixss-policy-antisamy-anythinggoes.xml">
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
</None>
<None Update="resources\antixss-policy-antisamy-ebay.xml">
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
</None>
<None Update="resources\antixss-policy-antisamy-myspace.xml">
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
</None>
<None Update="resources\antixss-policy-antisamy-slashdot.xml">
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
</None>
<None Update="resources\antixss-policy-antisamy-test.xml">
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
</None>
<None Update="resources\antixss-policy-antisamy.xml">
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
</None>
<None Update="resources\antixss-policy-Default.config">
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
</None>
<None Update="resources\antixss-policy-Default.xml">
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
</None>
<None Update="resources\antixss-policy.xsd">
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
</None>
</ItemGroup>
</Project>

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

View File

@@ -1,176 +0,0 @@
<?xml version="1.0" encoding="ISO-8859-1"?>
<!--
W3C rules retrieved from:
http://www.w3.org/TR/html401/struct/global.html
-->
<!--
Slashdot allowed tags taken from "Reply" page:
<b> <i> <p> <br> <a> <ol> <ul> <li> <dl> <dt> <dd> <em> <strong> <tt> <blockquote> <div> <ecode> <quote>
-->
<anti-samy-rules xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:noNamespaceSchemaLocation="antixss-policy.xsd">
<directives>
<directive name="omitXmlDeclaration" value="true"/>
<directive name="omitDoctypeDeclaration" value="true"/>
<directive name="maxInputSize" value="5000"/>
<directive name="useXHTML" value="true"/>
<directive name="formatOutput" value="true"/>
<directive name="embedStyleSheets" value="false"/>
</directives>
<common-regexps>
<!--
From W3C:
This attribute assigns a class name or set of class names to an
element. Any number of elements may be assigned the same class
name or names. Multiple class names must be separated by white
space characters.
-->
<regexp name="htmlTitle" value="[\p{L}\p{N}\s-_',:\[\]!\./\\\(\)]*"/> <!-- force non-empty with a '+' at the end instead of '*' -->
<regexp name="onsiteURL" value="([\p{L}\p{N}\\/\.\?=\#&amp;;\-_~]+|\#(\w)+)"/>
<regexp name="offsiteURL" value="(\s)*((ht|f)tp(s?)://|mailto:)[\p{L}\p{N}]+[~\p{L}\p{N}\p{Zs}\-_\.@#$%&amp;;:,\?=/\+!]*(\s)*"/>
</common-regexps>
<!--
Tag.name = a, b, div, body, etc.
Tag.action = filter: remove tags, but keep content, validate: keep content as long as it passes rules, remove: remove tag and contents
Attribute.name = id, class, href, align, width, etc.
Attribute.onInvalid = what to do when the attribute is invalid, e.g., remove the tag (removeTag), remove the attribute (removeAttribute), filter the tag (filterTag)
Attribute.description = What rules in English you want to tell the users they can have for this attribute. Include helpful things so they'll be able to tune their HTML
-->
<!--
Some attributes are common to all (or most) HTML tags. There aren't many that qualify for this. You have to make sure there's no
collisions between any of these attribute names with attribute names of other tags that are for different purposes.
-->
<common-attributes>
<attribute name="lang" description="The 'lang' attribute tells the browser what language the element's attribute values and content are written in">
<regexp-list>
<regexp value="[a-zA-Z]{2,20}"/>
</regexp-list>
</attribute>
<attribute name="title" description="The 'title' attribute provides text that shows up in a 'tooltip' when a user hovers their mouse over the element">
<regexp-list>
<regexp name="htmlTitle"/>
</regexp-list>
</attribute>
<attribute name="href" onInvalid="filterTag">
<regexp-list>
<regexp name="onsiteURL"/>
<regexp name="offsiteURL"/>
</regexp-list>
</attribute>
<attribute name="align" description="The 'align' attribute of an HTML element is a direction word, like 'left', 'right' or 'center'">
<literal-list>
<literal value="center"/>
<literal value="left"/>
<literal value="right"/>
<literal value="justify"/>
<literal value="char"/>
</literal-list>
</attribute>
</common-attributes>
<!--
This requires normal updates as browsers continue to diverge from the W3C and each other. As long as the browser wars continue
this is going to continue. I'm not sure war is the right word for what's going on. Doesn't somebody have to win a war after
a while?
-->
<global-tag-attributes>
<attribute name="title"/>
<attribute name="lang"/>
</global-tag-attributes>
<tag-rules>
<!-- Tags related to JavaScript -->
<tag name="script" action="remove"/>
<tag name="noscript" action="remove"/>
<!-- Frame & related tags -->
<tag name="iframe" action="remove"/>
<tag name="frameset" action="remove"/>
<tag name="frame" action="remove"/>
<tag name="noframes" action="remove"/>
<!-- CSS related tags -->
<tag name="style" action="remove"/>
<!-- All reasonable formatting tags -->
<tag name="p" action="validate">
<attribute name="align"/>
</tag>
<tag name="div" action="validate"/>
<tag name="i" action="validate"/>
<tag name="b" action="validate"/>
<tag name="em" action="validate"/>
<tag name="blockquote" action="validate"/>
<tag name="tt" action="validate"/>
<tag name="br" action="truncate"/>
<!-- Custom Slashdot tags, though we're trimming the idea of having a possible mismatching end tag with the endtag="" attribute -->
<tag name="quote" action="validate"/>
<tag name="ecode" action="validate"/>
<!-- Anchor and anchor related tags -->
<tag name="a" action="validate">
<attribute name="href" onInvalid="filterTag"/>
<attribute name="nohref">
<literal-list>
<literal value="nohref"/>
<literal value=""/>
</literal-list>
</attribute>
<attribute name="rel">
<literal-list>
<literal value="nofollow"/>
</literal-list>
</attribute>
</tag>
<!-- List tags -->
<tag name="ul" action="validate"/>
<tag name="ol" action="validate"/>
<tag name="li" action="validate"/>
</tag-rules>
<!-- No CSS on Slashdot posts -->
<css-rules>
</css-rules>
</anti-samy-rules>

View File

@@ -1,862 +0,0 @@
<?xml version="1.0" encoding="ISO-8859-1" ?>
<!--
W3C rules retrieved from:
http://www.w3.org/TR/html401/struct/global.html
-->
<anti-samy-rules xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:noNamespaceSchemaLocation="antixss-policy.xsd">
<directives>
<directive name="omitXmlDeclaration" value="true"/>
<directive name="omitDoctypeDeclaration" value="true"/>
<directive name="maxInputSize" value="20001"/>
<directive name="useXHTML" value="true"/>
<directive name="formatOutput" value="true"/>
<!--
remember, this won't work for relative URIs - AntiSamy doesn't
know anything about the URL or your web structure
-->
<directive name="embedStyleSheets" value="false"/>
</directives>
<common-regexps>
<!--
From W3C:
This attribute assigns a class name or set of class names to an
element. Any number of elements may be assigned the same class
name or names. Multiple class names must be separated by white
space characters.
-->
<regexp name="colorNameOrCode" value="(#[0-9a-fA-F]{6}|[a-zA-Z]{1,20})"/>
<regexp name="number" value="[0-9]+"/>
<regexp name="anything" value=".*"/>
<regexp name="numberOrPercent" value="(\d)+(%{0,1})"/>
<regexp name="paragraph" value="([\p{L}\p{N},'\.\s\-_\(\)]|&amp;[0-9]{2};)*"/>
<regexp name="htmlId" value="[a-zA-Z0-9-_]+"/>
<regexp name="htmlTitle" value="[\p{L}\p{N}\s-_',:\[\]!\./\\\(\)]*"/> <!-- force non-empty with a '+' at the end instead of '*' -->
<regexp name="htmlClass" value="[a-zA-Z0-9\s,-_]+"/>
<regexp name="onsiteURL" value="([\p{L}\p{N}\\/\.\?=\#&amp;;\-_~]+|\#(\w)+)"/>
<regexp name="offsiteURL" value="(\s)*((ht|f)tp(s?)://|mailto:)[\p{L}\p{N}]+[~\p{L}\p{N}\p{Zs}\-_\.@#$%&amp;;:,\?=/\+!]*(\s)*"/>
<regexp name="boolean" value="(true|false)"/>
<regexp name="singlePrintable" value="[a-zA-Z0-9]{1}"/> <!-- \w allows the '_' character -->
<!-- This is for elements (ex: elemName { ... }) -->
<regexp name="cssElementSelector" value="[a-zA-Z0-9\-_]+|\*"/>
<!-- This is to list out any element names that are *not* valid -->
<regexp name="cssElementExclusion" value=""/>
<!-- This if for classes (ex: .className { ... }) -->
<regexp name="cssClassSelector" value="\.[a-zA-Z0-9\-_]+"/>
<!-- This is to list out any class names that are *not* valid -->
<regexp name="cssClassExclusion" value=""/>
<!-- This is for ID selectors (ex: #myId { ... } -->
<regexp name="cssIDSelector" value="#[a-zA-Z0-9\-_]+"/>
<!-- This is for ID selectors (ex: #myId { ... } -->
<regexp name="cssId" value="#[a-zA-Z0-9\-_]+"/>
<!-- This is to list out any IDs that are *not* valid - FIXME: What should the default be to avoid div hijacking? *? -->
<regexp name="cssIDExclusion" value=""/>
<!-- This is for pseudo-element selector (ex. foo:pseudo-element { ... } -->
<regexp name="cssPseudoElementSelector" value=":[a-zA-Z0-9\-_]+"/>
<!-- This is to list out any psuedo-element names that are *not* valid -->
<regexp name="cssPsuedoElementExclusion" value=""/>
<!-- This is for attribute selectors (ex. foo[attr=value] { ... } -->
<regexp name="cssAttributeSelector" value="\[[a-zA-Z0-9-_]+((=|~=|\|=){1}[a-zA-Z0-9\-_]+){1}\]"/>
<!-- This is to list out any attribute names that are *not* valid -->
<regexp name="cssAttributeExclusion" value=""/>
<!-- This is for resources referenced from CSS (such as background images and other imported stylesheets) -->
<regexp name="cssOnsiteUri" value="url\(([\p{L}\p{N}\\/\.\?=\#&amp;;\-_~]+|\#(\w)+)\)"/>
<regexp name="cssOffsiteUri" value="url\((\s)*((ht|f)tp(s?)://)[\p{L}\p{N}]+[~\p{L}\p{N}\p{Zs}\-_\.@#$%&amp;;:,\?=/\+!]*(\s)*\)"/>
<!-- This is for comments within CSS (ex. /* comment */) -->
<regexp name="cssCommentText" value="[\p{L}\p{N}-_,\/\\\.\s\(\)!\?\=\$#%\^&amp;:\[\]&quot;']+"/>
<regexp name="integer" value="(-|\+)?[0-9]+"/>
<regexp name="number" value="(-|\+)?([0-9]+(.[0-9]+)?)"/>
<regexp name="angle" value="(-|\+)?([0-9]+(.[0-9]+)?)(deg|grads|rad)"/>
<regexp name="time" value="([0-9]+(.[0-9]+)?)(ms|s)"/>
<regexp name="frequency" value="([0-9]+(.[0-9]+)?)(hz|khz)"/>
<regexp name="length" value="((-|\+)?0|(-|\+)?([0-9]+(.[0-9]+)?)(em|ex|px|in|cm|mm|pt|pc))"/>
<regexp name="percentage" value="(-|\+)?([0-9]+(.[0-9]+)?)%"/>
<regexp name="cssColor" value="(aqua|black|blue|fuchsia|gray|grey|green|lime|maroon|navy|olive|purple|red|silver|teal|white|yellow)|(^#[0-9a-fA-F]{3,3}$)|(^#[0-9a-fA-F]{6,6}$)|rgba?\\(\\s*([1]?[0-9]{1,2}|2[0-4][0-9]|25[0-5])\\s*,\\s*([1]?[0-9]{1,2}|2[0-4][0-9]|25[0-5])\\s*,\\s*([1]?[0-9]{1,2}|2[0-4][0-9]|25[0-5])(\\s*,\\s*[1])?\\s*\\)"/>
<regexp name="absolute-size" value="(xx-small|x-small|small|medium|large|x-large|xx-large)"/>
<regexp name="relative-size" value="(larger|smaller)"/>
</common-regexps>
<!--
Tag.name = a, b, div, body, etc.
Tag.action = filter: remove tags, but keep content, validate: keep content as long as it passes rules, remove: remove tag and contents
Attribute.name = id, class, href, align, width, etc.
Attribute.onInvalid = what to do when the attribute is invalid, e.g., remove the tag (removeTag), remove the attribute (removeAttribute), filter the tag (filterTag)
Attribute.description = What rules in English you want to tell the users they can have for this attribute. Include helpful things so they'll be able to tune their HTML
-->
<!--
Some attributes are common to all (or most) HTML tags. There aren't many that qualify for this. You have to make sure there's no
collisions between any of these attribute names with attribute names of other tags that are for different purposes.
-->
<common-attributes>
<!-- Common to all HTML tags -->
<attribute name="id" description="The 'id' of any HTML attribute should not contain anything besides letters and numbers">
<regexp-list>
<regexp value="[a-zA-Z0-9_\-\:]+"/>
</regexp-list>
</attribute>
<attribute name="class" description="The 'class' of any HTML attribute is usually a single word, but it can also be a list of class names separated by spaces">
<regexp-list>
<regexp name="htmlClass"/>
</regexp-list>
</attribute>
<attribute name="lang" description="The 'lang' attribute tells the browser what language the element's attribute values and content are written in">
<regexp-list>
<regexp value="[a-zA-Z]{2,20}"/>
</regexp-list>
</attribute>
<attribute name="title" description="The 'title' attribute provides text that shows up in a 'tooltip' when a user hovers their mouse over the element">
<regexp-list>
<regexp name="htmlTitle"/>
</regexp-list>
</attribute>
<attribute name="alt" description="The 'alt' attribute provides alternative text to users when its visual representation is not available">
<regexp-list>
<regexp name="paragraph"/>
</regexp-list>
</attribute>
<!-- the "style" attribute will be validated by an inline stylesheet scanner, so no need to define anything here - i hate having to special case this but no other choice -->
<attribute name="style" description="The 'style' attribute provides the ability for users to change many attributes of the tag's contents using a strict syntax"/>
<attribute name="media">
<literal-list>
<literal value="screen"/>
<literal value="tty"/>
<literal value="tv"/>
<literal value="projection"/>
<literal value="handheld"/>
<literal value="print"/>
<literal value="braille"/>
<literal value="aural"/>
<literal value="all"/>
</literal-list>
</attribute>
<!-- Anchor related -->
<!-- onInvalid="filterTag" has been removed as per suggestion at OWASP SJ 2007 - just "name" is valid -->
<attribute name="href">
<regexp-list>
<regexp name="onsiteURL"/>
<regexp name="offsiteURL"/>
</regexp-list>
</attribute>
<attribute name="name">
<regexp-list>
<regexp value="[a-zA-Z0-9-_\$]+"/>
<!--
have to allow the $ for .NET controls - although,
will users be supplying input that has server-generated
.NET control names? methinks not, but i want to pass my
test cases
-->
</regexp-list>
</attribute>
<attribute name="shape" description="The 'shape' attribute defines the shape of the selectable area">
<literal-list>
<literal value="default"/>
<literal value="rect"/>
<literal value="circle"/>
<literal value="poly"/>
</literal-list>
</attribute>
<!-- Table attributes -->
<attribute name="border">
<regexp-list>
<regexp name="number"/>
</regexp-list>
</attribute>
<attribute name="cellpadding">
<regexp-list>
<regexp name="number"/>
</regexp-list>
</attribute>
<attribute name="cellspacing">
<regexp-list>
<regexp name="number"/>
</regexp-list>
</attribute>
<attribute name="colspan">
<regexp-list>
<regexp name="number"/>
</regexp-list>
</attribute>
<attribute name="rowspan">
<regexp-list>
<regexp name="number"/>
</regexp-list>
</attribute>
<attribute name="background">
<regexp-list>
<regexp name="onsiteURL"/>
</regexp-list>
</attribute>
<attribute name="bgcolor">
<regexp-list>
<regexp name="colorNameOrCode"/>
</regexp-list>
</attribute>
<attribute name="abbrev">
<regexp-list>
<regexp name="paragraph"/>
</regexp-list>
</attribute>
<attribute name="headers" description="The 'headers' attribute is a space-separated list of cell IDs">
<regexp-list>
<regexp value="[a-zA-Z0-9\s*]*"/>
</regexp-list>
</attribute>
<attribute name="charoff">
<regexp-list>
<regexp value="numberOrPercent"/>
</regexp-list>
</attribute>
<attribute name="char">
<regexp-list>
<regexp value=".*{0,1}"/>
</regexp-list>
</attribute>
<attribute name="axis" description="The 'headers' attribute is a comma-separated list of related header cells">
<regexp-list>
<regexp value="[a-zA-Z0-9\s*,]*"/>
</regexp-list>
</attribute>
<attribute name="nowrap" description="The 'nowrap' attribute tells the browser not to wrap text that goes over one line">
<regexp-list>
<regexp name="anything"/>
<!-- <regexp value="(nowrap){0,1}"/> -->
</regexp-list>
</attribute>
<!-- Common positioning attributes -->
<attribute name="width">
<regexp-list>
<regexp name="numberOrPercent"/>
</regexp-list>
</attribute>
<attribute name="height">
<regexp-list>
<regexp name="numberOrPercent"/>
</regexp-list>
</attribute>
<attribute name="align" description="The 'align' attribute of an HTML element is a direction word, like 'left', 'right' or 'center'">
<literal-list>
<literal value="center"/>
<literal value="middle"/>
<literal value="left"/>
<literal value="right"/>
<literal value="justify"/>
<literal value="char"/>
</literal-list>
</attribute>
<attribute name="valign" description="The 'valign' attribute of an HTML attribute is a direction word, like 'baseline','bottom','middle' or 'top'">
<literal-list>
<literal value="baseline"/>
<literal value="bottom"/>
<literal value="middle"/>
<literal value="top"/>
</literal-list>
</attribute>
<!-- Intrinsic JavaScript Events -->
<attribute name="onFocus" description="The 'onFocus' event is executed when the control associated with the tag gains focus">
<literal-list>
<literal value="javascript:void(0)"/>
<literal value="javascript:history.go(-1)"/>
</literal-list>
</attribute>
<attribute name="onBlur" description="The 'onBlur' event is executed when the control associated with the tag loses focus">
<literal-list>
<literal value="javascript:void(0)"/>
<literal value="javascript:history.go(-1)"/>
</literal-list>
</attribute>
<attribute name="onClick" description="The 'onClick' event is executed when the control associated with the tag is clicked">
<literal-list>
<literal value="javascript:void(0)"/>
<literal value="javascript:history.go(-1)"/>
</literal-list>
</attribute>
<attribute name="onDblClick" description="The 'onDblClick' event is executed when the control associated with the tag is clicked twice immediately">
<literal-list>
<literal value="javascript:void(0)"/>
<literal value="javascript:history.go(-1)"/>
</literal-list>
</attribute>
<attribute name="onMouseDown" description="The 'onMouseDown' event is executed when the control associated with the tag is clicked but not yet released">
<literal-list>
<literal value="javascript:void(0)"/>
<literal value="javascript:history.go(-1)"/>
</literal-list>
</attribute>
<attribute name="onMouseUp" description="The 'onMouseUp' event is executed when the control associated with the tag is clicked after the button is released">
<literal-list>
<literal value="javascript:void(0)"/>
<literal value="javascript:history.go(-1)"/>
</literal-list>
</attribute>
<attribute name="onMouseOver" description="The 'onMouseOver' event is executed when the user's mouse hovers over the control associated with the tag">
<literal-list>
<literal value="javascript:void(0)"/>
<literal value="javascript:history.go(-1)"/>
</literal-list>
</attribute>
<attribute name="scope" description="The 'scope' attribute defines what's covered by the header cells">
<literal-list>
<literal value="row"/>
<literal value="col"/>
<literal value="rowgroup"/>
<literal value="colgroup"/>
</literal-list>
</attribute>
<!-- If you want users to be able to mess with tabindex, uncomment this -->
<!--
<attribute name="tabindex" description="...">
<regexp-list>
<regexp name="number"/>
</regexp-list>
</attribute>
-->
<!-- Input/form related common attributes -->
<attribute name="disabled">
<regexp-list>
<regexp name="anything"/>
</regexp-list>
</attribute>
<attribute name="readonly">
<regexp-list>
<regexp name="anything"/>
</regexp-list>
</attribute>
<attribute name="accesskey">
<regexp-list>
<regexp name="anything"/>
</regexp-list>
</attribute>
<attribute name="size">
<regexp-list>
<regexp name="number"/>
</regexp-list>
</attribute>
<attribute name="autocomplete">
<literal-list>
<literal value="on"/>
<literal value="off"/>
</literal-list>
</attribute>
<attribute name="rows">
<regexp-list>
<regexp name="number"/>
</regexp-list>
</attribute>
<attribute name="cols">
<regexp-list>
<regexp name="number"/>
</regexp-list>
</attribute>
</common-attributes>
<!--
This requires normal updates as browsers continue to diverge from the W3C and each other. As long as the browser wars continue
this is going to continue. I'm not sure war is the right word for what's going on. Doesn't somebody have to win a war after
a while? Even wars of attrition, surely?
-->
<global-tag-attributes>
<!-- Not valid in base, head, html, meta, param, script, style, and title elements. -->
<attribute name="id"/>
<attribute name="style"/>
<attribute name="title"/>
<attribute name="class"/>
<!-- Not valid in base, br, frame, frameset, hr, iframe, param, and script elements. -->
<attribute name="lang"/>
</global-tag-attributes>
<tag-rules>
<!-- Tags related to JavaScript -->
<tag name="script" action="remove"/>
<tag name="noscript" action="validate"/> <!-- although no javascript can fire inside a noscript tag, css is still a viable attack vector -->
<!-- Frame & related tags -->
<tag name="iframe" action="remove"/>
<tag name="frameset" action="remove"/>
<tag name="frame" action="remove"/>
<!-- Form related tags -->
<tag name="label" action="validate">
<attribute name="for">
<regexp-list>
<regexp name="htmlId"/>
</regexp-list>
</attribute>
</tag>
<!-- All formatting tags -->
<tag name="h1" action="validate"/>
<tag name="h2" action="validate"/>
<tag name="h3" action="validate"/>
<tag name="h4" action="validate"/>
<tag name="h5" action="validate"/>
<tag name="h6" action="validate"/>
<tag name="p" action="validate">
<attribute name="align"/>
</tag>
<tag name="i" action="validate"/>
<tag name="b" action="validate"/>
<tag name="u" action="validate"/>
<tag name="strong" action="validate"/>
<tag name="em" action="validate"/>
<tag name="small" action="validate"/>
<tag name="big" action="validate"/>
<tag name="pre" action="validate"/>
<tag name="code" action="validate"/>
<tag name="cite" action="validate"/>
<tag name="samp" action="validate"/>
<tag name="sub" action="validate"/>
<tag name="sup" action="validate"/>
<tag name="strike" action="validate"/>
<tag name="center" action="validate"/>
<tag name="blockquote" action="validate"/>
<tag name="hr" action="validate"/>
<tag name="br" action="validate"/>
<!--tag name="col" action="validate"/-->
<tag name="font" action="validate">
<attribute name="color">
<regexp-list>
<regexp name="colorNameOrCode"/>
</regexp-list>
</attribute>
<attribute name="face">
<regexp-list>
<regexp value="[\w;, ]+"/>
</regexp-list>
</attribute>
<attribute name="size">
<regexp-list>
<regexp value="(\+|-){0,1}(\d)+"/>
</regexp-list>
</attribute>
</tag>
<!-- Anchor and anchor related tags -->
<tag name="a" action="validate">
<!-- onInvalid="filterTag" has been removed as per suggestion at OWASP SJ 2007 - just "name" is valid -->
<attribute name="href"/>
<attribute name="onFocus"/>
<attribute name="onBlur"/>
<attribute name="nohref">
<regexp-list>
<regexp name="anything"/>
</regexp-list>
</attribute>
<attribute name="rel">
<literal-list>
<literal value="nofollow"/>
</literal-list>
</attribute>
<attribute name="name"/>
</tag>
<tag name="map" action="validate"/>
<!-- base tag removed per demo - this could be enabled with literal-list values you allow -->
<!--
<tag name="base" action="validate">
<attribute name="href"/>
</tag>
-->
<!-- Stylesheet Tags -->
<tag name="style" action="validate">
<attribute name="type">
<literal-list>
<literal value="text/css"/>
</literal-list>
</attribute>
<attribute name="media"/>
</tag>
<tag name="span" action="validate"/>
<tag name="div" action="validate">
<attribute name="align"/>
</tag>
<!-- <attribute name="id"/> what could an attacker do if they could overwrite an existing div definition? prolly something bad -->
<!-- <attribute name="class"/> what could an attacker do if they could specify any class in the namespace? prolly something bad -->
<!-- Image & image related tags -->
<tag name="img" action="validate">
<attribute name="src" onInvalid="removeTag">
<regexp-list>
<regexp name="onsiteURL"/>
<regexp name="offsiteURL"/>
</regexp-list>
</attribute>
<attribute name="name"/>
<attribute name="alt"/>
<attribute name="height"/>
<attribute name="width"/>
<attribute name="border"/>
<attribute name="align"/>
<attribute name="hspace">
<regexp-list>
<regexp name="number"/>
</regexp-list>
</attribute>
<attribute name="vspace">
<regexp-list>
<regexp name="number"/>
</regexp-list>
</attribute>
</tag>
<!-- no way to do this safely without hooking up the same code to @import to embed the remote stylesheet (malicious user could change offsite resource to be malicious after validation -->
<!-- <attribute name="href" onInvalid="removeTag"/> -->
<tag name="link" action="validate">
<!-- <attribute name="href" onInvalid="removeTag"/> -->
<attribute name="media"/>
<attribute name="type" onInvalid="removeTag">
<literal-list>
<literal value="text/css"/>
<literal value="application/rss+xml"/>
<literal value="image/x-icon"/>
</literal-list>
</attribute>
<attribute name="rel">
<literal-list>
<literal value="stylesheet"/>
<literal value="shortcut icon"/>
<literal value="search"/>
<literal value="copyright"/>
<literal value="top"/>
<literal value="alternate"/>
</literal-list>
</attribute>
</tag>
<!-- List tags -->
<tag name="ul" action="validate"/>
<tag name="ol" action="validate"/>
<tag name="li" action="validate"/>
<!-- Dictionary tags -->
<tag name="dd" action="truncate"/>
<tag name="dl" action="truncate"/>
<tag name="dt" action="truncate"/>
<!-- Table tags (tbody, thead, tfoot)-->
<tag name="thead" action="validate">
<attribute name="align"/>
<attribute name="char"/>
<attribute name="charoff"/>
<attribute name="valign"/>
</tag>
<tag name="tbody" action="validate">
<attribute name="align"/>
<attribute name="char"/>
<attribute name="charoff"/>
<attribute name="valign"/>
</tag>
<tag name="tfoot" action="validate">
<attribute name="align"/>
<attribute name="char"/>
<attribute name="charoff"/>
<attribute name="valign"/>
</tag>
<tag name="table" action="validate">
<attribute name="height"/>
<attribute name="width"/>
<attribute name="border"/>
<attribute name="bgcolor"/>
<attribute name="cellpadding"/>
<attribute name="cellspacing"/>
<attribute name="background"/>
<attribute name="align"/>
<attribute name="noresize">
<literal-list>
<literal value="noresize"/>
<literal value=""/>
</literal-list>
</attribute>
</tag>
<tag name="td" action="validate">
<attribute name="background"/>
<attribute name="bgcolor"/>
<attribute name="abbrev"/>
<attribute name="axis"/>
<attribute name="headers"/>
<attribute name="scope"/>
<attribute name="nowrap"/>
<attribute name="height"/>
<attribute name="width"/>
<attribute name="align"/>
<attribute name="char"/>
<attribute name="charoff"/>
<attribute name="valign"/>
<attribute name="colspan"/>
<attribute name="rowspan"/>
</tag>
<tag name="th" action="validate">
<attribute name="abbrev"/>
<attribute name="axis"/>
<attribute name="headers"/>
<attribute name="scope"/>
<attribute name="nowrap"/>
<attribute name="bgcolor"/>
<attribute name="height"/>
<attribute name="width"/>
<attribute name="align"/>
<attribute name="char"/>
<attribute name="charoff"/>
<attribute name="valign"/>
<attribute name="colspan"/>
<attribute name="rowspan"/>
</tag>
<tag name="tr" action="validate">
<attribute name="height"/>
<attribute name="width"/>
<attribute name="align"/>
<attribute name="valign"/>
<attribute name="char"/>
<attribute name="charoff"/>
<attribute name="background"/>
</tag>
<tag name="colgroup" action="validate">
<attribute name="span">
<regexp-list>
<regexp name="number"/>
</regexp-list>
</attribute>
<attribute name="width"/>
<attribute name="align"/>
<attribute name="char"/>
<attribute name="charoff"/>
<attribute name="valign"/>
</tag>
<tag name="col" action="validate">
<attribute name="align"/>
<attribute name="char"/>
<attribute name="charoff"/>
<attribute name="valign"/>
<attribute name="span">
<regexp-list>
<regexp name="number"/>
</regexp-list>
</attribute>
<attribute name="width"/>
</tag>
<tag name="fieldset" action="validate"/>
<tag name="legend" action="validate"/>
</tag-rules>
<!-- CSS validation processing rules -->
<css-rules>
<!--
<property name="counter-increment" default="none" description="The 'counter-increment' property accepts one or more names of counters (identifiers), each one optionally followed by an integer.">
<category-list>
<category value="all"/>
</category-list>
<literal-list>
<literal value="none"/>
<literal value="inherit"/>
</literal-list>
<regexp-list>
<regexp name="cssId"/>
<regexp name="integer"/>
</regexp-list>
</property>
-->
<property name="font-family" description="This property specifies a prioritized list of font family names and/or generic family names.">
<category-list>
<category value="visual"/>
</category-list>
<literal-list>
<literal value="serif"/>
<literal value="arial"/>
<literal value="lucida console"/>
<literal value="sans-serif"/>
<literal value="cursive"/>
<literal value="verdana"/>
<literal value="fantasy"/>
<literal value="monospace"/>
</literal-list>
<regexp-list>
<regexp value="[\w,\-&apos;&quot; ]+"/>
</regexp-list>
</property>
<property name="page" description="The 'page' property can be used to specify a particular type of page where an element should be displayed.">
<category-list>
<category value="visual"/>
<category value="paged"/>
</category-list>
<literal-list>
<literal value="auto"/>
</literal-list>
<regexp-list>
<regexp name="cssId"/>
</regexp-list>
</property>
</css-rules>
</anti-samy-rules>

File diff suppressed because it is too large Load Diff

View File

@@ -1,154 +0,0 @@
<?xml version="1.0" encoding="UTF-8"?>
<xsd:schema
xmlns:xsd="http://www.w3.org/2001/XMLSchema">
<xsd:element name="rules">
<xsd:complexType>
<xsd:sequence>
<xsd:element name="directives" type="Directives" maxOccurs="1" minOccurs="1"/>
<xsd:element name="common-regexps" type="CommonRegexps" maxOccurs="1" minOccurs="1"/>
<xsd:element name="common-attributes" type="AttributeList" maxOccurs="1" minOccurs="1"/>
<xsd:element name="global-tag-attributes" type="AttributeList" maxOccurs="1" minOccurs="1"/>
<xsd:element name="tag-rules" type="TagRules" minOccurs="1" maxOccurs="1"/>
<xsd:element name="css-rules" type="CSSRules" minOccurs="1" maxOccurs="1"/>
</xsd:sequence>
</xsd:complexType>
</xsd:element>
<xsd:complexType name="Directives">
<xsd:sequence maxOccurs="unbounded">
<xsd:element name="directive" type="Directive" minOccurs="0"/>
</xsd:sequence>
</xsd:complexType>
<xsd:complexType name="Directive">
<xsd:attribute name="name" use="required"/>
<xsd:attribute name="value" use="required"/>
</xsd:complexType>
<xsd:complexType name="CommonRegexps">
<xsd:sequence maxOccurs="unbounded">
<xsd:element name="regexp" type="RegExp" minOccurs="0"/>
</xsd:sequence>
</xsd:complexType>
<xsd:complexType name="AttributeList">
<xsd:sequence maxOccurs="unbounded">
<xsd:element name="attribute" type="Attribute" minOccurs="0"/>
</xsd:sequence>
</xsd:complexType>
<xsd:complexType name="TagRules">
<xsd:sequence maxOccurs="unbounded">
<xsd:element name="tag" type="Tag" minOccurs="0"/>
</xsd:sequence>
</xsd:complexType>
<xsd:complexType name="Tag">
<xsd:sequence maxOccurs="unbounded">
<xsd:element name="attribute" type="Attribute" minOccurs="0" />
</xsd:sequence>
<xsd:attribute name="name" use="required"/>
<xsd:attribute name="action" use="required">
<xsd:simpleType>
<xsd:restriction base="xsd:string">
<xsd:enumeration value="Remove" />
<xsd:enumeration value="Truncate" />
<xsd:enumeration value="Validate" />
<xsd:enumeration value="Filter" />
</xsd:restriction>
</xsd:simpleType>
</xsd:attribute>
</xsd:complexType>
<xsd:complexType name="Attribute">
<xsd:sequence>
<xsd:element name="regexp-list" type="RegexpList" minOccurs="0"/>
<xsd:element name="literal-list" type="LiteralList" minOccurs="0"/>
</xsd:sequence>
<xsd:attribute name="name" use="required"/>
<xsd:attribute name="description"/>
<xsd:attribute name="onInvalid">
<xsd:simpleType>
<xsd:restriction base="xsd:string">
<xsd:enumeration value="RemoveAttribute" />
<xsd:enumeration value="RemoveTag" />
<xsd:enumeration value="FilterTag" />
</xsd:restriction>
</xsd:simpleType>
</xsd:attribute>
</xsd:complexType>
<xsd:complexType name="RegexpList">
<xsd:sequence maxOccurs="unbounded">
<xsd:element name="regexp" type="RegExp" minOccurs="0"/>
</xsd:sequence>
</xsd:complexType>
<xsd:complexType name="RegExp">
<xsd:attribute name="name" type="xsd:string"/>
<xsd:attribute name="value" type="xsd:string"/>
</xsd:complexType>
<xsd:complexType name="LiteralList">
<xsd:sequence maxOccurs="unbounded">
<xsd:element name="literal" type="Literal" minOccurs="0"/>
</xsd:sequence>
</xsd:complexType>
<xsd:complexType name="Literal">
<xsd:attribute name="value" type="xsd:string"/>
</xsd:complexType>
<xsd:complexType name="CSSRules">
<xsd:sequence maxOccurs="unbounded">
<xsd:element name="property" type="Property" minOccurs="0"/>
</xsd:sequence>
</xsd:complexType>
<xsd:complexType name="Property">
<xsd:sequence>
<xsd:element name="category-list" type="CategoryList" minOccurs="0"/>
<xsd:element name="literal-list" type="LiteralList" minOccurs="0"/>
<xsd:element name="regexp-list" type="RegexpList" minOccurs="0"/>
<xsd:element name="shorthand-list" type="ShorthandList" minOccurs="0"/>
</xsd:sequence>
<xsd:attribute name="name" type="xsd:string" use="required"/>
<xsd:attribute name="default" type="xsd:string"/>
<xsd:attribute name="description" type="xsd:string"/>
</xsd:complexType>
<xsd:complexType name="ShorthandList">
<xsd:sequence maxOccurs="unbounded">
<xsd:element name="shorthand" type="Shorthand" minOccurs="0"/>
</xsd:sequence>
</xsd:complexType>
<xsd:complexType name="Shorthand">
<xsd:attribute name="name" type="xsd:string" use="required"/>
</xsd:complexType>
<xsd:complexType name="CategoryList">
<xsd:sequence maxOccurs="unbounded">
<xsd:element name="category" type="Category" minOccurs="0"/>
</xsd:sequence>
</xsd:complexType>
<xsd:complexType name="Category">
<xsd:attribute name="value" type="xsd:string" use="required"/>
</xsd:complexType>
<xsd:complexType name="Entity">
<xsd:attribute name="name" type="xsd:string" use="required"/>
<xsd:attribute name="cdata" type="xsd:string" use="required"/>
</xsd:complexType>
</xsd:schema>

View File

@@ -27,7 +27,11 @@
</ItemGroup>
<ItemGroup>
<ProjectReference Include="..\AntiXssUF.Mvc\AntiXssUF.Mvc.csproj" />
<PackageReference Include="Microsoft.VisualStudio.Web.CodeGeneration.Design" Version="3.1.1" />
</ItemGroup>
<ItemGroup>
<ProjectReference Include="..\AntiXssUF\AntiXssUF.csproj" />
</ItemGroup>
<ItemGroup>

View File

@@ -20,7 +20,6 @@ namespace AntiXssUF.TestSite.Controllers
private StringBuilder html;
public HomeController(ILogger<HomeController> logger, IFilterPolicyFactory policyFactory)
{
_logger = logger;
this.policyFactory = policyFactory;
}
@@ -29,15 +28,18 @@ namespace AntiXssUF.TestSite.Controllers
var clean = filter.Filters(source);
return Content(clean);
}
StringBuilder stringBuilder = new StringBuilder();
void FilterAttacks(RichText richText, Func<string, bool> fn, [CallerMemberName] string propertyName = null)
{
html.Append($"\n==== in {propertyName} ==================================================\n原文:\n{ HttpUtility.HtmlEncode(richText.Source)}\n");
html.Append("过滤\n");
stringBuilder.Append($"\n==== in {propertyName} ==================================================\n原文:\n{richText.Source}\n");
stringBuilder.Append("过滤\n");
string clean = richText.ToString();
html.Append(HttpUtility.HtmlEncode(clean));
stringBuilder.Append(clean);
var isTrue = fn(clean);
stringBuilder.Append($"\n状态{isTrue}");
html.Append($"\n状态{fn(clean)}");
}
void testScriptAttacks()
{
@@ -56,13 +58,11 @@ namespace AntiXssUF.TestSite.Controllers
FilterAttacks("<img src='http://www.myspace.com/img.gif'>", str => str.IndexOf("<img", StringComparison.OrdinalIgnoreCase) != -1);
FilterAttacks("<img src=javascript:alert(document.cookie)>", str => str.IndexOf("<img", StringComparison.OrdinalIgnoreCase) == -1);
FilterAttacks("<IMG SRC=&#106;&#97;&#118;&#97;&#115;&#99;&#114;&#105;&#112;&#116;&#58;&#97;&#108;&#101;&#114;&#116;&#40;&#39;&#88;&#83;&#83;&#39;&#41;>",
str => str.IndexOf("<img",StringComparison.OrdinalIgnoreCase) == -1);
str => str.IndexOf("<img", StringComparison.OrdinalIgnoreCase) == -1);
FilterAttacks("<IMG SRC=&#0000106&#0000097&#0000118&#0000097&#0000115&#0000099&#0000114&#0000105&#0000112&#0000116&#0000058&#0000097&#0000108&#0000101&#0000114&#0000116&#0000040&#0000039&#0000088&#0000083&#0000083&#0000039&#0000041>", str => str.IndexOf("&amp;", StringComparison.OrdinalIgnoreCase) != -1);
FilterAttacks("<IMG SRC=&#0000106&#0000097&#0000118&#0000097&#0000115&#0000099&#0000114&#0000105&#0000112&#0000116&#0000058&#0000097&#0000108&#0000101&#0000114&#0000116&#0000040&#0000039&#0000088&#0000083&#0000083&#0000039&#0000041>", str => string.IsNullOrEmpty(str));
FilterAttacks("&#0000106&#0000097&#0000118&#0000097&#0000115&#0000099&#0000114&#0000105&#0000112&#0000116&#0000058&#0000097&#0000108&#0000101&#0000114&#0000116&#0000040&#0000039&#0000088&#0000083&#0000083&#0000039&#0000041>", str => str.IndexOf("&amp;", StringComparison.OrdinalIgnoreCase) != -1);
FilterAttacks("<IMG SRC=&#x6A&#x61&#x76&#x61&#x73&#x63&#x72&#x69&#x70&#x74&#x3A&#x61&#x6C&#x65&#x72&#x74&#x28&#x27&#x58&#x53&#x53&#x27&#x29>", str => str.IndexOf("&amp;", StringComparison.OrdinalIgnoreCase) != -1);
FilterAttacks("<IMG SRC=&#x6A&#x61&#x76&#x61&#x73&#x63&#x72&#x69&#x70&#x74&#x3A&#x61&#x6C&#x65&#x72&#x74&#x28&#x27&#x58&#x53&#x53&#x27&#x29>", str => string.IsNullOrEmpty(str));
FilterAttacks("<IMG SRC=\"jav&#x0D;ascript:alert('XSS');\">", str => str.IndexOf("alert", StringComparison.OrdinalIgnoreCase) == -1);
FilterAttacks("<IMG SRC=\"javascript:alert('XSS')\"", str => str.IndexOf("javascript", StringComparison.OrdinalIgnoreCase) == -1);
@@ -70,10 +70,9 @@ namespace AntiXssUF.TestSite.Controllers
FilterAttacks("<BGSOUND SRC=\"javascript:alert('XSS');\">", str => str.IndexOf("javascript", StringComparison.OrdinalIgnoreCase) == -1);
}
void testHrefAttacks()
public void testHrefAttacks()
{
FilterAttacks("<LINK REL=\"stylesheet\" HREF=\"javascript:alert('XSS');\">", str => str.IndexOf("href") == -1);
FilterAttacks("<LINK REL=\"stylesheet\" HREF=\"http://ha.ckers.org/xss.css\">", str => str.IndexOf("href") == -1);
@@ -120,6 +119,8 @@ namespace AntiXssUF.TestSite.Controllers
}
void testCssAttacks()
{
FilterAttacks("<div style=\"position:absolute\">", str => str.IndexOf("position") == -1);
@@ -127,41 +128,71 @@ namespace AntiXssUF.TestSite.Controllers
FilterAttacks("<div style=\"z-index:25\">", str => str.IndexOf("position") == -1);
FilterAttacks("<style>z-index:25</style>", str => str.IndexOf("position") == -1);
}
public IActionResult Index()
{
IEnumerable<object> list = null;
if( !(list?.Count()).HasValue) {
}
//RichText richText = "<INPUT TYPE=\"IMAGE\" SRC=\"javascript:alert('XSS');\">";
//string ss = richText;
////var policy = policyFactory.CreatePolicy("json").Result;
////var policy = policyFactory.CreatePolicy("DefaultPolicy").Result;
////var str = Newtonsoft.Json.JsonConvert.SerializeObject(new
////{
//// policy.Directives,
//// policy.CommonRegularExpressions,
//// CommonAttributes = policy.CommonAttributes.Select(e => e.Value),
//// CssRules = policy.CssRules.Select(e => e.Value),
//// GlobalAttributes = policy.GlobalAttributes.Values,
//// TagRules = policy.TagRules.Values
//// CommonAttributes = policy.CommonAttributes.Values.Select(e => new
//// {
//// e.Name,
//// OnInvalid = e.OnInvalid == default(PolicyHtmlAttributeOnInvalid) ? null : e.OnInvalid.ToString(),
//// AllowedRegExp = e.AllowedRegExp == null || e.AllowedRegExp.Length == 0 ? null : e.AllowedRegExp,
//// AllowedValues = e.AllowedValues == null || e.AllowedValues.Length == 0 ? null : e.AllowedValues,
//// Description = string.IsNullOrEmpty(e.Description) ? null : e.Description,
//// }),
//// CssRules = policy.CssRules.Values.Select(e => new
//// {
//// e.Name,
//// Shorthands = e.Shorthands == null || e.Shorthands.Length == 0 ? null : e.Shorthands,
//// AllowedRegExp = e.AllowedRegExp == null || e.AllowedRegExp.Length == 0 ? null : e.AllowedRegExp,
//// AllowedValues = e.AllowedValues == null || e.AllowedValues.Length == 0 ? null : e.AllowedValues,
//// Description = string.IsNullOrEmpty(e.Description) ? null : e.Description,
//// }),
//// GlobalAttributes = policy.GlobalAttributes.Values.Select(e => new
//// {
//// e.Name,
//// OnInvalid = e.OnInvalid == default(PolicyHtmlAttributeOnInvalid) ? null : e.OnInvalid.ToString(),
//// AllowedRegExp = e.AllowedRegExp == null || e.AllowedRegExp.Length == 0 ? null : e.AllowedRegExp,
//// AllowedValues = e.AllowedValues == null || e.AllowedValues.Length == 0 ? null : e.AllowedValues,
//// Description = string.IsNullOrEmpty(e.Description) ? null : e.Description,
//// }),
//// TagRules = policy.TagRules.Values.Select(tag => new
//// {
//// Action = tag.Action == default(PolicyHtmlTagAction) ? null : tag.Action.ToString(),
//// tag.Name,
//// AllowedAttributes = tag.AllowedAttributes == null || tag.AllowedAttributes.Values.Count == 0 ? null :
//// tag.AllowedAttributes.Values.Select(e => new
//// {
//// e.Name,
//// OnInvalid = e.OnInvalid == default(PolicyHtmlAttributeOnInvalid) ? null : e.OnInvalid.ToString(),
//// AllowedRegExp = e.AllowedRegExp == null || e.AllowedRegExp.Length == 0 ? null : e.AllowedRegExp,
//// AllowedValues = e.AllowedValues == null || e.AllowedValues.Length == 0 ? null : e.AllowedValues,
//// Description = string.IsNullOrEmpty(e.Description) ? null : e.Description,
//// }),
//// })
////}, new Newtonsoft.Json.JsonSerializerSettings() { NullValueHandling = Newtonsoft.Json.NullValueHandling.Ignore });
//
//////
Stopwatch stopwatch = new Stopwatch();
stopwatch.Start();
html = new StringBuilder();
FilterAttacks("<IMG SRC=java\0script:alert(\"XSS\")>", str => str.IndexOf("<img", StringComparison.OrdinalIgnoreCase) == -1);
testCssAttacks();
testHrefAttacks();
testScriptAttacks();
testImgAttacks();
stopwatch.Stop();
html.Append($"\n==============程序运行的时间:{stopwatch.Elapsed.TotalMilliseconds}毫秒");
ViewBag.html = html.ToString();
stringBuilder.Append($"\n==============程序运行的时间:{stopwatch.Elapsed.TotalMilliseconds}毫秒");
ViewBag.Test = stringBuilder.ToString();
return View();
}
[HttpGet]

View File

@@ -27,7 +27,7 @@ namespace AntiXssUF.TestSite
// This method gets called by the runtime. Use this method to add services to the container.
public void ConfigureServices(IServiceCollection services)
{
services.AddXssFilter(opt=>opt.DefaultSchemeName= "test")
services.AddXssFilter(opt=>opt.DefaultSchemeName= "DefaultPolicy")
.AddScheme<AntisamyPolicy>("antisamy", () => File.ReadAllTextAsync(Path.Combine(HostEnvironment.ContentRootPath, "resources/antisamy.xml")))
.AddScheme<AntisamyPolicy>("anythinggoes", () => File.ReadAllTextAsync(Path.Combine(HostEnvironment.ContentRootPath, "resources/antisamy-anythinggoes.xml")))
.AddScheme<AntisamyPolicy>("ebay", () => File.ReadAllTextAsync(Path.Combine(HostEnvironment.ContentRootPath, "resources/antisamy-ebay.xml")))

View File

@@ -2,30 +2,4 @@
ViewData["Title"] = "测试";
}
<div>
<style>
.output {
position: absolute;
top: 50px;
left: 0;
right: 0;
overflow: hidden;
bottom: 0;
background-color: white;
z-index:1;
}
.txt {
height: 100%;
width: 100%;
margin: 0;
display: block;
border: none;
}
</style>
<div class="output">
<textarea class="txt">
@Html.Raw(ViewBag.html)
</textarea>
</div>
</div>
@Html.Raw(Html.Encode(ViewBag.Test).Replace("&#xA;", "<br />"))

View File

@@ -16,7 +16,7 @@ Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Solution Items", "Solution
appveyor.yml = appveyor.yml
EndProjectSection
EndProject
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "AntiXssUF.Mvc", "AntiXssUF.Mvc\AntiXssUF.Mvc.csproj", "{0252B16F-E2CA-4CC9-BA5E-52701D90EB49}"
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Framework461Test", "Framework461Test\Framework461Test.csproj", "{AFDE17BE-9067-44B7-9AED-8184ABB57E5C}"
EndProject
Global
GlobalSection(SolutionConfigurationPlatforms) = preSolution
@@ -32,10 +32,10 @@ Global
{313AE160-70B2-4A7C-9E1A-F00353C0A75A}.Debug|Any CPU.Build.0 = Debug|Any CPU
{313AE160-70B2-4A7C-9E1A-F00353C0A75A}.Release|Any CPU.ActiveCfg = Release|Any CPU
{313AE160-70B2-4A7C-9E1A-F00353C0A75A}.Release|Any CPU.Build.0 = Release|Any CPU
{0252B16F-E2CA-4CC9-BA5E-52701D90EB49}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{0252B16F-E2CA-4CC9-BA5E-52701D90EB49}.Debug|Any CPU.Build.0 = Debug|Any CPU
{0252B16F-E2CA-4CC9-BA5E-52701D90EB49}.Release|Any CPU.ActiveCfg = Release|Any CPU
{0252B16F-E2CA-4CC9-BA5E-52701D90EB49}.Release|Any CPU.Build.0 = Release|Any CPU
{AFDE17BE-9067-44B7-9AED-8184ABB57E5C}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{AFDE17BE-9067-44B7-9AED-8184ABB57E5C}.Debug|Any CPU.Build.0 = Debug|Any CPU
{AFDE17BE-9067-44B7-9AED-8184ABB57E5C}.Release|Any CPU.ActiveCfg = Release|Any CPU
{AFDE17BE-9067-44B7-9AED-8184ABB57E5C}.Release|Any CPU.Build.0 = Release|Any CPU
EndGlobalSection
GlobalSection(SolutionProperties) = preSolution
HideSolutionNode = FALSE
@@ -43,7 +43,7 @@ Global
GlobalSection(NestedProjects) = preSolution
{1F7B7964-68FC-4AC9-BE42-EC8294F779E6} = {D658E57D-27C2-439A-8D75-C0C4FBB1F6E1}
{313AE160-70B2-4A7C-9E1A-F00353C0A75A} = {0676D379-1AB7-4009-8E06-6D5D05B11D06}
{0252B16F-E2CA-4CC9-BA5E-52701D90EB49} = {0676D379-1AB7-4009-8E06-6D5D05B11D06}
{AFDE17BE-9067-44B7-9AED-8184ABB57E5C} = {D658E57D-27C2-439A-8D75-C0C4FBB1F6E1}
EndGlobalSection
GlobalSection(ExtensibilityGlobals) = postSolution
SolutionGuid = {2A023FAF-6AAB-40ED-984D-E961234DDC13}

View File

@@ -1,21 +1,25 @@
<Project Sdk="Microsoft.NET.Sdk">
<Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup>
<TargetFramework>netstandard2.1</TargetFramework>
<TargetFrameworks>net461;netstandard2.0;netstandard2.1;netcoreapp2.1;netcoreapp3.1</TargetFrameworks>
<AssemblyName>AntiXssUF</AssemblyName>
<RootNamespace>Ufangx.Xss</RootNamespace>
<GeneratePackageOnBuild>true</GeneratePackageOnBuild>
<Description>xss anit policy filter</Description>
<Authors>Jackson.bruce</Authors>
<Company>ufangx</Company>
<Copyright>Copyright (c) 2020 Jackson.Bruce</Copyright>
<Copyright>Copyright (c) 2020-$([System.DateTime]::Now.Year) Jackson.Bruce</Copyright>
<PackageProjectUrl>https://github.com/JacksonBruce/AntiXssUF</PackageProjectUrl>
<RepositoryUrl>https://github.com/JacksonBruce/AntiXssUF.git</RepositoryUrl>
<PackageTags>Anti Xss .NETStandard</PackageTags>
<PackageReleaseNotes>https://github.com/JacksonBruce/AntiXssUF/blob/master/README.md</PackageReleaseNotes>
<RepositoryType>git</RepositoryType>
<DocumentationFile>bin\$(Configuration)\$(TargetFramework)\AntiXssUF.xml</DocumentationFile>
<EnableDefaultCompileItems>false</EnableDefaultCompileItems>
<Version>2.0.0</Version>
</PropertyGroup>
<ItemGroup>
<None Remove="resources\DefaultPolicy.json" />
</ItemGroup>
@@ -31,11 +35,172 @@
<ItemGroup>
<PackageReference Include="AngleSharp" Version="0.13.0" />
<PackageReference Include="AngleSharp.Css" Version="0.13.0" />
<PackageReference Include="Microsoft.Extensions.Caching.Abstractions" Version="3.1.2" />
<PackageReference Include="Microsoft.Extensions.Configuration.Binder" Version="3.1.2" />
<PackageReference Include="Microsoft.Extensions.Configuration.Json" Version="3.1.2" />
<PackageReference Include="Microsoft.Extensions.DependencyInjection" Version="3.1.2" />
<PackageReference Include="Microsoft.Extensions.Options" Version="3.1.2" />
<Compile Include="AntisamyPolicy.cs" />
<Compile Include="AntiXssUFServiceCollectionExtensions.cs" />
<Compile Include="Configures.cs" />
<Compile Include="CssFilter.cs" />
<Compile Include="ExtensionMethods.cs" />
<Compile Include="FilterPolicyBuilder.cs" />
<Compile Include="FilterPolicyException.cs" />
<Compile Include="FilterPolicyFactory.cs" />
<Compile Include="FilterPolicyOptions.cs" />
<Compile Include="FilterPolicyProvider.cs" />
<Compile Include="FilterRegExp.cs" />
<Compile Include="HtmlFilter.cs" />
<Compile Include="ICssFilter.cs" />
<Compile Include="IFilterPolicy.cs" />
<Compile Include="IFilterPolicyFactory.cs" />
<Compile Include="IFilterPolicyProvider.cs" />
<Compile Include="IHtmlFilter.cs" />
<Compile Include="JsonFilterPolicy.cs" />
<Compile Include="PolicyAttribute.cs" />
<Compile Include="PolicyCssProperty.cs" />
<Compile Include="PolicyHtmlAttribute.cs" />
<Compile Include="PolicyHtmlAttributeOnInvalid.cs" />
<Compile Include="PolicyHtmlTag.cs" />
<Compile Include="PolicyHtmlTagAction.cs" />
<Compile Include="RichText.cs" />
<Compile Include="XssFilterBuilder.cs" />
<Compile Include="XssShemeNameAttribute.netcoreapp.cs" />
<Compile Include="IXssSchemeName.netcoreapp.cs" />
<Compile Include="RichTextBinder.netcoreapp.cs" />
<Compile Include="RichTextBinderProvider.netcoreapp.cs" />
<Compile Include="JsonFilterPolicy.netstandard.cs" />
<Compile Include="FilterPolicyProvider.netstandard.cs" />
<Compile Include="Configures.netstandard.cs" />
</ItemGroup>
<ItemGroup Condition="'$(TargetFramework)' == 'netcoreapp2.1'">
<PackageReference Include="Microsoft.Extensions.Configuration">
<Version>3.1.2</Version>
</PackageReference>
<PackageReference Include="Microsoft.Extensions.Configuration.Abstractions">
<Version>3.1.2</Version>
</PackageReference>
<PackageReference Include="Microsoft.Extensions.Configuration.Binder">
<Version>3.1.2</Version>
</PackageReference>
<PackageReference Include="Microsoft.Extensions.Configuration.Json">
<Version>3.1.2</Version>
</PackageReference>
<PackageReference Include="Microsoft.Extensions.DependencyInjection">
<Version>3.1.2</Version>
</PackageReference>
<PackageReference Include="Microsoft.Extensions.Options">
<Version>3.1.2</Version>
</PackageReference>
<PackageReference Include="Microsoft.AspNetCore.Mvc.Abstractions" Version="2.2.0" />
<PackageReference Include="Microsoft.AspNetCore.Mvc.Core" Version="2.2.5" />
</ItemGroup>
<ItemGroup Condition="'$(TargetFramework)' == 'netcoreapp3.1'">
<PackageReference Include="Microsoft.Extensions.Configuration">
<Version>3.1.2</Version>
</PackageReference>
<PackageReference Include="Microsoft.Extensions.Configuration.Abstractions">
<Version>3.1.2</Version>
</PackageReference>
<PackageReference Include="Microsoft.Extensions.Configuration.Binder">
<Version>3.1.2</Version>
</PackageReference>
<PackageReference Include="Microsoft.Extensions.Configuration.Json">
<Version>3.1.2</Version>
</PackageReference>
<PackageReference Include="Microsoft.Extensions.DependencyInjection">
<Version>3.1.2</Version>
</PackageReference>
<PackageReference Include="Microsoft.Extensions.Options">
<Version>3.1.2</Version>
</PackageReference>
<PackageReference Include="Microsoft.AspNetCore.Mvc.Abstractions" Version="2.2.0" />
<PackageReference Include="Microsoft.AspNetCore.Mvc.Core" Version="2.2.5" />
</ItemGroup>
<ItemGroup Condition="'$(TargetFramework)' == 'netstandard2.0'">
<PackageReference Include="Microsoft.Extensions.Configuration">
<Version>3.1.2</Version>
</PackageReference>
<PackageReference Include="Microsoft.Extensions.Configuration.Abstractions">
<Version>3.1.2</Version>
</PackageReference>
<PackageReference Include="Microsoft.Extensions.Configuration.Binder">
<Version>3.1.2</Version>
</PackageReference>
<PackageReference Include="Microsoft.Extensions.Configuration.Json">
<Version>3.1.2</Version>
</PackageReference>
<PackageReference Include="Microsoft.Extensions.DependencyInjection">
<Version>3.1.2</Version>
</PackageReference>
<PackageReference Include="Microsoft.Extensions.Options">
<Version>3.1.2</Version>
</PackageReference>
<Compile Remove="XssShemeNameAttribute.netcoreapp.cs" />
<Compile Remove="IXssSchemeName.netcoreapp.cs" />
<Compile Remove="RichTextBinder.netcoreapp.cs" />
<Compile Remove="RichTextBinderProvider.netcoreapp.cs" />
</ItemGroup>
<ItemGroup Condition="'$(TargetFramework)' == 'netstandard2.1'">
<PackageReference Include="Microsoft.Extensions.Configuration">
<Version>3.1.2</Version>
</PackageReference>
<PackageReference Include="Microsoft.Extensions.Configuration.Abstractions">
<Version>3.1.2</Version>
</PackageReference>
<PackageReference Include="Microsoft.Extensions.Configuration.Binder">
<Version>3.1.2</Version>
</PackageReference>
<PackageReference Include="Microsoft.Extensions.Configuration.Json">
<Version>3.1.2</Version>
</PackageReference>
<PackageReference Include="Microsoft.Extensions.DependencyInjection">
<Version>3.1.2</Version>
</PackageReference>
<PackageReference Include="Microsoft.Extensions.Options">
<Version>3.1.2</Version>
</PackageReference>
<Compile Remove="XssShemeNameAttribute.netcoreapp.cs" />
<Compile Remove="IXssSchemeName.netcoreapp.cs" />
<Compile Remove="RichTextBinder.netcoreapp.cs" />
<Compile Remove="RichTextBinderProvider.netcoreapp.cs" />
</ItemGroup>
<ItemGroup Condition=" '$(TargetFramework)' == 'net461'">
<Reference Include="System" />
<Reference Include="Microsoft.CSharp" />
<Reference Include="System.Web" />
<PackageReference Include="Newtonsoft.Json" Version="12.0.3" />
<PackageReference Include="Microsoft.AspNet.Mvc" Version="5.2.7" />
<Compile Include="Configures.net461.cs" />
<Compile Include="JsonFilterPolicy.net461.cs" />
<Compile Include="FilterPolicyProvider.net461.cs" />
<Compile Include="XssSchemeNameAttribute.net461.cs" />
<Compile Include="IXssSchemeName.net461.cs" />
<Compile Include="RichTextBinder.net461.cs" />
<Compile Remove="XssShemeNameAttribute.netcoreapp.cs" />
<Compile Remove="IXssSchemeName.netcoreapp.cs" />
<Compile Remove="RichTextBinder.netcoreapp.cs" />
<Compile Remove="RichTextBinderProvider.netcoreapp.cs" />
<Compile Remove="JsonFilterPolicy.netstandard.cs" />
<Compile Remove="FilterPolicyProvider.netstandard.cs" />
<Compile Remove="Configures.netstandard.cs" />
</ItemGroup>
</Project>

View File

@@ -5,17 +5,23 @@ using Ufangx.Xss;
namespace Microsoft.Extensions.DependencyInjection
{
/// <summary>
/// 服务扩展方法
/// </summary>
public static class AntiXssUFServiceCollectionExtensions
{
/// <summary>
/// 添加Xss过滤器
/// </summary>
/// <param name="services"></param>
/// <param name="configureOptions"></param>
/// <returns></returns>
public static XssFilterBuilder AddXssFilter(this IServiceCollection services, Action<FilterPolicyOptions> configureOptions=null) {
if (services is null)
{
throw new ArgumentNullException(nameof(services));
}
if (configureOptions != null)
{
services.Configure(configureOptions);
}
new Configures(services).Options(configureOptions);
services.AddSingleton<IFilterPolicyProvider, FilterPolicyProvider>();
services.AddTransient<IFilterPolicyFactory, FilterPolicyFactory>();
return XssFilterBuilder.Builder ?? new XssFilterBuilder(services);

View File

@@ -6,6 +6,9 @@ using System.Web;
namespace Ufangx.Xss
{
/// <summary>
/// OwaspAntisamy配置策略
/// </summary>
[Serializable]
public class AntisamyPolicy: IFilterPolicy
{
@@ -14,23 +17,43 @@ namespace Ufangx.Xss
Dictionary<string, PolicyHtmlTag> tagRules;
Dictionary<string, PolicyCssProperty> cssRules;
private string name;
/// <summary>
/// 策略名称
/// </summary>
public string Name => name;
/// <summary>
/// 公用正则表达式
/// </summary>
public Dictionary<string, string> CommonRegularExpressions =>commonRegularExpressions;
/// <summary>
/// 控制设置
/// </summary>
public Dictionary<string, string> Directives => directives;
/// <summary>
/// 公用属性
/// </summary>
public Dictionary<string, PolicyHtmlAttribute> CommonAttributes => commonAttributes;
/// <summary>
/// 全局属性白名单
/// </summary>
public Dictionary<string, PolicyHtmlAttribute> GlobalAttributes =>globalAttributes;
/// <summary>
/// html标签白名单
/// </summary>
public Dictionary<string, PolicyHtmlTag> TagRules => tagRules;
/// <summary>
/// 样式表规则白名单
/// </summary>
public Dictionary<string, PolicyCssProperty> CssRules =>cssRules;
/// <summary>
/// 是否已经初始化
/// </summary>
public bool Initialized { get; private set; }
/// <summary>
/// 初始化策略
/// </summary>
/// <param name="config">XML配置文档</param>
/// <param name="name">策略名称</param>
public void Init(string config,string name)
{
if (Initialized) return;
@@ -42,7 +65,6 @@ namespace Ufangx.Xss
{
throw new ArgumentException("message", nameof(name));
}
this.name = name;
XDocument doc;
try
{
@@ -52,9 +74,11 @@ namespace Ufangx.Xss
try
{
Init(doc);
Initialized = true;
this.name = name;
}
catch (Exception x) { throw new FilterPolicyException("XSSAttacks策略文档不是一个有效的架构。", x); }
Initialized = true;
}

24
AntiXssUF/Configures.cs Normal file
View File

@@ -0,0 +1,24 @@
using Microsoft.Extensions.DependencyInjection;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
namespace Ufangx.Xss
{
internal partial class Configures
{
private readonly IServiceCollection services;
public Configures(IServiceCollection services)
{
this.services = services;
}
public void Options<TOptions>(Action<TOptions> configure,string name=null) where TOptions : class, new() {
_Options(configure,name);
}
partial void _Options<TOptions>(Action<TOptions> configure,string name) where TOptions : class, new();
}
}

View File

@@ -0,0 +1,32 @@
using Microsoft.Extensions.DependencyInjection;
using System;
using System.Collections.Generic;
namespace Ufangx.Xss
{
internal partial class Configures
{
static Dictionary<Type, List<object>> _options = new Dictionary<Type, List<object>>();
partial void _Options<TOptions>(Action<TOptions> configure, string name) where TOptions : class, new()
{
var key = typeof(TOptions);
if (!_options.ContainsKey(key)) _options.Add(key, new List<object>());
if (configure != null) { _options[key].Add(configure); }
services.AddTransient(provider => {
var opt = new TOptions();
var list = _options[key];
foreach (var item in list)
{
if (item is Action<TOptions> conf) {
conf(opt);
}
}
return opt;
});
}
}
}

View File

@@ -0,0 +1,19 @@
using Microsoft.Extensions.DependencyInjection;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
namespace Ufangx.Xss
{
internal partial class Configures
{
partial void _Options<TOptions>(Action<TOptions> configure, string name) where TOptions : class, new()
{
if (configure == null) return;
if (string.IsNullOrEmpty(name)) services.Configure(configure);
else services.Configure(name, configure);
}
}
}

View File

@@ -12,18 +12,36 @@ using System.Threading.Tasks;
namespace Ufangx.Xss
{
/// <summary>
/// 样式过滤器
/// </summary>
public class CssFilter : ICssFilter
{
#region
/// <summary>
/// 创建样式过滤器
/// </summary>
/// <param name="policy"></param>
public CssFilter(IFilterPolicy policy)
{
Policy = policy ?? throw new ArgumentNullException(nameof(policy));
EmbedStyleSheets = policy.Directive<bool>("embedStyleSheets");
}
#endregion
/// <summary>
/// 过滤策略
/// </summary>
protected virtual IFilterPolicy Policy { get; }
/// <summary>
/// 验证规则
/// </summary>
protected virtual bool EmbedStyleSheets { get; set; }
/// <summary>
/// 验证规则
/// </summary>
/// <param name="attr"></param>
/// <returns></returns>
protected virtual bool Validate(ICssProperty attr)
{
if (attr == null) return false;
@@ -32,7 +50,11 @@ namespace Ufangx.Xss
return Policy.ValidateAttribute(property, attr.Value) || (property.Shorthands?.Any(shorthandPropertyName => Policy.ValidateAttribute(Policy.CssProperty(shorthandPropertyName), attr.Value)) ?? false);
}
/// <summary>
/// 过滤规则
/// </summary>
/// <param name="cssStyleSheet"></param>
/// <returns></returns>
protected virtual string Filters(ICssStyleSheet cssStyleSheet)
{
if (cssStyleSheet == null || cssStyleSheet.Rules.Length == 0) return string.Empty;
@@ -46,7 +68,11 @@ namespace Ufangx.Xss
}
return cssStyleSheet.ToCss();
}
/// <summary>
/// 验证规则
/// </summary>
/// <param name="rule"></param>
/// <returns></returns>
protected virtual bool Validate(ICssRule rule)
{
if (rule is ICssStyleRule styleRule)
@@ -77,6 +103,11 @@ namespace Ufangx.Xss
return false;
}
/// <summary>
/// 验证规则
/// </summary>
/// <param name="styles"></param>
/// <returns></returns>
protected virtual bool Validate(ICssStyleDeclaration styles)
{
if (styles == null) return false;
@@ -92,6 +123,11 @@ namespace Ufangx.Xss
return styles.Length > 0;
}
/// <summary>
/// 过滤样式
/// </summary>
/// <param name="code"></param>
/// <returns></returns>
public string Filters(string code)
{
if (string.IsNullOrWhiteSpace(code)) return string.Empty;
@@ -114,7 +150,7 @@ namespace Ufangx.Xss
}
catch (Exception ex)
{
throw;
throw ex;
}
return Filters(styleSheet);

View File

@@ -7,11 +7,15 @@ using System.Linq;
namespace Ufangx.Xss
{
/// <summary>
/// 过滤策略扩展方法
/// </summary>
public static class ExtensionMethods
{
/// <summary>
/// 验证属性的值是否有效
/// </summary>
/// <param name="policy"></param>
/// <param name="attr"></param>
/// <param name="value"></param>
/// <returns></returns>
@@ -34,7 +38,7 @@ namespace Ufangx.Xss
if (attr.AllowedRegExp != null)
{
///验证是否符合指定的正则表达式
//验证是否符合指定的正则表达式
foreach (var regx in attr.AllowedRegExp)
{
if (string.IsNullOrWhiteSpace(regx.Name) && string.IsNullOrWhiteSpace(regx.Value)) continue;
@@ -59,30 +63,74 @@ namespace Ufangx.Xss
static T Get<T>(IDictionary<string, T> collection, string key)where T:class {
return key == null || collection == null || !collection.ContainsKey(key = key.ToLower()) ? null : collection[key];
}
/// <summary>
/// 获取标签策略
/// </summary>
/// <param name="policy"></param>
/// <param name="tagName"></param>
/// <returns></returns>
public static PolicyHtmlTag Tag(this IFilterPolicy policy, string tagName)
{
return Get(policy?.TagRules, tagName);
}
/// <summary>
/// 获取样式属性过滤策略
/// </summary>
/// <param name="policy"></param>
/// <param name="name"></param>
/// <returns></returns>
public static PolicyCssProperty CssProperty(this IFilterPolicy policy, string name)
{
return Get(policy?.CssRules, name);
}
/// <summary>
/// 获取通用属性过滤策略
/// </summary>
/// <param name="policy"></param>
/// <param name="name"></param>
/// <returns></returns>
public static PolicyHtmlAttribute CommonHtmlAttribute(this IFilterPolicy policy, string name)
{
return Get(policy?.CommonAttributes, name);
}
/// <summary>
/// 获取全局属性过滤策略
/// </summary>
/// <param name="policy"></param>
/// <param name="name"></param>
/// <returns></returns>
public static PolicyHtmlAttribute GlobalHtmlAttribute(this IFilterPolicy policy, string name)
{
return Get(policy?.GlobalAttributes, name);
}
/// <summary>
/// 获取通用正则表达式
/// </summary>
/// <param name="policy"></param>
/// <param name="name"></param>
/// <returns></returns>
public static string RegularExpression(this IFilterPolicy policy, string name)
{
return Get(policy?.CommonRegularExpressions, name);
}
/// <summary>
///
/// </summary>
/// <param name="policy"></param>
/// <param name="name"></param>
/// <returns></returns>
public static string Directive(this IFilterPolicy policy, string name)
{
return Get(policy?.Directives, name);
}
/// <summary>
///
/// </summary>
/// <typeparam name="T"></typeparam>
/// <param name="policy"></param>
/// <param name="name"></param>
/// <param name="default"></param>
/// <returns></returns>
public static T Directive<T>(this IFilterPolicy policy, string name,T @default=default(T)) where T : struct
{
string v = Directive(policy,name);
@@ -109,6 +157,13 @@ namespace Ufangx.Xss
}
return @default;
}
/// <summary>
/// 获取属性过滤策略
/// </summary>
/// <param name="policy"></param>
/// <param name="name"></param>
/// <param name="tag"></param>
/// <returns></returns>
public static PolicyHtmlAttribute AllowedAttribute(this IFilterPolicy policy,string name, PolicyHtmlTag tag)
{
var tagAttr = tag.AllowedAttributes.ContainsKey(name) ? tag.AllowedAttributes[name] : null;

View File

@@ -5,14 +5,29 @@ using System.Threading.Tasks;
namespace Ufangx.Xss
{
/// <summary>
/// 策略生成器
/// </summary>
public class FilterPolicyBuilder
{
/// <summary>
/// 创建策略生成器
/// </summary>
/// <param name="Name"></param>
public FilterPolicyBuilder(string Name) {
this.Name = Name;
}
/// <summary>
/// 策略名称
/// </summary>
public string Name { get; }
/// <summary>
/// 策略类型
/// </summary>
public Type PolicyType { get; set; }
/// <summary>
/// 获取策略配置方法
/// </summary>
public Func<Task<string>> GetConfig { get; set; }
}

View File

@@ -6,11 +6,31 @@ using System.Text;
namespace Ufangx.Xss
{
/// <summary>
/// 策略异常
/// </summary>
public class FilterPolicyException:Exception
{
/// <summary>
/// 创建策略异常
/// </summary>
public FilterPolicyException() { }
/// <summary>
///
/// </summary>
/// <param name="message"></param>
public FilterPolicyException(string message) : base(message) { }
/// <summary>
///
/// </summary>
/// <param name="message"></param>
/// <param name="innerException"></param>
public FilterPolicyException(string message, Exception innerException) : base(message, innerException) { }
/// <summary>
///
/// </summary>
/// <param name="info"></param>
/// <param name="context"></param>
public FilterPolicyException(SerializationInfo info, StreamingContext context) : base(info, context) { }
}
}

View File

@@ -1,5 +1,4 @@
using Microsoft.Extensions.Caching.Distributed;
using Microsoft.Extensions.DependencyInjection;
using Microsoft.Extensions.DependencyInjection;
using System;
using System.Collections.Generic;
using System.Text;
@@ -7,14 +6,25 @@ using System.Threading.Tasks;
namespace Ufangx.Xss
{
/// <summary>
/// 过滤策略工厂
/// </summary>
public class FilterPolicyFactory : IFilterPolicyFactory
{
private readonly IFilterPolicyProvider provider;
/// <summary>
/// 创建过滤策略工厂
/// </summary>
/// <param name="provider"></param>
public FilterPolicyFactory(IFilterPolicyProvider provider)
{
this.provider = provider;
}
/// <summary>
/// 创建过滤策略
/// </summary>
/// <param name="name"></param>
/// <returns></returns>
public async Task<IFilterPolicy> CreatePolicy(string name = null)
{
FilterPolicyBuilder builder;
@@ -41,14 +51,32 @@ namespace Ufangx.Xss
}
return policy;
}
/// <summary>
/// 创建html过滤器
/// </summary>
/// <param name="policyName"></param>
/// <returns></returns>
public async Task<IHtmlFilter> CreateHtmlFilter(string policyName = null)
=> await CreateHtmlFilter(await CreatePolicy(policyName));
/// <summary>
/// 创建Css过滤器
/// </summary>
/// <param name="policyName"></param>
/// <returns></returns>
public async Task<ICssFilter> CreateCssFilter(string policyName=null)
=> await CreateCssFilter(await CreatePolicy(policyName));
/// <summary>
/// 创建html过滤器
/// </summary>
/// <param name="policy"></param>
/// <returns></returns>
public Task<IHtmlFilter> CreateHtmlFilter(IFilterPolicy policy)
=> Task.FromResult<IHtmlFilter>(new HtmlFilter(policy));
/// <summary>
/// 创建Css过滤器
/// </summary>
/// <param name="policy"></param>
/// <returns></returns>
public Task<ICssFilter> CreateCssFilter(IFilterPolicy policy)
=> Task.FromResult<ICssFilter>(new CssFilter(policy));
}

View File

@@ -5,12 +5,25 @@ using System.Threading.Tasks;
namespace Ufangx.Xss
{
/// <summary>
///
/// </summary>
public class FilterPolicyOptions
{
private readonly IList<FilterPolicyBuilder> _schemes = new List<FilterPolicyBuilder>();
/// <summary>
/// 策略集合
/// </summary>
public IEnumerable<FilterPolicyBuilder> Schemes => _schemes;
/// <summary>
/// 策略字典集合
/// </summary>
public IDictionary<string, FilterPolicyBuilder> SchemeMap { get; } = new Dictionary<string, FilterPolicyBuilder>(StringComparer.Ordinal);
/// <summary>
/// 添加策略
/// </summary>
/// <param name="name"></param>
/// <param name="configureBuilder"></param>
public void AddScheme(string name, Action<FilterPolicyBuilder> configureBuilder)
{
if (name == null)
@@ -30,18 +43,33 @@ namespace Ufangx.Xss
_schemes.Add(builder);
SchemeMap[name] = builder;
}
/// <summary>
/// 添加策略
/// </summary>
/// <typeparam name="TPolicyType"></typeparam>
/// <param name="name"></param>
/// <param name="config"></param>
public void AddScheme<TPolicyType>(string name, string config) where TPolicyType : IFilterPolicy
=> AddScheme(name, b =>
{
b.GetConfig = () => Task.FromResult(config);
b.PolicyType = typeof(TPolicyType);
});
/// <summary>
/// 添加策略
/// </summary>
/// <typeparam name="TPolicyType"></typeparam>
/// <param name="name"></param>
/// <param name="config"></param>
public void AddScheme<TPolicyType>(string name, Func<Task<string>> config) where TPolicyType : IFilterPolicy
=> AddScheme(name, b =>
{
b.GetConfig = config;
b.PolicyType = typeof(TPolicyType);
});
/// <summary>
/// 默认过滤策略
/// </summary>
public string DefaultSchemeName { get; set; }
}
}

View File

@@ -1,5 +1,4 @@
using Microsoft.Extensions.Options;
using System;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
@@ -7,27 +6,11 @@ using System.Threading.Tasks;
namespace Ufangx.Xss
{
public class FilterPolicyProvider : IFilterPolicyProvider
/// <summary>
/// 策略管理提供者
/// </summary>
public partial class FilterPolicyProvider : IFilterPolicyProvider
{
public FilterPolicyProvider(IOptions<FilterPolicyOptions> options)
: this(options, new Dictionary<string, FilterPolicyBuilder>(StringComparer.OrdinalIgnoreCase))
{
}
protected FilterPolicyProvider(IOptions<FilterPolicyOptions> options, IDictionary<string, FilterPolicyBuilder> schemes)
{
_options = options.Value;
_schemes = schemes ?? throw new ArgumentNullException(nameof(schemes));
_requestHandlers = new List<FilterPolicyBuilder>();
foreach (var builder in _options.Schemes)
{
///var scheme = builder.Build();
AddScheme(builder);
}
}
private readonly FilterPolicyOptions _options;
private readonly IDictionary<string, FilterPolicyBuilder> _schemes;
@@ -35,7 +18,11 @@ namespace Ufangx.Xss
private readonly object _lock = new object();
private FilterPolicyBuilder[] _requestHandlersCopy;
private FilterPolicyBuilder[] _schemesCopy;
/// <summary>
/// 尝试添加策略方案
/// </summary>
/// <param name="scheme"></param>
/// <returns></returns>
public virtual bool TryAddScheme(FilterPolicyBuilder scheme)
{
if (_schemes.ContainsKey(scheme.Name))
@@ -58,6 +45,10 @@ namespace Ufangx.Xss
return true;
}
}
/// <summary>
/// 添加策略方案
/// </summary>
/// <param name="scheme"></param>
public virtual void AddScheme(FilterPolicyBuilder scheme)
{
if (_schemes.ContainsKey(scheme.Name))
@@ -72,6 +63,10 @@ namespace Ufangx.Xss
}
}
}
/// <summary>
/// 移除策略方案
/// </summary>
/// <param name="name"></param>
public virtual void RemoveScheme(string name)
{
if (!_schemes.ContainsKey(name))
@@ -92,12 +87,25 @@ namespace Ufangx.Xss
}
}
}
/// <summary>
/// 获取策略
/// </summary>
/// <param name="name"></param>
/// <returns></returns>
public virtual Task<FilterPolicyBuilder> GetSchemeAsync(string name)
=> Task.FromResult(_schemes.ContainsKey(name) ? _schemes[name] : null);
/// <summary>
/// 获取默认策略
/// </summary>
/// <returns></returns>
public virtual Task<FilterPolicyBuilder> GetDefaultSchemeAsync()
=> _options.DefaultSchemeName != null
? GetSchemeAsync(_options.DefaultSchemeName)
: Task.FromResult<FilterPolicyBuilder>(null);
/// <summary>
/// 返回所有策略方案
/// </summary>
/// <returns></returns>
public virtual async Task<IEnumerable<FilterPolicyBuilder>> GetSchemesAsync()
=> await Task.FromResult(_requestHandlersCopy);
}

View File

@@ -0,0 +1,38 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
namespace Ufangx.Xss
{
public partial class FilterPolicyProvider : IFilterPolicyProvider
{
/// <summary>
/// 创建策略方案提供者
/// </summary>
/// <param name="options"></param>
public FilterPolicyProvider(FilterPolicyOptions options)
: this(options, new Dictionary<string, FilterPolicyBuilder>(StringComparer.OrdinalIgnoreCase))
{
}
/// <summary>
/// 创建策略方案提供者
/// </summary>
/// <param name="options"></param>
/// <param name="schemes"></param>
protected FilterPolicyProvider(FilterPolicyOptions options, IDictionary<string, FilterPolicyBuilder> schemes)
{
_options = options;
_schemes = schemes ?? throw new ArgumentNullException(nameof(schemes));
_requestHandlers = new List<FilterPolicyBuilder>();
foreach (var builder in _options.Schemes)
{
AddScheme(builder);
}
}
}
}

View File

@@ -0,0 +1,39 @@
using Microsoft.Extensions.Options;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
namespace Ufangx.Xss
{
public partial class FilterPolicyProvider : IFilterPolicyProvider
{
/// <summary>
/// 创建策略方案提供者
/// </summary>
/// <param name="options"></param>
public FilterPolicyProvider(IOptions<FilterPolicyOptions> options)
: this(options, new Dictionary<string, FilterPolicyBuilder>(StringComparer.OrdinalIgnoreCase))
{
}
/// <summary>
/// 创建策略方案提供者
/// </summary>
/// <param name="options"></param>
/// <param name="schemes"></param>
protected FilterPolicyProvider(IOptions<FilterPolicyOptions> options, IDictionary<string, FilterPolicyBuilder> schemes)
{
_options = options.Value;
_schemes = schemes ?? throw new ArgumentNullException(nameof(schemes));
_requestHandlers = new List<FilterPolicyBuilder>();
foreach (var builder in _options.Schemes)
{
AddScheme(builder);
}
}
}
}

View File

@@ -4,15 +4,32 @@ using System.Text;
namespace Ufangx.Xss
{
/// <summary>
/// 过滤正则表达式
/// </summary>
[Serializable]
public class FilterRegExp
{
/// <summary>
/// 表达式名称
/// </summary>
public string Name { get; set; }
/// <summary>
/// 正则表达式
/// </summary>
public string Value { get; set; }
}
/// <summary>
/// 正则表达式比较器
/// </summary>
public class FilterRegExpComparer : IEqualityComparer<FilterRegExp>
{
/// <summary>
///
/// </summary>
/// <param name="x"></param>
/// <param name="y"></param>
/// <returns></returns>
public bool Equals(FilterRegExp x, FilterRegExp y)
{
if (ReferenceEquals(x, y)||x==null&&y==null) return true;
@@ -20,7 +37,11 @@ namespace Ufangx.Xss
return
string.Equals(x.Name, y.Name, StringComparison.OrdinalIgnoreCase) && string.Equals(x.Value, y.Value, StringComparison.OrdinalIgnoreCase);
}
/// <summary>
///
/// </summary>
/// <param name="obj"></param>
/// <returns></returns>
public int GetHashCode(FilterRegExp obj)
{
return (obj.Name ?? string.Empty).GetHashCode() ^ (obj.Value ?? string.Empty).GetHashCode();

View File

@@ -11,21 +11,39 @@ using System.Xml;
using Microsoft.Extensions.DependencyInjection;
namespace Ufangx.Xss
{
/// <summary>
/// html过滤器
/// </summary>
public class HtmlFilter : IHtmlFilter
{
#region
/// <summary>
/// 创建html过滤器
/// </summary>
/// <param name="policy"></param>
/// <param name="cssFilter"></param>
public HtmlFilter(IFilterPolicy policy, ICssFilter cssFilter)
{
Policy = policy ?? throw new ArgumentNullException(nameof(policy));
_cssFilter = cssFilter;
}
/// <summary>
/// 创建html过滤器
/// </summary>
/// <param name="policy"></param>
public HtmlFilter(IFilterPolicy policy):this(policy,null)
{ }
#endregion
#region
/// <summary>
/// 当前过滤策略
/// </summary>
protected virtual IFilterPolicy Policy { get; }
ICssFilter _cssFilter;
/// <summary>
/// 当前css过滤器
/// </summary>
protected virtual ICssFilter CssFilter {
get {
if (_cssFilter == null) {
@@ -38,7 +56,11 @@ namespace Ufangx.Xss
#endregion
#region
/// <summary>
/// 过滤html
/// </summary>
/// <param name="html"></param>
/// <returns></returns>
public virtual string Filters(string html)
{
if (html == null || html.Length == 0)
@@ -111,26 +133,31 @@ namespace Ufangx.Xss
switch (actoin)
{
case PolicyHtmlTagAction.Filter:
///删除当前节点,但保留其有效的子节点
//删除当前节点,但保留其有效的子节点
PromoteChildren(node);
return;
case PolicyHtmlTagAction.Validate:
///过滤当前元素的属性与及子节点
//过滤当前元素的属性与及子节点
ValidateAction(node, nodeName, tag);
return;
case PolicyHtmlTagAction.Truncate:
///删除当前节点的所有属性以及子节点,但保留文本和备注节点。
//删除当前节点的所有属性以及子节点,但保留文本和备注节点。
TruncateAction(node);
return;
default:
///将当前节点从父节点中删除。
//将当前节点从父节点中删除。
var parentNode = node.Parent;
parentNode.RemoveChild(node);
break;
}
}
/// <summary>
/// 验证html标签
/// </summary>
/// <param name="node"></param>
/// <param name="tagName"></param>
/// <param name="tag"></param>
protected virtual void ValidateAction(IElement node, string tagName, PolicyHtmlTag tag)
{
var parentNode = node.Parent;
@@ -169,6 +196,10 @@ namespace Ufangx.Xss
try
{
attribute.Value = CssFilter.Filters(_value);
if (string.IsNullOrWhiteSpace(attribute.Value)) {
node.RemoveAttribute(name);
currentAttributeIndex--;
}
}
catch
{
@@ -178,7 +209,7 @@ namespace Ufangx.Xss
continue;
}
#endregion
///如果未能通过验证,将执行指定的操作
//如果未能通过验证,将执行指定的操作
if (!Policy.ValidateAttribute(attr, _value))
{
switch (attr.OnInvalid)
@@ -188,7 +219,7 @@ namespace Ufangx.Xss
parentNode.RemoveChild(node);
return;
case PolicyHtmlAttributeOnInvalid.FilterTag:
///删除当前节点,但保留其有效的子节点
//删除当前节点,但保留其有效的子节点
PromoteChildren(node);
return;
default:
@@ -201,7 +232,7 @@ namespace Ufangx.Xss
}
}
#endregion
///过滤当前元素的子节点
//过滤当前元素的子节点
FiltersTags(node.ChildNodes);
}
@@ -258,11 +289,11 @@ namespace Ufangx.Xss
/// <param name="node"></param>
protected virtual void PromoteChildren(IElement node)
{
///过滤子节点
//过滤子节点
FiltersTags(node.ChildNodes);
var nodeList = node.ChildNodes;
var parent = node.Parent;
///将它的所有子节点往上移到父节点的前面
//将它的所有子节点往上移到父节点的前面
while (nodeList.Length > 0)
{
var removeNode = node.RemoveChild(nodeList[0]);

View File

@@ -1,7 +1,15 @@
namespace Ufangx.Xss
{
/// <summary>
/// Css过滤器
/// </summary>
public interface ICssFilter
{
/// <summary>
/// 过滤css
/// </summary>
/// <param name="code"></param>
/// <returns></returns>
string Filters(string code);
}
}

View File

@@ -2,17 +2,48 @@
namespace Ufangx.Xss
{
/// <summary>
/// 过滤策略
/// </summary>
public interface IFilterPolicy
{
/// <summary>
/// 策略名称
/// </summary>
string Name { get; }
/// <summary>
/// 初始化策略
/// </summary>
/// <param name="config">配置文档</param>
/// <param name="name">策略名称</param>
void Init(string config, string name);
/// <summary>
/// 是否已经初始化
/// </summary>
bool Initialized { get; }
/// <summary>
/// 公用正则表达式
/// </summary>
Dictionary<string, string> CommonRegularExpressions { get; }
/// <summary>
/// 控制设置
/// </summary>
Dictionary<string, string> Directives { get; }
/// <summary>
/// 公用属性
/// </summary>
Dictionary<string, PolicyHtmlAttribute> CommonAttributes { get; }
/// <summary>
/// 全局属性白名单
/// </summary>
Dictionary<string, PolicyHtmlAttribute> GlobalAttributes { get; }
/// <summary>
/// html标签白名单
/// </summary>
Dictionary<string, PolicyHtmlTag> TagRules { get; }
/// <summary>
/// 样式表规则白名单
/// </summary>
Dictionary<string, PolicyCssProperty> CssRules { get; }
}
}

View File

@@ -2,12 +2,40 @@
namespace Ufangx.Xss
{
/// <summary>
/// 过来策略工厂
/// </summary>
public interface IFilterPolicyFactory
{
/// <summary>
/// 创建过滤策略
/// </summary>
/// <param name="name"></param>
/// <returns></returns>
Task<IFilterPolicy> CreatePolicy(string name = null);
/// <summary>
/// 创建html过滤器
/// </summary>
/// <param name="policyName"></param>
/// <returns></returns>
Task<IHtmlFilter> CreateHtmlFilter(string policyName = null);
/// <summary>
/// 创建css过滤器
/// </summary>
/// <param name="policyName"></param>
/// <returns></returns>
Task<ICssFilter> CreateCssFilter(string policyName = null);
/// <summary>
/// 创建html过滤器
/// </summary>
/// <param name="policy"></param>
/// <returns></returns>
Task<IHtmlFilter> CreateHtmlFilter(IFilterPolicy policy);
/// <summary>
/// 创建css过滤器
/// </summary>
/// <param name="policy"></param>
/// <returns></returns>
Task<ICssFilter> CreateCssFilter(IFilterPolicy policy);
}
}

View File

@@ -3,13 +3,42 @@ using System.Threading.Tasks;
namespace Ufangx.Xss
{
/// <summary>
/// 策略提供者接口
/// </summary>
public interface IFilterPolicyProvider
{
/// <summary>
/// 添加策略方案
/// </summary>
/// <param name="scheme"></param>
void AddScheme(FilterPolicyBuilder scheme);
/// <summary>
/// 获取默认策略方案
/// </summary>
/// <returns></returns>
Task<FilterPolicyBuilder> GetDefaultSchemeAsync();
/// <summary>
/// 获取策略方案
/// </summary>
/// <param name="name"></param>
/// <returns></returns>
Task<FilterPolicyBuilder> GetSchemeAsync(string name);
/// <summary>
/// 返回所有策略方案
/// </summary>
/// <returns></returns>
Task<IEnumerable<FilterPolicyBuilder>> GetSchemesAsync();
/// <summary>
/// 移除策略方案
/// </summary>
/// <param name="name"></param>
void RemoveScheme(string name);
/// <summary>
/// 尝试添加策略
/// </summary>
/// <param name="scheme"></param>
/// <returns></returns>
bool TryAddScheme(FilterPolicyBuilder scheme);
}
}

View File

@@ -1,7 +1,15 @@
namespace Ufangx.Xss
{
/// <summary>
/// html过滤器
/// </summary>
public interface IHtmlFilter
{
/// <summary>
/// 过滤html
/// </summary>
/// <param name="html"></param>
/// <returns></returns>
string Filters(string html);
}
}

View File

@@ -0,0 +1,22 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.Web;
namespace Ufangx.Xss
{
/// <summary>
///
/// </summary>
public interface IXssSchemeName
{
/// <summary>
///
/// </summary>
/// <param name="httpContext"></param>
/// <returns></returns>
Task<string> GetSchemeName(HttpContextBase httpContext);
}
}

View File

@@ -3,8 +3,16 @@ using System.Threading.Tasks;
namespace Ufangx.Xss
{
/// <summary>
///
/// </summary>
public interface IXssSchemeName
{
/// <summary>
///
/// </summary>
/// <param name="httpContext"></param>
/// <returns></returns>
Task<string> GetSchemeName(HttpContext httpContext);
}
}

View File

@@ -1,5 +1,4 @@
using Microsoft.Extensions.Configuration;
using System;
using System;
using System.Linq;
using System.Collections.Generic;
using System.IO;
@@ -7,7 +6,10 @@ using System.Text;
namespace Ufangx.Xss
{
public class JsonFilterPolicy : IFilterPolicy
/// <summary>
/// json格式过滤策略配置
/// </summary>
public partial class JsonFilterPolicy : IFilterPolicy
{
Dictionary<string, string> commonRegularExpressions, directives;
Dictionary<string, PolicyHtmlAttribute> commonAttributes, globalAttributes;
@@ -15,62 +17,41 @@ namespace Ufangx.Xss
Dictionary<string, PolicyCssProperty> cssRules;
private string name;
private bool initialized;
/// <summary>
/// 策略名称
/// </summary>
public string Name => name;
/// <summary>
/// 是否已经初始化
/// </summary>
public bool Initialized => initialized;
/// <summary>
/// 公用正则表达式
/// </summary>
public Dictionary<string, string> CommonRegularExpressions => commonRegularExpressions;
/// <summary>
/// 控制设置
/// </summary>
public Dictionary<string, string> Directives => directives;
/// <summary>
/// 公用属性
/// </summary>
public Dictionary<string, PolicyHtmlAttribute> CommonAttributes => commonAttributes;
/// <summary>
/// 全局属性白名单
/// </summary>
public Dictionary<string, PolicyHtmlAttribute> GlobalAttributes => globalAttributes;
/// <summary>
/// html标签白名单
/// </summary>
public Dictionary<string, PolicyHtmlTag> TagRules => tagRules;
/// <summary>
/// 样式表规则白名单
/// </summary>
public Dictionary<string, PolicyCssProperty> CssRules => cssRules;
Dictionary<string, PolicyHtmlAttribute> GetPolicyHtmlAttributes(IEnumerable<IConfigurationSection> sections)
=> sections.Select(e => new PolicyHtmlAttribute(e.GetValue<string>("Name"))
{
AllowedRegExp = e.GetSection("AllowedRegExp").Get<FilterRegExp[]>(),
AllowedValues = e.GetSection("AllowedValues").Get<string[]>(),
Description = e.GetValue<string>("Description"),
OnInvalid = e.GetValue<PolicyHtmlAttributeOnInvalid>("OnInvalid")
}).ToDictionary(e => e.Name, e => e, StringComparer.OrdinalIgnoreCase);
public void Init(IConfigurationRoot configuration, string name) {
if (Initialized) return;
if (configuration is null)
{
throw new ArgumentNullException(nameof(configuration));
}
initialized = true;
this.name = name;
commonRegularExpressions = new Dictionary<string, string>(configuration.GetSection("CommonRegularExpressions").Get<Dictionary<string, string>>(),StringComparer.OrdinalIgnoreCase);
directives =new Dictionary<string, string>(configuration.GetSection("Directives").Get< Dictionary<string, string>>(), StringComparer.OrdinalIgnoreCase);
commonAttributes = GetPolicyHtmlAttributes(configuration.GetSection("CommonAttributes").GetChildren());
globalAttributes = GetPolicyHtmlAttributes(configuration.GetSection("GlobalAttributes").GetChildren());
cssRules = configuration.GetSection("CssRules").GetChildren().Select(e =>
new PolicyCssProperty(e.GetValue<string>("Name"))
{
AllowedRegExp = e.GetSection("AllowedRegExp").Get<FilterRegExp[]>(),
AllowedValues = e.GetSection("AllowedValues").Get<string[]>(),
Description = e.GetValue<string>("Description"),
Shorthands = e.GetSection("Shorthands").Get<string[]>()
}).ToDictionary(e => e.Name, e => e, StringComparer.OrdinalIgnoreCase);
tagRules = configuration.GetSection("TagRules").GetChildren().Select(e =>
new PolicyHtmlTag(GetPolicyHtmlAttributes(e.GetSection("AllowedAttributes").GetChildren()))
{
Name = e.GetValue<string>("Name"),
Action = e.GetValue<PolicyHtmlTagAction>("Action")
}
).ToDictionary(e => e.Name, e => e, StringComparer.OrdinalIgnoreCase);
}
public void Init(string config, string name)
=> Init(Initialized?null:new MemoryStream(Encoding.UTF8.GetBytes(config), false), name);
public void Init(Stream config, string name)
=> Init(Initialized ? null : new ConfigurationBuilder().AddJsonStream(config ?? throw new ArgumentNullException(nameof(config))).Build(), null);

View File

@@ -0,0 +1,81 @@
using Newtonsoft.Json;
using Newtonsoft.Json.Linq;
using System;
using System.Linq;
using System.Collections.Generic;
using System.IO;
using System.Text;
namespace Ufangx.Xss
{
/// <summary>
/// json格式过滤策略配置
/// </summary>
public partial class JsonFilterPolicy : IFilterPolicy
{
Dictionary<string, PolicyHtmlAttribute> GetPolicyHtmlAttributes(IEnumerable<JToken> sections)
=> sections?.Select(e => new PolicyHtmlAttribute(e.Value<string>("Name"))
{
AllowedRegExp = e.SelectToken("AllowedRegExp")?.ToObject<FilterRegExp[]>(),
AllowedValues = e.SelectToken("AllowedValues")?.ToObject<string[]>(),
Description = e.Value<string>("Description"),
OnInvalid = GetValue<PolicyHtmlAttributeOnInvalid>(e.SelectToken("OnInvalid"))
}).ToDictionary(e => e.Name, e => e, StringComparer.OrdinalIgnoreCase);
TEnum GetValue<TEnum>(JToken token,TEnum @default=default(TEnum)) where TEnum : struct
=> token is JValue jValue && Enum.TryParse(jValue.ToString(), true, out TEnum value) ? value : @default;
/// <summary>
/// 初始化策略
/// </summary>
/// <param name="configuration">配置文档</param>
/// <param name="name">策略名称</param>
public void Init(JToken configuration, string name) {
if (Initialized) return;
if (configuration is null)
{
throw new ArgumentNullException(nameof(configuration));
}
commonRegularExpressions = new Dictionary<string, string>(configuration.SelectToken("CommonRegularExpressions").ToObject<Dictionary<string, string>>(),StringComparer.OrdinalIgnoreCase);
directives =new Dictionary<string, string>(configuration.SelectToken("Directives").ToObject<Dictionary<string, string>>(), StringComparer.OrdinalIgnoreCase);
commonAttributes = GetPolicyHtmlAttributes(configuration.SelectToken("CommonAttributes").Children());
globalAttributes = GetPolicyHtmlAttributes(configuration.SelectToken("GlobalAttributes").Children());
cssRules = configuration.SelectToken("CssRules").Children().Select(e =>
new PolicyCssProperty(e.Value<string>("Name"))
{
AllowedRegExp = e.SelectToken("AllowedRegExp")?.ToObject<FilterRegExp[]>(),
AllowedValues = e.SelectToken("AllowedValues")?.ToObject<string[]>(),
Description = e.Value<string>("Description"),
Shorthands = e.SelectToken("Shorthands")?.ToObject<string[]>()
}).ToDictionary(e => e.Name, e => e, StringComparer.OrdinalIgnoreCase);
tagRules = configuration.SelectToken("TagRules").Children().Select(e =>
new PolicyHtmlTag(GetPolicyHtmlAttributes(e.SelectToken("AllowedAttributes")?.Children()??Enumerable.Empty<JToken>()))
{
Name = e.Value<string>("Name"),
Action =GetValue<PolicyHtmlTagAction>(e.SelectToken("Action"))
}
).ToDictionary(e => e.Name, e => e, StringComparer.OrdinalIgnoreCase);
initialized = true;
this.name = name;
}
/// <summary>
/// 初始化策略
/// </summary>
/// <param name="config">json配置文档</param>
/// <param name="name">策略名称</param>
public void Init(string config, string name)
=> Init(Initialized ? null : JsonConvert.DeserializeObject(config) as JToken, name);
/// <summary>
/// 初始化策略
/// </summary>
/// <param name="config">json配置文档</param>
/// <param name="name">策略名称</param>
public void Init(Stream config, string name)
=> Init(Initialized ? null : JsonConvert.DeserializeObject(new StreamReader(config).ReadToEnd()) as JToken, null);
}
}

View File

@@ -0,0 +1,78 @@
using Microsoft.Extensions.Configuration;
using System;
using System.Linq;
using System.Collections.Generic;
using System.IO;
using System.Text;
namespace Ufangx.Xss
{
/// <summary>
/// json格式过滤策略配置
/// </summary>
public partial class JsonFilterPolicy : IFilterPolicy
{
Dictionary<string, PolicyHtmlAttribute> GetPolicyHtmlAttributes(IEnumerable<IConfigurationSection> sections)
=> sections.Select(e => new PolicyHtmlAttribute(e.GetValue<string>("Name"))
{
AllowedRegExp = e.GetSection("AllowedRegExp").Get<FilterRegExp[]>(),
AllowedValues = e.GetSection("AllowedValues").Get<string[]>(),
Description = e.GetValue<string>("Description"),
OnInvalid = e.GetValue<PolicyHtmlAttributeOnInvalid>("OnInvalid")
}).ToDictionary(e => e.Name, e => e, StringComparer.OrdinalIgnoreCase);
/// <summary>
/// 初始化策略
/// </summary>
/// <param name="configuration">配置文档</param>
/// <param name="name">策略名称</param>
public void Init(IConfigurationRoot configuration, string name) {
if (Initialized) return;
if (configuration is null)
{
throw new ArgumentNullException(nameof(configuration));
}
commonRegularExpressions = new Dictionary<string, string>(configuration.GetSection("CommonRegularExpressions").Get<Dictionary<string, string>>(),StringComparer.OrdinalIgnoreCase);
directives =new Dictionary<string, string>(configuration.GetSection("Directives").Get< Dictionary<string, string>>(), StringComparer.OrdinalIgnoreCase);
commonAttributes = GetPolicyHtmlAttributes(configuration.GetSection("CommonAttributes").GetChildren());
globalAttributes = GetPolicyHtmlAttributes(configuration.GetSection("GlobalAttributes").GetChildren());
cssRules = configuration.GetSection("CssRules").GetChildren().Select(e =>
new PolicyCssProperty(e.GetValue<string>("Name"))
{
AllowedRegExp = e.GetSection("AllowedRegExp").Get<FilterRegExp[]>(),
AllowedValues = e.GetSection("AllowedValues").Get<string[]>(),
Description = e.GetValue<string>("Description"),
Shorthands = e.GetSection("Shorthands").Get<string[]>()
}).ToDictionary(e => e.Name, e => e, StringComparer.OrdinalIgnoreCase);
tagRules = configuration.GetSection("TagRules").GetChildren().Select(e =>
new PolicyHtmlTag(GetPolicyHtmlAttributes(e.GetSection("AllowedAttributes").GetChildren()))
{
Name = e.GetValue<string>("Name"),
Action = e.GetValue<PolicyHtmlTagAction>("Action")
}
).ToDictionary(e => e.Name, e => e, StringComparer.OrdinalIgnoreCase);
initialized = true;
this.name = name;
}
/// <summary>
/// 初始化策略
/// </summary>
/// <param name="config">json配置文档</param>
/// <param name="name">策略名称</param>
public void Init(string config, string name)
=> Init(Initialized?null:new MemoryStream(Encoding.UTF8.GetBytes(config), false), name);
/// <summary>
///
/// </summary>
/// <param name="config">json配置文档</param>
/// <param name="name">策略名称</param>
public void Init(Stream config, string name)
=> Init(Initialized ? null : new ConfigurationBuilder().AddJsonStream(config ?? throw new ArgumentNullException(nameof(config))).Build(), null);
}
}

View File

@@ -6,29 +6,47 @@ using System.Threading.Tasks;
namespace Ufangx.Xss
{
/// <summary>
/// 属性过滤策略
/// </summary>
[Serializable]
public class PolicyAttribute
{
/// <summary>
/// 创建属性过滤策略
/// </summary>
/// <param name="name"></param>
public PolicyAttribute(string name)
{
Name = name;
}
/// <summary>
/// 正则表达式白名单
/// </summary>
public FilterRegExp[] AllowedRegExp
{
get;
set;
}
/// <summary>
/// 属性值白名单
/// </summary>
public string[] AllowedValues
{
get;
set;
}
/// <summary>
/// 属性名称
/// </summary>
public string Name
{
get;
protected set;
}
/// <summary>
/// 策略描述
/// </summary>
public string Description
{
get;

View File

@@ -6,10 +6,20 @@ using System.Threading.Tasks;
namespace Ufangx.Xss
{
/// <summary>
/// 样式属性过滤策略
/// </summary>
[Serializable]
public class PolicyCssProperty : PolicyAttribute
{
/// <summary>
/// 创建样式属性过滤策略
/// </summary>
/// <param name="name"></param>
public PolicyCssProperty(string name) : base(name) { }
/// <summary>
/// 样式简短名称列表
/// </summary>
public string[] Shorthands { get; set; }
}
}

View File

@@ -6,14 +6,24 @@ using System.Threading.Tasks;
namespace Ufangx.Xss
{
/// <summary>
/// html标签属性过滤策略
/// </summary>
[Serializable]
public class PolicyHtmlAttribute : PolicyAttribute
{
/// <summary>
/// 创建html标签属性过滤策略
/// </summary>
/// <param name="name"></param>
public PolicyHtmlAttribute(string name)
: base(name)
{
}
/// <summary>
/// 属性值被验证失败后的处理方式,如果当前属性值是无效的,那么是移除属性,还是移除整个标签,或者过滤标签保留内容
/// </summary>
public PolicyHtmlAttributeOnInvalid OnInvalid
{
get;

View File

@@ -6,11 +6,17 @@ using System.Threading.Tasks;
namespace Ufangx.Xss
{
/// <summary>
///html标签策略
/// </summary>
[Serializable]
public class PolicyHtmlTag
{
private readonly Dictionary<string, PolicyHtmlAttribute> allowedAttributes;
/// <summary>
/// 创建html标签策略
/// </summary>
/// <param name="attributes"></param>
public PolicyHtmlTag(Dictionary<string, PolicyHtmlAttribute> attributes)
{
if (attributes != null)
@@ -18,12 +24,21 @@ namespace Ufangx.Xss
allowedAttributes = attributes;
}
}
/// <summary>
/// 标签白名单
/// </summary>
public Dictionary<string, PolicyHtmlAttribute> AllowedAttributes => allowedAttributes;
/// <summary>
/// 标签的过滤动作
/// </summary>
public PolicyHtmlTagAction Action
{
get;
set;
}
/// <summary>
/// 标签名称
/// </summary>
public string Name
{
get;

View File

@@ -6,6 +6,9 @@ using System.Threading.Tasks;
namespace Ufangx.Xss
{
/// <summary>
/// 过滤标签的动作
/// </summary>
public enum PolicyHtmlTagAction
{
/// <summary>

View File

@@ -2,20 +2,24 @@
using System.Collections.Generic;
using System.IO;
using System.Linq;
using System.Reflection;
using System.Text;
using System.Text.RegularExpressions;
using System.Web;
using Microsoft.Extensions.DependencyInjection;
namespace Ufangx.Xss
{
public class RichText
/// <summary>
/// 富文本
/// </summary>
public partial class RichText
{
#region
/// <summary>
/// 实例化一个富文本对象
/// </summary>
/// <param name="text">未被过滤的源文本</param>
/// <param name="policy">过滤的安全策略,如果不提供将启用默认的安全策略</param>
/// <param name="source">未被过滤的源文本</param>
/// <param name="htmlFilter">html过滤器</param>
public RichText(string source, IHtmlFilter htmlFilter)
{
this.source = source;
@@ -27,9 +31,14 @@ namespace Ufangx.Xss
private readonly IHtmlFilter htmlFilter;
string html;
/// <summary>
/// html源码
/// </summary>
public string Source => source;
/// <summary>
/// 输出干净的html
/// </summary>
/// <returns></returns>
public override string ToString()
{
if (html == null)
@@ -38,7 +47,23 @@ namespace Ufangx.Xss
}
return html;
}
static IFilterPolicy presupposedPolicy;
/// <summary>
/// 内置的过滤策略
/// </summary>
/// <returns></returns>
public static IFilterPolicy GetPresupposedPolicy()
{
if (presupposedPolicy == null)
{
var policy = new JsonFilterPolicy();
policy.Init(Assembly.GetExecutingAssembly().GetManifestResourceStream("Ufangx.Xss.resources.DefaultPolicy.json"), "Presupposed");
presupposedPolicy = policy;
}
return presupposedPolicy;
}
#region
/// <summary>
/// 字符串隐式转换为富文本对象
@@ -48,7 +73,7 @@ namespace Ufangx.Xss
public static implicit operator RichText(string text)
{
var factory = XssFilterBuilder.Builder?.ServiceProvider?.GetService<IFilterPolicyFactory>();
return new RichText(text, factory == null ? new HtmlFilter(XssFilterBuilder.GetPresupposedPolicy()) : factory.CreateHtmlFilter().Result);
return new RichText(text, factory == null ? new HtmlFilter(GetPresupposedPolicy()) : factory.CreateHtmlFilter().Result);
}
/// <summary>
/// 富文本对象隐式转换为字符串
@@ -102,7 +127,7 @@ namespace Ufangx.Xss
}
return new RichText(string.Concat(a.source, b.source), a.htmlFilter);
}
///这个是强制转换操作符的重载,在这里不需要了,因为有隐式转换了
//这个是强制转换操作符的重载,在这里不需要了,因为有隐式转换了
//public static explicit operator string(RichText text)
//{
// return text == null ? null : text.ToString();

View File

@@ -0,0 +1,49 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Web;
using System.Web.Mvc;
using Ufangx.Xss;
namespace Ufangx.Xss
{
/// <summary>
/// 模型绑定器
/// </summary>
public class RichTextBinder : IModelBinder
{
/// <summary>
///
/// </summary>
/// <param name="controllerContext"></param>
/// <param name="bindingContext"></param>
/// <returns></returns>
public object BindModel(ControllerContext controllerContext, ModelBindingContext bindingContext)
{
var modelName = bindingContext.ModelName;
IUnvalidatedValueProvider valueProvider = bindingContext.ValueProvider as IUnvalidatedValueProvider;
var valueProviderResult = valueProvider == null ? bindingContext.ValueProvider.GetValue(modelName) : valueProvider.GetValue(modelName,true);
if (valueProviderResult == null)
{
return null;
}
bindingContext.ModelState.SetModelValue(modelName, valueProviderResult);
string value = valueProviderResult.AttemptedValue;
if (string.IsNullOrEmpty(value))
{
return null;
}
IXssSchemeName scheme = bindingContext.ModelMetadata?.ContainerType?.GetProperty(bindingContext.ModelMetadata.PropertyName).GetCustomAttributes(true)?.Cast<IXssSchemeName>()?.FirstOrDefault() ?? null;
if (scheme == null) {
return (RichText)value;
}
RichText richText = new RichText(value, DependencyResolver.Current.GetService<IFilterPolicyFactory>().CreateHtmlFilter(scheme.GetSchemeName(controllerContext.HttpContext).Result).Result);
return richText;
}
}
}

View File

@@ -9,10 +9,17 @@ using Microsoft.Extensions.DependencyInjection;
namespace Ufangx.Xss
{
/// <summary>
///
/// </summary>
public class RichTextBinder : IModelBinder
{
/// <summary>
///
/// </summary>
/// <param name="bindingContext"></param>
/// <returns></returns>
public async Task BindModelAsync(ModelBindingContext bindingContext)
{
if (bindingContext == null)

View File

@@ -5,8 +5,16 @@ using Microsoft.AspNetCore.Mvc.ModelBinding.Binders;
namespace Ufangx.Xss
{
/// <summary>
///
/// </summary>
public class RichTextBinderProvider : IModelBinderProvider
{
/// <summary>
///
/// </summary>
/// <param name="context"></param>
/// <returns></returns>
public IModelBinder GetBinder(ModelBinderProviderContext context)
{
if (context == null)

View File

@@ -5,32 +5,40 @@ using System.Text;
using System.Threading.Tasks;
using Microsoft.Extensions.DependencyInjection;
using Microsoft.Extensions.DependencyInjection.Extensions;
using Microsoft.Extensions.Options;
namespace Ufangx.Xss
{
public class XssFilterBuilder
/// <summary>
/// Xss过滤器生成者
/// </summary>
public partial class XssFilterBuilder
{
internal static XssFilterBuilder Builder { get; private set; }
private readonly Configures configures;
internal XssFilterBuilder(IServiceCollection services)
{
Services = services;
Builder = this;
configures = new Configures(services);
}
/// <summary>
/// 服务注册集合
/// </summary>
public virtual IServiceCollection Services { get; }
IServiceProvider _serviceProvider;
internal virtual IServiceProvider ServiceProvider => _serviceProvider ??= Services.BuildServiceProvider();
internal virtual IServiceProvider ServiceProvider => _serviceProvider ?? (_serviceProvider = Services.BuildServiceProvider());
//partial void configureFilterPolicyOptions(string name, Type PolicyType, Func<Task<string>> configure);
private XssFilterBuilder AddSchemeHelper<TPolicy>(string name, Func<Task<string>> configure)
where TPolicy : class, IFilterPolicy
{
Services.Configure<FilterPolicyOptions>(o =>
o.AddScheme(name, scheme => {
scheme.PolicyType = typeof(TPolicy);
scheme.GetConfig = configure;
})
);
configures.Options<FilterPolicyOptions>(o => o.AddScheme(name, scheme =>
{
scheme.PolicyType = typeof(TPolicy);
scheme.GetConfig = configure;
}));
Services.AddSingleton<TPolicy>();
return this;
}
@@ -40,37 +48,59 @@ namespace Ufangx.Xss
{
if (configureOptions != null)
{
Services.Configure(name, configureOptions);
configures.Options(configureOptions, name);
}
return AddSchemeHelper<TPolicy>(name, configure);
}
/// <summary>
/// 添加过滤策略方案
/// </summary>
/// <typeparam name="TOptions"></typeparam>
/// <typeparam name="TPolicy"></typeparam>
/// <param name="name"></param>
/// <param name="configure"></param>
/// <param name="configureOptions"></param>
/// <returns></returns>
public XssFilterBuilder AddScheme<TOptions, TPolicy>(string name, Func<Task<string>> configure, Action<TOptions> configureOptions)
where TOptions : class, new()
where TPolicy : class, IFilterPolicy
=> AddSchemeHelper<TOptions, TPolicy>(name, configure, configureOptions);
/// <summary>
/// 添加过滤策略方案
/// </summary>
/// <typeparam name="TOptions"></typeparam>
/// <typeparam name="TPolicy"></typeparam>
/// <param name="name"></param>
/// <param name="configure"></param>
/// <param name="configureOptions"></param>
/// <returns></returns>
public XssFilterBuilder AddScheme<TOptions, TPolicy>(string name, string configure, Action<TOptions> configureOptions)
where TOptions : class, new()
where TPolicy : class, IFilterPolicy
=> AddSchemeHelper<TOptions, TPolicy>(name, () => Task.FromResult(configure), configureOptions);
/// <summary>
/// 添加过滤策略方案
/// </summary>
/// <typeparam name="TPolicy"></typeparam>
/// <param name="name"></param>
/// <param name="configure"></param>
/// <returns></returns>
public XssFilterBuilder AddScheme<TPolicy>(string name, Func<Task<string>> configure)
where TPolicy : class, IFilterPolicy
=> AddSchemeHelper<TPolicy>(name, configure);
/// <summary>
/// 添加过滤策略方案
/// </summary>
/// <typeparam name="TPolicy"></typeparam>
/// <param name="name"></param>
/// <param name="configure"></param>
/// <returns></returns>
public XssFilterBuilder AddScheme<TPolicy>(string name, string configure)
where TPolicy : class, IFilterPolicy
=> AddSchemeHelper<TPolicy>(name, () => Task.FromResult(configure));
static IFilterPolicy presupposedPolicy;
public static IFilterPolicy GetPresupposedPolicy() {
if (presupposedPolicy == null)
{
var policy = new JsonFilterPolicy();
policy.Init(Assembly.GetExecutingAssembly().GetManifestResourceStream("Ufangx.Xss.resources.DefaultPolicy.json"), "Presupposed");
presupposedPolicy = policy;
}
return presupposedPolicy;
}
}
}

View File

@@ -0,0 +1,34 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Threading.Tasks;
using System.Web;
namespace Ufangx.Xss
{
/// <summary>
///
/// </summary>
[AttributeUsage(AttributeTargets.Property, AllowMultiple = false, Inherited = true)]
public class XssSchemeNameAttribute : Attribute, IXssSchemeName
{
private readonly string scheme;
/// <summary>
///
/// </summary>
/// <param name="scheme"></param>
public XssSchemeNameAttribute(string scheme)
{
this.scheme = scheme;
}
/// <summary>
///
/// </summary>
/// <param name="httpContext"></param>
/// <returns></returns>
public Task<string> GetSchemeName(HttpContextBase httpContext)
=> Task.FromResult(scheme);
}
}

View File

@@ -6,17 +6,26 @@ using System.Threading.Tasks;
namespace Ufangx.Xss
{
/// <summary>
///
/// </summary>
[AttributeUsage(AttributeTargets.Property | AttributeTargets.Parameter, AllowMultiple = false, Inherited = true)]
public class XssSchemeNameAttribute : Attribute, IXssSchemeName
{
private readonly string scheme;
/// <summary>
///
/// </summary>
/// <param name="scheme"></param>
public XssSchemeNameAttribute(string scheme)
{
this.scheme = scheme;
}
/// <summary>
///
/// </summary>
/// <param name="httpContext"></param>
/// <returns></returns>
public Task<string> GetSchemeName(HttpContext httpContext)
=> Task.FromResult(scheme);
}

File diff suppressed because it is too large Load Diff

View File

@@ -0,0 +1,84 @@
<?xml version="1.0" encoding="utf-8"?>
<Project ToolsVersion="15.0" DefaultTargets="Build" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
<Import Project="..\packages\MSTest.TestAdapter.2.1.0\build\net45\MSTest.TestAdapter.props" Condition="Exists('..\packages\MSTest.TestAdapter.2.1.0\build\net45\MSTest.TestAdapter.props')" />
<Import Project="$(MSBuildExtensionsPath)\$(MSBuildToolsVersion)\Microsoft.Common.props" Condition="Exists('$(MSBuildExtensionsPath)\$(MSBuildToolsVersion)\Microsoft.Common.props')" />
<PropertyGroup>
<Configuration Condition=" '$(Configuration)' == '' ">Debug</Configuration>
<Platform Condition=" '$(Platform)' == '' ">AnyCPU</Platform>
<ProjectGuid>{AFDE17BE-9067-44B7-9AED-8184ABB57E5C}</ProjectGuid>
<OutputType>Library</OutputType>
<AppDesignerFolder>Properties</AppDesignerFolder>
<RootNamespace>Framework461Test</RootNamespace>
<AssemblyName>Framework461Test</AssemblyName>
<TargetFrameworkVersion>v4.6.1</TargetFrameworkVersion>
<FileAlignment>512</FileAlignment>
<ProjectTypeGuids>{3AC096D0-A1C2-E12C-1390-A8335801FDAB};{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}</ProjectTypeGuids>
<VisualStudioVersion Condition="'$(VisualStudioVersion)' == ''">15.0</VisualStudioVersion>
<VSToolsPath Condition="'$(VSToolsPath)' == ''">$(MSBuildExtensionsPath32)\Microsoft\VisualStudio\v$(VisualStudioVersion)</VSToolsPath>
<ReferencePath>$(ProgramFiles)\Common Files\microsoft shared\VSTT\$(VisualStudioVersion)\UITestExtensionPackages</ReferencePath>
<IsCodedUITest>False</IsCodedUITest>
<TestProjectType>UnitTest</TestProjectType>
<NuGetPackageImportStamp>
</NuGetPackageImportStamp>
</PropertyGroup>
<PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Debug|AnyCPU' ">
<DebugSymbols>true</DebugSymbols>
<DebugType>full</DebugType>
<Optimize>false</Optimize>
<OutputPath>bin\Debug\</OutputPath>
<DefineConstants>DEBUG;TRACE</DefineConstants>
<ErrorReport>prompt</ErrorReport>
<WarningLevel>4</WarningLevel>
</PropertyGroup>
<PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Release|AnyCPU' ">
<DebugType>pdbonly</DebugType>
<Optimize>true</Optimize>
<OutputPath>bin\Release\</OutputPath>
<DefineConstants>TRACE</DefineConstants>
<ErrorReport>prompt</ErrorReport>
<WarningLevel>4</WarningLevel>
</PropertyGroup>
<ItemGroup>
<Reference Include="AngleSharp, Version=0.13.0.0, Culture=neutral, PublicKeyToken=e83494dcdc6d31ea, processorArchitecture=MSIL">
<HintPath>..\packages\AngleSharp.0.13.0\lib\net46\AngleSharp.dll</HintPath>
</Reference>
<Reference Include="Microsoft.VisualStudio.TestPlatform.TestFramework, Version=14.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a, processorArchitecture=MSIL">
<HintPath>..\packages\MSTest.TestFramework.2.1.0\lib\net45\Microsoft.VisualStudio.TestPlatform.TestFramework.dll</HintPath>
</Reference>
<Reference Include="Microsoft.VisualStudio.TestPlatform.TestFramework.Extensions, Version=14.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a, processorArchitecture=MSIL">
<HintPath>..\packages\MSTest.TestFramework.2.1.0\lib\net45\Microsoft.VisualStudio.TestPlatform.TestFramework.Extensions.dll</HintPath>
</Reference>
<Reference Include="System" />
<Reference Include="System.Core" />
<Reference Include="System.Runtime.CompilerServices.Unsafe, Version=4.0.4.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a, processorArchitecture=MSIL">
<HintPath>..\packages\System.Runtime.CompilerServices.Unsafe.4.5.0\lib\netstandard2.0\System.Runtime.CompilerServices.Unsafe.dll</HintPath>
</Reference>
<Reference Include="System.Text.Encoding.CodePages, Version=4.1.1.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a, processorArchitecture=MSIL">
<HintPath>..\packages\System.Text.Encoding.CodePages.4.5.0\lib\net461\System.Text.Encoding.CodePages.dll</HintPath>
</Reference>
</ItemGroup>
<ItemGroup>
<Compile Include="UnitTest1.cs" />
<Compile Include="Properties\AssemblyInfo.cs" />
</ItemGroup>
<ItemGroup>
<None Include="app.config" />
<None Include="packages.config" />
</ItemGroup>
<ItemGroup>
<ProjectReference Include="..\AntiXssUF\AntiXssUF.csproj">
<Project>{313ae160-70b2-4a7c-9e1a-f00353c0a75a}</Project>
<Name>AntiXssUF</Name>
</ProjectReference>
</ItemGroup>
<Import Project="$(VSToolsPath)\TeamTest\Microsoft.TestTools.targets" Condition="Exists('$(VSToolsPath)\TeamTest\Microsoft.TestTools.targets')" />
<Import Project="$(MSBuildToolsPath)\Microsoft.CSharp.targets" />
<Target Name="EnsureNuGetPackageBuildImports" BeforeTargets="PrepareForBuild">
<PropertyGroup>
<ErrorText>这台计算机上缺少此项目引用的 NuGet 程序包。使用“NuGet 程序包还原”可下载这些程序包。有关更多信息,请参见 http://go.microsoft.com/fwlink/?LinkID=322105。缺少的文件是 {0}。</ErrorText>
</PropertyGroup>
<Error Condition="!Exists('..\packages\MSTest.TestAdapter.2.1.0\build\net45\MSTest.TestAdapter.props')" Text="$([System.String]::Format('$(ErrorText)', '..\packages\MSTest.TestAdapter.2.1.0\build\net45\MSTest.TestAdapter.props'))" />
<Error Condition="!Exists('..\packages\MSTest.TestAdapter.2.1.0\build\net45\MSTest.TestAdapter.targets')" Text="$([System.String]::Format('$(ErrorText)', '..\packages\MSTest.TestAdapter.2.1.0\build\net45\MSTest.TestAdapter.targets'))" />
</Target>
<Import Project="..\packages\MSTest.TestAdapter.2.1.0\build\net45\MSTest.TestAdapter.targets" Condition="Exists('..\packages\MSTest.TestAdapter.2.1.0\build\net45\MSTest.TestAdapter.targets')" />
</Project>

View File

@@ -0,0 +1,20 @@
using System.Reflection;
using System.Runtime.CompilerServices;
using System.Runtime.InteropServices;
[assembly: AssemblyTitle("Framework461Test")]
[assembly: AssemblyDescription("")]
[assembly: AssemblyConfiguration("")]
[assembly: AssemblyCompany("")]
[assembly: AssemblyProduct("Framework461Test")]
[assembly: AssemblyCopyright("Copyright © 2020")]
[assembly: AssemblyTrademark("")]
[assembly: AssemblyCulture("")]
[assembly: ComVisible(false)]
[assembly: Guid("afde17be-9067-44b7-9aed-8184abb57e5c")]
// [assembly: AssemblyVersion("1.0.*")]
[assembly: AssemblyVersion("1.0.0.0")]
[assembly: AssemblyFileVersion("1.0.0.0")]

View File

@@ -0,0 +1,203 @@
using System;
using Microsoft.VisualStudio.TestTools.UnitTesting;
using Ufangx.Xss;
using System.Collections.Generic;
using System.Linq;
using System.Threading;
using System.Reflection;
using System.Runtime.CompilerServices;
using System.Text;
using System.Diagnostics;
namespace Framework461Test
{
[TestClass]
public class UnitTest1
{
void FilterAttacks(RichText richText, Func<string, bool> fn, [CallerMemberName] string propertyName = null)
{
StringBuilder stringBuilder = new StringBuilder();
stringBuilder.Append($"\n==== in {propertyName} ==================================================\n原文:\n{richText.Source}\n");
stringBuilder.Append("过滤\n");
string clean = richText.ToString();
stringBuilder.Append(clean);
var isTrue = fn(clean);
stringBuilder.Append($"\n状态{isTrue}");
Console.WriteLine(stringBuilder.ToString());
Assert.IsTrue(isTrue);
}
[TestMethod]
public void testScriptAttacks()
{
FilterAttacks("<script src=\"/test.js\"></script>", str => str.IndexOf("script", StringComparison.OrdinalIgnoreCase) == -1);
FilterAttacks("test<script>alert(document.cookie)</script>", str => str.IndexOf("script", StringComparison.OrdinalIgnoreCase) == -1);
FilterAttacks("<<<><<script src=http://fake-evil.ru/test.js>", str => str.IndexOf("<script", StringComparison.OrdinalIgnoreCase) == -1);
FilterAttacks("<script<script src=http://fake-evil.ru/test.js>>", str => str.IndexOf("<script", StringComparison.OrdinalIgnoreCase) == -1);
FilterAttacks("<SCRIPT/XSS SRC=\"http://ha.ckers.org/xss.js\"></SCRIPT>", str => str.IndexOf("<script", StringComparison.OrdinalIgnoreCase) == -1);
FilterAttacks("<BODY onload!#$%&()*~+-_.,:;?@[/|\\]^`=alert(\"XSS\")>", str => str.IndexOf("onload", StringComparison.OrdinalIgnoreCase) == -1);
FilterAttacks("<BODY ONLOAD=alert('XSS')>", str => str.IndexOf("alert") == -1);
FilterAttacks("<iframe src=http://ha.ckers.org/scriptlet.html <", str => str.IndexOf("<iframe", StringComparison.OrdinalIgnoreCase) == -1);
FilterAttacks("<INPUT TYPE=\"IMAGE\" SRC=\"javascript:alert('XSS');\">", str => str.IndexOf("src", StringComparison.OrdinalIgnoreCase) == -1);
}
[TestMethod]
public void testImgAttacks()
{
FilterAttacks("<img src='http://www.myspace.com/img.gif'>", str => str.IndexOf("<img", StringComparison.OrdinalIgnoreCase) != -1);
FilterAttacks("<img src=javascript:alert(document.cookie)>", str => str.IndexOf("<img", StringComparison.OrdinalIgnoreCase) == -1);
FilterAttacks("<IMG SRC=&#106;&#97;&#118;&#97;&#115;&#99;&#114;&#105;&#112;&#116;&#58;&#97;&#108;&#101;&#114;&#116;&#40;&#39;&#88;&#83;&#83;&#39;&#41;>",
str => str.IndexOf("<img", StringComparison.OrdinalIgnoreCase) == -1);
FilterAttacks("<IMG SRC=&#0000106&#0000097&#0000118&#0000097&#0000115&#0000099&#0000114&#0000105&#0000112&#0000116&#0000058&#0000097&#0000108&#0000101&#0000114&#0000116&#0000040&#0000039&#0000088&#0000083&#0000083&#0000039&#0000041>", str => string.IsNullOrEmpty(str));
FilterAttacks("<IMG SRC=&#x6A&#x61&#x76&#x61&#x73&#x63&#x72&#x69&#x70&#x74&#x3A&#x61&#x6C&#x65&#x72&#x74&#x28&#x27&#x58&#x53&#x53&#x27&#x29>", str => string.IsNullOrEmpty(str));
FilterAttacks("<IMG SRC=\"jav&#x0D;ascript:alert('XSS');\">", str => str.IndexOf("alert", StringComparison.OrdinalIgnoreCase) == -1);
FilterAttacks("<IMG SRC=\"javascript:alert('XSS')\"", str => str.IndexOf("javascript", StringComparison.OrdinalIgnoreCase) == -1);
FilterAttacks("<IMG LOWSRC=\"javascript:alert('XSS')\">", str => str.IndexOf("javascript", StringComparison.OrdinalIgnoreCase) == -1);
FilterAttacks("<BGSOUND SRC=\"javascript:alert('XSS');\">", str => str.IndexOf("javascript", StringComparison.OrdinalIgnoreCase) == -1);
}
[TestMethod]
public void testHrefAttacks()
{
FilterAttacks("<LINK REL=\"stylesheet\" HREF=\"javascript:alert('XSS');\">", str => str.IndexOf("href") == -1);
FilterAttacks("<LINK REL=\"stylesheet\" HREF=\"http://ha.ckers.org/xss.css\">", str => str.IndexOf("href") == -1);
FilterAttacks("<STYLE>@import'http://ha.ckers.org/xss.css';</STYLE>", str => str.IndexOf("ha.ckers.org") == -1);
FilterAttacks("<STYLE>BODY{-moz-binding:url(\"http://ha.ckers.org/xssmoz.xml#xss\")}</STYLE>", str => str.IndexOf("ha.ckers.org") == -1);
FilterAttacks("<STYLE>BODY{-moz-binding:url(\"http://ha.ckers.org/xssmoz.xml#xss\")}</STYLE>", str => str.IndexOf("xss.htc") == -1);
FilterAttacks("<STYLE>li {list-style-image: url(\"javascript:alert('XSS')\");}</STYLE><UL><LI>XSS", str => str.IndexOf("javascript") == -1);
FilterAttacks("<IMG SRC='vbscript:msgbox(\"XSS\")'>", str => str.IndexOf("vbscript") == -1);
FilterAttacks("<META HTTP-EQUIV=\"refresh\" CONTENT=\"0; URL=http://;URL=javascript:alert('XSS');\">", str => str.IndexOf("<meta") == -1);
FilterAttacks("<META HTTP-EQUIV=\"refresh\" CONTENT=\"0;url=javascript:alert('XSS');\">", str => str.IndexOf("<meta") == -1);
FilterAttacks("<META HTTP-EQUIV=\"refresh\" CONTENT=\"0;url=data:text/html;base64,PHNjcmlwdD5hbGVydCgnWFNTJyk8L3NjcmlwdD4K\">", str => str.IndexOf("<meta") == -1);
FilterAttacks("<IFRAME SRC=\"javascript:alert('XSS');\"></IFRAME>", str => str.IndexOf("iframe") == -1);
FilterAttacks("<FRAMESET><FRAME SRC=\"javascript:alert('XSS');\"></FRAMESET>", str => str.IndexOf("javascript") == -1);
FilterAttacks("<TABLE BACKGROUND=\"javascript:alert('XSS')\">", str => str.IndexOf("background") == -1);
FilterAttacks("<TABLE><TD BACKGROUND=\"javascript:alert('XSS')\">", str => str.IndexOf("background") == -1);
FilterAttacks("<DIV STYLE=\"background-image: url(javascript:alert('XSS'))\">", str => str.IndexOf("javascript") == -1);
FilterAttacks("<DIV STYLE=\"width: expression(alert('XSS'));\">", str => str.IndexOf("alert") == -1);
FilterAttacks("<IMG STYLE=\"xss:expr/*XSS*/ession(alert('XSS'))\">", str => str.IndexOf("alert") == -1);
FilterAttacks("<STYLE>@im\\port'\\ja\\vasc\\ript:alert(\"XSS\")';</STYLE>", str => str.IndexOf("ript:alert") == -1);
FilterAttacks("<BASE HREF=\"javascript:alert('XSS');//\">", str => str.IndexOf("javascript") == -1);
FilterAttacks("<BaSe hReF=\"http://arbitrary.com/\">", str => str.IndexOf("<base") == -1);
FilterAttacks("<OBJECT TYPE=\"text/x-scriptlet\" DATA=\"http://ha.ckers.org/scriptlet.html\"></OBJECT>", str => str.IndexOf("<object") == -1);
FilterAttacks("<OBJECT classid=clsid:ae24fdae-03c6-11d1-8b76-0080c744f389><param name=url value=javascript:alert('XSS')></OBJECT>", str => str.IndexOf("<object") == -1);
FilterAttacks("<EMBED SRC=\"http://ha.ckers.org/xss.swf\" AllowScriptAccess=\"always\"></EMBED>", str => str.IndexOf("<embed") == -1);
FilterAttacks("<EMBED SRC=\"data:image/svg+xml;base64,PHN2ZyB4bWxuczpzdmc9Imh0dH A6Ly93d3cudzMub3JnLzIwMDAvc3ZnIiB4bWxucz0iaHR0cDovL3d3dy53My5vcmcv MjAwMC9zdmciIHhtbG5zOnhsaW5rPSJodHRwOi8vd3d3LnczLm9yZy8xOTk5L3hs aW5rIiB2ZXJzaW9uPSIxLjAiIHg9IjAiIHk9IjAiIHdpZHRoPSIxOTQiIGhlaWdodD0iMjAw IiBpZD0ieHNzIj48c2NyaXB0IHR5cGU9InRleHQvZWNtYXNjcmlwdCI+YWxlcnQoIlh TUyIpOzwvc2NyaXB0Pjwvc3ZnPg==\" type=\"image/svg+xml\" AllowScriptAccess=\"always\"></EMBED>", str => str.IndexOf("<embed") == -1);
FilterAttacks("<SCRIPT a=\">\" SRC=\"http://ha.ckers.org/xss.js\"></SCRIPT>", str => str.IndexOf("<script") == -1);
FilterAttacks("<SCRIPT a=\">\" '' SRC=\"http://ha.ckers.org/xss.js\"></SCRIPT>", str => str.IndexOf("<script") == -1);
FilterAttacks("<SCRIPT a=`>` SRC=\"http://ha.ckers.org/xss.js\"></SCRIPT>", str => str.IndexOf("<script") == -1);
FilterAttacks("<SCRIPT a=\">'>\" SRC=\"http://ha.ckers.org/xss.js\"></SCRIPT>", str => str.IndexOf("<script") == -1);
FilterAttacks("<SCRIPT>document.write(\"<SCRI\");</SCRIPT>PT SRC=\"http://ha.ckers.org/xss.js\"></SCRIPT>", str => str.IndexOf("script") == -1);
FilterAttacks("<SCRIPT SRC=http://ha.ckers.org/xss.js", str => str.IndexOf("<script") == -1);
FilterAttacks("<div/style=&#92&#45&#92&#109&#111&#92&#122&#92&#45&#98&#92&#105&#92&#110&#100&#92&#105&#110&#92&#103:&#92&#117&#114&#108&#40&#47&#47&#98&#117&#115&#105&#110&#101&#115&#115&#92&#105&#92&#110&#102&#111&#46&#99&#111&#46&#117&#107&#92&#47&#108&#97&#98&#115&#92&#47&#120&#98&#108&#92&#47&#120&#98&#108&#92&#46&#120&#109&#108&#92&#35&#120&#115&#115&#41&>", str => str.IndexOf("style") == -1);
FilterAttacks("<a href='aim: &c:\\windows\\system32\\calc.exe' ini='C:\\Documents and Settings\\All Users\\Start Menu\\Programs\\Startup\\pwnd.bat'>", str => str.IndexOf("aim.exe") == -1);
FilterAttacks("<!--\n<A href=\n- --><a href=javascript:alert:document.domain>test-->", str => str.IndexOf("javascript") == -1);
FilterAttacks("<a></a style=\"\"xx:expr/**/ession(document.appendChild(document.createElement('script')).src='http://h4k.in/i.js')\">", str => str.IndexOf("document") == -1);
}
[TestMethod]
public void testCssAttacks()
{
FilterAttacks("<div style=\"position:absolute\">", str => str.IndexOf("position") == -1);
FilterAttacks("<style>b { position:absolute;color:red; }</style>", str => str.IndexOf("position") == -1);
FilterAttacks("<div style=\"z-index:25\">", str => str.IndexOf("position") == -1);
FilterAttacks("<style>z-index:25</style>", str => str.IndexOf("position") == -1);
}
[TestMethod]
public void TestAll()
{
Stopwatch stopwatch = new Stopwatch();
stopwatch.Start();
FilterAttacks("<IMG SRC=java\0script:alert(\"XSS\")>", str => str.IndexOf("<img", StringComparison.OrdinalIgnoreCase) == -1);
testCssAttacks();
testHrefAttacks();
testScriptAttacks();
testImgAttacks();
stopwatch.Stop();
Console.WriteLine($"\n==============程序运行的时间:{stopwatch.Elapsed.TotalMilliseconds}毫秒");
}
[TestMethod]
public void ThreadTest()
{
const int numThreads = 5;
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 tests = new UnitTest1();
var waiting = numThreads;
var methods = typeof(UnitTest1).GetTypeInfo().GetMethods()
.Where(m => m.GetCustomAttributes(typeof(TestMethodAttribute), 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);
}
#pragma warning disable CA1031 // Do not catch general exception types
catch (Exception ex)
{
Interlocked.CompareExchange(ref firstException, ex, null);
Interlocked.Increment(ref failures);
}
#pragma warning restore CA1031 // Do not catch general exception types
})).ToList();
foreach (var thread in threads)
thread.Start();
foreach (var thread in threads)
thread.Join();
Assert.IsNull(firstException);
Assert.AreEqual(0, failures);
}
}
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];
}
}
}
}

View File

@@ -0,0 +1,11 @@
<?xml version="1.0" encoding="utf-8"?>
<configuration>
<runtime>
<assemblyBinding xmlns="urn:schemas-microsoft-com:asm.v1">
<dependentAssembly>
<assemblyIdentity name="System.Text.Encoding.CodePages" publicKeyToken="b03f5f7f11d50a3a" culture="neutral" />
<bindingRedirect oldVersion="0.0.0.0-4.1.1.0" newVersion="4.1.1.0" />
</dependentAssembly>
</assemblyBinding>
</runtime>
</configuration>

View File

@@ -0,0 +1,8 @@
<?xml version="1.0" encoding="utf-8"?>
<packages>
<package id="AngleSharp" version="0.13.0" targetFramework="net461" />
<package id="MSTest.TestAdapter" version="2.1.0" targetFramework="net461" />
<package id="MSTest.TestFramework" version="2.1.0" targetFramework="net461" />
<package id="System.Runtime.CompilerServices.Unsafe" version="4.5.0" targetFramework="net461" />
<package id="System.Text.Encoding.CodePages" version="4.5.0" targetFramework="net461" />
</packages>