2010-06-24 5 views
28

J'essaie de créer une fonction qui supprime les balises html et les attributs qui ne figurent pas dans une liste blanche. J'ai le code HTML suivant:Tags de bande HTML Pack Agility NOT IN whitelist

<b>first text </b> 
<b>second text here 
     <a>some text here</a> 
<a>some text here</a> 

</b> 
<a>some twxt here</a> 

J'utilise pack agilité HTML et le code que j'ai à ce jour est la suivante:

static List<string> WhiteNodeList = new List<string> { "b" }; 
static List<string> WhiteAttrList = new List<string> { }; 
static HtmlNode htmlNode; 
public static void RemoveNotInWhiteList(out string _output, HtmlNode pNode, List<string> pWhiteList, List<string> attrWhiteList) 
{ 

// remove all attributes not on white list 
foreach (var item in pNode.ChildNodes) 
{ 
    item.Attributes.Where(u => attrWhiteList.Contains(u.Name) == false).ToList().ForEach(u => RemoveAttribute(u)); 

} 

// remove all html and their innerText and attributes if not on whitelist. 
//pNode.ChildNodes.Where(u => pWhiteList.Contains(u.Name) == false).ToList().ForEach(u => u.Remove()); 
//pNode.ChildNodes.Where(u => pWhiteList.Contains(u.Name) == false).ToList().ForEach(u => u.ParentNode.ReplaceChild(ConvertHtmlToNode(u.InnerHtml),u)); 
//pNode.ChildNodes.Where(u => pWhiteList.Contains(u.Name) == false).ToList().ForEach(u => u.Remove()); 

for (int i = 0; i < pNode.ChildNodes.Count; i++) 
{ 
    if (!pWhiteList.Contains(pNode.ChildNodes[i].Name)) 
    { 
    HtmlNode _newNode = ConvertHtmlToNode(pNode.ChildNodes[i].InnerHtml); 
    pNode.ChildNodes[i].ParentNode.ReplaceChild(_newNode, pNode.ChildNodes[i]); 
    if (pNode.ChildNodes[i].HasChildNodes && !string.IsNullOrEmpty(pNode.ChildNodes[i].InnerText.Trim().Replace("\r\n", ""))) 
    { 
    HtmlNode outputNode1 = pNode.ChildNodes[i]; 
    for (int j = 0; j < pNode.ChildNodes[i].ChildNodes.Count; j++) 
    { 
    string _childNodeOutput; 
    RemoveNotInWhiteList(out _childNodeOutput, 
      pNode.ChildNodes[i], WhiteNodeList, WhiteAttrList); 
    pNode.ChildNodes[i].ReplaceChild(ConvertHtmlToNode(_childNodeOutput), pNode.ChildNodes[i].ChildNodes[j]); 
    i++; 
    } 
    } 
    } 
} 

// Console.WriteLine(pNode.OuterHtml); 
_output = pNode.OuterHtml; 
} 

private static void RemoveAttribute(HtmlAttribute u) 
{ 
u.Value = u.Value.ToLower().Replace("javascript", ""); 
u.Remove(); 

} 

public static HtmlNode ConvertHtmlToNode(string html) 
{ 
HtmlAgilityPack.HtmlDocument doc = new HtmlAgilityPack.HtmlDocument(); 
doc.LoadHtml(html); 
if (doc.DocumentNode.ChildNodes.Count == 1) 
    return doc.DocumentNode.ChildNodes[0]; 
else return doc.DocumentNode; 
} 

La sortie Je suis tryig à atteindre est

<b>first text </b> 
<b>second text here 
     some text here 
some text here 

</b> 
some twxt here 

Cela signifie que je veux seulement garder les balises <b>.
La raison pour laquelle je fais cela est parce que certains des utilisateurs font cpoy-coller à partir de MS WORD dans ny WYSYWYG html éditeur.

Merci!

Répondre

28

heh, apparemment, je trouve PRESQUE une réponse dans quelqu'un de blog fait ....

using System.Collections.Generic; 
using System.Linq; 
using HtmlAgilityPack; 

namespace Wayloop.Blog.Core.Markup 
{ 
    public static class HtmlSanitizer 
    { 
     private static readonly IDictionary<string, string[]> Whitelist; 

