From 14958be302ebbf16fc324cc0bdb5546e4af4c110 Mon Sep 17 00:00:00 2001 From: IK Date: Sun, 13 Sep 2026 15:14:21 +0700 Subject: [PATCH] security: externalize astro scripts and styles --- README.md | 4 ++-- apps/web/astro.config.mjs | 5 +++++ apps/web/src/pages/report.astro | 23 ++++++++++++++--------- apps/web/src/pages/spots/[id].astro | 2 +- deploy/Caddyfile | 2 +- docs/ROADMAP.md | 2 +- docs/csp-inventory.md | 13 +++++-------- 7 files changed, 29 insertions(+), 22 deletions(-) diff --git a/README.md b/README.md index b29048b..46208a4 100644 --- a/README.md +++ b/README.md @@ -46,7 +46,7 @@ RF4DB/RF4-STAT/RF4MAP/RF4 Posts сначала принимаются в изо `python -m rf4_research.media_cli --coverage` сравнивает manifest с датированным `data/media/catalog-baseline.json`: отдельно считает файлы, уникальные нормализованные подписи и кандидатов без подписи, поэтому дубли и общие учебные схемы не завышают покрытие. Сейчас не покрыты минимум 24 рыбы и все 19 водоёмов, а до ручного review не подтверждены 251 рыба и все 19 водоёмов. Общий target снастей остаётся `null`, пока разрешённый источник не отдаст проверяемый полный счётчик. -Актуальный внешний ориентир — 19 водоёмов и 252 вида рыб; локальная альфа пока содержит 2+2 сущности. Media-manifest включает 452 кандидата: 228 изображений рыб, 149 приманок и 75 справочных изображений; подтверждённых entity-карт водоёмов пока нет. Вручную проверены и сопоставлены 1 рыба, 2 приманки и 1 официальная схема; 446 файлов остаются в очереди, 1 URL признан невалидным. Полное число «снастей» пока не заявляется: приманки — лишь одна часть каталога наряду с удилищами, катушками, лесками, крючками и оснастками. +Актуальный внешний ориентир — 19 водоёмов и 252 вида рыб; локальная альфа пока содержит 2+2 сущности. Media-manifest включает 452 кандидата: 228 изображений рыб, 149 приманок и 75 справочных изображений; подтверждённых entity-карт водоёмов пока нет. Вручную проверены 20 ассетов: 1 рыба, 9 приманок/наживок и 10 справочных схем; 425 записей остаются в очереди, 1 файл ожидает ревью, 6 URL признаны невалидными. Полное число «снастей» пока не заявляется: приманки — лишь одна часть каталога наряду с удилищами, катушками, лесками, крючками и оснастками. Для измерений на собственном сервере подготовлен read-only `deploy/load-smoke.py`: он считает p50/p95/max и HTTP-коды для activity/records, а при наличии `ADMIN_TOKEN` — staging/moderation. Методика и безопасные ступени нагрузки описаны в [docs/load-testing.md](docs/load-testing.md); локальные цифры не выдаются за production baseline. @@ -91,7 +91,7 @@ FastAPI ─ PostgreSQL 17 Наружу production-профиль публикует только Caddy. PostgreSQL, API, Astro и MinIO находятся в Docker-сетях. Caddy завершает TLS и защищает административные страницы Basic Auth; административный API отдельно проверяет Bearer-токен в FastAPI. Basic не накладывается на API-запросы. -Production CSP ограничивает browser-запросы текущим доменом и отдельным files-доменом для изображений, запрещает plugins, frames, inline handlers, eval, wildcard и HTTP. Оставшиеся inline script/style зависимости и путь к nonce/hash перечислены в [CSP inventory](docs/csp-inventory.md). +Production CSP ограничивает browser-запросы текущим доменом и отдельным files-доменом для изображений, запрещает plugins, frames, inline handlers, eval, wildcard и HTTP. Page scripts и scoped styles принудительно выпускаются отдельными same-origin `_astro`-ассетами, поэтому `style-src` больше не допускает inline style elements; оставшиеся исключения и путь к nonce/hash перечислены в [CSP inventory](docs/csp-inventory.md). Gitea Actions workflow `.gitea/workflows/ci.yml` на каждый push и pull request проверяет Python, миграции на чистой PostgreSQL, Astro build и полный Compose/Playwright-сценарий. При падении E2E сохраняются логи контейнеров и Playwright-артефакты. diff --git a/apps/web/astro.config.mjs b/apps/web/astro.config.mjs index 1453311..1d0e886 100644 --- a/apps/web/astro.config.mjs +++ b/apps/web/astro.config.mjs @@ -7,4 +7,9 @@ export default defineConfig({ output: "server", adapter: node({ mode: "standalone" }), server: { host: true, port: 4321 }, + vite: { + // Keep executable scripts and compiled component styles in same-origin + // assets so production CSP does not need broad inline allowances. + build: { assetsInlineLimit: 0 }, + }, }); diff --git a/apps/web/src/pages/report.astro b/apps/web/src/pages/report.astro index 4a55a8a..19ab4fd 100644 --- a/apps/web/src/pages/report.astro +++ b/apps/web/src/pages/report.astro @@ -7,6 +7,7 @@ const state = Astro.url.searchParams.get("state"); const reportId = Astro.url.searchParams.get("report_id"); --- +
+1 Помочь сообществу

Добавить
свой улов

Около минуты — и рабочая точка появится в общей статистике после проверки модератором.

{state === "sent" &&
Улов отправлен на модерацию. Спасибо!
} {state === "screenshot_sent" &&
Скриншот добавлен к ранее созданной заявке.
} @@ -20,13 +21,15 @@ const reportId = Astro.url.searchParams.get("report_id");
Главное обязательно

Координаты — два целых числа с карты. Вес: 1,25 кг = 1250 г.

Дополнительные сведения необязательно

Ник и скриншот необязательны. Из изображения удаляются EXIF и прочие метаданные.

} -