security: externalize astro scripts and styles

This commit is contained in:
ik
2026-09-13 15:14:21 +07:00
parent cd319a32c2
commit 14958be302
7 changed files with 29 additions and 22 deletions
+1 -1
View File
@@ -54,7 +54,7 @@
- [x] **Q11 · Декомпозиция API.** Catalog, activity/spots, records/community/status/import-history, submissions и весь admin API вынесены в отдельные `APIRouter`. `main.py` оставляет composition root, middleware, health/readiness и временные совместимые экспорты rate-limit для тестового контракта; URL и OpenAPI сохранены.
- [ ] **Q12 · Query-plan gate.** В рамках Q07 снять `EXPLAIN (ANALYZE, BUFFERS)` для activity, records, spot detail и public spot pages на реалистичном наборе данных. Существующие индексы миграции `0011_query_indexes` не дублировать; индекс с `fish_id`, SQL-агрегацию или materialized view добавлять только по измеренному плану и p95.
- [x] **Q13 · Production bootstrap в CI.** Отдельный workflow запускает `deploy/test-production-bootstrap.sh` вручную или раз в неделю, а не на каждом push. Вывод bootstrap всегда сохраняется 14 дней; при падении добавляются Compose status и Playwright diagnostics.
- [ ] **Q14 · Полная CSP — origin-policy внедрена.** Production ограничивает default/connect/form/font/media/manifest текущим доменом, изображения — self/data/`FILES_DOMAIN`, запрещает inline event handlers, eval, wildcard и HTTP. Инвентаризация зафиксировала динамический JSON-LD, page scripts, scoped styles и CSS variables; из-за них `unsafe-inline` временно остаётся только для script/style элементов и style attributes. Далее вынести page scripts, решить nonce/hash JSON-LD и убрать исключения поэтапно с bootstrap-проверкой report/admin/OG/screenshots.
- [ ] **Q14 · Полная CSP — в работе.** Production ограничивает default/connect/form/font/media/manifest текущим доменом, изображения — self/data/`FILES_DOMAIN`, запрещает inline event handlers, eval, wildcard и HTTP. Page scripts переведены в Astro client modules, конфигурация формы передаётся через `data-*`, а `assetsInlineLimit: 0` гарантирует внешние `_astro`-ассеты для малых scripts/scoped styles. `unsafe-inline` удалён из `style-src`; остаются динамический JSON-LD в `script-src` и два вычисляемых CSS variable в `style-src-attr`. Далее внедрить nonce/hash и убрать оба исключения с bootstrap-проверкой report/admin/OG/screenshots.
- [x] **Q15 · Частичная деградация главной.** SSR независимо получает activity, community signals и оба справочника через settled-результаты. Отказ секции показывает собственный `StatePanel`, сохраняет остальные данные и HTTP 200 с `X-RF4-Partial`/`Cache-Control: no-store`; только отказ всех четырёх частей возвращает 503, `Retry-After` и noindex. Client-side loading и optimistic UI не добавлялись.
- [x] **Q16 · Контракт OpenAPI.** `apps/api/openapi.json` детерминированно генерируется из FastAPI; CI проверяет его актуальность после backend suite. Изменение artifact обязательно рассматривается вместе с реализацией, а ручное редактирование не используется.
- [x] **Q17 · Эксплуатационные документы.** Зафиксированы ADR по Astro/FastAPI/PostgreSQL, локальному cache, scheduler/cooldown и разделению PostgreSQL/MinIO. Incident runbook покрывает заполнение диска, отказ PostgreSQL/MinIO, зависшие импорты, ошибки миграций, компрометацию секретов и критерии закрытия без опасных reset/recreate операций.
+5 -8
View File
@@ -4,18 +4,15 @@
## Временные inline-зависимости
- динамический JSON-LD в `Layout.astro`;
- inline bootstrap в `report.astro` и `spots/[id].astro`;
- page scripts главной и трёх admin-экранов;
- scoped `<style>` Astro-компонентов;
- динамический JSON-LD в `Layout.astro` остаётся единственным намеренно inline script;
- вычисляемые CSS custom properties для шкалы активности и timeline.
Текущая CSP поэтому временно допускает `unsafe-inline` отдельно для script/style elements и style attributes. Inline event handlers запрещены `script-src-attr 'none'`; `unsafe-eval`, wildcard, HTTP и сторонние connect/script origin отсутствуют.
Vite собирает даже малые page scripts и scoped Astro styles в same-origin `_astro` assets (`assetsInlineLimit: 0`). Поэтому `style-src` уже ограничен `'self'`; временный `unsafe-inline` остаётся только для динамического JSON-LD в `script-src` и вычисляемых style attributes. Inline event handlers запрещены `script-src-attr 'none'`; `unsafe-eval`, wildcard, HTTP и сторонние connect/script origin отсутствуют.
## Путь к nonce/hash
1. Перенести page scripts в импортируемые клиентские модули, конфигурацию передавать через безопасные `data-*`.
1. Page scripts перенесены в собираемые клиентские модули; конфигурация формы передаётся через безопасный `data-state`. Нулевой inline limit запрещает Vite встраивать малые модули обратно в HTML.
2. JSON-LD либо хэшировать на уровне SSR-заголовка, либо выдавать nonce из web-runtime; статический nonce запрещён.
3. Оставить scoped styles в скомпилированных `_astro` assets; вычисляемые значения заменить классами/`data-*` или nonce style.
4. Удалить `unsafe-inline` из `script-src`, затем из `style-src`; `style-src-attr` убрать последним.
3. Scoped styles уже находятся в скомпилированных `_astro` assets, и `unsafe-inline` удалён из `style-src`; вычисляемые значения заменить классами/`data-*`.
4. Удалить `unsafe-inline` из `script-src` после решения JSON-LD; `style-src-attr` убрать последним.
5. Проверить report, spots, admin, JSON-LD и signed screenshots в production bootstrap. Не добавлять `blob:`, `*` или произвольные CDN для устранения ошибок.