     static HtmlSanitizer() 
     { 
      Whitelist = new Dictionary<string, string[]> { 
       { "a", new[] { "href" } }, 
       { "strong", null }, 
       { "em", null }, 
       { "blockquote", null }, 
       }; 
     } 

     public static string Sanitize(string input) 
     { 
      var htmlDocument = new HtmlDocument(); 

      htmlDocument.LoadHtml(input); 
      SanitizeNode(htmlDocument.DocumentNode); 

      return htmlDocument.DocumentNode.WriteTo().Trim(); 
     } 

     private static void SanitizeChildren(HtmlNode parentNode) 
     { 
      for (int i = parentNode.ChildNodes.Count - 1; i >= 0; i--) { 
       SanitizeNode(parentNode.ChildNodes[i]); 
      } 
     } 

     private static void SanitizeNode(HtmlNode node) 
     { 
      if (node.NodeType == HtmlNodeType.Element) { 
       if (!Whitelist.ContainsKey(node.Name)) { 
        node.ParentNode.RemoveChild(node); 
        return; 
       } 

       if (node.HasAttributes) { 
        for (int i = node.Attributes.Count - 1; i >= 0; i--) { 
         HtmlAttribute currentAttribute = node.Attributes[i]; 
         string[] allowedAttributes = Whitelist[node.Name]; 
         if (!allowedAttributes.Contains(currentAttribute.Name)) { 
          node.Attributes.Remove(currentAttribute); 
         } 
        } 
       } 
      } 

      if (node.HasChildNodes) { 
       SanitizeChildren(node); 
      } 
     } 
    } 
} 

I got HtmlSanitizer from here Apparemment, il ne bande pas th balises, mais supprime le altoghether élément.

OK, voici la solution pour ceux qui en auront besoin plus tard.

public static class HtmlSanitizer 
    { 
     private static readonly IDictionary<string, string[]> Whitelist; 
     private static List<string> DeletableNodesXpath = new List<string>(); 

     static HtmlSanitizer() 
     { 
      Whitelist = new Dictionary<string, string[]> { 
       { "a", new[] { "href" } }, 
       { "strong", null }, 
       { "em", null }, 
       { "blockquote", null }, 
       { "b", null}, 
       { "p", null}, 
       { "ul", null}, 
       { "ol", null}, 
       { "li", null}, 
       { "div", new[] { "align" } }, 
       { "strike", null}, 
       { "u", null},     
       { "sub", null}, 
       { "sup", null}, 
       { "table", null }, 
       { "tr", null }, 
       { "td", null }, 
       { "th", null } 
       }; 
     } 

     public static string Sanitize(string input) 
     { 
      if (input.Trim().Length < 1) 
       return string.Empty; 
      var htmlDocument = new HtmlDocument(); 

      htmlDocument.LoadHtml(input);    
      SanitizeNode(htmlDocument.DocumentNode); 
      string xPath = HtmlSanitizer.CreateXPath(); 

      return StripHtml(htmlDocument.DocumentNode.WriteTo().Trim(), xPath); 
     } 

     private static void SanitizeChildren(HtmlNode parentNode) 
     { 
      for (int i = parentNode.ChildNodes.Count - 1; i >= 0; i--) 
      { 
       SanitizeNode(parentNode.ChildNodes[i]); 
      } 
     } 

     private static void SanitizeNode(HtmlNode node) 
     { 
      if (node.NodeType == HtmlNodeType.Element) 
      { 
       if (!Whitelist.ContainsKey(node.Name)) 
       { 
        if (!DeletableNodesXpath.Contains(node.Name)) 
        {      
         //DeletableNodesXpath.Add(node.Name.Replace("?","")); 
         node.Name = "removeableNode"; 
         DeletableNodesXpath.Add(node.Name); 
        } 
        if (node.HasChildNodes) 
        { 
         SanitizeChildren(node); 
        }     

        return; 
       } 

       if (node.HasAttributes) 
       { 
        for (int i = node.Attributes.Count - 1; i >= 0; i--) 
        { 
         HtmlAttribute currentAttribute = node.Attributes[i]; 
         string[] allowedAttributes = Whitelist[node.Name]; 
         if (allowedAttributes != null) 
         { 
          if (!allowedAttributes.Contains(currentAttribute.Name)) 
          { 
           node.Attributes.Remove(currentAttribute); 
          } 
         } 
         else 
         { 
          node.Attributes.Remove(currentAttribute); 
         } 
        } 
       } 
      } 

      if (node.HasChildNodes) 
      { 
       SanitizeChildren(node); 
      } 
     } 

     private static string StripHtml(string html, string xPath) 
     { 
      HtmlDocument htmlDoc = new HtmlDocument(); 
      htmlDoc.LoadHtml(html); 
      if (xPath.Length > 0) 
      { 
       HtmlNodeCollection invalidNodes = htmlDoc.DocumentNode.SelectNodes(@xPath); 
       foreach (HtmlNode node in invalidNodes) 
       { 
        node.ParentNode.RemoveChild(node, true); 
       } 
      } 
      return htmlDoc.DocumentNode.WriteContentTo(); ; 
     } 

     private static string CreateXPath() 
     { 
      string _xPath = string.Empty; 
      for (int i = 0; i < DeletableNodesXpath.Count; i++) 
      { 
       if (i != DeletableNodesXpath.Count - 1) 
       { 
        _xPath += string.Format("//{0}|", DeletableNodesXpath[i].ToString()); 
       } 
       else _xPath += string.Format("//{0}", DeletableNodesXpath[i].ToString()); 
      } 
      return _xPath; 
     } 
    } 

