Beispiel #1
0
        /// <summary>Get the allowed attributes defined in the provided tag <see cref="XmlElement"/>.</summary>
        /// <param name="tagNode">The node to retrieve the values from.</param>
        /// <param name="tagName">The name of the tag which has attributes defined.</param>
        /// <param name="parseContext">The parse context.</param>
        /// <returns>A dictionary with the allowed attributes.</returns>
        private static Dictionary <string, Attribute> GetTagAllowedAttributes(XmlElement tagNode, string tagName, ParseContext parseContext)
        {
            var allowedAttributes = new Dictionary <string, Attribute>();

            foreach (XmlElement attributeNode in PolicyParserUtil.GetChildrenByTagName(tagNode, "attribute"))
            {
                string attributeName = XmlUtil.GetAttributeValue(attributeNode, "name");
                if (!attributeNode.HasChildNodes)
                {
                    /* All they provided was the name, so they must want a common attribute. */
                    Attribute attribute = parseContext.commonAttributes.GetValueOrTypeDefault(attributeName.ToLowerInvariant());

                    if (attribute != null)
                    {
                        /* If they provide onInvalid/description values here they will override the common values. */
                        string onInvalid   = XmlUtil.GetAttributeValue(attributeNode, "onInvalid");
                        string description = XmlUtil.GetAttributeValue(attributeNode, "description");

                        if (!string.IsNullOrEmpty(onInvalid))
                        {
                            attribute.OnInvalid = onInvalid;
                        }
                        if (!string.IsNullOrEmpty(description))
                        {
                            attribute.Description = description;
                        }

                        allowedAttributes.Add(attributeName, attribute.Clone() as Attribute);
                    }
                    else
                    {
                        throw new PolicyException($"Attribute '{XmlUtil.GetAttributeValue(attributeNode, "name")}' was referenced as a common attribute in definition of '{tagName}', but does not exist in <common-attributes>");
                    }
                }
                else
                {
                    /* Custom attribute for this tag */
                    var attribute = new Attribute(XmlUtil.GetAttributeValue(attributeNode, "name"))
                    {
                        AllowedValues = PolicyParserUtil.GetAttributeOrValueFromGrandchildren(attributeNode, "literal-list", "literal", "value"),
                        AllowedRegExp = GetAllowedRegexpsForRules(attributeNode, tagName, parseContext),
                        Description   = XmlUtil.GetAttributeValue(attributeNode, "description"),
                        OnInvalid     = XmlUtil.GetAttributeValue(attributeNode, "onInvalid")
                    };

                    allowedAttributes.Add(attributeName, attribute);
                }
            }

            return(allowedAttributes);
        }
Beispiel #2
0
        /// <summary>A method for returning one of the dynamic &lt;common-attribute&gt; entries by name.</summary>
        /// <param name="name">The name of the dynamic common-attribute we want to look up.</param>
        /// <returns>An <see cref="Attribute"/> associated with the common-attribute lookup name specified, or null if not found.</returns>
        internal Attribute GetDynamicAttributeByName(string name)
        {
            Attribute dynamicAttribute = null;
            string    nameLowercase    = name.ToLowerInvariant();

            foreach (KeyValuePair <string, Attribute> attributeEntry in dynamicAttributes)
            {
                if (nameLowercase.StartsWith(attributeEntry.Key))
                {
                    dynamicAttribute = attributeEntry.Value;
                    break;
                }
            }
            return(dynamicAttribute);
        }
Beispiel #3
0
 /// <summary> Go through &lt;global-tag-attributes&gt; section of the policy file.</summary>
 /// <param name="globalAttributeListNode">Top level of &lt;global-tag-attributes&gt;</param>
 /// <param name="parseContext">The <see cref="ParseContext"/> containing the global attributes dictionary to fill.</param>
 private static void ParseGlobalAttributes(XmlNode globalAttributeListNode, ParseContext parseContext)
 {
     foreach (XmlElement node in PolicyParserUtil.GetChildrenByTagName(globalAttributeListNode, "attribute"))
     {
         string    name  = XmlUtil.GetAttributeValue(node, "name");
         Attribute toAdd = parseContext.commonAttributes.GetValueOrTypeDefault(name.ToLowerInvariant());
         if (toAdd != null)
         {
             parseContext.globalAttributes.Add(name.ToLowerInvariant(), toAdd);
         }
         else
         {
             throw new PolicyException($"Global attribute '{name}' was not defined in <common-attributes>");
         }
     }
 }
