CSP и inline-стили

Shepherd.js использует динамическое создание DOM-элементов и inline-стилей для отображения подсказок, тултипов и шагов в пользовательском интерфейсе. Это позволяет гибко позиционировать элементы, анимации и кастомизацию внешнего вида. Однако при строгой политике безопасности контента (CSP, Content Security Policy) inline-стили и скрипты могут блокироваться браузером, что приведёт к некорректной работе библиотеки. Понимание и правильная настройка CSP является критически важной для интеграции Shepherd.js в защищённые веб-приложения.


Особенности использования inline-стилей в Shepherd.js

Shepherd.js применяет inline-стили в нескольких ключевых местах:

  1. Позиционирование шагов: каждый шаг создаётся с абсолютным позиционированием (top, left) для точного размещения относительно целевого элемента.
  2. Стилизация стрелок и тултипов: динамически создаются псевдоэлементы и inline-стили для плавных анимаций и визуальной адаптации к размеру окна.
  3. Анимации появления и скрытия: inline-стили используют свойства CSS transition и opacity для анимаций появления шагов и оверлеев.

Эти подходы обеспечивают универсальность работы библиотеки, но вызывают конфликты при CSP, запрещающей style-src 'unsafe-inline'.


Настройка CSP для работы с Shepherd.js

Для корректного отображения шагов с inline-стилями необходимо скорректировать политику CSP. Возможны два подхода:

1. Разрешение inline-стилей через unsafe-inline

Content-Security-Policy: default-src 'self'; style-src 'self' 'unsafe-inline';
  • style-src 'unsafe-inline' позволяет использовать inline-стили напрямую.
  • Этот метод простой, но снижает уровень безопасности, поскольку разрешает любые inline-стили, включая потенциально вредоносные.

2. Использование хэшированных стилей или nonce

Content-Security-Policy: default-src 'self'; style-src 'self' 'nonce-abc123';
  • Каждое динамически создаваемое inline-правило должно включать соответствующий nonce.
  • Shepherd.js не поддерживает автоматическую генерацию nonce для inline-стилей, поэтому для полной безопасности необходимо использовать кастомные CSS-классы и внешние стили.

Альтернатива inline-стилям: внешние стили и кастомизация

Чтобы полностью соответствовать строгой CSP без unsafe-inline, можно:

  1. Переопределить стили шагов через классы CSS:

    • Каждый шаг имеет контейнер с классом .shepherd-step.

    • Стили можно задавать в отдельном CSS-файле:

      .shepherd-step {
        background-color: #fff;
        border-radius: 4px;
        box-shadow: 0 4px 8px rgba(0,0,0,0.2);
      }
      .shepherd-arrow {
        width: 12px;
        height: 12px;
        background-color: #fff;
        transform: rotate(45deg);
      }
  2. Использовать опцию attachTo и динамические классы:

    • attachTo: { element: '#myElement', on: 'bottom' } позволяет задать точку привязки.
    • Класс можно динамически изменять через события before-show или show, чтобы реализовать кастомную анимацию без inline-стилей.
  3. Переопределение inline-стилей через JS:

    • Shepherd предоставляет методы step.element и step.tip, через которые можно добавлять классы и стили программно:

      tour.on('show', function(step) {
        step.el.classList.add('custom-shepherd-step');
      });

Практические советы по интеграции с CSP

  • Минимизировать использование inline-стилей: основной подход — это внешний CSS и кастомные классы. Inline-стили оставлять только для критичных динамических позиций.

  • Контроль анимаций: при строгой CSP анимации, реализованные через inline transition или transform, можно заменить на классы с ключевыми кадрами в CSS:

    .fade-in {
      animation: fadeIn 0.3s ease-in-out forwards;
    }
    @keyframes fadeIn {
      from { opacity: 0; }
      to { opacity: 1; }
    }
  • Тестирование на разных браузерах: некоторые браузеры по-разному трактуют CSP, особенно для dynamically inserted <style> и <script> элементов.

  • Использование последних версий Shepherd.js: новые версии библиотеки лучше интегрируются с CSP и позволяют более гибко управлять стилями через кастомные классы и темы.


Вывод

Shepherd.js требует внимания к CSP, так как inline-стили являются основой работы подсказок и шагов. Наиболее безопасный способ интеграции — использование внешнего CSS с кастомными классами, ограничение inline-стилей до минимума и правильная настройка style-src через nonce, если необходим строгий контроль безопасности. Понимание механизма позиционирования и динамической генерации элементов позволяет адаптировать библиотеку даже под самые жёсткие политики CSP.