Библиотека Jevix для Python


    Всем доброго времени суток!
    Недавно я задал вопрос на habre, про аналог библиотеки jevix для python.
    Мне подсказали решение, но этих решений мне было недостаточно, да и всем известно что Jevix самая популярная библиотека для работы с html.

    Для тех кто впервые слышит по Jevix

    Jevix — средство автоматического применения правил набора текстов, наделённое способностью унифицировать разметку HTML/XML документов, контролировать перечень допустимых тегов и аттрибутов, предотвращать возможные XSS-атаки в коде документов.

    Автором этой библиотеки для php является, хабра житель ur001, ссылка на проект jevix.ru/project


    Предыстория

    Мне очень нравится эта библиотека, я ее использовал и не раз в своих проектах, но как говорится времена меняются и я начал учить python + django и столкнулся с такой проблемой, взялся я делать проект где нужно жестко контролировать передаваемый html код и не дать врагу никакого шанса на XSS-атаки.

    Вот только беда, я не смог найти jevix библиотеки для python или достойного аналога! Вот значит посидев подумав я и решил переписать jevix+php на jevix+python.

    Поехали

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

    Что еще не реализовано

    1. Автозамена, для таких символов как ( r ) на ®
    2. Добавление отступов от определенных строчек
    3. нельзя отключить расстановку br в определенных тегах, таких как object… по этому пока что отключил ее полностью


    Как пользоваться?

    Все банально просто, я старался оставить все как есть и даже название переменных.
    Инициализируем
    from jevix import Jevix
    p = Jevix()
    

    Настраиваем
    p.cfgAllowTags(['ls','ddcut','a', 'img', 'i', 'p', 'b', 'u', 's', 'video', 'em',  'strong', 'nobr', 'li', 'ol', 'ul', 'sup', 'abbr', 'sub', 'acronym', 'h4', 'h5', 'h6', 'br', 'hr', 'pre', 'code', 'object', 'param', 'embed', 'blockquote', 'iframe','table','th','tr','td'])
    
        p.cfgSetTagShort(['br','img', 'hr', 'ddcut','ls']);
    
        p.cfgSetTagPreformatted(['pre','code','video', 'iframe'])
    
        p.cfgAllowTagParams('img', {
            0:'src', 'alt' : '#text', 1:'title', 'align': ['right', 'left', 'center', 'middle'],
            'width':'#int', 'height':'#int', 'hspace':'#int', 'vspace':'#int',
            'class' : ['image-center', 'image-left', 'image-right']
        })
        p.cfgAllowTagParams('a', {
            0:'title', 1:'href', 'rel' : '#text', 'name' : '#text', 'target' : ['_blank']
        })
    
        p.cfgAllowTagParams('ddcut', {
            0:'name',
            })
    
        p.cfgAllowTagParams('acronym', {
            0:'title',
            })
    
        p.cfgAllowTagParams('abbr', {
            0:'title',
            })
    
        p.cfgAllowTagParams('param', {
            'width' : '#int', 'height' : '#int',
            'src' : {'#domain':['youtube.com','rutube.ru','vimeo.com']}
        })
    
        p.cfgAllowTagParams('iframe', {
            'name' : '#text',
            'value' : '#text',
            'height' : '#int',
            'width' : '#int',
            'src' : {'#domain':['youtube.com','rutube.ru','vimeo.com','video.yandex.ru']},
        })
    
        p.cfgAllowTagParams('ls', {
            'user' : '#text'
        })
    
        p.cfgAllowTagParams('td', {
            'colspan':'#int','rowspan':'#int','align':['right', 'left', 'center', 'justify'],
            'height':'#int','width':'#int'
        })
    
        p.cfgAllowTagParams('table', {
            'border':'#int',
            'cellpadding':'#int','cellspacing':'#int','align':['right', 'left', 'center'],
            'height':'#int','width':'#int'
        })
    
        p.cfgAllowTagParams('embed', {
            'src' : {'#domain':['youtube.com','rutube.ru','vimeo.com','video.yandex.ru']}, 'type' : '#text',
            'allowscriptaccess' : '#text',
            'allowfullscreen' : '#text','width' : '#int', 'height' : '#int', 'flashvars': '#text',
            'wmode': '#text'
        })
    
        p.cfgAllowTagParams('object', {
            'width' : '#int',
            'height' : '#int',
            'data' : {'#domain':['youtube.com','rutube.ru','vimeo.com','video.yandex.ru']},
    
        })
    
    
    
        p.cfgSetTagParamsRequired('img', ['src'])
        p.cfgSetTagParamsRequired('a', ['href'])
        p.cfgSetTagParamsRequired('iframe', ['src'])
    
        p.cfgSetTagCutWithContent(['script',  'style'])
    
        p.cfgSetTagChilds('ul', ['li'], False, True)
        p.cfgSetTagChilds('ol', ['li'], False, True)
        p.cfgSetTagChilds('object', ['param'], False, True)
        p.cfgSetTagChilds('object', ['embed'], False, False)
        p.cfgSetTagChilds('table', ['tr'], False, True)
        p.cfgSetTagChilds('tr', ['td','th'], False, True)
    
        p.cfgSetTagIsEmpty(['param','embed','a','iframe']);
        p.cfgSetTagNoAutoBr(['ul','ol','object','table','tr'])
        p.cfgSetTagBlockType(['h4','h5','h6','ol','ul','blockquote','pre','table','iframe', 'object'])
    
        p.cfgSetAutoBrMode(True)
    
        p.cfgSetTagNoTypography(['code','video','object', 'iframe'])
    
    errors = []
    text= u"""<iframe width="560" height="315" src="http://youtube.com/embed/lGnGQXUeaVc" frameborder="0" allowfullscreen></iframe>"""
    
    


    Ну и выполняем
    text, errors = p.parse(text)
    print text, errors
    


    Ссылки:
    на официальный проект — jevix.ru
    чтоб потестить в online python+jevix — jevix.vir-mir.ru
    проект на github — github.com/vir-mir/jevix
    Метки:
    Поделиться публикацией
    Комментарии 26
    • 0
      def eror(self, message):

      Так торопились, так торопились? :)
      • +2
        нет, именно так и называет функция в php версии, я не знаю какими побуждениями пользовался автор назвав ее так.
        • 0
          Так это еще хуже, вы даже не смотрели что переносили...?
      • НЛО прилетело и опубликовало эту надпись здесь
        • 0
          извиняюсь опечатка, «Автозамена»
        • +4
          Из коротенького определения так и не понял, что такое Jevix. Можно как-нибудь иначе описать? Скажем, не: «Flask — это питоний фреймворк для веб-приложений», а «Вот предположим, вам нужно сделать крупный тяжёлый сайт без использования CMS, которые генерят глагну за 0.7 секунд и жрут 60 метров памяти. Хорошей идеей в этом случае будет использовать CMF под названием Flask, ибо...». Втыкаете? Не «решение», а «проблема — решение». И… простите, но портировать с одного языка на другой полностью, не используя возможности языка, на который переносите, — как-то не очень хорошо. Может быть, не стоит повторять ошибки и опечатки исходника?..
          • 0
            Jevix преобразует HTML в безопасный HTML (без XSS) и выполняет типографические подстановки (переводит минусы в тире и т.д.).
          • 0
            Jevix — это библиотека для обработки html кода вводимого пользователем, Jevix-php используется в сайте habrahabr и играет самую важную роль, с прошью его мы можем писать такие красочно оформленные посты, комментарии и все что тут есть, а НЛО спит спокойно и не боится что кто-то запустит вредоносный js или покорежит страницу, каким нибудь css стилем…

            А Вы что-то другое описали.
            • +1
              PEP-8 и PEP-20 не одобряют.
              • +3
                В Python для такого есть отличная библиотека bleach
                • 0
                  Добавлю, что для django поверх этой библиотеки есть приложение django-html_sanitizer
                  • 0
                    Я правильно понимаю, что можно либо разрешить использование какого-то атрибута у тега, либо не разрешить? Например
                    <div style="width: 300px;">
                    

                    Он может разрешить либо любые style, либо никаких, да?

                    Мы в своё время навелосипедили свой HTML cleaner (подробнее написал комментом ниже) именно из-за того, что нужно было чистить после WYSIWYG и хотелось получить результат как можно ближе к рельности.
                    • +1
                      я так понимаю вы об этом?
                      bleach.readthedocs.org/en/latest/clean.html#styles-whitelist

                      вроде бы все гибко настраивается, если надо — просто кастомный чистильщик можно довесить
                      • 0
                        Конечно, всё-равно не так гибко, как хотелось бы, но, наверно, если бы нашли эту библиотеку перед написанием велосипеда, то остановились бы на ней. Спасибо.
                        • +1
                          ну, я думаю эта библиотека покрывает бОльшую часть необходимого. у меня по крайней мере не хватает фантазии, что бы еще я от нее мог желать :)
                          • 0
                            Пример высосан из пальца, но во всяких object, params есть атрибуты, которых не должно (порой они не мешают, но чем чище — тем лучше) быть у других тегов. То есть нужен не список всех тегов и список всех возможных атрибутов, а по сути иерархический набор правил (ну, по крайней мере, в своём велосипеде мы так и сделали :))

                            {
                                 тег, {
                                     разрешённый атрибут: {
                                        разрешённый стиль, (разрешённые значения данного стиля),
                                        ...
                                    },
                                    ...
                                },
                                ...
                            }
                            
                            • +1
                              ну у них почти так и сделано на самом деле:

                              теги задаются списком
                              атрибуты задаются словарем тег: разрешенные атрибуты (тег может быть *)
                              стили так же задаются словарем тег: разрешенные стили для этого тега (также разрешается *)

                              единственное что они не сделали — еще шага вглубь с валидацией значений стиля, я понимаю что это может иногда пригодиться, но думаю не так уж часто.
                      • +1
                        Если я правильно понял задачу, то валидацию значений аттрибутов можно реализовать при помощи callable-filters
                        PS: Опередили пока писал.
                    • +1
                      У меня тоже была необходимость чистить вводимый пользователями html, тогда мы с ilblackdragon написали свой велосипед и по сей день он отлично работает. Одна беда — никак не вынесем велосипед в отдельный проект и живёт он у нас в django-misc вот тут — HTML cleaner.

                      Логика достаточно простая — есть white-list, описанный в needs.cfg, из которого путём python generator.py создаётся clear.py, в котором мы ходим по DOM и на основе regexp принимаем решение оставить ли тег, если да, то какие свойства у него можно оставить. Потом можно взять clear.py как отдельный файл и вкрутить в нужный проект:

                      from clear import clear_html_code
                      
                      clear_html_code("<b>Hello world</b>")
                      


                      PS Меня пагает ваш Python код… правда страшно… при этом я совершенно не вижу смысла сохранять совместимость в названиях с точностью до буквы — есть очень «хорошие» примеры — Internet Explorer и Windows — они сохраняли обратную совместимость с учётом старых багов с целью того, чтобы не ломать программы, которые эти баги уже эксплуатируют…
                    • 0
                      Мне подсказали решение, но этих решений мне было недостаточно, да и всем известно что Jevix самая популярная библиотека для работы с html.

                      Вам советовали решения на основе html5lib, можно узнать что именно было недостаточно? Я глубоко конечно не вдавался, но кажется что Вы хотите делать то же самое, но с более простой настройкой нужных аттрибутов для элементов, а смотря на wiki.whatwg.org/wiki/Sanitization_rules, code.google.com/p/html5lib/source/browse/python/html5lib/sanitizer.py, для этого мне кажется проще в html5lib.sanitizer.HTMLSanitizerMixin.sanitize_token добавить свою проверку зависящую от элемента.
                      • 0
                        • 0
                          Зачем вы повторили Jevix с его устаревшим самостоятельным разбором HTML?

                          Вы могли бы уместить библиотеку в пару сотен строк, если бы использовали какую-либо реализацию DOM. В таком случае, можно было бы иметь гораздо более гибкие правила и богатейший набор возможностей.
                          • 0
                            Главную вещь вы так и не решили, все white list sanitizer'ы которые я видел, достаточно хорошо удаляют не нужные теги и чистят атрибуты, но. Допустим мы хотим как на хабре вставить кусок кода, к примеру такой:

                            <html>
                                <p>
                                   habrahabr
                                </p>
                            </html>
                            


                            В вашей нынешней реализации, всё что внутри тега source тоже почистится и выведет только надпись habrahabr
                            • 0
                              Был не прав у вас есть тег <code> в котором не чистится внутри.
                            • 0
                              А почему вы взяли не Perl версию? Версия PHP давно уже заброшена и не развивается. Или на Perl не далеко ушел тоже?

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