Skip to content

Commit

Permalink
Merge pull request #5 from spassarop/develop
Browse files Browse the repository at this point in the history
Changes for 1.0.2

- Updated localization handling and resources: Language is more inclusive as in most cases it takes parent culture, meaning that "en-US" will use "en" instead of failing.
- Default policies are now included when installing the NuGet package. They will also be removed when uninstalling.
- Small refactors and improvements on main project and tests.
  • Loading branch information
spassarop authored Jan 13, 2021
2 parents d3a3551 + fb25008 commit f54b0a1
Show file tree
Hide file tree
Showing 33 changed files with 213 additions and 1,010 deletions.
12 changes: 0 additions & 12 deletions .gitattributes
Original file line number Diff line number Diff line change
@@ -1,14 +1,2 @@
# Auto detect text files and perform LF normalization
* text=auto

*.cs text diff=csharp
*.java text diff=java
*.html text diff=html
*.css text
*.js text
*.sql text

*.csproj text merge=union
*.sln text merge=union eol=crlf

*.docx diff=astextplain
1 change: 1 addition & 0 deletions .github/ISSUE_TEMPLATE/bug_report.md
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,7 @@ Steps to reproduce the behavior:
1. Set up this policy '...'
2. Call AntiSamy with this input '....'
3. See error
*If possible, include a unit test.*

**Expected behavior**
A clear and concise description of what you expected to happen.
Expand Down
File renamed without changes.
File renamed without changes.
Original file line number Diff line number Diff line change
Expand Up @@ -4,54 +4,54 @@
<xsd:element name="anti-samy-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="dynamic-tag-attributes" type="AttributeList" maxOccurs="1" minOccurs="0"/>
<xsd:element name="tags-to-encode" type="TagsToEncodeList" minOccurs="0" maxOccurs="1"/>
<xsd:element name="tag-rules" type="TagRules" minOccurs="1" maxOccurs="1"/>
<xsd:element name="css-rules" type="CSSRules" minOccurs="1" maxOccurs="1"/>
<xsd:element name="allowed-empty-tags" type="AllowedEmptyTags" minOccurs="0" maxOccurs="1"/>
<xsd:element name="directives" type="Directives"/>
<xsd:element name="common-regexps" type="CommonRegexps"/>
<xsd:element name="common-attributes" type="AttributeList"/>
<xsd:element name="global-tag-attributes" type="AttributeList"/>
<xsd:element name="dynamic-tag-attributes" type="AttributeList" minOccurs="0"/>
<xsd:element name="tags-to-encode" type="TagsToEncodeList" minOccurs="0"/>
<xsd:element name="tag-rules" type="TagRules"/>
<xsd:element name="css-rules" type="CSSRules"/>
<xsd:element name="allowed-empty-tags" type="AllowedEmptyTags" minOccurs="0"/>
</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="TagsToEncodeList">
<xsd:sequence maxOccurs="unbounded">
<xsd:element name="tag" 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" />
Expand Down Expand Up @@ -96,13 +96,13 @@
<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:sequence>
</xsd:complexType>

<xsd:complexType name="Property">
<xsd:sequence>
<xsd:element name="category-list" type="CategoryList" minOccurs="0"/>
Expand All @@ -123,7 +123,7 @@

<xsd:complexType name="Shorthand">
<xsd:attribute name="name" type="xsd:string" use="required"/>
</xsd:complexType>
</xsd:complexType>

<xsd:complexType name="CategoryList">
<xsd:sequence maxOccurs="unbounded">
Expand All @@ -136,7 +136,7 @@
</xsd:complexType>

