/// <summary> Go through <global-tag-attributes> section of the policy file.</summary> /// <param name="globalAttributeListNode">Top level of <global-tag-attributes> /// </param> /// <returns> A HashMap of global Attributes that need validation for every tag. /// </returns> /// <throws> PolicyException </throws> private Hashtable parseGlobalAttributes(XmlNode globalAttributeListNode) { XmlNodeList globalAttributeNodes = globalAttributeListNode.SelectNodes("attribute"); Hashtable globalAttributes = new Hashtable(); string _name = ""; //string _value = ""; foreach (XmlNode node in globalAttributeNodes) { if (node.NodeType == XmlNodeType.Element) { _name = node.Attributes[0].Value; //_value = node.Attributes[1].Value; Attribute toAdd = getCommonAttributeByName(_name); if (toAdd != null) { globalAttributes.Add(_name, toAdd); } else { throw new PolicyException("Global attribute '" + _name + "' was not defined in <common-attributes>"); } //if (!globalAttributes.ContainsKey(_name)) // globalAttributes.Add(_name, new AntiSamyPattern(_name, _value)); } } return(globalAttributes); }
/// <summary> Private method for parsing the <tag-rules> from the XML file.</summary> /// <param name="root">The root element for <tag-rules> /// </param> /// <returns> A List<Tag> containing the rules. /// </returns> /// <throws> PolicyException </throws> private Hashtable parseTagRules(XmlNode tagAttributeListNode) { Hashtable tags = new Hashtable(); XmlNodeList tagList = tagAttributeListNode.SelectNodes("tag"); foreach (XmlNode tagNode in tagList) { if (tagNode.NodeType == XmlNodeType.Element) { String name = (tagNode.Attributes["name"] == null ? null : tagNode.Attributes["name"].Value); String action = (tagNode.Attributes["action"] == null ? null : tagNode.Attributes["action"].Value); Tag tag = new Tag(name); if (tagNames == null) tagNames = new ArrayList(); tagNames.Add(name); tag.Action = action; //XmlNodeList attributeList = tagNode.SelectNodes("attribute"); XmlNodeList attributeList = tagNode.SelectNodes("attribute"); foreach (XmlNode attributeNode in attributeList) { if (!attributeNode.HasChildNodes) { Attribute attribute = getCommonAttributeByName(attributeNode.Attributes["name"].Value); if (attribute != null) { String onInvalid = (attributeNode.Attributes["onInvalid"] == null ? null : attributeNode.Attributes["onInvalid"].Value); String description = (attributeNode.Attributes["description"] == null ? null : attributeNode.Attributes["description"].Value); if (onInvalid != null && onInvalid.Length > 0) attribute.OnInvalid = onInvalid; if (description != null && description.Length > 0) attribute.Description = description; tag.addAttribute((org.owasp.validator.html.model.Attribute)attribute.Clone()); } else { //TODO: make this work with .NET //throw new PolicyException("Attribute '"+XMLUtil.getAttributeValue(attributeNode,"name")+"' was referenced as a common attribute in definition of '"+tag.getName()+"', but does not exist in <common-attributes>"); } } else { /* Custom attribute for this tag */ Attribute attribute = new Attribute(attributeNode.Attributes["name"].Value); attribute.OnInvalid = (attributeNode.Attributes["onInvalid"] != null ? attributeNode.Attributes["onInvalid"].Value : null); attribute.Description = (attributeNode.Attributes["description"] != null ? attributeNode.Attributes["description"].Value : null); XmlNode regExpListNode = attributeNode.SelectNodes("regexp-list")[0]; if (regExpListNode != null) { XmlNodeList regExpList = regExpListNode.SelectNodes("regexp"); foreach (XmlNode regExpNode in regExpList) { string regExpName = (regExpNode.Attributes["name"] == null ? null : regExpNode.Attributes["name"].Value); string value = (regExpNode.Attributes["value"] == null ? null : regExpNode.Attributes["value"].Value); if (regExpName != null && regExpName.Length > 0) { //AntiSamyPattern pattern = getRegularExpression(regExpName); string pattern = getRegularExpression(regExpName); if (pattern != null) attribute.addAllowedRegExp(pattern); //attribute.addAllowedRegExp(pattern.Pattern); else { throw new PolicyException("Regular expression '" + regExpName + "' was referenced as a common regexp in definition of '" + tag.Name + "', but does not exist in <common-regexp>"); } } else if (value != null && value.Length > 0) { //TODO: see if I need to reimplement pattern.compile attribute.addAllowedRegExp(REGEXP_BEGIN + value + REGEXP_END); } } } XmlNode literalListNode = attributeNode.SelectNodes("literal-list")[0]; if (literalListNode != null) { XmlNodeList literalNodes = literalListNode.SelectNodes("literal"); foreach (XmlNode literalNode in literalNodes) { string value = (literalNode.Attributes["value"] == null ? null : literalNode.Attributes["value"].Value); if (value != null && value.Length > 0) { attribute.addAllowedValue(value); } else if (literalNode.Value != null) { attribute.addAllowedValue(literalNode.Value); } } } tag.addAttribute(attribute); } } tags.Add(name, tag); } } return tags; }
private void recursiveValidateTag(HtmlNode node) { int maxinputsize = int.Parse(policy.getDirective("maxInputSize")); num++; HtmlNode parentNode = node.ParentNode; HtmlNode tmp = null; string tagName = node.Name; //check this out //might not be robust enough if (tagName.ToLower().Equals("#text")) // || tagName.ToLower().Equals("#comment")) { return; } Tag tag = policy.getTagByName(tagName.ToLower()); if (tag == null || "filter".Equals(tag.Action)) { StringBuilder errBuff = new StringBuilder(); if (tagName == null || tagName.Trim().Equals("")) { errBuff.Append("An unprocessable "); } else { errBuff.Append("The <b>" + HTMLEntityEncoder.htmlEntityEncode(tagName.ToLower()) + "</b> "); } errBuff.Append("tag has been filtered for security reasons. The contents of the tag will "); errBuff.Append("remain in place."); errorMessages.Add(errBuff.ToString()); for (int i = 0; i < node.ChildNodes.Count; i++) { tmp = node.ChildNodes[i]; recursiveValidateTag(tmp); if (tmp.ParentNode == null) { i--; } } promoteChildren(node); return; } else if ("validate".Equals(tag.Action)) { if ("style".Equals(tagName.ToLower()) && policy.getTagByName("style") != null) { CssScanner styleScanner = new CssScanner(policy); try { CleanResults cr = styleScanner.scanStyleSheet(node.FirstChild.InnerHtml, maxinputsize); foreach (string msg in cr.getErrorMessages()) { errorMessages.Add(msg.ToString()); } /* * If IE gets an empty style tag, i.e. <style/> * it will break all CSS on the page. I wish I * was kidding. So, if after validation no CSS * properties are left, we would normally be left * with an empty style tag and break all CSS. To * prevent that, we have this check. */ if (cr.getCleanHTML() == null || cr.getCleanHTML().Equals("")) { //node.getFirstChild().setNodeValue("/* */"); node.FirstChild.InnerHtml = "/* */"; } else { //node.getFirstChild().setNodeValue(cr.getCleanHTML()); node.FirstChild.InnerHtml = cr.getCleanHTML(); } } // catch (DomException e) // { // addError(ErrorMessageUtil.ERROR_CSS_TAG_MALFORMED, new Object[] { HTMLEntityEncoder.htmlEntityEncode(node.getFirstChild().getNodeValue()) }); // parentNode.removeChild(node); // } catch (ScanException e) { Console.WriteLine("Scan Exception: " + e.Message); //addError(ErrorMessageUtil.ERROR_CSS_TAG_MALFORMED, new Object[] { HTMLEntityEncoder.htmlEntityEncode(node.getFirstChild().getNodeValue()) }); parentNode.RemoveChild(node); } } HtmlAttribute attribute = null; for (int currentAttributeIndex = 0; currentAttributeIndex < node.Attributes.Count; currentAttributeIndex++) { attribute = node.Attributes[currentAttributeIndex]; string name = attribute.Name; string _value = attribute.Value; Attribute attr = tag.getAttributeByName(name); if (attr == null) { attr = policy.getGlobalAttributeByName(name); } bool isAttributeValid = false; if ("style".Equals(name.ToLower()) && attr != null) { CssScanner styleScanner = new CssScanner(policy); try { CleanResults cr = styleScanner.scanInlineStyle(_value, tagName, maxinputsize); //attribute.setNodeValue(cr.getCleanHTML()); attribute.Value = cr.getCleanHTML(); ArrayList cssScanErrorMessages = cr.getErrorMessages(); foreach (string msg in cr.getErrorMessages()) { errorMessages.Add(msg.ToString()); } } /* * catch (DOMException e) * { * * addError(ErrorMessageUtil.ERROR_CSS_ATTRIBUTE_MALFORMED, new Object[] { tagName, HTMLEntityEncoder.htmlEntityEncode(node.getNodeValue()) }); * * ele.removeAttribute(name); * currentAttributeIndex--; * * } */ catch (ScanException ex) { Console.WriteLine(ex.Message); //addError(ErrorMessageUtil.ERROR_CSS_ATTRIBUTE_MALFORMED, new Object[] { tagName, HTMLEntityEncoder.htmlEntityEncode(node.getNodeValue()) }); //ele.removeAttribute(name); currentAttributeIndex--; } } else { if (attr != null) { //try to find out how robust this is - do I need to do this in a loop? _value = HtmlEntity.DeEntitize(_value); foreach (string allowedValue in attr.AllowedValues) { if (isAttributeValid) { break; } if (allowedValue != null && allowedValue.ToLower().Equals(_value.ToLower())) { isAttributeValid = true; } } foreach (string ptn in attr.AllowedRegExp) { if (isAttributeValid) { break; } string pattern = "^" + ptn + "$"; Match m = Regex.Match(_value, pattern); if (m.Success) { isAttributeValid = true; } } if (!isAttributeValid) { string onInvalidAction = attr.OnInvalid; StringBuilder errBuff = new StringBuilder(); errBuff.Append("The <b>" + HTMLEntityEncoder.htmlEntityEncode(tagName) + "</b> tag contained an attribute that we couldn't process. "); errBuff.Append("The <b>" + HTMLEntityEncoder.htmlEntityEncode(name) + "</b> attribute had a value of <u>" + HTMLEntityEncoder.htmlEntityEncode(_value) + "</u>. "); errBuff.Append("This value could not be accepted for security reasons. We have chosen to "); //Console.WriteLine(policy); if ("removeTag".Equals(onInvalidAction)) { parentNode.RemoveChild(node); errBuff.Append("remove the <b>" + HTMLEntityEncoder.htmlEntityEncode(tagName) + "</b> tag and its contents in order to process this input. "); } else if ("filterTag".Equals(onInvalidAction)) { for (int i = 0; i < node.ChildNodes.Count; i++) { tmp = node.ChildNodes[i]; recursiveValidateTag(tmp); if (tmp.ParentNode == null) { i--; } } promoteChildren(node); errBuff.Append("filter the <b>" + HTMLEntityEncoder.htmlEntityEncode(tagName) + "</b> tag and leave its contents in place so that we could process this input."); } else { node.Attributes.Remove(attr.Name); currentAttributeIndex--; errBuff.Append("remove the <b>" + HTMLEntityEncoder.htmlEntityEncode(name) + "</b> attribute from the tag and leave everything else in place so that we could process this input."); } errorMessages.Add(errBuff.ToString()); if ("removeTag".Equals(onInvalidAction) || "filterTag".Equals(onInvalidAction)) { return; // can't process any more if we remove/filter the tag } } } else { StringBuilder errBuff = new StringBuilder(); errBuff.Append("The <b>" + HTMLEntityEncoder.htmlEntityEncode(name)); errBuff.Append("</b> attribute of the <b>" + HTMLEntityEncoder.htmlEntityEncode(tagName) + "</b> tag has been removed for security reasons. "); errBuff.Append("This removal should not affect the display of the HTML submitted."); errorMessages.Add(errBuff.ToString()); node.Attributes.Remove(name); currentAttributeIndex--; } // end if attribute is or is not found in policy file } // end if style.equals("name") } // end while loop through attributes for (int i = 0; i < node.ChildNodes.Count; i++) { tmp = node.ChildNodes[i]; recursiveValidateTag(tmp); if (tmp.ParentNode == null) { i--; } } } else if ("truncate".Equals(tag.Action)) { Console.WriteLine("truncate"); HtmlAttributeCollection nnmap = node.Attributes; while (nnmap.Count > 0) { StringBuilder errBuff = new StringBuilder(); errBuff.Append("The <b>" + HTMLEntityEncoder.htmlEntityEncode(nnmap[0].Name)); errBuff.Append("</b> attribute of the <b>" + HTMLEntityEncoder.htmlEntityEncode(tagName) + "</b> tag has been removed for security reasons. "); errBuff.Append("This removal should not affect the display of the HTML submitted."); node.Attributes.Remove(nnmap[0].Name); errorMessages.Add(errBuff.ToString()); } HtmlNodeCollection cList = node.ChildNodes; int i = 0; int j = 0; int length = cList.Count; while (i < length) { HtmlNode nodeToRemove = cList[j]; if (nodeToRemove.NodeType != HtmlNodeType.Text && nodeToRemove.NodeType != HtmlNodeType.Comment) { node.RemoveChild(nodeToRemove); } else { j++; } i++; } } else { errorMessages.Add("The <b>" + HTMLEntityEncoder.htmlEntityEncode(tagName) + "</b> tag has been removed for security reasons."); parentNode.RemoveChild(node); } }
/// <summary> Private method for parsing the <tag-rules> from the XML file.</summary> /// <param name="root">The root element for <tag-rules> /// </param> /// <returns> A List<Tag> containing the rules. /// </returns> /// <throws> PolicyException </throws> private Hashtable parseTagRules(XmlNode tagAttributeListNode) { Hashtable tags = new Hashtable(); XmlNodeList tagList = tagAttributeListNode.SelectNodes("tag"); foreach (XmlNode tagNode in tagList) { if (tagNode.NodeType == XmlNodeType.Element) { String name = (tagNode.Attributes["name"] == null ? null : tagNode.Attributes["name"].Value); String action = (tagNode.Attributes["action"] == null ? null : tagNode.Attributes["action"].Value); Tag tag = new Tag(name); if (tagNames == null) { tagNames = new ArrayList(); } tagNames.Add(name); tag.Action = action; //XmlNodeList attributeList = tagNode.SelectNodes("attribute"); XmlNodeList attributeList = tagNode.SelectNodes("attribute"); foreach (XmlNode attributeNode in attributeList) { if (!attributeNode.HasChildNodes) { Attribute attribute = getCommonAttributeByName(attributeNode.Attributes["name"].Value); if (attribute != null) { String onInvalid = (attributeNode.Attributes["onInvalid"] == null ? null : attributeNode.Attributes["onInvalid"].Value); String description = (attributeNode.Attributes["description"] == null ? null : attributeNode.Attributes["description"].Value); if (onInvalid != null && onInvalid.Length > 0) { attribute.OnInvalid = onInvalid; } if (description != null && description.Length > 0) { attribute.Description = description; } tag.addAttribute((org.owasp.validator.html.model.Attribute)attribute.Clone()); } else { //TODO: make this work with .NET //throw new PolicyException("Attribute '"+XMLUtil.getAttributeValue(attributeNode,"name")+"' was referenced as a common attribute in definition of '"+tag.getName()+"', but does not exist in <common-attributes>"); } } else { /* Custom attribute for this tag */ Attribute attribute = new Attribute(attributeNode.Attributes["name"].Value); attribute.OnInvalid = (attributeNode.Attributes["onInvalid"] != null ? attributeNode.Attributes["onInvalid"].Value : null); attribute.Description = (attributeNode.Attributes["description"] != null ? attributeNode.Attributes["description"].Value : null); XmlNode regExpListNode = attributeNode.SelectNodes("regexp-list")[0]; if (regExpListNode != null) { XmlNodeList regExpList = regExpListNode.SelectNodes("regexp"); foreach (XmlNode regExpNode in regExpList) { string regExpName = (regExpNode.Attributes["name"] == null ? null : regExpNode.Attributes["name"].Value); string value = (regExpNode.Attributes["value"] == null ? null : regExpNode.Attributes["value"].Value); if (regExpName != null && regExpName.Length > 0) { //AntiSamyPattern pattern = getRegularExpression(regExpName); string pattern = getRegularExpression(regExpName); if (pattern != null) { attribute.addAllowedRegExp(pattern); } //attribute.addAllowedRegExp(pattern.Pattern); else { throw new PolicyException("Regular expression '" + regExpName + "' was referenced as a common regexp in definition of '" + tag.Name + "', but does not exist in <common-regexp>"); } } else if (value != null && value.Length > 0) { //TODO: see if I need to reimplement pattern.compile attribute.addAllowedRegExp(REGEXP_BEGIN + value + REGEXP_END); } } } XmlNode literalListNode = attributeNode.SelectNodes("literal-list")[0]; if (literalListNode != null) { XmlNodeList literalNodes = literalListNode.SelectNodes("literal"); foreach (XmlNode literalNode in literalNodes) { string value = (literalNode.Attributes["value"] == null ? null : literalNode.Attributes["value"].Value); if (value != null && value.Length > 0) { attribute.addAllowedValue(value); } else if (literalNode.Value != null) { attribute.addAllowedValue(literalNode.Value); } } } tag.addAttribute(attribute); } } tags.Add(name, tag); } } return(tags); }