private static void SanitizeHtmlNode(HtmlAgilityPack.HtmlNode node) { if (node.NodeType == HtmlAgilityPack.HtmlNodeType.Element) { // Remove elements if they're blacklisted if (_blacklistedTags.Contains(node.Name)) { node.Remove(); return; } // For style tags, validate the inner contents of our // style declarations. if (node.Name == "style" && !IsValidStyle(node.InnerText)) { node.Remove(); return; } // Now filter out invalid attributes, most of which will be removed, // but certain invalid attributes (such as blacklisted MIME types) will result // in removing the entire element. if (node.HasAttributes) { for (int i = node.Attributes.Count - 1; i >= 0; i--) { HtmlAgilityPack.HtmlAttribute currentAttribute = node.Attributes[i]; // Lowercase the attribute and its value for ease of use. var attr = currentAttribute.Name.ToLower(); var val = currentAttribute.Value.ToLower(); // Remove event handlers if (attr.StartsWith("on")) { node.Attributes.Remove(currentAttribute); } // Remove stuff with a forbidden MIME type - note that this removes // the entire node, not just the attribute. else if (attr == "type" && val != null && IsBlacklistedMimeType(val)) { node.Remove(); return; } // Remove CSS Expressions else if (attr == "style" && val != null && (!IsValidStyle(val) || HasScriptLinks(val))) { node.Attributes.Remove(currentAttribute); } // Remove script links from all attributes else if (val != null && HasScriptLinks(val)) { node.Attributes.Remove(currentAttribute); } } } } // Look through child nodes recursively if (node.HasChildNodes) { for (int i = node.ChildNodes.Count - 1; i >= 0; i--) { SanitizeHtmlNode(node.ChildNodes[i]); } } }