Как отфильтровать все HTML-теги, кроме определенного белого списка? - PullRequest
35 голосов
/ 21 ноября 2008

Это для .NET. IgnoreCase установлен, а MultiLine НЕ установлен.

Обычно я приличный в регулярных выражениях, может быть, у меня заканчивается кофеин ...

Пользователям разрешено вводить объекты в кодировке HTML (

u, i, b, h3, h4, br, a, img

Самозакрывающиеся
и image разрешены, с или без дополнительного пробела, но не обязательны.

Я хочу:

  1. Уберите все начальные и конечные теги HTML, кроме перечисленных выше.
  2. Удалить атрибуты из оставшихся тегов, , за исключением якоря могут иметь href.

Мой шаблон поиска (заменен пустой строкой):

<(?!i|b|h3|h4|a|img|/i|/b|/h3|/h4|/a|/img)[^>]+>

Этот , кажется, удаляет все, кроме начальных и конечных тегов, которые я хочу, но есть три проблемы:

  1. Ужасно включать конечную версию каждого разрешенного тега.
  2. Атрибуты выживают. Может ли это произойти за одну замену?
  3. Теги начиная с разрешенные имена тегов проскальзывают. Например, "" и "

Следующий предложенный шаблон не удаляет теги, которые не имеют атрибутов.

</?(?!i|b|h3|h4|a|img)\b[^>]*>

Как упоминалось ниже, ">" допустимо в значении атрибута, но можно с уверенностью сказать, что я не буду это поддерживать. Также не будет никаких блоков CDATA и т. Д. Просто немного HTML.

Ответ Loophole пока лучший, спасибо! Вот его модель (надеясь, что PRE работает лучше для меня):

static string SanitizeHtml(string html)
{
    string acceptable = "script|link|title";
    string stringPattern = @"</?(?(?=" + acceptable + @")notag|[a-zA-Z0-9]+)(?:\s[a-zA-Z0-9\-]+=?(?:([""']?).*?\1?)?)*\s*/?>";
    return Regex.Replace(html, stringPattern, "sausage");
}

Некоторые небольшие изменения, я думаю, все еще могут быть сделаны к этому ответу:

  1. Я думаю, что это можно изменить, чтобы захватить простые комментарии HTML (те, которые сами не содержат тегов), добавив «! -» к «приемлемой» переменной и внеся небольшое изменение в конец выражения. чтобы разрешить дополнительный трейлинг "\ s -".

  2. Я думаю, что это сломалось бы, если между атрибутами есть несколько пробельных символов (пример: сильно отформатированный HTML с разрывами строк и табуляцией между атрибутами).

Редактировать 2009-07-23: Вот окончательное решение, с которым я столкнулся (в VB.NET):

 Dim AcceptableTags As String = "i|b|u|sup|sub|ol|ul|li|br|h2|h3|h4|h5|span|div|p|a|img|blockquote"
 Dim WhiteListPattern As String = "</?(?(?=" & AcceptableTags & _
      ")notag|[a-zA-Z0-9]+)(?:\s[a-zA-Z0-9\-]+=?(?:([""']?).*?\1?)?)*\s*/?>"
 html = Regex.Replace(html, WhiteListPattern, "", RegExOptions.Compiled)

Предостережение заключается в том, что атрибут HREF тегов A по-прежнему очищается, что не является идеальным.

Ответы [ 7 ]

30 голосов
/ 25 ноября 2008

Вот функция, которую я написал для этой задачи:

static string SanitizeHtml(string html)
{
    string acceptable = "script|link|title";
    string stringPattern = @"</?(?(?=" + acceptable + @")notag|[a-zA-Z0-9]+)(?:\s[a-zA-Z0-9\-]+=?(?:(["",']?).*?\1?)?)*\s*/?>";
    return Regex.Replace(html, stringPattern, "sausage");
}

Редактировать: По какой-то причине я опубликовал исправление к своему предыдущему ответу в качестве отдельного ответа, поэтому я собираю их здесь.

Я немного объясню регулярное выражение, потому что оно немного длинное.

Первая часть соответствует открывающей скобке и косой черте 0 или 1 (в случае, если это закрывающий тег).

Далее вы видите конструкцию if-then с заглядыванием вперед. (? (? = SomeTag) затем | еще) Я проверяю, является ли следующая часть строки одним из допустимых тегов. Вы можете видеть, что я объединяю строку регулярного выражения с допустимой переменной, которая является приемлемыми именами тегов, разделенными строкой вертикулы, так что любой из терминов будет совпадать. Если это совпадение, вы можете заметить, что я вставил слово «notag», потому что ни один тег не соответствует этому, и если это приемлемо, я хочу оставить его в покое. В противном случае я перехожу к другой части, где я сопоставляю имя любого тега [a-z, A-Z, 0-9] +

Далее я хочу сопоставить 0 или более атрибутов, которые, как я предполагаю, находятся в форме attribute = "value". так что теперь я группирую эту часть, представляющую атрибут, но я использую?:, чтобы предотвратить захват этой группы для скорости: (?: \ s [az, AZ, 0-9, -] + =? (?: ([" »,"]?). ? \ 1?))

Здесь я начинаю с символа пробела, который будет находиться между именами тега и атрибута, затем сопоставляю имя атрибута: [a-z, A-Z, 0-9, -] +

далее я сопоставляю знак равенства, а затем любую кавычку. Я группирую цитату, чтобы она была захвачена, и я могу сделать обратную ссылку позже \ 1, чтобы соответствовать цитате того же типа. Между этими двумя цитатами видно, что я использую точку для сопоставления чего угодно, однако я использую ленивую версию *? вместо жадной версии *, так что она будет соответствовать только следующей кавычке, которая закончит это значение.

далее мы ставим * после закрытия групп круглыми скобками, чтобы он совпадал с несколькими комбинациями attirbute / value (или ни одной). Наконец, мы сопоставляем некоторые пробелы с \ s и заканчивая косой чертой 0 или 1 в тегах для самозакрывающихся тегов в стиле xml.

Вы можете видеть, что я заменяю теги колбасой, потому что я голоден, но вы также можете заменить их пустой строкой, чтобы просто убрать их.

11 голосов
/ 21 ноября 2008

Это хороший рабочий пример фильтрации HTML-тегов:

Очистка HTML

2 голосов
/ 16 января 2009

Я только что заметил, что текущее решение допускает теги, которые начинаются с любых допустимых тегов. Таким образом, если «b» является приемлемым тегом, «blink» тоже. Не так уж и много, но стоит подумать, если вы строго относитесь к тому, как фильтровать HTML. Вы, конечно, не захотите использовать «s» в качестве допустимого тега, так как он разрешит «script».

2 голосов
/ 21 ноября 2008

Атрибуты являются основной проблемой при использовании регулярных выражений для работы с HTML. Примите во внимание огромное количество потенциальных атрибутов, а также тот факт, что большинство из них являются необязательными, а также тот факт, что они могут появляться в любом порядке, и тот факт, что «>» является допустимым символом в указанных значениях атрибутов. Когда вы начнете пытаться принять все это во внимание, регулярное выражение, с которым вам нужно будет справиться, быстро станет неуправляемым.

Вместо этого я бы использовал HTML-анализатор на основе событий или тот, который дает вам дерево DOM, через которое вы можете пройти.

1 голос
/ 21 ноября 2008

Причина, по которой добавление слова border \ b не сработало, заключается в том, что вы не поместили его в поле зрения. Таким образом, \ b будет предприниматься после <, где он всегда будет совпадать, если <запускает HTML-тег. </p>

Поместите его в ракурс так:

<(?!/?(i|b|h3|h4|a|img)\b)[^>]+>

Здесь также показано, как можно поставить / перед списком тегов, а не с каждым тегом.

0 голосов
/ 05 ноября 2012
    /// <summary>
    /// Trims the ignoring spacified tags
    /// </summary>
    /// <param name="text">the text from which html is to be removed</param>
    /// <param name="isRemoveScript">specify if you want to remove scripts</param>
    /// <param name="ignorableTags">specify the tags that are to be ignored while stripping</param>
    /// <returns>Stripped Text</returns>
    public static string StripHtml(string text, bool isRemoveScript, params string[] ignorableTags)
    {
        if (!string.IsNullOrEmpty(text))
        {
            text = text.Replace("&lt;", "<");
            text = text.Replace("&gt;", ">");
            string ignorePattern = null;

            if (isRemoveScript)
            {
                text = Regex.Replace(text, "<script[^<]*</script>", string.Empty, RegexOptions.IgnoreCase);
            }
            if (!ignorableTags.Contains("style"))
            {
                text = Regex.Replace(text, "<style[^<]*</style>", string.Empty, RegexOptions.IgnoreCase);
            }
            foreach (string tag in ignorableTags)
            {
                //the character b spoils the regex so replace it with strong
                if (tag.Equals("b"))
                {
                    text = text.Replace("<b>", "<strong>");
                    text = text.Replace("</b>", "</strong>");
                    if (ignorableTags.Contains("strong"))
                    {
                        ignorePattern = string.Format("{0}(?!strong)(?!/strong)", ignorePattern);
                    }
                }
                else
                {
                    //Create ignore pattern fo the tags to ignore
                    ignorePattern = string.Format("{0}(?!{1})(?!/{1})", ignorePattern, tag);
                }

            }
            //finally add the ignore pattern into regex <[^<]*> which is used to match all html tags
            ignorePattern = string.Format(@"<{0}[^<]*>", ignorePattern);
            text = Regex.Replace(text, ignorePattern, "", RegexOptions.IgnoreCase);
        }

        return text;
    }
0 голосов
/ 25 ноября 2008

Я думаю, что изначально намеревался сделать значения необязательными, но не выполнил их, так как я вижу, что я добавил ? после знака равенства и сгруппировал часть значения соответствия. Давайте добавим ? после этой группы (помеченной кароткой), чтобы сделать ее необязательной в матче. Я сейчас не в своем компиляторе, но посмотрим, сработает ли это:

@"</?(?(?=" + acceptable + @")notag|[a-z,A-Z,0-9]+)(?:\s[a-z,A-Z,0-9,\-]+=?(?:(["",']?).*?\1?)?)*\s*/?>";
                                                                                             ^
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...