Advertisement
Willcode4cash

HTML Whitelist/Blacklist Parser

Sep 9th, 2016
118
0
Never
Not a member of Pastebin yet? Sign Up, it unlocks many cool features!
C# 7.15 KB | None | 0 0
  1. namespace MyApp.Extensions
  2. {
  3.     using System;
  4.     using System.Collections.Generic;
  5.     using System.Linq;
  6.     using System.Text;
  7.     using HtmlAgilityPack;
  8.  
  9.     public static class HtmlUtility
  10.     {
  11.         private static readonly Dictionary<string, string[]> ValidHtmlTags =
  12.             new Dictionary<string, string[]>
  13.             {
  14.                 {"p", new string[]          {"style", "class", "align"}},
  15.                 {"div", new string[]        {"style", "class", "align"}},
  16.                 {"span", new string[]       {"style", "class"}},
  17.                 {"br", new string[]         {"style", "class"}},
  18.                 {"hr", new string[]         {"style", "class"}},
  19.                 {"label", new string[]      {"style", "class"}},
  20.  
  21.                 {"h1", new string[]         {"style", "class"}},
  22.                 {"h2", new string[]         {"style", "class"}},
  23.                 {"h3", new string[]         {"style", "class"}},
  24.                 {"h4", new string[]         {"style", "class"}},
  25.                 {"h5", new string[]         {"style", "class"}},
  26.                 {"h6", new string[]         {"style", "class"}},
  27.  
  28.                 {"font", new string[]       {"style", "class", "color", "face", "size"}},
  29.                 {"strong", new string[]     {"style", "class"}},
  30.                 {"b", new string[]          {"style", "class"}},
  31.                 {"em", new string[]         {"style", "class"}},
  32.                 {"i", new string[]          {"style", "class"}},
  33.                 {"u", new string[]          {"style", "class"}},
  34.                 {"strike", new string[]     {"style", "class"}},
  35.                 {"ol", new string[]         {"style", "class"}},
  36.                 {"ul", new string[]         {"style", "class"}},
  37.                 {"li", new string[]         {"style", "class"}},
  38.                 {"blockquote", new string[] {"style", "class"}},
  39.                 {"code", new string[]       {"style", "class"}},
  40.  
  41.                 {"a", new string[]          {"style", "class", "href", "title"}},
  42.  
  43.                 {"img", new string[]        {"style", "class", "src", "height", "width", "alt", "title", "hspace", "vspace", "border"}},
  44.                 {"table", new string[]      {"style", "class"}},
  45.                 {"thead", new string[]      {"style", "class"}},
  46.                 {"tbody", new string[]      {"style", "class"}},
  47.                 {"tfoot", new string[]      {"style", "class"}},
  48.                 {"th", new string[]         {"style", "class", "scope"}},
  49.                 {"tr", new string[]         {"style", "class"}},
  50.                 {"td", new string[]         {"style", "class", "colspan"}},
  51.  
  52.                 {"q", new string[]          {"style", "class", "cite"}},
  53.                 {"cite", new string[]       {"style", "class"}},
  54.                 {"abbr", new string[]       {"style", "class"}},
  55.                 {"acronym", new string[]    {"style", "class"}},
  56.                 {"del", new string[]        {"style", "class"}},
  57.                 {"ins", new string[]        {"style", "class"}}
  58.             };
  59.  
  60.         /// <summary>
  61.         /// Takes raw HTML input and cleans against a whitelist
  62.         /// </summary>
  63.         /// <param name="source">Html source</param>
  64.         /// <returns>Clean output</returns>
  65.         public static string SanitizeHtml(this string source)
  66.         {
  67.             HtmlDocument html = GetHtml(source);
  68.             if (html == null) return String.Empty;
  69.  
  70.             // All the nodes
  71.             HtmlNode allNodes = html.DocumentNode;
  72.  
  73.             // Select whitelist tag names
  74.             string[] whitelist = (from kv in ValidHtmlTags
  75.                                   select kv.Key).ToArray();
  76.  
  77.             // Scrub tags not in whitelist
  78.             CleanNodes(allNodes, whitelist);
  79.  
  80.             // Filter the attributes of the remaining
  81.             foreach (KeyValuePair<string, string[]> tag in ValidHtmlTags)
  82.             {
  83.                 IEnumerable<HtmlNode> nodes = (from n in allNodes.DescendantsAndSelf()
  84.                                                where n.Name == tag.Key
  85.                                                select n);
  86.  
  87.                 if (nodes == null) continue;
  88.  
  89.                 foreach (var n in nodes)
  90.                 {
  91.                     if (!n.HasAttributes) continue;
  92.  
  93.                     // Get all the allowed attributes for this tag
  94.                     HtmlAttribute[] attr = n.Attributes.ToArray();
  95.                     foreach (HtmlAttribute a in attr)
  96.                     {
  97.                         if (!tag.Value.Contains(a.Name))
  98.                         {
  99.                             a.Remove(); // Wasn't in the list
  100.                         }
  101.                         else
  102.                         {
  103.                             // AntiXss
  104.                             a.Value =
  105.                                 Microsoft.Security.Application.Encoder.UrlPathEncode(a.Value);
  106.                         }
  107.                     }
  108.                 }
  109.             }
  110.  
  111.             return allNodes.InnerHtml;
  112.         }
  113.  
  114.         /// <summary>
  115.         /// Takes a raw source and removes all HTML tags
  116.         /// </summary>
  117.         /// <param name="source"></param>
  118.         /// <returns></returns>
  119.         public static string StripHtml(this string source)
  120.         {
  121.             source = SanitizeHtml(source);
  122.  
  123.             // No need to continue if we have no clean Html
  124.             if (String.IsNullOrEmpty(source))
  125.                 return String.Empty;
  126.  
  127.             HtmlDocument html = GetHtml(source);
  128.             StringBuilder result = new StringBuilder();
  129.  
  130.             // For each node, extract only the innerText
  131.             foreach (HtmlNode node in html.DocumentNode.ChildNodes)
  132.                 result.Append(node.InnerText);
  133.  
  134.             return result.ToString();
  135.         }
  136.  
  137.         /// <summary>
  138.         /// Recursively delete nodes not in the whitelist
  139.         /// </summary>
  140.         private static void CleanNodes(HtmlNode node, string[] whitelist)
  141.         {
  142.             if (node.NodeType == HtmlNodeType.Element)
  143.             {
  144.                 if (!whitelist.Contains(node.Name))
  145.                 {
  146.                     node.ParentNode.RemoveChild(node);
  147.                     return; // We're done
  148.                 }
  149.             }
  150.  
  151.             if (node.HasChildNodes)
  152.                 CleanChildren(node, whitelist);
  153.         }
  154.  
  155.         /// <summary>
  156.         /// Apply CleanNodes to each of the child nodes
  157.         /// </summary>
  158.         private static void CleanChildren(HtmlNode parent, string[] whitelist)
  159.         {
  160.             for (int i = parent.ChildNodes.Count - 1; i >= 0; i--)
  161.                 CleanNodes(parent.ChildNodes[i], whitelist);
  162.         }
  163.  
  164.         /// <summary>
  165.         /// Helper function that returns an HTML document from text
  166.         /// </summary>
  167.         private static HtmlDocument GetHtml(string source)
  168.         {
  169.             var html = new HtmlDocument
  170.             {
  171.                 OptionFixNestedTags = true,
  172.                 OptionAutoCloseOnEnd = true,
  173.                 OptionDefaultStreamEncoding = Encoding.UTF8
  174.             };
  175.  
  176.             html.LoadHtml(source);
  177.  
  178.             return html;
  179.         }
  180.     }
  181. }
Advertisement
Add Comment
Please, Sign In to add comment
Advertisement