Type to search Tours, Hints, options, and events.

to navigate · Enter to open · Esc to close

Documentation

Security

How UX Driver escapes popover content, and how to opt out safely.

Driver.js writes popover content and button labels with innerHTML. Left alone, that makes every title and description an HTML injection sink. UX Driver escapes plain strings before serializing them, so the default path is safe even when the text comes from the database.

Escaped by default

ObjectEscaped options
Tourtitle, description, progressText, nextBtnText, prevBtnText, doneBtnText
Steptitle, description, progressText, nextBtnText, prevBtnText, doneBtnText
Highlighttitle, description, and the Step options above
HintsbuttonText
Hinttitle, description

The label options exist at both levels — a Tour sets them for the whole walkthrough, a Step overrides them for itself — and both are escaped identically.

Component

<twig:Driver:Step title="<b>Billing</b>" description="<script>alert(1)</script>" />

Twig builder

{% set tour = create_tour('billing-tour')
    .addStep('.billing', '<b>Billing</b>', '<script>alert(1)</script>') %}

PHP builder

$tour = $this->tourBuilder->create('billing-tour')
    ->addStep('.billing', '<b>Billing</b>', '<script>alert(1)</script>');

That popover shows the literal characters. No bold text, no script. Escaping happens in the model classes, so it is identical in all three modes.

Trusted HTML

When markup is the point — a <strong>, a <code>, a line break — ux_driver_html() marks a string as trusted and skips escaping:

Component

<twig:Driver:Step :title="ux_driver_html('<strong>Billing</strong>')" />

Twig builder

{% set tour = create_tour('billing-tour')
    .addStep('.billing', ux_driver_html('<strong>Billing</strong>')) %}

PHP builder

use Twig\Markup;

$tour = $this->tourBuilder->create('billing-tour')
    ->addStep('.billing', new Markup('<strong>Billing</strong>', 'UTF-8'));

ux_driver_html() is only the Twig door to the same thing: any Twig\Markup instance skips escaping.

Sanitizing rich text

If popover content genuinely has to come from user-authored rich text, sanitize it first with symfony/html-sanitizer and mark the result trusted:

use Symfony\Component\HtmlSanitizer\HtmlSanitizerInterface;
use Twig\Markup;

public function __construct(private readonly HtmlSanitizerInterface $htmlSanitizer)
{
}

public function step(string $userHtml): Markup
{
    return new Markup($this->htmlSanitizer->sanitize($userHtml), 'UTF-8');
}

Configure the sanitizer’s allowed elements to the smallest set the popover needs — inline formatting is usually enough, and a popover has no business rendering <iframe> or <form>. The policy is the application’s to choose; the bundle only decides whether to escape.