2010-06-24 3 views
28

화이트리스트에없는 html 태그 및 속성을 제거하는 기능을 만들려고합니다. 내가 tryig 달성하고있는 출력HTML 민첩성 팩 스트립 태그가 화이트리스트에 없음

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

</b> 
some twxt here 
입니다

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

: 나는 HTML 민첩성 팩과 내가 지금까지 가지고있는 코드이다 사용하고

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

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

: 나는 다음과 같은 HTML을

즉, 나는 단지 <b> 태그를 유지하기를 원합니다.
사용자 중 일부가 MS WORD에서 Ny WYSYWYG html 편집기로 붙여 넣기 때문에이 작업을 수행하는 이유가 있습니다.

감사합니다.

답변

28

ㅎ은, 분명히 나는 ​​거의 분명히이 태그 번째 제거하지 않습니다 .... 만든 블로그 게시물 누군가

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

을 답을 찾았지만, 요소 altoghether를 제거합니다.

좋아, 나중에 나중에 필요로하는 사람들을위한 해결책입니다.

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

XML 네임 스페이스 노드를 구문 분석하면 xpath 구문 분석시 충돌이 발생하므로 노드의 이름을 변경했습니다.

+1

HtmlSanitizer에 대한 링크가 끊어집니다. 이것은 Meltdown이 참조하는 코드 일 수 있습니다. https://gist.github.com/814428 –

+0

이는 Whitelist 유효성 검사 클래스를 작성한 코드가 아닙니다. 원래 저자는 RegEx를 사용하지 않았습니다. 저자의 원래 코드는 제가 게시 한 코드의 첫 번째 코드입니다. –

+0

이 코드는 작동하지 않으며, 유해한 코드가 포함 된 스크립트 섹션뿐만 아니라 제출 단추가있는 폼을 쉽게 저장할 수 있습니다. –

7

코드 주셔서 감사합니다.

내가 몇 최적화했다

...

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

이 줄의 구성은 무엇입니까? if (Config.TagsWhiteList.ContainsKey (node.Name) == false) –

+0

이것은 단지 다른 목록입니다. 원하는대로 변경할 수 있습니다. – Yacov

+0

참고로이 시도를하면 결과 마크 업과 관련된 문제가 발생했습니다. 재귀를 사용한 멀티 스레딩 최적화로 인해 일관성이 없습니다 (순서가 잘못된 섹션, 모든 서식이 제대로 제거되지는 않음). – Elsa

관련 문제