Экранирует место вывода — фронт; бэк хранит имя блокнота и ячейки как есть и отдаёт JSON.
CSP и nosniff в Caddy
index.html отдаёт Caddy (Caddyfile.j2), поэтому заголовки ставятся там, а не в Go API:
Content-Security-Policyна ответе сindex.html. Правило зависит от того, что грузит фронт: чанки — с CDN бакета релизов напрямую, картинки и шрифты — сstatic.cellestial.ru, API — свой домен; Monaco, скорее всего, потребуетworker-srcи послаблений вstyle-src.X-Content-Type-Options: nosniffна всех ответах.
Сначала Content-Security-Policy-Report-Only: браузер пишет нарушения в консоль и ничего не блокирует. Нарушений на стенде не осталось — переключить на Content-Security-Policy.
Где выводится чужой текст
Файл загружает пользователь, поэтому его содержимое и результат исполнения — непроверенный текст. Что редактор только для чтения, ничего не меняет: XSS — о том, как текст попадает на экран, а не о том, кто его правит.
- Свой редактор (Monaco editor). Во втором модуле только просмотр: код выводится текстом. Подсветка синтаксиса (следующий модуль) делит код на токены, и каждый токен выводится текстовым узлом. Склеивать
<span class="keyword">с кодом в строку и вставлять черезinnerHTMLнельзя: строка"<img src=x onerror=alert(1)>"в Python-файле выполнится. - Вывод исполнения (Исполнение файлов). stdout и stderr — текстом. Python и R умеют отдавать HTML-таблицы и SVG-графики: такой вывод — только в
<iframe sandbox>или картинкой через<img>(в<img>скрипты не выполняются), в страницу не вставлять. - Поиск по файлу (Поиск по файлу). Совпадения подсвечиваются
<mark>, но текст вокруг — текстовыми узлами, без склейки HTML-строк.
Рендера markdown в HTML во втором модуле нет; появится — только через санитайзер HTML.
Насколько это опасно сейчас
Пользователь видит только свои файлы: внедрённый скрипт выполнится только у автора файла, то есть атака на себя, риск низкий. Защиту закладываем сразу: как только появится общий доступ к блокнотам, риск станет настоящим, а переделывать вывод в трёх местах дороже.