{{ $value }} - виводить значення з екрануванням HTML (через htmlspecialchars). Символи <, >, ", ', & стають сутностями, і рядок <script> показується як текст.
<p>{{ $comment->body }}</p> {{-- безпечно --}}
{!! $value !!} - виводить як є, без екранування:
<div>{!! $comment->body !!}</div> {{-- XSS, якщо body від користувача --}}
Доречно лише для HTML, який згенеровано вашим кодом чи пройшов санітизацію (наприклад, Markdown, перетворений у HTML з білим списком тегів).
Де екранування {{ }} не допомагає:
1. Атрибути з URL:
<a href="{{ $user->website }}">Сайт</a>
Значення екрановане, але javascript:alert(1) - валідний URL без жодного спецсимволу HTML. Посилання з даних користувача треба перевіряти на схему (http/https).
2. Всередині <script>:
<script>
const name = '{{ $user->name }}'; {{-- ламається на лапках і переносах рядків --}}
</script>
Екранування для HTML не те саме, що для JavaScript. Для передачі даних у скрипт - Js::from() чи @js:
<script>
const user = {{ Js::from($user->only('id', 'name')) }};
</script>
<div x-data="{ settings: @js($settings) }">
Js::from кодує значення в JSON з екрануванням, безпечним для вставки в HTML.
3. Атрибути подій і стилі: onclick="{{ ... }}", style="{{ ... }}" - контексти JavaScript і CSS. Дані користувача туди не вставляти.
4. Компоненти, що самі вставляють HTML: {!! $slot !!} у власних компонентах, ->toHtml(), HtmlString - обходять екранування.
Подвійне кодування: {{ }} за замовчуванням не кодує вже закодовані сутності двічі (& лишиться &); Blade::withoutDoubleEncoding() керує цим.
Правило для рев'ю коду: кожен {!! !!} має бути обґрунтований - звідки береться HTML і хто гарантує, що він безпечний.