<xsd:complexType name="Entity">
<xsd:attribute name="name" type="xsd:string" use="required"/>
<xsd:attribute name="name" type="xsd:string" use="required"/>
<xsd:attribute name="cdata" type="xsd:string" use="required"/>
</xsd:complexType>
</xsd:schema>
1 change: 0 additions & 1 deletion OWASP.AntiSamy/Css/CssScanner.cs
Original file line number Diff line number Diff line change
Expand Up @@ -96,7 +96,6 @@ public CleanResults ScanStyleSheet(string taintedCss)
/// <exception cref="ParseException"/>
public CleanResults ScanInlineStyle(string taintedCss, string tagName)
{
// TODO: Do something about tagName (probably delete it later)
return DoScan(taintedCss, isInlineCss: true, tagName: tagName);
}

Expand Down
9 changes: 1 addition & 8 deletions OWASP.AntiSamy/Html/AntiSamy.cs
Original file line number Diff line number Diff line change
Expand Up @@ -131,14 +131,7 @@ public CleanResults Scan(StreamReader reader, StreamWriter writer, Policy policy
/// <exception cref="System.Globalization.CultureNotFoundException"/>
public void SetCulture(string cultureName)
{
if (Constants.SUPPORTED_LANGUAGES.Contains(cultureName))
{
Util.ErrorMessageUtil.CurrentCultureName = cultureName;
}
else
{
throw new System.Globalization.CultureNotFoundException(string.Format(Constants.ERROR_CULTURE_NOTSUPPORTED, string.Join(", ", Constants.SUPPORTED_LANGUAGES)));
}
Util.ErrorMessageUtil.SetCulture(cultureName);
}
}
}
20 changes: 13 additions & 7 deletions OWASP.AntiSamy/Html/Policy.cs
Original file line number Diff line number Diff line change
Expand Up @@ -43,6 +43,8 @@ namespace OWASP.AntiSamy.Html
/// </summary>
public class Policy
{
static private XmlSchemaSet defaultPolicySchemaSet = null;

private readonly Dictionary<string, string> commonRegularExpressions;
private readonly Dictionary<string, Attribute> commonAttributes;
private readonly Dictionary<string, Tag> tagRules;
Expand Down Expand Up @@ -113,7 +115,7 @@ protected Policy(Policy old, Dictionary<string, string> directives, Dictionary<s
requireClosingTagsMatcher = old.requireClosingTagsMatcher;
}

/// <summary> This retrieves a policy based on a default location ("Resources/antisamy.xml") or from the embedded XML.</summary>
/// <summary> This retrieves a policy based on a default location ("AntiSamyPolicyExamples/antisamy.xml") or from the embedded XML.</summary>
/// <returns> A populated <see cref="Policy"/> object based on the XML policy file located in the default location.</returns>
/// <exception cref="PolicyException"></exception>
public static Policy GetInstance()
Expand Down Expand Up @@ -317,11 +319,15 @@ private static void ValidateSchema(XmlDocument document)
{
try
{
Stream xsdStream = new MemoryStream(Encoding.UTF8.GetBytes(
Properties.Resources.ResourceManager.GetObject(Constants.DEFAULT_POLICY_SCHEMA_RESOURCE_KEY) as string));
var schemaSet = new XmlSchemaSet();
schemaSet.Add("", XmlReader.Create(xsdStream));
document.Schemas = schemaSet;
if (defaultPolicySchemaSet == null)
{
Stream xsdStream = new MemoryStream(Encoding.UTF8.GetBytes(
Properties.Resources.ResourceManager.GetObject(Constants.DEFAULT_POLICY_SCHEMA_RESOURCE_KEY) as string));
defaultPolicySchemaSet = new XmlSchemaSet();
defaultPolicySchemaSet.Add("", XmlReader.Create(xsdStream));
}

document.Schemas = defaultPolicySchemaSet;
document.Schemas.Compile();
document.Validate(PolicySchemaValidationEventHandler);
}
Expand Down Expand Up @@ -454,7 +460,7 @@ private static void ParseCommonAttributes(XmlNode commonAttributeListNode, Parse
{
foreach (XmlElement node in PolicyParserUtil.GetChildrenByTagName(commonAttributeListNode, "attribute"))
{
// TODO: DEFAULT_ONINVALID seems to have been removed from common attributes. Do we need this code?
// TODO: Throw exception if onInvalid is defined but is not an expected option?
string onInvalid = XmlUtil.GetAttributeValue(node, "onInvalid");
string name = XmlUtil.GetAttributeValue(node, "name");
var attribute = new Attribute(name)
Expand Down
27 changes: 14 additions & 13 deletions OWASP.AntiSamy/Html/Scan/AntiSamyDomScanner.cs
Original file line number Diff line number Diff line change
Expand Up @@ -404,7 +404,9 @@ private void TruncateTag(HtmlNode node, string tagName)
while (attributes.Count > 0)
{
AddError(Constants.ERROR_ATTRIBUTE_NOT_IN_POLICY,
HtmlEntityEncoder.HtmlEntityEncode(tagName), HtmlEntityEncoder.HtmlEntityEncode(attributes[0].Name));
HtmlEntityEncoder.HtmlEntityEncode(tagName),
HtmlEntityEncoder.HtmlEntityEncode(attributes[0].Name),
HtmlEntityEncoder.HtmlEntityEncode(attributes[0].Value));

node.Attributes.Remove(attributes[0].Name);
}
Expand Down Expand Up @@ -481,8 +483,6 @@ private bool ProcessAttributes(HtmlNode node, Tag tag)
attribute = Policy.GetGlobalAttributeByName(name);
}

bool isAttributeValid = false;

if (name.ToLowerInvariant() == "style" && attribute != null)
{
var styleScanner = new CssScanner(Policy);
Expand Down Expand Up @@ -516,7 +516,7 @@ private bool ProcessAttributes(HtmlNode node, Tag tag)
value = HtmlEntity.DeEntitize(value);
string lowerCaseValue = value.ToLowerInvariant();

isAttributeValid = attribute.AllowedValues.Any(v => v != null && v.ToLowerInvariant() == lowerCaseValue)
bool isAttributeValid = attribute.AllowedValues.Any(v => v != null && v.ToLowerInvariant() == lowerCaseValue)
|| attribute.AllowedRegExp.Any(r => r != null && Regex.IsMatch(value, "^" + r + "$"));

if (!isAttributeValid)
Expand All @@ -532,7 +532,7 @@ private bool ProcessAttributes(HtmlNode node, Tag tag)
}
else if (onInvalidAction == "filterTag")
{
// Remove the attribute and keep the rest of the tag
// Remove the node and move up the rest that was inside the tag after processing
ProcessChildren(node);
PromoteChildren(node);
AddError(Constants.ERROR_ATTRIBUTE_CAUSE_FILTER,
Expand All @@ -542,7 +542,7 @@ private bool ProcessAttributes(HtmlNode node, Tag tag)
}
else if (onInvalidAction == "encodeTag")
{
// Remove the attribute and keep the rest of the tag
// Encode the node and move up the rest that was inside the tag after processing
ProcessChildren(node);
EncodeAndPromoteChildren(node);
AddError(Constants.ERROR_ATTRIBUTE_CAUSE_ENCODE,
Expand All @@ -552,6 +552,7 @@ private bool ProcessAttributes(HtmlNode node, Tag tag)
}
else
{
// Just remove the attribute
node.Attributes.Remove(attribute.Name);
currentAttributeIndex--;
AddError(Constants.ERROR_ATTRIBUTE_INVALID,
Expand All @@ -560,9 +561,9 @@ private bool ProcessAttributes(HtmlNode node, Tag tag)
HtmlEntityEncoder.HtmlEntityEncode(value));
}

if (onInvalidAction == "removeTag" || onInvalidAction == "filterTag")
if (new string[] { "removeTag", "filterTag", "encodeTag" }.Contains(onInvalidAction))
{
return false; // Can't process any more if we remove/filter the tag
return false; // Can't process any more if we remove/filter/encode the tag
}
}
}
Expand Down Expand Up @@ -612,7 +613,7 @@ private void PromoteChildren(HtmlNode node)
RemoveNode(node);
}

private string StripNonValidXmlCharacters(string textToClean)
private static string StripNonValidXmlCharacters(string textToClean)
{
if (string.IsNullOrEmpty(textToClean))
{
Expand All @@ -639,22 +640,22 @@ private string StripNonValidXmlCharacters(string textToClean)
return cleanText.ToString();
}

private string NodeToString(HtmlNode node)
private static string NodeToString(HtmlNode node)
{
var nodeToString = new StringBuilder("<" + node.Name);

foreach (HtmlAttribute attribute in node.GetAttributes())
{
nodeToString
.Append(" ")
.Append(' ')
.Append(HtmlEntityEncoder.HtmlEntityEncode(attribute.Name))
.Append("=\"")
.Append(HtmlEntityEncoder.HtmlEntityEncode(attribute.Value))
.Append("\"");
.Append('"');
}
if (node.HasChildNodes)
{
nodeToString.Append(">");
nodeToString.Append('>');
}
else
{
Expand Down
5 changes: 2 additions & 3 deletions OWASP.AntiSamy/Html/Scan/Constants.cs
Original file line number Diff line number Diff line change
Expand Up @@ -67,7 +67,7 @@ internal static class Constants
public static readonly string ACTION_TRUNCATE = "truncate";
public static readonly string ACTION_ENCODE = "encode";

public static readonly string DEFAULT_POLICY_URI = "Resources/antisamy.xml";
public static readonly string DEFAULT_POLICY_URI = "AntiSamyPolicyExamples/antisamy.xml";
public static readonly string DEFAULT_POLICY_RESOURCE_KEY = "DEFAULT_ANTISAMY_POLICY_XML";
public static readonly string DEFAULT_POLICY_SCHEMA_RESOURCE_KEY = "DEFAULT_ANTISAMY_POLICY_XSD";
public static readonly string DEFAULT_ONINVALID = "removeAttribute";
Expand Down Expand Up @@ -111,8 +111,7 @@ internal static class Constants

// Supported languages
public static readonly List<string> SUPPORTED_LANGUAGES = new List<string> {
"de-DE", "en-AU", "en-CA", "en-GB", "en-US", "es-MX",
"it-IT", "no-NB", "pt-BR", "pt-PT", "ru-RU", "zh-CN"
"de", "en", "es", "it", "nb", "pt", "ru", "zh-CN"
};
}
}
38 changes: 38 additions & 0 deletions OWASP.AntiSamy/Html/Util/ErrorMessageUtil.cs
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,8 @@

using System.Globalization;
using System.Resources;
using System.Text.RegularExpressions;
using OWASP.AntiSamy.Html.Scan;

namespace OWASP.AntiSamy.Html.Util
{
Expand All @@ -49,5 +51,41 @@ public static string GetMessage(string messageKey, params object[] arguments)

return string.Format(rawMessage, arguments);
}

internal static void SetCulture(string cultureName)
{
if (IsValidCultureFormat(cultureName) && IsCultureSupported(cultureName))
{
CurrentCultureName = cultureName;
}
else
{
throw new CultureNotFoundException(string.Format(Constants.ERROR_CULTURE_NOTSUPPORTED, string.Join(", ", Constants.SUPPORTED_LANGUAGES)));
}
}

private static bool IsValidCultureFormat(string cultureName)
{
// Enough control for cultures supported today
return new Regex(@"^[a-z]{2}(-[A-Z]{2})?$", RegexOptions.Compiled).IsMatch(cultureName);
}

private static bool IsCultureSupported(string cultureName)
{
if (Constants.SUPPORTED_LANGUAGES.Contains(cultureName))
{
return true;
}

try
{
string parentCulture = new CultureInfo(cultureName).Parent.Name;
return Constants.SUPPORTED_LANGUAGES.Contains(parentCulture);
}
catch
{
return false;
}
}
}
}
Loading

0 comments on commit f54b0a1

Please sign in to comment.