From 913f28f2f31ebd136158d281606d0471a4f4ff75 Mon Sep 17 00:00:00 2001 From: erjemin Date: Thu, 5 Mar 2026 03:31:30 +0300 Subject: [PATCH] =?UTF-8?q?add:=20=D1=81=D0=B0=D0=BD=D0=B8=D1=82=D0=B0?= =?UTF-8?q?=D0=B9=D0=B7=D0=B8=D0=BD=D0=B3=20=D0=BF=D0=BB=D0=B5=D0=B9=D1=81?= =?UTF-8?q?=D1=85=D0=BE=D0=BB=D0=B4=D0=B5=D1=80=D0=BE=D0=B2?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- etpgrf/config.py | 3 +-- etpgrf/sanitizer.py | 33 ++++++++++++++++++++++++++++++--- tests/test_sanitizer.py | 20 ++++++++++++++++++-- 3 files changed, 49 insertions(+), 7 deletions(-) diff --git a/etpgrf/config.py b/etpgrf/config.py index 93e9aa1..656f0d4 100644 --- a/etpgrf/config.py +++ b/etpgrf/config.py @@ -102,8 +102,7 @@ CHAR_PLACEHOLDER = '\uFFFC' # Уникальная строка-заполн CHAR_AMP_PLACEHOLDER = '\uFFFD' # Маркер-плейсхолдер для амперсанда (&), чтобы избежать его двойного кодирования в & при замене на мнемонику. CHAR_NODE_SEPARATOR = '\uFFFF' # Маркер границы текстовых узлов (Non-character). -# === КОНСТАНТЫ ДЛЯ САНИТИЗАЦИИ === -# TODO: Их обработку (очистку) нужно добавить в модуль sanitization.py на входе. +# === ПЛЕЙСХОЛДЕРЫ (ДЛЯ САНИТАЙЗИНГА НА ХОДЕ) === CHARS_SYMBOLS_TO_BAN = frozenset([ CHAR_UNIT_SEPARATOR, CHAR_PLACEHOLDER, CHAR_AMP_PLACEHOLDER, CHAR_NODE_SEPARATOR ]) diff --git a/etpgrf/sanitizer.py b/etpgrf/sanitizer.py index d77672b..1046d9d 100644 --- a/etpgrf/sanitizer.py +++ b/etpgrf/sanitizer.py @@ -4,9 +4,10 @@ import logging from bs4 import BeautifulSoup from .config import (SANITIZE_ALL_HTML, SANITIZE_ETPGRF, SANITIZE_NONE, - HANGING_PUNCTUATION_CLASSES, PROTECTED_HTML_TAGS, + PROTECTED_HTML_TAGS, HANGING_PUNCTUATION_SYMBOLS_CLASSES, - HANGING_PUNCTUATION_SPACE_CLASSES_FLAT) + HANGING_PUNCTUATION_SPACE_CLASSES_FLAT, + CHARS_SYMBOLS_TO_BAN) logger = logging.getLogger(__name__) @@ -50,6 +51,7 @@ class SanitizerProcessor: """ if self.mode == SANITIZE_ETPGRF: if not self._etp_selector: + self._strip_banned_chars_from_soup(soup) return soup # Используем CSS-селектор для быстрого поиска всех нужных элементов @@ -60,6 +62,7 @@ class SanitizerProcessor: for span in spans_to_clean: span.unwrap() + self._strip_banned_chars_from_soup(soup) return soup elif self.mode == SANITIZE_ALL_HTML: @@ -74,7 +77,31 @@ class SanitizerProcessor: # 2. Извлекаем чистый текст из оставшегося дерева. # get_text() работает на уровне C (в lxml) и намного быстрее ручного обхода. - return soup.get_text() + text = soup.get_text() + return self._strip_banned_chars_from_string(text) # Если режим не задан, ничего не делаем return soup + + def _strip_banned_chars_from_soup(self, soup: BeautifulSoup) -> None: + """ + Удаляет запрещенные символы из всего содержимого soup-объекта. + + :param soup: Объект BeautifulSoup для обработки. + """ + for element in soup.find_all(string=True): + if isinstance(element, str): + new_string = self._strip_banned_chars_from_string(element) + element.replace_with(new_string) + + def _strip_banned_chars_from_string(self, text: str) -> str: + """ + Удаляет запрещенные символы из строки. + + :param text: Исходная строка. + :return: Строка без запрещенных символов. + """ + # Удаляем все символы, которые есть в CHARS_SYMBOLS_TO_BAN + for char in CHARS_SYMBOLS_TO_BAN: + text = text.replace(char, "") + return text diff --git a/tests/test_sanitizer.py b/tests/test_sanitizer.py index 9d219e0..c79b3de 100644 --- a/tests/test_sanitizer.py +++ b/tests/test_sanitizer.py @@ -4,7 +4,7 @@ import pytest from bs4 import BeautifulSoup from etpgrf.sanitizer import SanitizerProcessor -from etpgrf.config import SANITIZE_NONE, SANITIZE_ETPGRF, SANITIZE_ALL_HTML +from etpgrf.config import SANITIZE_NONE, SANITIZE_ETPGRF, SANITIZE_ALL_HTML, CHARS_SYMBOLS_TO_BAN def test_sanitizer_mode_none(): @@ -83,4 +83,20 @@ def test_sanitizer_mode_etpgrf(case_id, description, html_input, expected_html): result_soup = processor.process(soup) - assert str(result_soup) == expected_html \ No newline at end of file + assert str(result_soup) == expected_html + + +@pytest.mark.parametrize("mode", [SANITIZE_ETPGRF, SANITIZE_ALL_HTML]) +def test_sanitizer_strips_service_placeholders(mode): + """ + Проверяет, что в обоих режимах удаляются запрещенные символы (плейсхолдеры, используемые внутри типографа). + Это важно для защиты от потенциальных XSS-атак или других проблем с безопасностью, связанных с этими символами. + """ + placeholder = next(iter(CHARS_SYMBOLS_TO_BAN)) + html_input = f'

Start{placeholder}End

' + soup = BeautifulSoup(html_input, 'html.parser') + processor = SanitizerProcessor(mode=mode) + result = processor.process(soup) + output = str(result) if isinstance(result, BeautifulSoup) else result + assert placeholder not in output + assert 'StartEnd' in output