2010-06-24 15 views
28

Estoy intentando crear una función que elimine las etiquetas html y los atributos que no están en una lista blanca. Tengo el siguiente código HTML:Etiquetas de la tira HTML Agility Pack NO EN la lista blanca

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

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

estoy usando paquete de agilidad HTML y el código que tengo hasta ahora es:

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 salida Soy tryig lograr es

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

</b> 
some twxt here 

Eso significa que solo quiero conservar las etiquetas <b>.
La razón por la que estoy haciendo esto es porque algunos de los usuarios copian y pegan desde MS WORD en el editor de wysywyg html.

Gracias.!

Respuesta

28

je, al parecer, que se encuentra casi una respuesta en una entrada de blog que alguien hizo ....

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 Al parecer, no elimina º etiquetas, pero elimina la altoghether elemento.

OK, aquí está la solución para quienes la necesiten más adelante.

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; 
     } 
    } 

Me cambió el nombre del nodo, porque si tuviera que analizar un nodo de espacio de nombres XML se estrellaría en el análisis XPath.

+1

El enlace a HtmlSanitizer está roto. Este puede ser el código al que se refiere Meltdown: https://gist.github.com/814428 –

+0

No es de ninguna manera el código desde el que creé la clase de validación de la Lista blanca. El autor original no usó RegEx. El código original del autor es la primera pieza de código que publiqué. –

+0

Este código no funciona, puedo guardar un formulario con el botón de enviar fácilmente, así como una sección de script que contiene código dañino. –

7

Gracias por el código - gran cosa !!!!

hice algunas optimización ...

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é es Config de esta línea? if (Config.TagsWhiteList.ContainsKey (node.Name) == false) –

+0

Esta es solo otra lista, puedes cambiarla como quieras :) – Yacov

+0

Como nota al margen, cuando probé esto me encontré con problemas con el marcado resultante ser inconsistente (secciones fuera de servicio, no todo el formato se elimina adecuadamente) probablemente debido a la optimización de múltiples subprocesos con la recursión. – Elsa

Cuestiones relacionadas