Je rebaptisées le nœud parce que si je devais analyser un nœud d'espace de noms XML, il se briserait sur l'analyse syntaxique de XPath.

+1

Le lien vers HtmlSanitizer est cassé. Cela peut être le code auquel Meltdown fait référence: https://gist.github.com/814428 –

+0

Ce n'est en aucun cas le code à partir duquel j'ai créé la classe de validation Whitelist. L'auteur original n'a pas utilisé RegEx. Le code original de l'auteur est le premier morceau de code que j'ai posté. –

+0

Ce code ne fonctionne pas, je peux facilement enregistrer un formulaire avec le bouton Soumettre ainsi qu'une section de script contenant du code nuisible. –

7

Merci pour le code - super chose !!!!

Je l'ai fait quelques optimisation ...

class TagSanitizer 
{ 
    List<HtmlNode> _deleteNodes = new List<HtmlNode>(); 

    public static void Sanitize(HtmlNode node) 
    { 
     new TagSanitizer().Clean(node); 
    } 

    void Clean(HtmlNode node) 
    { 
     CleanRecursive(node); 
     for (int i = _deleteNodes.Count - 1; i >= 0; i--) 
     { 
      HtmlNode nodeToDelete = _deleteNodes[i]; 
      nodeToDelete.ParentNode.RemoveChild(nodeToDelete, true); 
     } 
    } 

    void CleanRecursive(HtmlNode node) 
    { 
     if (node.NodeType == HtmlNodeType.Element) 
     { 
      if (Config.TagsWhiteList.ContainsKey(node.Name) == false) 
      { 
       _deleteNodes.Add(node); 
      } 
      else if (node.HasAttributes) 
      { 
       for (int i = node.Attributes.Count - 1; i >= 0; i--) 
       { 
        HtmlAttribute currentAttribute = node.Attributes[i]; 

        string[] allowedAttributes = Config.TagsWhiteList[node.Name]; 
        if (allowedAttributes != null) 
        { 
         if (allowedAttributes.Contains(currentAttribute.Name) == false) 
         { 
          node.Attributes.Remove(currentAttribute); 
         } 
        } 
        else 
        { 
         node.Attributes.Remove(currentAttribute); 
        } 
       } 
      } 
     } 

     if (node.HasChildNodes) 
     { 
      node.ChildNodes.ToList().ForEach(v => CleanRecursive(v)); 
     } 
    } 
} 
+1

Qu'est-ce que Config à partir de cette ligne? if (Config.TagsWhiteList.ContainsKey (node.Name) == false) –

+0

Ceci est juste une autre liste, Vous pouvez le changer comment vous voulez :) – Yacov

+0

En note, quand j'ai essayé cela, j'ai rencontré des problèmes avec le balisage qui en résulte être inconsistant (sections en désordre, tout le formatage n'étant pas correctement supprimé) probablement dû à l'optimisation multi-thread avec la récursivité. – Elsa

Questions connexes