Beispiel #4
0
 /// <summary> Go through &lt;dynamic-tag-attributes&gt; section of the policy file.</summary>
 /// <param name="dynamicAttributeListNode">Top level of &lt;dynamic-tag-attributes&gt;</param>
 /// <param name="parseContext">The <see cref="ParseContext"/> containing the dynamic attributes dictionary to fill.</param>
 private static void ParseDynamicAttributes(XmlNode dynamicAttributeListNode, ParseContext parseContext)
 {
     foreach (XmlElement node in PolicyParserUtil.GetChildrenByTagName(dynamicAttributeListNode, "attribute"))
     {
         string    name  = XmlUtil.GetAttributeValue(node, "name");
         Attribute toAdd = parseContext.commonAttributes.GetValueOrTypeDefault(name.ToLowerInvariant());
         if (toAdd != null)
         {
             string attributeName = name.ToLowerInvariant().Substring(0, name.Length - 1);
             parseContext.dynamicAttributes.Add(attributeName, toAdd);
         }
         else
         {
             throw new PolicyException($"Dynamic attribute '{name}' was not defined in <common-attributes>");
         }
     }
 }
Beispiel #5
0
        /// <summary> Go through the &lt;common-attributes&gt; section of the policy file.</summary>
        /// <param name="commonAttributeListNode">Top level of &lt;common-attributes&gt;.</param>
        /// <param name="parseContext">The <see cref="ParseContext"/> containing the common attributes dictionary to fill.</param>
        private static void ParseCommonAttributes(XmlNode commonAttributeListNode, ParseContext parseContext)
        {
            foreach (XmlElement node in PolicyParserUtil.GetChildrenByTagName(commonAttributeListNode, "attribute"))
            {
                // 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)
                {
                    AllowedRegExp = GetAllowedRegexpsForCommonAttributes(node, parseContext),
                    AllowedValues = PolicyParserUtil.GetAttributeOrValueFromGrandchildren(node, "literal-list", "literal", "value"),
                    Description   = XmlUtil.GetAttributeValue(node, "description"),
                    OnInvalid     = string.IsNullOrEmpty(onInvalid) ? Constants.DEFAULT_ONINVALID : onInvalid,
                };

                parseContext.commonAttributes.Add(name.ToLowerInvariant(), attribute);
            }
        }
Beispiel #6
0
        private bool ProcessAttributes(HtmlNode node, Tag tag)
        {
            string tagName = tag.Name;
            int    currentAttributeIndex = 0;

            while (currentAttributeIndex < node.Attributes.Count)
            {
                HtmlAttribute htmlAttribute = node.Attributes[currentAttributeIndex];
                string        name          = htmlAttribute.Name;
                string        value         = htmlAttribute.Value;
                Attribute     attribute     = tag.GetAttributeByName(name);

                if (attribute == null)
                {
                    attribute = Policy.GetGlobalAttributeByName(name);

                    // Not a global attribute, perhaps it is a dynamic attribute, if allowed.
                    if (attribute == null && Policy.AllowsDynamicAttributes)
                    {
                        attribute = Policy.GetDynamicAttributeByName(name);
                    }
                }

                if (name.ToLowerInvariant() == "style" && attribute != null)
                {
                    var styleScanner = new CssScanner(Policy);

                    try
                    {
                        CleanResults cleanInlineStyle = styleScanner.ScanInlineStyle(value, tagName);
                        htmlAttribute.Value = cleanInlineStyle.GetCleanHtml();
                        errorMessages.AddRange(cleanInlineStyle.GetErrorMessages());
                    }
                    catch (Exception exc)
                    {
                        if (exc is ScanException || exc is ParseException)
                        {
                            AddError(Constants.ERROR_CSS_ATTRIBUTE_MALFORMED,
                                     HtmlEntityEncoder.HtmlEntityEncode(value), HtmlEntityEncoder.HtmlEntityEncode(tagName));

                            node.Attributes.Remove(name);
                            currentAttributeIndex--;
                        }
                        else
                        {
                            throw;
                        }
                    }
                }
                else
                {
                    if (attribute != null)
                    {
                        value = HtmlEntity.DeEntitize(value);
                        string lowerCaseValue = value.ToLowerInvariant();

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

                        if (!isAttributeValid)
                        {
                            string onInvalidAction = attribute.OnInvalid;
                            if (onInvalidAction == "removeTag")
                            {
                                RemoveNode(node);
                                AddError(Constants.ERROR_ATTRIBUTE_INVALID_REMOVED,
                                         HtmlEntityEncoder.HtmlEntityEncode(tagName),
                                         HtmlEntityEncoder.HtmlEntityEncode(name),
                                         HtmlEntityEncoder.HtmlEntityEncode(value));
                            }
                            else if (onInvalidAction == "filterTag")
                            {
                                // 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,
                                         HtmlEntityEncoder.HtmlEntityEncode(tagName),
                                         HtmlEntityEncoder.HtmlEntityEncode(name),
                                         HtmlEntityEncoder.HtmlEntityEncode(value));
                            }
                            else if (onInvalidAction == "encodeTag")
                            {
                                // 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,
                                         HtmlEntityEncoder.HtmlEntityEncode(tagName),
                                         HtmlEntityEncoder.HtmlEntityEncode(name),
                                         HtmlEntityEncoder.HtmlEntityEncode(value));
                            }
                            else
                            {
                                // Just remove the attribute
                                node.Attributes.Remove(attribute.Name);
                                currentAttributeIndex--;
                                AddError(Constants.ERROR_ATTRIBUTE_INVALID,
                                         HtmlEntityEncoder.HtmlEntityEncode(tagName),
                                         HtmlEntityEncoder.HtmlEntityEncode(name),
                                         HtmlEntityEncoder.HtmlEntityEncode(value));
                            }

                            if (new string[] { "removeTag", "filterTag", "encodeTag" }.Contains(onInvalidAction))
                            {
                                return(false); // Can't process any more if we remove/filter/encode the tag
                            }
                        }
                    }
                    else
                    {
                        AddError(Constants.ERROR_ATTRIBUTE_NOT_IN_POLICY,
                                 HtmlEntityEncoder.HtmlEntityEncode(tagName),
                                 HtmlEntityEncoder.HtmlEntityEncode(name),
                                 HtmlEntityEncoder.HtmlEntityEncode(value));
                        node.Attributes.Remove(name);
                        currentAttributeIndex--;
                    }
                }

                currentAttributeIndex++;
            }

            return(true);
        }
