Отчет о внедрении контент‑фильтрации, Markdown и предпросмотра
Дата: 2025‑10‑07
Модуль: modules/ContentFilter
1. Резюме
- Реализован безопасный конвейер обработки контента (HTML/Plain/Markdown) с серверной санитизацией и ограниченными inline‑стилями.
- Добавлен Markdown‑профиль с Parsedown (если установлен), далее обязательная HTML‑санитизация.
- Расширена БД: исходник (content_source), формат (content_format), итоговый HTML (content_html). Миграция и бэкфилл.
- В формах создания/редактирования статей добавлен выбор формата и живой предпросмотр (кнопка + авто, дебаунс 400 мс).
- Исправлены ошибки SQL (1064), ENUM truncate (1265), Class not found; добавлены фолбэки совместимости.арапрпа
2. Цели и задачи
- Сохранение полезного HTML и отсечение опасных конструкций (XSS, event‑атрибуты, javascript:/data: и пр.).
- Поддержка трех форматов ввода: HTML, Plain Text, Markdown.
- Улучшение UX: выбор формата + live‑предпросмотр именно санитизированного результата.
- Хранение исходника и результата для удобного редактирования и быстрой выдачи.
3. Архитектура и компоненты
-
modules/ContentFilter/src/Models/FilterConfig.php
- Профили: PROFILE_RICH, PROFILE_PLAIN, PROFILE_MD.
- Белый список тегов/атрибутов; разрешение style у избранных тегов (дальше чистит CssSanitizer).
- Разрешенные протоколы: http/https/mailto; запрет javascript:/data:.
-
modules/ContentFilter/src/Services/CssSanitizer.php
- Свойства: color, background-color, text-align, text-decoration, font-weight, font-style, font-size, line-height.
- Валидация значений: цвета (hex/rgb/rgba/имена), перечисления, длины (px/em/rem/%). Запрет url()/expression()/behavior/-moz-binding.
-
modules/ContentFilter/src/Services/HtmlSanitizer.php
- sanitize(): по профилю (plain → escape; md → markdownToHtml → sanitizeHtml; rich → sanitizeHtml).
- sanitizeHtml(): DOMDocument, очистка тегов/атрибутов, rel для target, фильтр ссылок и изображений, размеры img.
- markdownToHtml(): Parsedown при наличии (modules/ContentFilter/Vendor/Parsedown.php) либо легкий конвертер.
- Автоподключение зависимостей через require_once (без Composer).
-
modules/ContentFilter/preview.php
- POST input + format → JSON { ok, sanitized }.
- Профили: html → rich, plain → plain, md → md.
4. Интеграция в статьи
-
articles/create.php
- Select «Формат текста» (HTML/Plain/Markdown).
- Валидация по исходному тексту ($content_raw_for_form), минимум 100 символов.
- Санитизация по выбранному профилю; запись: content (санитизированный HTML), content_source, content_format, content_html.
- Live‑предпросмотр: кнопка + авто (дебаунс 400 мс) через modules/ContentFilter/preview.php.
- Fix SHOW COLUMNS LIKE (без плейсхолдера, $pdo->quote + query).
- Фолбэк ENUM: если столбец content_format ещё без 'md', сохраняется 'html', но санитизация идёт по реальному выбору.
-
articles/edit.php
- Добавлен «Формат текста» и предпросмотр (как в create.php).
- Исходник для редактирования — content_source (если есть), иначе legacy content.
- Фолбэк ENUM на случай не обновлённой схемы (md → html при записи).
-
articles/view.php
- Выводит content_html (если есть), иначе content.
5. Миграция БД
- database/migrations/20251007_add_content_fields_and_backfill.php
- Добавляет: content_source MEDIUMTEXT, content_format ENUM('html','plain','md'), content_html MEDIUMTEXT.
- При наличии столбца content_format без 'md' — ALTER … MODIFY до ('html','plain','md').
- Бэкфилл пакетами по id‑окну (batch=500); профили выбираются по имеющемуся формату или по наличию тегов.
- SHOW COLUMNS LIKE без плейсхолдера (через $pdo->quote), совместимо с MySQL.
6. Безопасность
- Удаление on* атрибутов, блокировка javascript:/data: в href/src, запрет опасных CSS‑конструкций.
- rel="noopener noreferrer nofollow" для ссылок с target.
- Проверки img: допустимые протоколы/относительные пути, числовые width/height.
- Жесткий whitelist тегов/атрибутов и CSS свойств/значений.
7. UX: предпросмотр
- Кнопка «Предпросмотр» и авто‑обновление при вводе/смене формата (дебаунс 400 мс).
- Предпросмотр совпадает с финальным сохраняемым HTML (после санитизации).
8. Markdown
- Parsedown (если файл Vendor/Parsedown.php присутствует) — полноценный Markdown: заголовки, списки, цитаты, блоки кода, горизонтальные линии и пр.
- Даже при Parsedown результат проходит HtmlSanitizer.
9. Тестирование
tests/ContentFilterTest.php— сценарные проверки: удаление on*, запрет javascript:, чистка style, plain‑escape, md‑конверсия.- Ручные сценарии:
10. Обратная совместимость
- Без миграции код безопасно игнорирует новые поля; при узком ENUM 'md' сохраняется как 'html'.
- View использует content_html при наличии, иначе content.
11. Ограничения и roadmap
- Лёгкий Markdown ограничен — рекомендуется держать Parsedown.
- Не детализируется валидация атрибутов таблиц (colspan/rowspan и т. п.).
- Список допустимых CSS свойств можно расширять по запросу.
- Возможен переход на League CommonMark при использовании Composer.
12. Производительность
- DOM‑санитизация даёт умеренную нагрузку; приемлемо для статей/превью.
- Бэкфилл миграции — пакетами по 500 с прохождением по id для снижения блокировок.
13. Использование
- Авторы: выбирайте формат (HTML/Plain/Markdown), пользуйтесь предпросмотром.
- Админы: запустите миграцию 20251007_add_content_fields_and_backfill.php.
- Разработчики: HtmlSanitizer с нужным профилем, настройки whitelist — в FilterConfig/CssSanitizer.
14. Ключевые изменения по файлам
- modules/ContentFilter/src/Models/FilterConfig.php — профили, whitelist тегов/атрибутов, протоколы.
- modules/ContentFilter/src/Services/CssSanitizer.php — очистка style по whitelist.
- modules/ContentFilter/src/Services/HtmlSanitizer.php — профили, DOM‑санитизация, Markdown (Parsedown/fallback), автоподключение.
- modules/ContentFilter/preview.php — JSON‑эндпойнт предпросмотра.
- articles/create.php — селектор формата, санитизация, предпросмотр (кнопка + авто), фиксы валидации/SHOW COLUMNS.
- articles/edit.php — селектор формата, предпросмотр (кнопка + авто), фолбэк ENUM.
- articles/view.php — вывод content_html.
- database/migrations/20251007_add_content_fields_and_backfill.php — новые поля, ENUM 'md', бэкфилл, SHOW COLUMNS fix.
- tests/ContentFilterTest.php — базовые проверки.
15. Как проверить Parsedown
В режиме «Markdown» введите:
# Заголовок
- элемент 1
- элемент 2
> цитата
---А для блока кода используйте тройные бэктики:
```php
echo "hi";
В предпросмотре должны появиться: `h1`, `ul`/`li`, `blockquote`, `hr`, а код — в `pre`/`code`.
## 16. План отката
- Удалить modules/ContentFilter/Vendor/Parsedown.php — включится лёгкий конвертер.
- При необходимости скрыть селектор формата и зафиксировать HTML/Plain.
- Миграцию не откатывать: новые поля безопасны и полезны.
Готово к эксплуатации. Для расширения whitelist или интеграции в другие разделы используйте сервисы модуля ContentFilter.



Комментарии (1)
Чтобы оставить комментарий, пожалуйста, войдите в систему или зарегистрируйтесь.