Beispiel #7
0
        private void ValidateTag(HtmlNode node, HtmlNode parentNode, string tagName, Tag tag, bool isMasqueradingParam)
        {
            // If doing <param> as <embed>, now is the time to convert it.
            string nameAttributeValue = null;

            if (isMasqueradingParam)
            {
                nameAttributeValue = node.Attributes["name"]?.Value;
                if (!string.IsNullOrEmpty(nameAttributeValue))
                {
                    string valueAttributeValue = node.Attributes["value"]?.Value;
                    node.SetAttributeValue(nameAttributeValue, valueAttributeValue);
                    node.SetAttributeValue("name", null);
                    node.SetAttributeValue("value", null);
                    tag = Policy.GetTagByName("embed");
                }
            }

            /*
             * Check to see if it's a <style> tag. We have to special case this
             * tag so we can hand it off to the custom style sheet validating parser.
             */
            if (tagName.ToLowerInvariant() == "style" && Policy.GetTagByName("style") != null && !ProcessStyleTag(node, parentNode))
            {
                return;
            }

            /*
             * Go through the attributes in the tainted tag and validate them against the values we have for them.
             * If we don't have a rule for the attribute we remove the attribute.
             */
            if (!ProcessAttributes(node, tag))
            {
                return;
            }

            if (Policy.AddNofollowInAnchors && tagName.ToLowerInvariant() == "a")
            {
                node.SetAttributeValue("rel", "nofollow");
            }

            if (tagName.ToLowerInvariant() == "a")
            {
                bool addNofollow = Policy.AddNofollowInAnchors;
                bool addNoopenerAndNoreferrer = false;

                if (Policy.AddNoopenerAndNoreferrerInAnchors)
                {
                    string targetAttribute = node.GetAttributeValue("target", null);
                    if (targetAttribute != null && targetAttribute.ToLowerInvariant() == "_blank")
                    {
                        addNoopenerAndNoreferrer = true;
                    }
                }

                string relAttribute = node.GetAttributeValue("rel", null);
                string relValue     = Attribute.MergeRelValuesInAnchor(addNofollow, addNoopenerAndNoreferrer, relAttribute ?? string.Empty);
                if (!string.IsNullOrEmpty(relValue))
                {
                    node.SetAttributeValue("rel", relValue.Trim());
                }
            }

            ProcessChildren(node);

            // If we have been dealing with a <param> that has been converted to an <embed>, convert it back.
            if (isMasqueradingParam && !string.IsNullOrEmpty(nameAttributeValue))
            {
                string valueAttributeValue = node.Attributes[nameAttributeValue]?.Value;
                node.SetAttributeValue("name", nameAttributeValue);
                node.SetAttributeValue("value", string.IsNullOrEmpty(valueAttributeValue) ? string.Empty : valueAttributeValue);

                // Original attribute may have been removed already by the validation
                if (node.Attributes[nameAttributeValue] != null)
                {
                    node.Attributes.Remove(node.Attributes[nameAttributeValue]);
                }
            }
        }